From 064db996bdfbea1077881b95c43ee7bc74cf3351 Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 22 Dec 2016 16:59:01 +0800 Subject: NetWorkPkg: Move to new location Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- Core/NetworkPkg/Application/IfConfig6/IfConfig6.c | 1776 +++++++++++ Core/NetworkPkg/Application/IfConfig6/IfConfig6.h | 79 + .../NetworkPkg/Application/IfConfig6/IfConfig6.inf | 59 + .../NetworkPkg/Application/IfConfig6/IfConfig6.uni | Bin 0 -> 1884 bytes .../Application/IfConfig6/IfConfig6Extra.uni | Bin 0 -> 1328 bytes .../Application/IfConfig6/IfConfig6Strings.uni | Bin 0 -> 15680 bytes Core/NetworkPkg/Application/IpsecConfig/Delete.c | 110 + Core/NetworkPkg/Application/IpsecConfig/Delete.h | 42 + Core/NetworkPkg/Application/IpsecConfig/Dump.c | 579 ++++ Core/NetworkPkg/Application/IpsecConfig/Dump.h | 34 + Core/NetworkPkg/Application/IpsecConfig/ForEach.c | 115 + Core/NetworkPkg/Application/IpsecConfig/ForEach.h | 54 + Core/NetworkPkg/Application/IpsecConfig/Helper.c | 420 +++ Core/NetworkPkg/Application/IpsecConfig/Helper.h | 143 + Core/NetworkPkg/Application/IpsecConfig/Indexer.c | 255 ++ Core/NetworkPkg/Application/IpsecConfig/Indexer.h | 58 + .../Application/IpsecConfig/IpSecConfig.c | 810 +++++ .../Application/IpsecConfig/IpSecConfig.h | 150 + .../Application/IpsecConfig/IpSecConfig.inf | 68 + .../Application/IpsecConfig/IpSecConfig.uni | Bin 0 -> 1972 bytes .../Application/IpsecConfig/IpSecConfigExtra.uni | Bin 0 -> 1338 bytes .../Application/IpsecConfig/IpSecConfigStrings.uni | Bin 0 -> 24240 bytes Core/NetworkPkg/Application/IpsecConfig/Match.c | 163 + Core/NetworkPkg/Application/IpsecConfig/Match.h | 41 + .../Application/IpsecConfig/PolicyEntryOperation.c | 2081 ++++++++++++ .../Application/IpsecConfig/PolicyEntryOperation.h | 159 + Core/NetworkPkg/Application/Ping6/Ia32/Tsc.c | 28 + Core/NetworkPkg/Application/Ping6/Ipf/Itc.c | 28 + Core/NetworkPkg/Application/Ping6/Ping6.c | 1178 +++++++ Core/NetworkPkg/Application/Ping6/Ping6.h | 87 + Core/NetworkPkg/Application/Ping6/Ping6.inf | 70 + Core/NetworkPkg/Application/Ping6/Ping6.uni | Bin 0 -> 1788 bytes Core/NetworkPkg/Application/Ping6/Ping6Extra.uni | Bin 0 -> 1312 bytes Core/NetworkPkg/Application/Ping6/Ping6Strings.uni | Bin 0 -> 7484 bytes Core/NetworkPkg/Application/Ping6/X64/Tsc.c | 28 + Core/NetworkPkg/Application/VConfig/VConfig.c | 668 ++++ Core/NetworkPkg/Application/VConfig/VConfig.inf | 53 + Core/NetworkPkg/Application/VConfig/VConfig.uni | Bin 0 -> 1804 bytes .../Application/VConfig/VConfigExtra.uni | Bin 0 -> 1328 bytes .../Application/VConfig/VConfigStrings.uni | Bin 0 -> 8624 bytes Core/NetworkPkg/Contributions.txt | 218 ++ Core/NetworkPkg/Dhcp6Dxe/ComponentName.c | 441 +++ Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c | 819 +++++ Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h | 156 + Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf | 82 + Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.uni | Bin 0 -> 2016 bytes Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni | Bin 0 -> 1318 bytes Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c | 1216 +++++++ Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h | 605 ++++ Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c | 3170 ++++++++++++++++++ Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h | 227 ++ Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c | 1330 ++++++++ Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h | 360 +++ Core/NetworkPkg/DnsDxe/ComponentName.c | 443 +++ Core/NetworkPkg/DnsDxe/DnsDhcp.c | 920 ++++++ Core/NetworkPkg/DnsDxe/DnsDhcp.h | 145 + Core/NetworkPkg/DnsDxe/DnsDriver.c | 1554 +++++++++ Core/NetworkPkg/DnsDxe/DnsDriver.h | 606 ++++ Core/NetworkPkg/DnsDxe/DnsDxe.inf | 78 + Core/NetworkPkg/DnsDxe/DnsDxe.uni | Bin 0 -> 1960 bytes Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni | Bin 0 -> 1306 bytes Core/NetworkPkg/DnsDxe/DnsImpl.c | 2077 ++++++++++++ Core/NetworkPkg/DnsDxe/DnsImpl.h | 1161 +++++++ Core/NetworkPkg/DnsDxe/DnsProtocol.c | 1591 +++++++++ Core/NetworkPkg/HttpBootDxe/HttpBootClient.c | 1077 +++++++ Core/NetworkPkg/HttpBootDxe/HttpBootClient.h | 139 + .../NetworkPkg/HttpBootDxe/HttpBootComponentName.c | 183 ++ .../NetworkPkg/HttpBootDxe/HttpBootComponentName.h | 99 + Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c | 829 +++++ Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h | 292 ++ Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c | 984 ++++++ Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h | 198 ++ Core/NetworkPkg/HttpBootDxe/HttpBootDxe.c | 1168 +++++++ Core/NetworkPkg/HttpBootDxe/HttpBootDxe.h | 450 +++ Core/NetworkPkg/HttpBootDxe/HttpBootDxe.inf | 77 + Core/NetworkPkg/HttpBootDxe/HttpBootDxe.uni | Bin 0 -> 2040 bytes Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni | Bin 0 -> 1328 bytes Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c | 435 +++ Core/NetworkPkg/HttpBootDxe/HttpBootImpl.h | 50 + Core/NetworkPkg/HttpBootDxe/HttpBootSupport.c | 987 ++++++ Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h | 320 ++ Core/NetworkPkg/HttpDxe/ComponentName.c | 138 + Core/NetworkPkg/HttpDxe/ComponentName.h | 98 + Core/NetworkPkg/HttpDxe/HttpDns.c | 415 +++ Core/NetworkPkg/HttpDxe/HttpDns.h | 58 + Core/NetworkPkg/HttpDxe/HttpDriver.c | 1065 +++++++ Core/NetworkPkg/HttpDxe/HttpDriver.h | 397 +++ Core/NetworkPkg/HttpDxe/HttpDxe.inf | 69 + Core/NetworkPkg/HttpDxe/HttpDxe.uni | Bin 0 -> 1854 bytes Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni | Bin 0 -> 1310 bytes Core/NetworkPkg/HttpDxe/HttpImpl.c | 1329 ++++++++ Core/NetworkPkg/HttpDxe/HttpImpl.h | 240 ++ Core/NetworkPkg/HttpDxe/HttpProto.c | 2098 ++++++++++++ Core/NetworkPkg/HttpDxe/HttpProto.h | 590 ++++ .../NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.c | 126 + .../NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.h | 212 ++ .../HttpUtilitiesDxe/HttpUtilitiesDxe.inf | 51 + .../HttpUtilitiesDxe/HttpUtilitiesDxe.uni | Bin 0 -> 1700 bytes .../HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni | Bin 0 -> 1348 bytes .../HttpUtilitiesDxe/HttpUtilitiesImpl.c | 279 ++ .../HttpUtilitiesDxe/HttpUtilitiesProtocol.c | 393 +++ Core/NetworkPkg/IScsiDxe/ComponentName.c | 344 ++ Core/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c | 67 + Core/NetworkPkg/IScsiDxe/IScsiCHAP.c | 477 +++ Core/NetworkPkg/IScsiDxe/IScsiCHAP.h | 108 + Core/NetworkPkg/IScsiDxe/IScsiConfig.c | 2661 ++++++++++++++++ Core/NetworkPkg/IScsiDxe/IScsiConfig.h | 157 + Core/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h | 185 ++ Core/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni | Bin 0 -> 12526 bytes Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr | 361 +++ Core/NetworkPkg/IScsiDxe/IScsiDhcp.c | 498 +++ Core/NetworkPkg/IScsiDxe/IScsiDhcp.h | 62 + Core/NetworkPkg/IScsiDxe/IScsiDhcp6.c | 525 +++ Core/NetworkPkg/IScsiDxe/IScsiDhcp6.h | 80 + Core/NetworkPkg/IScsiDxe/IScsiDriver.c | 1647 ++++++++++ Core/NetworkPkg/IScsiDxe/IScsiDriver.h | 809 +++++ Core/NetworkPkg/IScsiDxe/IScsiDxe.inf | 128 + Core/NetworkPkg/IScsiDxe/IScsiDxe.uni | Bin 0 -> 1876 bytes Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni | Bin 0 -> 1316 bytes Core/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c | 425 +++ Core/NetworkPkg/IScsiDxe/IScsiIbft.c | 546 ++++ Core/NetworkPkg/IScsiDxe/IScsiIbft.h | 39 + Core/NetworkPkg/IScsiDxe/IScsiImpl.h | 198 ++ Core/NetworkPkg/IScsiDxe/IScsiInitiatorName.c | 136 + Core/NetworkPkg/IScsiDxe/IScsiMisc.c | 1547 +++++++++ Core/NetworkPkg/IScsiDxe/IScsiMisc.h | 406 +++ Core/NetworkPkg/IScsiDxe/IScsiProto.c | 3133 ++++++++++++++++++ Core/NetworkPkg/IScsiDxe/IScsiProto.h | 1036 ++++++ Core/NetworkPkg/Include/Guid/IScsiConfigHii.h | 26 + Core/NetworkPkg/Include/Guid/Ip6ConfigHii.h | 25 + Core/NetworkPkg/Ip6Dxe/ComponentName.c | 448 +++ Core/NetworkPkg/Ip6Dxe/Ip6Common.c | 673 ++++ Core/NetworkPkg/Ip6Dxe/Ip6Common.h | 318 ++ Core/NetworkPkg/Ip6Dxe/Ip6Config.vfr | 178 ++ Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c | 2388 ++++++++++++++ Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h | 295 ++ Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c | 2095 ++++++++++++ Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h | 68 + Core/NetworkPkg/Ip6Dxe/Ip6Driver.c | 1000 ++++++ Core/NetworkPkg/Ip6Dxe/Ip6Driver.h | 192 ++ Core/NetworkPkg/Ip6Dxe/Ip6Dxe.inf | 116 + Core/NetworkPkg/Ip6Dxe/Ip6Dxe.uni | Bin 0 -> 2912 bytes Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni | Bin 0 -> 1310 bytes Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni | Bin 0 -> 10244 bytes Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c | 684 ++++ Core/NetworkPkg/Ip6Dxe/Ip6Icmp.h | 108 + Core/NetworkPkg/Ip6Dxe/Ip6If.c | 798 +++++ Core/NetworkPkg/Ip6Dxe/Ip6If.h | 267 ++ Core/NetworkPkg/Ip6Dxe/Ip6Impl.c | 1847 +++++++++++ Core/NetworkPkg/Ip6Dxe/Ip6Impl.h | 752 +++++ Core/NetworkPkg/Ip6Dxe/Ip6Input.c | 1831 +++++++++++ Core/NetworkPkg/Ip6Dxe/Ip6Input.h | 235 ++ Core/NetworkPkg/Ip6Dxe/Ip6Mld.c | 908 ++++++ Core/NetworkPkg/Ip6Dxe/Ip6Mld.h | 198 ++ Core/NetworkPkg/Ip6Dxe/Ip6Nd.c | 3155 ++++++++++++++++++ Core/NetworkPkg/Ip6Dxe/Ip6Nd.h | 749 +++++ Core/NetworkPkg/Ip6Dxe/Ip6NvData.h | 69 + Core/NetworkPkg/Ip6Dxe/Ip6Option.c | 758 +++++ Core/NetworkPkg/Ip6Dxe/Ip6Option.h | 191 ++ Core/NetworkPkg/Ip6Dxe/Ip6Output.c | 1091 +++++++ Core/NetworkPkg/Ip6Dxe/Ip6Output.h | 141 + Core/NetworkPkg/Ip6Dxe/Ip6Route.c | 635 ++++ Core/NetworkPkg/Ip6Dxe/Ip6Route.h | 299 ++ Core/NetworkPkg/IpSecDxe/ComponentName.c | 351 ++ Core/NetworkPkg/IpSecDxe/IetfConstants.c | 388 +++ Core/NetworkPkg/IpSecDxe/Ike.h | 266 ++ Core/NetworkPkg/IpSecDxe/IkeCommon.c | 254 ++ Core/NetworkPkg/IpSecDxe/IkeCommon.h | 189 ++ Core/NetworkPkg/IpSecDxe/IkePacket.c | 265 ++ Core/NetworkPkg/IpSecDxe/IkePacket.h | 82 + Core/NetworkPkg/IpSecDxe/IkeService.c | 794 +++++ Core/NetworkPkg/IpSecDxe/IkeService.h | 262 ++ Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c | 199 ++ Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c | 809 +++++ Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h | 258 ++ Core/NetworkPkg/IpSecDxe/Ikev2/Info.c | 402 +++ Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c | 3369 ++++++++++++++++++++ Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h | 438 +++ Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c | 2262 +++++++++++++ Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c | 2787 ++++++++++++++++ Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h | 1134 +++++++ Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.c | 3138 ++++++++++++++++++ Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.h | 955 ++++++ Core/NetworkPkg/IpSecDxe/IpSecCryptIo.c | 1021 ++++++ Core/NetworkPkg/IpSecDxe/IpSecCryptIo.h | 827 +++++ Core/NetworkPkg/IpSecDxe/IpSecDebug.c | 334 ++ Core/NetworkPkg/IpSecDxe/IpSecDebug.h | 107 + Core/NetworkPkg/IpSecDxe/IpSecDriver.c | 665 ++++ Core/NetworkPkg/IpSecDxe/IpSecDxe.inf | 110 + Core/NetworkPkg/IpSecDxe/IpSecDxe.uni | Bin 0 -> 2676 bytes Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni | Bin 0 -> 1318 bytes Core/NetworkPkg/IpSecDxe/IpSecImpl.c | 2184 +++++++++++++ Core/NetworkPkg/IpSecDxe/IpSecImpl.h | 390 +++ Core/NetworkPkg/IpSecDxe/IpSecMain.c | 242 ++ Core/NetworkPkg/License.txt | 25 + Core/NetworkPkg/Mtftp6Dxe/ComponentName.c | 430 +++ Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c | 755 +++++ Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h | 152 + Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf | 76 + Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni | Bin 0 -> 1956 bytes Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni | Bin 0 -> 1318 bytes Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c | 647 ++++ Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h | 475 +++ Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c | 416 +++ Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h | 148 + Core/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c | 921 ++++++ Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c | 1213 +++++++ Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h | 359 +++ Core/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c | 604 ++++ Core/NetworkPkg/NetworkPkg.dec | 82 + Core/NetworkPkg/NetworkPkg.dsc | 117 + Core/NetworkPkg/NetworkPkg.uni | Bin 0 -> 7964 bytes Core/NetworkPkg/NetworkPkgExtra.uni | Bin 0 -> 1342 bytes Core/NetworkPkg/TcpDxe/ComponentName.c | 528 +++ Core/NetworkPkg/TcpDxe/SockImpl.c | 1230 +++++++ Core/NetworkPkg/TcpDxe/SockImpl.h | 103 + Core/NetworkPkg/TcpDxe/SockInterface.c | 999 ++++++ Core/NetworkPkg/TcpDxe/Socket.h | 924 ++++++ Core/NetworkPkg/TcpDxe/TcpDispatcher.c | 913 ++++++ Core/NetworkPkg/TcpDxe/TcpDriver.c | 1006 ++++++ Core/NetworkPkg/TcpDxe/TcpDriver.h | 296 ++ Core/NetworkPkg/TcpDxe/TcpDxe.inf | 93 + Core/NetworkPkg/TcpDxe/TcpDxe.uni | Bin 0 -> 2412 bytes Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni | Bin 0 -> 1310 bytes Core/NetworkPkg/TcpDxe/TcpFunc.h | 699 ++++ Core/NetworkPkg/TcpDxe/TcpInput.c | 1608 ++++++++++ Core/NetworkPkg/TcpDxe/TcpIo.c | 192 ++ Core/NetworkPkg/TcpDxe/TcpMain.c | 1075 +++++++ Core/NetworkPkg/TcpDxe/TcpMain.h | 766 +++++ Core/NetworkPkg/TcpDxe/TcpMisc.c | 1023 ++++++ Core/NetworkPkg/TcpDxe/TcpOption.c | 374 +++ Core/NetworkPkg/TcpDxe/TcpOption.h | 145 + Core/NetworkPkg/TcpDxe/TcpOutput.c | 1219 +++++++ Core/NetworkPkg/TcpDxe/TcpProto.h | 344 ++ Core/NetworkPkg/TcpDxe/TcpTimer.c | 593 ++++ Core/NetworkPkg/Udp6Dxe/ComponentName.c | 429 +++ Core/NetworkPkg/Udp6Dxe/Udp6Driver.c | 623 ++++ Core/NetworkPkg/Udp6Dxe/Udp6Driver.h | 182 ++ Core/NetworkPkg/Udp6Dxe/Udp6Dxe.inf | 69 + Core/NetworkPkg/Udp6Dxe/Udp6Dxe.uni | Bin 0 -> 2024 bytes Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni | Bin 0 -> 1314 bytes Core/NetworkPkg/Udp6Dxe/Udp6Impl.c | 1965 ++++++++++++ Core/NetworkPkg/Udp6Dxe/Udp6Impl.h | 654 ++++ Core/NetworkPkg/Udp6Dxe/Udp6Main.c | 853 +++++ Core/NetworkPkg/UefiPxeBcDxe/ComponentName.c | 358 +++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c | 1259 ++++++++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h | 100 + Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c | 1672 ++++++++++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h | 409 +++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c | 2096 ++++++++++++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h | 303 ++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c | 1825 +++++++++++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h | 181 ++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c | 2411 ++++++++++++++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h | 225 ++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c | 1113 +++++++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h | 137 + Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c | 1513 +++++++++ Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h | 515 +++ Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf | 109 + Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni | Bin 0 -> 2416 bytes Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni | Bin 0 -> 1332 bytes 262 files changed, 143527 insertions(+) create mode 100644 Core/NetworkPkg/Application/IfConfig6/IfConfig6.c create mode 100644 Core/NetworkPkg/Application/IfConfig6/IfConfig6.h create mode 100644 Core/NetworkPkg/Application/IfConfig6/IfConfig6.inf create mode 100644 Core/NetworkPkg/Application/IfConfig6/IfConfig6.uni create mode 100644 Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni create mode 100644 Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Delete.c create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Delete.h create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Dump.c create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Dump.h create mode 100644 Core/NetworkPkg/Application/IpsecConfig/ForEach.c create mode 100644 Core/NetworkPkg/Application/IpsecConfig/ForEach.h create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Helper.c create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Helper.h create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Indexer.c create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Indexer.h create mode 100644 Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.c create mode 100644 Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.h create mode 100644 Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf create mode 100644 Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.uni create mode 100644 Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni create mode 100644 Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Match.c create mode 100644 Core/NetworkPkg/Application/IpsecConfig/Match.h create mode 100644 Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c create mode 100644 Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h create mode 100644 Core/NetworkPkg/Application/Ping6/Ia32/Tsc.c create mode 100644 Core/NetworkPkg/Application/Ping6/Ipf/Itc.c create mode 100644 Core/NetworkPkg/Application/Ping6/Ping6.c create mode 100644 Core/NetworkPkg/Application/Ping6/Ping6.h create mode 100644 Core/NetworkPkg/Application/Ping6/Ping6.inf create mode 100644 Core/NetworkPkg/Application/Ping6/Ping6.uni create mode 100644 Core/NetworkPkg/Application/Ping6/Ping6Extra.uni create mode 100644 Core/NetworkPkg/Application/Ping6/Ping6Strings.uni create mode 100644 Core/NetworkPkg/Application/Ping6/X64/Tsc.c create mode 100644 Core/NetworkPkg/Application/VConfig/VConfig.c create mode 100644 Core/NetworkPkg/Application/VConfig/VConfig.inf create mode 100644 Core/NetworkPkg/Application/VConfig/VConfig.uni create mode 100644 Core/NetworkPkg/Application/VConfig/VConfigExtra.uni create mode 100644 Core/NetworkPkg/Application/VConfig/VConfigStrings.uni create mode 100644 Core/NetworkPkg/Contributions.txt create mode 100644 Core/NetworkPkg/Dhcp6Dxe/ComponentName.c create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.uni create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c create mode 100644 Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h create mode 100644 Core/NetworkPkg/DnsDxe/ComponentName.c create mode 100644 Core/NetworkPkg/DnsDxe/DnsDhcp.c create mode 100644 Core/NetworkPkg/DnsDxe/DnsDhcp.h create mode 100644 Core/NetworkPkg/DnsDxe/DnsDriver.c create mode 100644 Core/NetworkPkg/DnsDxe/DnsDriver.h create mode 100644 Core/NetworkPkg/DnsDxe/DnsDxe.inf create mode 100644 Core/NetworkPkg/DnsDxe/DnsDxe.uni create mode 100644 Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni create mode 100644 Core/NetworkPkg/DnsDxe/DnsImpl.c create mode 100644 Core/NetworkPkg/DnsDxe/DnsImpl.h create mode 100644 Core/NetworkPkg/DnsDxe/DnsProtocol.c create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootClient.c create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootClient.h create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.c create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.h create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootDxe.c create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootDxe.h create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootDxe.inf create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootDxe.uni create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootImpl.h create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootSupport.c create mode 100644 Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h create mode 100644 Core/NetworkPkg/HttpDxe/ComponentName.c create mode 100644 Core/NetworkPkg/HttpDxe/ComponentName.h create mode 100644 Core/NetworkPkg/HttpDxe/HttpDns.c create mode 100644 Core/NetworkPkg/HttpDxe/HttpDns.h create mode 100644 Core/NetworkPkg/HttpDxe/HttpDriver.c create mode 100644 Core/NetworkPkg/HttpDxe/HttpDriver.h create mode 100644 Core/NetworkPkg/HttpDxe/HttpDxe.inf create mode 100644 Core/NetworkPkg/HttpDxe/HttpDxe.uni create mode 100644 Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni create mode 100644 Core/NetworkPkg/HttpDxe/HttpImpl.c create mode 100644 Core/NetworkPkg/HttpDxe/HttpImpl.h create mode 100644 Core/NetworkPkg/HttpDxe/HttpProto.c create mode 100644 Core/NetworkPkg/HttpDxe/HttpProto.h create mode 100644 Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.c create mode 100644 Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.h create mode 100644 Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf create mode 100644 Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.uni create mode 100644 Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni create mode 100644 Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesImpl.c create mode 100644 Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesProtocol.c create mode 100644 Core/NetworkPkg/IScsiDxe/ComponentName.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiCHAP.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiCHAP.h create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiConfig.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiConfig.h create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiDhcp.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiDhcp.h create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiDhcp6.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiDhcp6.h create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiDriver.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiDriver.h create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiDxe.inf create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiDxe.uni create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiIbft.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiIbft.h create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiImpl.h create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiInitiatorName.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiMisc.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiMisc.h create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiProto.c create mode 100644 Core/NetworkPkg/IScsiDxe/IScsiProto.h create mode 100644 Core/NetworkPkg/Include/Guid/IScsiConfigHii.h create mode 100644 Core/NetworkPkg/Include/Guid/Ip6ConfigHii.h create mode 100644 Core/NetworkPkg/Ip6Dxe/ComponentName.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Common.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Common.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Config.vfr create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Driver.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Driver.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Dxe.inf create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Dxe.uni create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Icmp.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6If.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6If.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Impl.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Impl.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Input.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Input.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Mld.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Mld.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Nd.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Nd.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6NvData.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Option.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Option.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Output.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Output.h create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Route.c create mode 100644 Core/NetworkPkg/Ip6Dxe/Ip6Route.h create mode 100644 Core/NetworkPkg/IpSecDxe/ComponentName.c create mode 100644 Core/NetworkPkg/IpSecDxe/IetfConstants.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ike.h create mode 100644 Core/NetworkPkg/IpSecDxe/IkeCommon.c create mode 100644 Core/NetworkPkg/IpSecDxe/IkeCommon.h create mode 100644 Core/NetworkPkg/IpSecDxe/IkePacket.c create mode 100644 Core/NetworkPkg/IpSecDxe/IkePacket.h create mode 100644 Core/NetworkPkg/IpSecDxe/IkeService.c create mode 100644 Core/NetworkPkg/IpSecDxe/IkeService.h create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Info.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c create mode 100644 Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.c create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.h create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecCryptIo.c create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecCryptIo.h create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecDebug.c create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecDebug.h create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecDriver.c create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecDxe.inf create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecDxe.uni create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecImpl.c create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecImpl.h create mode 100644 Core/NetworkPkg/IpSecDxe/IpSecMain.c create mode 100644 Core/NetworkPkg/License.txt create mode 100644 Core/NetworkPkg/Mtftp6Dxe/ComponentName.c create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h create mode 100644 Core/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c create mode 100644 Core/NetworkPkg/NetworkPkg.dec create mode 100644 Core/NetworkPkg/NetworkPkg.dsc create mode 100644 Core/NetworkPkg/NetworkPkg.uni create mode 100644 Core/NetworkPkg/NetworkPkgExtra.uni create mode 100644 Core/NetworkPkg/TcpDxe/ComponentName.c create mode 100644 Core/NetworkPkg/TcpDxe/SockImpl.c create mode 100644 Core/NetworkPkg/TcpDxe/SockImpl.h create mode 100644 Core/NetworkPkg/TcpDxe/SockInterface.c create mode 100644 Core/NetworkPkg/TcpDxe/Socket.h create mode 100644 Core/NetworkPkg/TcpDxe/TcpDispatcher.c create mode 100644 Core/NetworkPkg/TcpDxe/TcpDriver.c create mode 100644 Core/NetworkPkg/TcpDxe/TcpDriver.h create mode 100644 Core/NetworkPkg/TcpDxe/TcpDxe.inf create mode 100644 Core/NetworkPkg/TcpDxe/TcpDxe.uni create mode 100644 Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni create mode 100644 Core/NetworkPkg/TcpDxe/TcpFunc.h create mode 100644 Core/NetworkPkg/TcpDxe/TcpInput.c create mode 100644 Core/NetworkPkg/TcpDxe/TcpIo.c create mode 100644 Core/NetworkPkg/TcpDxe/TcpMain.c create mode 100644 Core/NetworkPkg/TcpDxe/TcpMain.h create mode 100644 Core/NetworkPkg/TcpDxe/TcpMisc.c create mode 100644 Core/NetworkPkg/TcpDxe/TcpOption.c create mode 100644 Core/NetworkPkg/TcpDxe/TcpOption.h create mode 100644 Core/NetworkPkg/TcpDxe/TcpOutput.c create mode 100644 Core/NetworkPkg/TcpDxe/TcpProto.h create mode 100644 Core/NetworkPkg/TcpDxe/TcpTimer.c create mode 100644 Core/NetworkPkg/Udp6Dxe/ComponentName.c create mode 100644 Core/NetworkPkg/Udp6Dxe/Udp6Driver.c create mode 100644 Core/NetworkPkg/Udp6Dxe/Udp6Driver.h create mode 100644 Core/NetworkPkg/Udp6Dxe/Udp6Dxe.inf create mode 100644 Core/NetworkPkg/Udp6Dxe/Udp6Dxe.uni create mode 100644 Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni create mode 100644 Core/NetworkPkg/Udp6Dxe/Udp6Impl.c create mode 100644 Core/NetworkPkg/Udp6Dxe/Udp6Impl.h create mode 100644 Core/NetworkPkg/Udp6Dxe/Udp6Main.c create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/ComponentName.c create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni create mode 100644 Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni (limited to 'Core') diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6.c b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.c new file mode 100644 index 0000000000..8d464b8e61 --- /dev/null +++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.c @@ -0,0 +1,1776 @@ +/** @file + The implementation for Shell application IfConfig6. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "IfConfig6.h" + +EFI_HII_HANDLE mHiiHandle; + +SHELL_PARAM_ITEM mIfConfig6CheckList[] = { + { + L"-b", + TypeFlag + }, + { + L"-s", + TypeMaxValue + }, + { + L"-l", + TypeValue + }, + { + L"-r", + TypeValue + }, + { + L"-?", + TypeFlag + }, + { + NULL, + TypeMax + }, +}; + +VAR_CHECK_ITEM mSetCheckList[] = { + { + L"auto", + 0x00000001, + 0x00000001, + FlagTypeSingle + }, + { + L"man", + 0x00000002, + 0x00000001, + FlagTypeSingle + }, + { + L"host", + 0x00000004, + 0x00000002, + FlagTypeSingle + }, + { + L"dad", + 0x00000008, + 0x00000004, + FlagTypeSingle + }, + { + L"gw", + 0x00000010, + 0x00000008, + FlagTypeSingle + }, + { + L"dns", + 0x00000020, + 0x00000010, + FlagTypeSingle + }, + { + L"id", + 0x00000040, + 0x00000020, + FlagTypeSingle + }, + { + NULL, + 0x0, + 0x0, + FlagTypeSkipUnknown + }, +}; + +/** + Split a string with specified separator and save the substring to a list. + + @param[in] String The pointer of the input string. + @param[in] Separator The specified separator. + + @return The pointer of headnode of ARG_LIST. + +**/ +ARG_LIST * +SplitStrToList ( + IN CONST CHAR16 *String, + IN CHAR16 Separator + ) +{ + CHAR16 *Str; + CHAR16 *ArgStr; + ARG_LIST *ArgList; + ARG_LIST *ArgNode; + + if (String == NULL || *String == L'\0') { + return NULL; + } + + // + // Copy the CONST string to a local copy. + // + Str = AllocateCopyPool (StrSize (String), String); + ASSERT (Str != NULL); + ArgStr = Str; + + // + // init a node for the list head. + // + ArgNode = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST)); + ASSERT (ArgNode != NULL); + ArgList = ArgNode; + + // + // Split the local copy and save in the list node. + // + while (*Str != L'\0') { + if (*Str == Separator) { + *Str = L'\0'; + ArgNode->Arg = ArgStr; + ArgStr = Str + 1; + ArgNode->Next = (ARG_LIST *) AllocateZeroPool (sizeof (ARG_LIST)); + ASSERT (ArgNode->Next != NULL); + ArgNode = ArgNode->Next; + } + + Str++; + } + + ArgNode->Arg = ArgStr; + ArgNode->Next = NULL; + + return ArgList; +} + +/** + Check the correctness of input Args with '-s' option. + + @param[in] CheckList The pointer of VAR_CHECK_ITEM array. + @param[in] Name The pointer of input arg. + @param[in] Init The switch to execute the check. + + @return The value of VAR_CHECK_CODE. + +**/ +VAR_CHECK_CODE +IfConfig6RetriveCheckListByName( + IN VAR_CHECK_ITEM *CheckList, + IN CHAR16 *Name, + IN BOOLEAN Init +) +{ + STATIC UINT32 CheckDuplicate; + STATIC UINT32 CheckConflict; + VAR_CHECK_CODE RtCode; + UINT32 Index; + VAR_CHECK_ITEM Arg; + + if (Init) { + CheckDuplicate = 0; + CheckConflict = 0; + return VarCheckOk; + } + + RtCode = VarCheckOk; + Index = 0; + Arg = CheckList[Index]; + + // + // Check the Duplicated/Conflicted/Unknown input Args. + // + while (Arg.FlagStr != NULL) { + if (StrCmp (Arg.FlagStr, Name) == 0) { + + if (CheckDuplicate & Arg.FlagID) { + RtCode = VarCheckDuplicate; + break; + } + + if (CheckConflict & Arg.ConflictMask) { + RtCode = VarCheckConflict; + break; + } + + CheckDuplicate |= Arg.FlagID; + CheckConflict |= Arg.ConflictMask; + break; + } + + Arg = CheckList[++Index]; + } + + if (Arg.FlagStr == NULL) { + RtCode = VarCheckUnknown; + } + + return RtCode; +} + +/** + The notify function of create event when performing a manual config. + + @param[in] Event The event this notify function registered to. + @param[in] Context Pointer to the context data registered to the event. + +**/ +VOID +EFIAPI +IfConfig6ManualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Print MAC address. + + @param[in] Node The pointer of MAC address buffer. + @param[in] Size The size of MAC address buffer. + +**/ +VOID +IfConfig6PrintMacAddr ( + IN UINT8 *Node, + IN UINT32 Size + ) +{ + UINTN Index; + + ASSERT (Size <= MACADDRMAXSIZE); + + for (Index = 0; Index < Size; Index++) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_BODY), mHiiHandle, Node[Index]); + if (Index + 1 < Size) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle); + } + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle); +} + +/** + Print IPv6 address. + + @param[in] Ip The pointer of Ip bufffer in EFI_IPv6_ADDRESS format. + @param[in] PrefixLen The pointer of PrefixLen that describes the size Prefix. + +**/ +VOID +IfConfig6PrintIpAddr ( + IN EFI_IPv6_ADDRESS *Ip, + IN UINT8 *PrefixLen + ) +{ + UINTN Index; + BOOLEAN Short; + + Short = FALSE; + + for (Index = 0; Index < PREFIXMAXLEN; Index = Index + 2) { + + if (!Short && (Index + 1 < PREFIXMAXLEN) && (Index % 2 == 0) && (Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0)) { + // + // Deal with the case of ::. + // + if (Index == 0) { + // + // :: is at the beginning of the address. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle); + } + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle); + + while ((Ip->Addr[Index] == 0) && (Ip->Addr[Index + 1] == 0) && (Index < PREFIXMAXLEN)) { + Index = Index + 2; + if (Index > PREFIXMAXLEN - 2) { + break; + } + } + + Short = TRUE; + + if (Index == PREFIXMAXLEN) { + // + // :: is at the end of the address. + // + break; + } + } + + if (Index < PREFIXMAXLEN - 1) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index]); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_BODY), mHiiHandle, Ip->Addr[Index + 1]); + } + + if (Index + 2 < PREFIXMAXLEN) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_COLON), mHiiHandle); + } + } + + if (PrefixLen != NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_PREFIX_LEN), mHiiHandle, *PrefixLen); + } +} + +/** + Pick up host IPv6 address in string format from Args with "-s" option and convert it to EFI_IP6_CONFIG_MANUAL_ADDRESS format. + + @param[in, out] Arg The pointer of the address of ARG_LIST which save Args with the "-s" option. + @param[out] Buf The pointer of the address of EFI_IP6_CONFIG_MANUAL_ADDRESS. + @param[out] BufSize The pointer of BufSize that describes the size of Buf in bytes. + + @retval EFI_SUCCESS The convertion is successful. + @retval Others Does't find the host address, or it is an invalid IPv6 address in string format. + +**/ +EFI_STATUS +IfConfig6ParseManualAddressList ( + IN OUT ARG_LIST **Arg, + OUT EFI_IP6_CONFIG_MANUAL_ADDRESS **Buf, + OUT UINTN *BufSize + ) +{ + EFI_STATUS Status; + EFI_IP6_CONFIG_MANUAL_ADDRESS *AddrBuf; + ARG_LIST *VarArg; + EFI_IPv6_ADDRESS Address; + UINT8 Prefix; + UINT8 AddrCnt; + + Prefix = 0; + AddrCnt = 0; + *BufSize = 0; + *Buf = NULL; + VarArg = *Arg; + Status = EFI_SUCCESS; + + // + // Go through the list to check the correctness of input host ip6 address. + // + while ((!EFI_ERROR (Status)) && (VarArg != NULL)) { + + Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix); + + if (EFI_ERROR (Status)) { + // + // host ip ip ... gw + // + break; + } + + VarArg = VarArg->Next; + AddrCnt++; + } + + if (AddrCnt == 0) { + return EFI_INVALID_PARAMETER; + } + + AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + ASSERT (AddrBuf != NULL); + + AddrCnt = 0; + VarArg = *Arg; + Status = EFI_SUCCESS; + + // + // Go through the list to fill in the EFI_IP6_CONFIG_MANUAL_ADDRESS structure. + // + while ((!EFI_ERROR (Status)) && (VarArg != NULL)) { + + Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix); + + if (EFI_ERROR (Status)) { + break; + } + + // + // If prefix length is not set, set it as Zero here. In the IfConfigSetInterfaceInfo() + // Zero prefix, length will be transfered to default prefix length. + // + if (Prefix == 0xFF) { + Prefix = 0; + } + AddrBuf[AddrCnt].IsAnycast = FALSE; + AddrBuf[AddrCnt].PrefixLength = Prefix; + IP6_COPY_ADDRESS (&AddrBuf[AddrCnt].Address, &Address); + VarArg = VarArg->Next; + AddrCnt++; + } + + *Arg = VarArg; + + if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) { + goto ON_ERROR; + } + + *Buf = AddrBuf; + *BufSize = AddrCnt * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + + return EFI_SUCCESS; + +ON_ERROR: + + FreePool (AddrBuf); + return Status; +} + +/** + Pick up gw/dns IPv6 address in string format from Args with "-s" option and convert it to EFI_IPv6_ADDRESS format. + + @param[in, out] Arg The pointer of the address of ARG_LIST that save Args with the "-s" option. + @param[out] Buf The pointer of the address of EFI_IPv6_ADDRESS. + @param[out] BufSize The pointer of BufSize that describes the size of Buf in bytes. + + @retval EFI_SUCCESS The conversion is successful. + @retval Others Doesn't find the host address, or it is an invalid IPv6 address in string format. + +**/ +EFI_STATUS +IfConfig6ParseGwDnsAddressList ( + IN OUT ARG_LIST **Arg, + OUT EFI_IPv6_ADDRESS **Buf, + OUT UINTN *BufSize + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS *AddrBuf; + ARG_LIST *VarArg; + EFI_IPv6_ADDRESS Address; + UINT8 Prefix; + UINT8 AddrCnt; + + AddrCnt = 0; + *BufSize = 0; + *Buf = NULL; + VarArg = *Arg; + Status = EFI_SUCCESS; + + // + // Go through the list to check the correctness of input gw/dns address. + // + while ((!EFI_ERROR (Status)) && (VarArg != NULL)) { + + Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix); + + if (EFI_ERROR (Status)) { + // + // gw ip ip ... host + // + break; + } + + VarArg = VarArg->Next; + AddrCnt++; + } + + if (AddrCnt == 0) { + return EFI_INVALID_PARAMETER; + } + + AddrBuf = AllocateZeroPool (AddrCnt * sizeof (EFI_IPv6_ADDRESS)); + ASSERT (AddrBuf != NULL); + + AddrCnt = 0; + VarArg = *Arg; + Status = EFI_SUCCESS; + + // + // Go through the list to fill in the EFI_IPv6_ADDRESS structure. + // + while ((!EFI_ERROR (Status)) && (VarArg != NULL)) { + + Status = NetLibStrToIp6andPrefix (VarArg->Arg, &Address, &Prefix); + + if (EFI_ERROR (Status)) { + break; + } + + IP6_COPY_ADDRESS (&AddrBuf[AddrCnt], &Address); + + VarArg = VarArg->Next; + AddrCnt++; + } + + *Arg = VarArg; + + if (EFI_ERROR (Status) && (Status != EFI_INVALID_PARAMETER)) { + goto ON_ERROR; + } + + *Buf = AddrBuf; + *BufSize = AddrCnt * sizeof (EFI_IPv6_ADDRESS); + + return EFI_SUCCESS; + +ON_ERROR: + + FreePool (AddrBuf); + return Status; +} + +/** + Parse InterfaceId in string format from Args with the "-s" option and convert it to EFI_IP6_CONFIG_INTERFACE_ID format. + + @param[in, out] Arg The pointer of the address of ARG_LIST that saves Args with the "-s" option. + @param[out] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID. + + @retval EFI_SUCCESS The get status processed successfullly. + @retval EFI_INVALID_PARAMETER The get status process failed. + +**/ +EFI_STATUS +IfConfig6ParseInterfaceId ( + IN OUT ARG_LIST **Arg, + OUT EFI_IP6_CONFIG_INTERFACE_ID **IfId + ) +{ + UINT8 Index; + UINT8 NodeVal; + CHAR16 *IdStr; + + if (*Arg == NULL) { + return EFI_INVALID_PARAMETER; + } + + Index = 0; + IdStr = (*Arg)->Arg; + ASSERT (IfId != NULL); + *IfId = AllocateZeroPool (sizeof (EFI_IP6_CONFIG_INTERFACE_ID)); + ASSERT (*IfId != NULL); + + while ((*IdStr != L'\0') && (Index < 8)) { + + NodeVal = 0; + while ((*IdStr != L':') && (*IdStr != L'\0')) { + + if ((*IdStr <= L'F') && (*IdStr >= L'A')) { + NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'A' + 10); + } else if ((*IdStr <= L'f') && (*IdStr >= L'a')) { + NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'a' + 10); + } else if ((*IdStr <= L'9') && (*IdStr >= L'0')) { + NodeVal = (UINT8)((NodeVal << 4) + *IdStr - L'0'); + } else { + FreePool (*IfId); + return EFI_INVALID_PARAMETER; + } + + IdStr++; + } + + (*IfId)->Id[Index++] = NodeVal; + + if (*IdStr == L':') { + IdStr++; + } + } + + *Arg = (*Arg)->Next; + return EFI_SUCCESS; +} + +/** + Parse dad in string format from Args with the "-s" option and convert it to UINT32 format. + + @param[in, out] Arg The pointer of the address of ARG_LIST that saves Args with the "-s" option. + @param[out] Xmits The pointer of Xmits. + + @retval EFI_SUCCESS The get status processed successfully. + @retval others The get status process failed. + +**/ +EFI_STATUS +IfConfig6ParseDadXmits ( + IN OUT ARG_LIST **Arg, + OUT UINT32 *Xmits + ) +{ + CHAR16 *ValStr; + + if (*Arg == NULL) { + return EFI_INVALID_PARAMETER; + } + + ValStr = (*Arg)->Arg; + *Xmits = 0; + + while (*ValStr != L'\0') { + + if ((*ValStr <= L'9') && (*ValStr >= L'0')) { + + *Xmits = (*Xmits * 10) + (*ValStr - L'0'); + + } else { + + return EFI_INVALID_PARAMETER; + } + + ValStr++; + } + + *Arg = (*Arg)->Next; + return EFI_SUCCESS; +} + +/** + The get current status of all handles. + + @param[in] ImageHandle The handle of ImageHandle. + @param[in] IfName The pointer of IfName(interface name). + @param[in] IfList The pointer of IfList(interface list). + + @retval EFI_SUCCESS The get status processed successfully. + @retval others The get status process failed. + +**/ +EFI_STATUS +IfConfig6GetInterfaceInfo ( + IN EFI_HANDLE ImageHandle, + IN CHAR16 *IfName, + IN LIST_ENTRY *IfList + ) +{ + EFI_STATUS Status; + UINTN HandleIndex; + UINTN HandleNum; + EFI_HANDLE *HandleBuffer; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + IFCONFIG6_INTERFACE_CB *IfCb; + UINTN DataSize; + + HandleBuffer = NULL; + HandleNum = 0; + + IfInfo = NULL; + IfCb = NULL; + + // + // Locate all the handles with ip6 service binding protocol. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + &HandleNum, + &HandleBuffer + ); + if (EFI_ERROR (Status) || (HandleNum == 0)) { + return EFI_ABORTED; + } + + // + // Enumerate all handles that installed with ip6 service binding protocol. + // + for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) { + IfCb = NULL; + IfInfo = NULL; + DataSize = 0; + + // + // Ip6config protocol and ip6 service binding protocol are installed + // on the same handle. + // + ASSERT (HandleBuffer != NULL); + Status = gBS->HandleProtocol ( + HandleBuffer[HandleIndex], + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Ip6Cfg + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + // + // Get the interface information size. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + NULL + ); + + if (Status != EFI_BUFFER_TOO_SMALL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + IfInfo = AllocateZeroPool (DataSize); + + if (IfInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + // + // Get the interface info. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + IfInfo + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + // + // Check the interface name if required. + // + if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) != 0)) { + FreePool (IfInfo); + continue; + } + + DataSize = 0; + // + // Get the size of dns server list. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDnsServer, + &DataSize, + NULL + ); + + if ((Status != EFI_BUFFER_TOO_SMALL) && (Status != EFI_NOT_FOUND)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + IfCb = AllocateZeroPool (sizeof (IFCONFIG6_INTERFACE_CB) + DataSize); + + if (IfCb == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + IfCb->NicHandle = HandleBuffer[HandleIndex]; + IfCb->IfInfo = IfInfo; + IfCb->IfCfg = Ip6Cfg; + IfCb->DnsCnt = (UINT32) (DataSize / sizeof (EFI_IPv6_ADDRESS)); + + // + // Get the dns server list if has. + // + if (DataSize > 0) { + + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDnsServer, + &DataSize, + IfCb->DnsAddr + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + } + // + // Get the interface id if has. + // + DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID); + IfCb->IfId = AllocateZeroPool (DataSize); + + if (IfCb->IfId == NULL) { + goto ON_ERROR; + } + + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeAltInterfaceId, + &DataSize, + IfCb->IfId + ); + + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + if (Status == EFI_NOT_FOUND) { + FreePool (IfCb->IfId); + IfCb->IfId = NULL; + } + // + // Get the config policy. + // + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + &DataSize, + &IfCb->Policy + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + // + // Get the dad transmits. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &IfCb->Xmits + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + InsertTailList (IfList, &IfCb->Link); + + if ((IfName != NULL) && (StrCmp (IfName, IfInfo->Name) == 0)) { + // + // Only need the appointed interface, keep the allocated buffer. + // + IfCb = NULL; + IfInfo = NULL; + break; + } + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (IfInfo != NULL) { + FreePool (IfInfo); + } + + if (IfCb != NULL) { + if (IfCb->IfId != NULL) { + FreePool (IfCb->IfId); + } + + FreePool (IfCb); + } + + return Status; +} + +/** + The list process of the IfConfig6 application. + + @param[in] IfList The pointer of IfList(interface list). + + @retval EFI_SUCCESS The IfConfig6 list processed successfully. + @retval others The IfConfig6 list process failed. + +**/ +EFI_STATUS +IfConfig6ShowInterfaceInfo ( + IN LIST_ENTRY *IfList + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Entry; + IFCONFIG6_INTERFACE_CB *IfCb; + UINTN Index; + + Entry = IfList->ForwardLink; + Status = EFI_SUCCESS; + + if (IsListEmpty (IfList)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle); + } + + // + // Go through the interface list. + // + while (Entry != IfList) { + + IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link); + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle); + + // + // Print interface name. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IF_NAME), mHiiHandle, IfCb->IfInfo->Name); + + // + // Print interface config policy. + // + if (IfCb->Policy == Ip6ConfigPolicyAutomatic) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_AUTO), mHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_POLICY_MAN), mHiiHandle); + } + + // + // Print dad transmit. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DAD_TRANSMITS), mHiiHandle, IfCb->Xmits); + + // + // Print interface id if has. + // + if (IfCb->IfId != NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_INTERFACE_ID_HEAD), mHiiHandle); + + IfConfig6PrintMacAddr ( + IfCb->IfId->Id, + 8 + ); + } + // + // Print mac address of the interface. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_MAC_ADDR_HEAD), mHiiHandle); + + IfConfig6PrintMacAddr ( + IfCb->IfInfo->HwAddress.Addr, + IfCb->IfInfo->HwAddressSize + ); + + // + // Print ip addresses list of the interface. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_IP_ADDR_HEAD), mHiiHandle); + + for (Index = 0; Index < IfCb->IfInfo->AddressInfoCount; Index++) { + IfConfig6PrintIpAddr ( + &IfCb->IfInfo->AddressInfo[Index].Address, + &IfCb->IfInfo->AddressInfo[Index].PrefixLength + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle); + } + + // + // Print dns server addresses list of the interface if has. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_DNS_ADDR_HEAD), mHiiHandle); + + for (Index = 0; Index < IfCb->DnsCnt; Index++) { + IfConfig6PrintIpAddr ( + &IfCb->DnsAddr[Index], + NULL + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle); + } + + // + // Print route table of the interface if has. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_ROUTE_HEAD), mHiiHandle); + + for (Index = 0; Index < IfCb->IfInfo->RouteCount; Index++) { + IfConfig6PrintIpAddr ( + &IfCb->IfInfo->RouteTable[Index].Destination, + &IfCb->IfInfo->RouteTable[Index].PrefixLength + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_JOINT), mHiiHandle); + + IfConfig6PrintIpAddr ( + &IfCb->IfInfo->RouteTable[Index].Gateway, + NULL + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_NEWLINE), mHiiHandle); + } + + Entry = Entry->ForwardLink; + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_INFO_BREAK), mHiiHandle); + + return Status; +} + +/** + The clean process of the IfConfig6 application. + + @param[in] IfList The pointer of IfList(interface list). + + @retval EFI_SUCCESS The IfConfig6 clean processed successfully. + @retval others The IfConfig6 clean process failed. + +**/ +EFI_STATUS +IfConfig6ClearInterfaceInfo ( + IN LIST_ENTRY *IfList + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Entry; + IFCONFIG6_INTERFACE_CB *IfCb; + EFI_IP6_CONFIG_POLICY Policy; + + Policy = Ip6ConfigPolicyAutomatic; + Entry = IfList->ForwardLink; + Status = EFI_SUCCESS; + + if (IsListEmpty (IfList)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle); + } + + // + // Go through the interface list. + // + while (Entry != IfList) { + + IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link); + + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Policy + ); + + if (EFI_ERROR (Status)) { + break; + } + + Entry = Entry->ForwardLink; + } + + return Status; +} + +/** + The set process of the IfConfig6 application. + + @param[in] IfList The pointer of IfList(interface list). + @param[in] VarArg The pointer of ARG_LIST(Args with "-s" option). + + @retval EFI_SUCCESS The IfConfig6 set processed successfully. + @retval others The IfConfig6 set process failed. + +**/ +EFI_STATUS +IfConfig6SetInterfaceInfo ( + IN LIST_ENTRY *IfList, + IN ARG_LIST *VarArg + ) +{ + EFI_STATUS Status; + IFCONFIG6_INTERFACE_CB *IfCb; + EFI_IP6_CONFIG_MANUAL_ADDRESS *CfgManAddr; + EFI_IPv6_ADDRESS *CfgAddr; + UINTN AddrSize; + EFI_IP6_CONFIG_INTERFACE_ID *InterfaceId; + UINT32 DadXmits; + UINT32 CurDadXmits; + UINTN CurDadXmitsLen; + EFI_IP6_CONFIG_POLICY Policy; + + VAR_CHECK_CODE CheckCode; + EFI_EVENT TimeOutEvt; + EFI_EVENT MappedEvt; + BOOLEAN IsAddressOk; + + UINTN DataSize; + UINT32 Index; + UINT32 Index2; + BOOLEAN IsAddressSet; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + + CfgManAddr = NULL; + CfgAddr = NULL; + TimeOutEvt = NULL; + MappedEvt = NULL; + IfInfo = NULL; + InterfaceId = NULL; + CurDadXmits = 0; + + if (IsListEmpty (IfList)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_INTERFACE), mHiiHandle); + return EFI_INVALID_PARAMETER; + } + // + // Make sure to set only one interface each time. + // + IfCb = BASE_CR (IfList->ForwardLink, IFCONFIG6_INTERFACE_CB, Link); + Status = EFI_SUCCESS; + + // + // Initialize check list mechanism. + // + CheckCode = IfConfig6RetriveCheckListByName( + NULL, + NULL, + TRUE + ); + + // + // Create events & timers for asynchronous settings. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeOutEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IfConfig6ManualAddressNotify, + &IsAddressOk, + &MappedEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Parse the setting variables. + // + while (VarArg != NULL) { + // + // Check invalid parameters (duplication & unknown & conflict). + // + CheckCode = IfConfig6RetriveCheckListByName( + mSetCheckList, + VarArg->Arg, + FALSE + ); + + if (VarCheckOk != CheckCode) { + switch (CheckCode) { + case VarCheckDuplicate: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_DUPLICATE_COMMAND), mHiiHandle, VarArg->Arg); + break; + + case VarCheckConflict: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_CONFLICT_COMMAND), mHiiHandle, VarArg->Arg); + break; + + case VarCheckUnknown: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_UNKNOWN_COMMAND), mHiiHandle, VarArg->Arg); + break; + + default: + break; + } + + VarArg = VarArg->Next; + continue; + } + // + // Process valid variables. + // + if (StrCmp(VarArg->Arg, L"auto") == 0) { + // + // Set automaic config policy + // + Policy = Ip6ConfigPolicyAutomatic; + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Policy + ); + + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + VarArg= VarArg->Next; + + } else if (StrCmp (VarArg->Arg, L"man") == 0) { + // + // Set manual config policy. + // + Policy = Ip6ConfigPolicyManual; + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Policy + ); + + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + VarArg= VarArg->Next; + + } else if (StrCmp (VarArg->Arg, L"host") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseManualAddressList ( + &VarArg, + &CfgManAddr, + &AddrSize + ); + + if (EFI_ERROR (Status)) { + if (Status == EFI_INVALID_PARAMETER) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"host"); + continue; + } else { + goto ON_EXIT; + } + } + // + // Set static host ip6 address list. + // This is a asynchronous process. + // + IsAddressOk = FALSE; + + Status = IfCb->IfCfg->RegisterDataNotify ( + IfCb->IfCfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeManualAddress, + AddrSize, + CfgManAddr + ); + + if (Status == EFI_NOT_READY) { + // + // Get current dad transmits count. + // + CurDadXmitsLen = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + IfCb->IfCfg->GetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &CurDadXmitsLen, + &CurDadXmits + ); + + gBS->SetTimer (TimeOutEvt, TimerRelative, 50000000 + 10000000 * CurDadXmits); + + while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { + if (IsAddressOk) { + Status = EFI_SUCCESS; + break; + } + } + } + + IfCb->IfCfg->UnregisterDataNotify ( + IfCb->IfCfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_MAN_HOST), mHiiHandle, Status); + goto ON_EXIT; + } + + // + // Check whether the address is set successfully. + // + DataSize = 0; + + Status = IfCb->IfCfg->GetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + NULL + ); + + if (Status != EFI_BUFFER_TOO_SMALL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_EXIT; + } + + IfInfo = AllocateZeroPool (DataSize); + + if (IfInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = IfCb->IfCfg->GetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + IfInfo + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_EXIT; + } + + for ( Index = 0; Index < (UINTN) (AddrSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); Index++) { + IsAddressSet = FALSE; + // + // By default, the prefix length 0 is regarded as 64. + // + if (CfgManAddr[Index].PrefixLength == 0) { + CfgManAddr[Index].PrefixLength = 64; + } + + for (Index2 = 0; Index2 < IfInfo->AddressInfoCount; Index2++) { + if (EFI_IP6_EQUAL (&IfInfo->AddressInfo[Index2].Address, &CfgManAddr[Index].Address) && + (IfInfo->AddressInfo[Index2].PrefixLength == CfgManAddr[Index].PrefixLength)) { + IsAddressSet = TRUE; + break; + } + } + + if (!IsAddressSet) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_ADDRESS_FAILED), mHiiHandle); + IfConfig6PrintIpAddr ( + &CfgManAddr[Index].Address, + &CfgManAddr[Index].PrefixLength + ); + } + } + + } else if (StrCmp (VarArg->Arg, L"gw") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseGwDnsAddressList ( + &VarArg, + &CfgAddr, + &AddrSize + ); + + if (EFI_ERROR (Status)) { + if (Status == EFI_INVALID_PARAMETER) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"gw"); + continue; + } else { + goto ON_EXIT; + } + } + // + // Set static gateway ip6 address list. + // + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeGateway, + AddrSize, + CfgAddr + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + } else if (StrCmp (VarArg->Arg, L"dns") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseGwDnsAddressList ( + &VarArg, + &CfgAddr, + &AddrSize + ); + + if (EFI_ERROR (Status)) { + if (Status == EFI_INVALID_PARAMETER) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_ARGUMENTS), mHiiHandle, L"dns"); + continue; + } else { + goto ON_EXIT; + } + } + // + // Set static dhs server ip6 address list. + // + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeDnsServer, + AddrSize, + CfgAddr + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + } else if (StrCmp (VarArg->Arg, L"id") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseInterfaceId (&VarArg, &InterfaceId); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Set alternative interface id. + // + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeAltInterfaceId, + sizeof (EFI_IP6_CONFIG_INTERFACE_ID), + InterfaceId + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + } else if (StrCmp (VarArg->Arg, L"dad") == 0) { + // + // Parse till the next tag or the end of command line. + // + VarArg = VarArg->Next; + Status = IfConfig6ParseDadXmits (&VarArg, &DadXmits); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Set dad transmits count. + // + Status = IfCb->IfCfg->SetData ( + IfCb->IfCfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS), + &DadXmits + ); + + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + } + } + +ON_EXIT: + + if (CfgManAddr != NULL) { + FreePool (CfgManAddr); + } + + if (CfgAddr != NULL) { + FreePool (CfgAddr); + } + + if (MappedEvt != NULL) { + gBS->CloseEvent (MappedEvt); + } + + if (TimeOutEvt != NULL) { + gBS->CloseEvent (TimeOutEvt); + } + + if (IfInfo != NULL) { + FreePool (IfInfo); + } + + return Status; + +} + +/** + The IfConfig6 main process. + + @param[in] Private The pointer of IFCONFIG6_PRIVATE_DATA. + + @retval EFI_SUCCESS IfConfig6 processed successfully. + @retval others The IfConfig6 process failed. + +**/ +EFI_STATUS +IfConfig6 ( + IN IFCONFIG6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + // + // Get configure information of all interfaces. + // + Status = IfConfig6GetInterfaceInfo ( + Private->ImageHandle, + Private->IfName, + &Private->IfList + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + switch (Private->OpCode) { + case IfConfig6OpList: + Status = IfConfig6ShowInterfaceInfo (&Private->IfList); + break; + + case IfConfig6OpClear: + Status = IfConfig6ClearInterfaceInfo (&Private->IfList); + break; + + case IfConfig6OpSet: + Status = IfConfig6SetInterfaceInfo (&Private->IfList, Private->VarArg); + break; + + default: + Status = EFI_ABORTED; + } + +ON_EXIT: + + return Status; +} + +/** + The IfConfig6 cleanup process, free the allocated memory. + + @param[in] Private The pointer of IFCONFIG6_PRIVATE_DATA. + +**/ +VOID +IfConfig6Cleanup ( + IN IFCONFIG6_PRIVATE_DATA *Private + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + IFCONFIG6_INTERFACE_CB *IfCb; + ARG_LIST *ArgNode; + ARG_LIST *ArgHead; + + ASSERT (Private != NULL); + + // + // Clean the list which save the set config Args. + // + if (Private->VarArg != NULL) { + ArgHead = Private->VarArg; + + while (ArgHead->Next != NULL) { + ArgNode = ArgHead->Next; + FreePool (ArgHead); + ArgHead = ArgNode; + } + + FreePool (ArgHead); + } + + if (Private->IfName != NULL) + FreePool (Private->IfName); + + + // + // Clean the IFCONFIG6_INTERFACE_CB list. + // + Entry = Private->IfList.ForwardLink; + NextEntry = Entry->ForwardLink; + + while (Entry != &Private->IfList) { + + IfCb = BASE_CR (Entry, IFCONFIG6_INTERFACE_CB, Link); + + RemoveEntryList (&IfCb->Link); + + if (IfCb->IfId != NULL) { + + FreePool (IfCb->IfId); + } + + if (IfCb->IfInfo != NULL) { + + FreePool (IfCb->IfInfo); + } + + FreePool (IfCb); + + Entry = NextEntry; + NextEntry = Entry->ForwardLink; + } + + FreePool (Private); +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for the IfConfig6 application which parses the command line input and calls the IfConfig6 process. + + @param[in] ImageHandle The image handle of this application. + @param[in] SystemTable The pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others Some errors occur. + +**/ +EFI_STATUS +EFIAPI +IfConfig6Initialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + IFCONFIG6_PRIVATE_DATA *Private; + LIST_ENTRY *ParamPackage; + CONST CHAR16 *ValueStr; + ARG_LIST *ArgList; + CHAR16 *ProblemParam; + CHAR16 *Str; + + Private = NULL; + + // + // Register our string package with HII and return the handle to it. + // + mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IfConfig6Strings, NULL); + ASSERT (mHiiHandle != NULL); + + Status = ShellCommandLineParseEx (mIfConfig6CheckList, &ParamPackage, &ProblemParam, TRUE, FALSE); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_INVALID_COMMAND), mHiiHandle, ProblemParam); + goto ON_EXIT; + } + + // + // To handle no option. + // + if (!ShellCommandLineGetFlag (ParamPackage, L"-r") && !ShellCommandLineGetFlag (ParamPackage, L"-s") && + !ShellCommandLineGetFlag (ParamPackage, L"-?") && !ShellCommandLineGetFlag (ParamPackage, L"-l")) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_LACK_OPTION), mHiiHandle); + goto ON_EXIT; + } + // + // To handle conflict options. + // + if (((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-s"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-r")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-l"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-?"))) || + ((ShellCommandLineGetFlag (ParamPackage, L"-l")) && (ShellCommandLineGetFlag (ParamPackage, L"-?")))) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_CONFLICT_OPTIONS), mHiiHandle); + goto ON_EXIT; + } + // + // To show the help information of ifconfig6 command. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-?")) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_HELP), mHiiHandle); + goto ON_EXIT; + } + + Status = EFI_INVALID_PARAMETER; + + Private = AllocateZeroPool (sizeof (IFCONFIG6_PRIVATE_DATA)); + + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + InitializeListHead (&Private->IfList); + + // + // To get interface name for the list option. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-l")) { + Private->OpCode = IfConfig6OpList; + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l"); + if (ValueStr != NULL) { + Str = AllocateCopyPool (StrSize (ValueStr), ValueStr); + ASSERT (Str != NULL); + Private->IfName = Str; + } + } + // + // To get interface name for the clear option. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-r")) { + Private->OpCode = IfConfig6OpClear; + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-r"); + if (ValueStr != NULL) { + Str = AllocateCopyPool (StrSize (ValueStr), ValueStr); + ASSERT (Str != NULL); + Private->IfName = Str; + } + } + // + // To get interface name and corresponding Args for the set option. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-s")) { + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s"); + if (ValueStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_INTERFACE), mHiiHandle); + goto ON_EXIT; + } + // + // To split the configuration into multi-section. + // + ArgList = SplitStrToList (ValueStr, L' '); + ASSERT (ArgList != NULL); + + Private->OpCode = IfConfig6OpSet; + Private->IfName = ArgList->Arg; + + Private->VarArg = ArgList->Next; + + if (Private->IfName == NULL || Private->VarArg == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_ERR_LACK_COMMAND), mHiiHandle); + goto ON_EXIT; + } + } + // + // Main process of ifconfig6. + // + Status = IfConfig6 (Private); + +ON_EXIT: + + ShellCommandLineFreeVarList (ParamPackage); + HiiRemovePackages (mHiiHandle); + if (Private != NULL) + IfConfig6Cleanup (Private); + + return Status; +} + diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6.h b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.h new file mode 100644 index 0000000000..ad3a77566a --- /dev/null +++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.h @@ -0,0 +1,79 @@ +/** @file + The interface function declaration of shell application IfConfig6. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IFCONFIG6_H_ +#define _IFCONFIG6_H_ + +enum { + IfConfig6OpList = 1, + IfConfig6OpSet = 2, + IfConfig6OpClear = 3 +}; + +typedef enum { + VarCheckReserved = -1, + VarCheckOk = 0, + VarCheckDuplicate, + VarCheckConflict, + VarCheckUnknown, + VarCheckLackValue, + VarCheckOutOfMem +} VAR_CHECK_CODE; + +typedef enum { + FlagTypeSingle = 0, + FlagTypeNeedVar, + FlagTypeNeedSet, + FlagTypeSkipUnknown +} VAR_CHECK_FLAG_TYPE; + +#define MACADDRMAXSIZE 32 +#define PREFIXMAXLEN 16 + +typedef struct _IFCONFIG6_INTERFACE_CB { + EFI_HANDLE NicHandle; + LIST_ENTRY Link; + EFI_IP6_CONFIG_PROTOCOL *IfCfg; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + EFI_IP6_CONFIG_INTERFACE_ID *IfId; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS Xmits; + UINT32 DnsCnt; + EFI_IPv6_ADDRESS DnsAddr[1]; +} IFCONFIG6_INTERFACE_CB; + +typedef struct _ARG_LIST ARG_LIST; + +struct _ARG_LIST { + ARG_LIST *Next; + CHAR16 *Arg; +}; + +typedef struct _IFCONFIG6_PRIVATE_DATA { + EFI_HANDLE ImageHandle; + LIST_ENTRY IfList; + + UINT32 OpCode; + CHAR16 *IfName; + ARG_LIST *VarArg; +} IFCONFIG6_PRIVATE_DATA; + +typedef struct _VAR_CHECK_ITEM{ + CHAR16 *FlagStr; + UINT32 FlagID; + UINT32 ConflictMask; + VAR_CHECK_FLAG_TYPE FlagType; +} VAR_CHECK_ITEM; +#endif diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6.inf b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.inf new file mode 100644 index 0000000000..7b329f5569 --- /dev/null +++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.inf @@ -0,0 +1,59 @@ +## @file +# Shell application IfConfig6. +# +# It is shell application which is used to set and get configurations for the +# EFI IPv6 network stack. +# +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = IfConfig6 + FILE_GUID = 6F71926E-60CE-428d-AA58-A3D9FB879429 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = IfConfig6Initialize + MODULE_UNI_FILE = IfConfig6.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# +[Sources] + IfConfig6Strings.uni + IfConfig6.c + IfConfig6.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + BaseLib + UefiBootServicesTableLib + UefiApplicationEntryPoint + BaseMemoryLib + ShellLib + MemoryAllocationLib + DebugLib + HiiLib + NetLib + +[Protocols] + gEfiIp6ServiceBindingProtocolGuid ## CONSUMES + gEfiIp6ConfigProtocolGuid ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + IfConfig6Extra.uni diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6.uni b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.uni new file mode 100644 index 0000000000..7c3e20e58c Binary files /dev/null and b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.uni differ diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni new file mode 100644 index 0000000000..e39bcbea2b Binary files /dev/null and b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni differ diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni new file mode 100644 index 0000000000..e22879e2e4 Binary files /dev/null and b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni differ diff --git a/Core/NetworkPkg/Application/IpsecConfig/Delete.c b/Core/NetworkPkg/Application/IpsecConfig/Delete.c new file mode 100644 index 0000000000..caeb1c8350 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Delete.c @@ -0,0 +1,110 @@ +/** @file + The implementation of delete policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecConfig.h" +#include "Indexer.h" +#include "Delete.h" +#include "Match.h" +#include "ForEach.h" + +/** + Private function to delete entry information in database. + + @param[in] Selector The pointer to EFI_IPSEC_CONFIG_SELECTOR structure. + @param[in] Data The pointer to Data. + @param[in] Context The pointer to DELETE_POLICY_ENTRY_CONTEXT. + + @retval EFI_ABORTED Abort the iteration. + @retval EFI_SUCCESS Continue the iteration. +**/ +EFI_STATUS +DeletePolicyEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN DELETE_POLICY_ENTRY_CONTEXT *Context + ) +{ + if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) { + Context->Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Selector, + NULL, + NULL + ); + // + // Abort the iteration after the insertion. + // + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Flush or delete entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Delete entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval Others Some mistaken case. +**/ +EFI_STATUS +FlushOrDeletePolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + DELETE_POLICY_ENTRY_CONTEXT Context; + CONST CHAR16 *ValueStr; + + // + // If user wants to remove all. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-f")) { + Status = mIpSecConfig->SetData ( + mIpSecConfig, + DataType, + NULL, + NULL, + NULL + ); + } else { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d"); + if (ValueStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr); + return EFI_NOT_FOUND; + } + + Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage); + if (!EFI_ERROR (Status)) { + Context.DataType = DataType; + Context.Status = EFI_NOT_FOUND; + ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) DeletePolicyEntry, &Context); + Status = Context.Status; + + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr); + } else if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DELETE_FAILED), mHiiHandle, mAppName); + } + } + } + + return Status; +} diff --git a/Core/NetworkPkg/Application/IpsecConfig/Delete.h b/Core/NetworkPkg/Application/IpsecConfig/Delete.h new file mode 100644 index 0000000000..49f3b0d5fa --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Delete.h @@ -0,0 +1,42 @@ +/** @file + The internal structure and function declaration of delete policy entry function + in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __DELETE_H_ +#define __DELETE_H_ + +typedef struct { + EFI_IPSEC_CONFIG_DATA_TYPE DataType; + POLICY_ENTRY_INDEXER Indexer; + EFI_STATUS Status; //Indicate whether deletion succeeds. +} DELETE_POLICY_ENTRY_CONTEXT; + +/** + Flush or delete entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Delete entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval Others Some mistaken case. +**/ +EFI_STATUS +FlushOrDeletePolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ); + +#endif diff --git a/Core/NetworkPkg/Application/IpsecConfig/Dump.c b/Core/NetworkPkg/Application/IpsecConfig/Dump.c new file mode 100644 index 0000000000..1a82a63df3 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Dump.c @@ -0,0 +1,579 @@ +/** @file + The implementation of dump policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecConfig.h" +#include "Dump.h" +#include "ForEach.h" +#include "Helper.h" + +/** + Private function called to get the version infomation from an EFI_IP_ADDRESS_INFO structure. + + @param[in] AddressInfo The pointer to the EFI_IP_ADDRESS_INFO structure. + + @return the value of version. +**/ +UINTN +GetVerFromAddrInfo ( + IN EFI_IP_ADDRESS_INFO *AddressInfo +) +{ + if((AddressInfo->PrefixLength <= 32) && (AddressInfo->Address.Addr[1] == 0) && + (AddressInfo->Address.Addr[2] == 0) && (AddressInfo->Address.Addr[3] == 0)) { + return IP_VERSION_4; + } else { + return IP_VERSION_6; + } +} + +/** + Private function called to get the version information from a EFI_IP_ADDRESS structure. + + @param[in] Address The pointer to the EFI_IP_ADDRESS structure. + + @return The value of the version. +**/ +UINTN +GetVerFromIpAddr ( + IN EFI_IP_ADDRESS *Address +) +{ + if ((Address->Addr[1] == 0) && (Address->Addr[2] == 0) && (Address->Addr[3] == 0)) { + return IP_VERSION_4; + } else { + return IP_VERSION_6; + } +} + +/** + Private function called to print an ASCII string in unicode char format. + + @param[in] Str The pointer to the ASCII string. + @param[in] Length The value of the ASCII string length. +**/ +VOID +DumpAsciiString ( + IN CHAR8 *Str, + IN UINTN Length + ) +{ + UINTN Index; + Print (L"\""); + for (Index = 0; Index < Length; Index++) { + Print (L"%c", (CHAR16) Str[Index]); + } + Print (L"\""); +} + +/** + Private function called to print a buffer in Hex format. + + @param[in] Data The pointer to the buffer. + @param[in] Length The size of the buffer. + +**/ +VOID +DumpBuf ( + IN UINT8 *Data, + IN UINTN Length + ) +{ + UINTN Index; + for (Index = 0; Index < Length; Index++) { + Print (L"%02x ", Data[Index]); + } +} + +/** + Private function called to print EFI_IP_ADDRESS_INFO content. + + @param[in] AddressInfo The pointer to the EFI_IP_ADDRESS_INFO structure. +**/ +VOID +DumpAddressInfo ( + IN EFI_IP_ADDRESS_INFO *AddressInfo + ) +{ + if (IP_VERSION_4 == GetVerFromAddrInfo (AddressInfo)) { + Print ( + L"%d.%d.%d.%d", + (UINTN) AddressInfo->Address.v4.Addr[0], + (UINTN) AddressInfo->Address.v4.Addr[1], + (UINTN) AddressInfo->Address.v4.Addr[2], + (UINTN) AddressInfo->Address.v4.Addr[3] + ); + if (AddressInfo->PrefixLength != 32) { + Print (L"/%d", (UINTN) AddressInfo->PrefixLength); + } + } + + if (IP_VERSION_6 == GetVerFromAddrInfo (AddressInfo)) { + Print ( + L"%x:%x:%x:%x:%x:%x:%x:%x", + (((UINT16) AddressInfo->Address.v6.Addr[0]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[1]), + (((UINT16) AddressInfo->Address.v6.Addr[2]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[3]), + (((UINT16) AddressInfo->Address.v6.Addr[4]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[5]), + (((UINT16) AddressInfo->Address.v6.Addr[6]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[7]), + (((UINT16) AddressInfo->Address.v6.Addr[8]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[9]), + (((UINT16) AddressInfo->Address.v6.Addr[10]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[11]), + (((UINT16) AddressInfo->Address.v6.Addr[12]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[13]), + (((UINT16) AddressInfo->Address.v6.Addr[14]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[15]) + ); + if (AddressInfo->PrefixLength != 128) { + Print (L"/%d", AddressInfo->PrefixLength); + } + } +} + +/** + Private function called to print EFI_IP_ADDRESS content. + + @param[in] IpAddress The pointer to the EFI_IP_ADDRESS structure. +**/ +VOID +DumpIpAddress ( + IN EFI_IP_ADDRESS *IpAddress + ) +{ + if (IP_VERSION_4 == GetVerFromIpAddr (IpAddress)) { + Print ( + L"%d.%d.%d.%d", + (UINTN) IpAddress->v4.Addr[0], + (UINTN) IpAddress->v4.Addr[1], + (UINTN) IpAddress->v4.Addr[2], + (UINTN) IpAddress->v4.Addr[3] + ); + } + + if (IP_VERSION_6 == GetVerFromIpAddr (IpAddress)) { + Print ( + L"%x:%x:%x:%x:%x:%x:%x:%x", + (((UINT16) IpAddress->v6.Addr[0]) << 8) | ((UINT16) IpAddress->v6.Addr[1]), + (((UINT16) IpAddress->v6.Addr[2]) << 8) | ((UINT16) IpAddress->v6.Addr[3]), + (((UINT16) IpAddress->v6.Addr[4]) << 8) | ((UINT16) IpAddress->v6.Addr[5]), + (((UINT16) IpAddress->v6.Addr[6]) << 8) | ((UINT16) IpAddress->v6.Addr[7]), + (((UINT16) IpAddress->v6.Addr[8]) << 8) | ((UINT16) IpAddress->v6.Addr[9]), + (((UINT16) IpAddress->v6.Addr[10]) << 8) | ((UINT16) IpAddress->v6.Addr[11]), + (((UINT16) IpAddress->v6.Addr[12]) << 8) | ((UINT16) IpAddress->v6.Addr[13]), + (((UINT16) IpAddress->v6.Addr[14]) << 8) | ((UINT16) IpAddress->v6.Addr[15]) + ); + } + +} + +/** + Private function called to print EFI_IPSEC_SPD_SELECTOR content. + + @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. +**/ +VOID +DumpSpdSelector ( + IN EFI_IPSEC_SPD_SELECTOR *Selector + ) +{ + UINT32 Index; + CHAR16 *Str; + + for (Index = 0; Index < Selector->LocalAddressCount; Index++) { + if (Index > 0) { + Print (L","); + } + + DumpAddressInfo (&Selector->LocalAddress[Index]); + } + + if (Index == 0) { + Print (L"localhost"); + } + + Print (L" -> "); + + for (Index = 0; Index < Selector->RemoteAddressCount; Index++) { + if (Index > 0) { + Print (L","); + } + + DumpAddressInfo (&Selector->RemoteAddress[Index]); + } + + Str = MapIntegerToString (Selector->NextLayerProtocol, mMapIpProtocol); + if (Str != NULL) { + Print (L" %s", Str); + } else { + Print (L" proto:%d", (UINTN) Selector->NextLayerProtocol); + } + + if ((Selector->NextLayerProtocol == EFI_IP4_PROTO_TCP) || (Selector->NextLayerProtocol == EFI_IP4_PROTO_UDP)) { + Print (L" port:"); + if (Selector->LocalPort != EFI_IPSEC_ANY_PORT) { + Print (L"%d", Selector->LocalPort); + if (Selector->LocalPortRange != 0) { + Print (L"~%d", (UINTN) Selector->LocalPort + Selector->LocalPortRange); + } + } else { + Print (L"any"); + } + + Print (L" -> "); + if (Selector->RemotePort != EFI_IPSEC_ANY_PORT) { + Print (L"%d", Selector->RemotePort); + if (Selector->RemotePortRange != 0) { + Print (L"~%d", (UINTN) Selector->RemotePort + Selector->RemotePortRange); + } + } else { + Print (L"any"); + } + } else if (Selector->NextLayerProtocol == EFI_IP4_PROTO_ICMP) { + Print (L" class/code:"); + if (Selector->LocalPort != 0) { + Print (L"%d", (UINTN) (UINT8) Selector->LocalPort); + } else { + Print (L"any"); + } + + Print (L"/"); + if (Selector->RemotePort != 0) { + Print (L"%d", (UINTN) (UINT8) Selector->RemotePort); + } else { + Print (L"any"); + } + } +} + +/** + Print EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA content. + + @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in] Data The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] EntryIndex The pointer to the Index in SPD Database. + + @retval EFI_SUCCESS Dump SPD information successfully. +**/ +EFI_STATUS +DumpSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *Selector, + IN EFI_IPSEC_SPD_DATA *Data, + IN UINTN *EntryIndex + ) +{ + BOOLEAN HasPre; + CHAR16 DataName[128]; + CHAR16 *String1; + CHAR16 *String2; + CHAR16 *String3; + UINT8 Index; + + Print (L"%d.", (*EntryIndex)++); + + // + // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400 + // Protect PF:0x34323423 Name:First Entry + // ext-sequence sequence-overflow fragcheck life:[B0,S1024,H3600] + // ESP algo1 algo2 Tunnel [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx set] + // + + DumpSpdSelector (Selector); + Print (L"\n "); + + Print (L"%s ", MapIntegerToString (Data->Action, mMapIpSecAction)); + Print (L"PF:%08x ", Data->PackageFlag); + + Index = 0; + while (Data->Name[Index] != 0) { + DataName[Index] = (CHAR16) Data->Name[Index]; + Index++; + ASSERT (Index < 128); + } + DataName[Index] = L'\0'; + + Print (L"Name:%s", DataName); + + if (Data->Action == EfiIPsecActionProtect) { + Print (L"\n "); + if (Data->ProcessingPolicy->ExtSeqNum) { + Print (L"ext-sequence "); + } + + if (Data->ProcessingPolicy->SeqOverflow) { + Print (L"sequence-overflow "); + } + + if (Data->ProcessingPolicy->FragCheck) { + Print (L"fragment-check "); + } + + HasPre = FALSE; + if (Data->ProcessingPolicy->SaLifetime.ByteCount != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxB", Data->ProcessingPolicy->SaLifetime.ByteCount); + HasPre = TRUE; + } + + if (Data->ProcessingPolicy->SaLifetime.SoftLifetime != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxs", Data->ProcessingPolicy->SaLifetime.SoftLifetime); + HasPre = TRUE; + } + + if (Data->ProcessingPolicy->SaLifetime.HardLifetime != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxS", Data->ProcessingPolicy->SaLifetime.HardLifetime); + HasPre = TRUE; + } + + if (HasPre) { + Print (L"]"); + } + + if (HasPre || Data->ProcessingPolicy->ExtSeqNum || + Data->ProcessingPolicy->SeqOverflow || Data->ProcessingPolicy->FragCheck) { + Print (L"\n "); + } + + String1 = MapIntegerToString (Data->ProcessingPolicy->Proto, mMapIpSecProtocol); + String2 = MapIntegerToString (Data->ProcessingPolicy->AuthAlgoId, mMapAuthAlgo); + String3 = MapIntegerToString (Data->ProcessingPolicy->EncAlgoId, mMapEncAlgo); + Print ( + L"%s Auth:%s Encrypt:%s ", + String1, + String2, + String3 + ); + + Print (L"%s ", MapIntegerToString (Data->ProcessingPolicy->Mode, mMapIpSecMode)); + if (Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + Print (L"["); + DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress); + Print (L" -> "); + DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress); + Print (L" %s]", MapIntegerToString (Data->ProcessingPolicy->TunnelOption->DF, mMapDfOption)); + } + } + + Print (L"\n"); + + return EFI_SUCCESS; +} + +/** + Print EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA2 content. + + @param[in] SaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[in] Data The pointer to the EFI_IPSEC_SA_DATA2 structure. + @param[in] EntryIndex The pointer to the Index in the SAD Database. + + @retval EFI_SUCCESS Dump SAD information successfully. +**/ +EFI_STATUS +DumpSadEntry ( + IN EFI_IPSEC_SA_ID *SaId, + IN EFI_IPSEC_SA_DATA2 *Data, + IN UINTN *EntryIndex + ) +{ + BOOLEAN HasPre; + CHAR16 *AuthAlgoStr; + CHAR16 *EncAlgoStr; + + AuthAlgoStr = NULL; + EncAlgoStr = NULL; + + // + // SPI:1234 ESP Destination:xxx.xxx.xxx.xxx + // Mode:Transport SeqNum:134 AntiReplayWin:64 life:[0B,1023s,3400S] PathMTU:34 + // Auth:xxxx/password Encrypt:yyyy/password + // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400 + // + + Print (L"%d.", (*EntryIndex)++); + Print (L"0x%x %s ", (UINTN) SaId->Spi, MapIntegerToString (SaId->Proto, mMapIpSecProtocol)); + if (Data->Mode == EfiIPsecTunnel) { + Print (L"TunnelSourceAddress:"); + DumpIpAddress (&Data->TunnelSourceAddress); + Print (L"\n"); + Print (L" TunnelDestination:"); + DumpIpAddress (&Data->TunnelDestinationAddress); + Print (L"\n"); + } + + Print ( + L" Mode:%s SeqNum:%lx AntiReplayWin:%d ", + MapIntegerToString (Data->Mode, mMapIpSecMode), + Data->SNCount, + (UINTN) Data->AntiReplayWindows + ); + + HasPre = FALSE; + if (Data->SaLifetime.ByteCount != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxB", Data->SaLifetime.ByteCount); + HasPre = TRUE; + } + + if (Data->SaLifetime.SoftLifetime != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxs", Data->SaLifetime.SoftLifetime); + HasPre = TRUE; + } + + if (Data->SaLifetime.HardLifetime != 0) { + Print (HasPre ? L"," : L"life:["); + Print (L"%lxS", Data->SaLifetime.HardLifetime); + HasPre = TRUE; + } + + if (HasPre) { + Print (L"] "); + } + + Print (L"PathMTU:%d\n", (UINTN) Data->PathMTU); + + if (SaId->Proto == EfiIPsecAH) { + Print ( + L" Auth:%s/%s\n", + MapIntegerToString (Data->AlgoInfo.AhAlgoInfo.AuthAlgoId, mMapAuthAlgo), + Data->AlgoInfo.AhAlgoInfo.AuthKey + ); + } else { + AuthAlgoStr = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, mMapAuthAlgo); + EncAlgoStr = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.EncAlgoId, mMapEncAlgo); + + if (Data->ManualSet) { + // + // if the SAD is set manually the key is a Ascii string in most of time. + // Print the Key in Ascii string format. + // + Print (L" Auth:%s/",AuthAlgoStr); + DumpAsciiString ( + Data->AlgoInfo.EspAlgoInfo.AuthKey, + Data->AlgoInfo.EspAlgoInfo.AuthKeyLength + ); + Print (L"\n Encrypt:%s/",EncAlgoStr); + DumpAsciiString ( + Data->AlgoInfo.EspAlgoInfo.EncKey, + Data->AlgoInfo.EspAlgoInfo.EncKeyLength + ); + } else { + // + // if the SAD is created by IKE, the key is a set of hex value in buffer. + // Print the Key in Hex format. + // + Print (L" Auth:%s/",AuthAlgoStr); + DumpBuf ((UINT8 *)(Data->AlgoInfo.EspAlgoInfo.AuthKey), Data->AlgoInfo.EspAlgoInfo.AuthKeyLength); + + Print (L"\n Encrypt:%s/",EncAlgoStr); + DumpBuf ((UINT8 *)(Data->AlgoInfo.EspAlgoInfo.EncKey), Data->AlgoInfo.EspAlgoInfo.EncKeyLength); + } + } + Print (L"\n"); + if (Data->SpdSelector != NULL) { + Print (L" "); + DumpSpdSelector (Data->SpdSelector); + Print (L"\n"); + } + + return EFI_SUCCESS; +} + +/** + Print EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA content. + + @param[in] PadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[in] Data The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] EntryIndex The pointer to the Index in the PAD Database. + + @retval EFI_SUCCESS Dump PAD information successfully. +**/ +EFI_STATUS +DumpPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN EFI_IPSEC_PAD_DATA *Data, + IN UINTN *EntryIndex + ) +{ + CHAR16 *String1; + CHAR16 *String2; + + // + // ADDR:10.23.17.34/15 + // IDEv1 PreSharedSecret IKE-ID + // password + // + + Print (L"%d.", (*EntryIndex)++); + + if (PadId->PeerIdValid) { + Print (L"ID:%s", PadId->Id.PeerId); + } else { + Print (L"ADDR:"); + DumpAddressInfo (&PadId->Id.IpAddress); + } + + Print (L"\n"); + + String1 = MapIntegerToString (Data->AuthProtocol, mMapAuthProto); + String2 = MapIntegerToString (Data->AuthMethod, mMapAuthMethod); + Print ( + L" %s %s", + String1, + String2 + ); + + if (Data->IkeIdFlag) { + Print (L"IKE-ID"); + } + + Print (L"\n"); + + if (Data->AuthData != NULL) { + DumpAsciiString (Data->AuthData, Data->AuthDataSize); + Print (L"\n"); + } + + if (Data->RevocationData != NULL) { + Print (L" %s\n", Data->RevocationData); + } + + return EFI_SUCCESS; + +} + +VISIT_POLICY_ENTRY mDumpPolicyEntry[] = { + (VISIT_POLICY_ENTRY) DumpSpdEntry, + (VISIT_POLICY_ENTRY) DumpSadEntry, + (VISIT_POLICY_ENTRY) DumpPadEntry +}; + +/** + Print all entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Dump all information successfully. + @retval Others Some mistaken case. +**/ +EFI_STATUS +ListPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ) +{ + UINTN EntryIndex; + + EntryIndex = 0; + return ForeachPolicyEntry (DataType, mDumpPolicyEntry[DataType], &EntryIndex); +} + diff --git a/Core/NetworkPkg/Application/IpsecConfig/Dump.h b/Core/NetworkPkg/Application/IpsecConfig/Dump.h new file mode 100644 index 0000000000..3c475dd304 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Dump.h @@ -0,0 +1,34 @@ +/** @file + The function declaration of dump policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _DUMP_H_ +#define _DUMP_H_ + +/** + Print all entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Dump all information successfully. + @retval Others Some mistaken case. +**/ +EFI_STATUS +ListPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ); + +#endif diff --git a/Core/NetworkPkg/Application/IpsecConfig/ForEach.c b/Core/NetworkPkg/Application/IpsecConfig/ForEach.c new file mode 100644 index 0000000000..7b3b9b17a8 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/ForEach.c @@ -0,0 +1,115 @@ +/** @file + The implementation to go through each entry in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecConfig.h" +#include "ForEach.h" + + +/** + Enumerate all entries in the database to execute specified operations according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] Routine The pointer to the function of a specified operation. + @param[in] Context The pointer to the context of a function. + + @retval EFI_SUCCESS Execute specified operation successfully. +**/ +EFI_STATUS +ForeachPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN VISIT_POLICY_ENTRY Routine, + IN VOID *Context + ) +{ + EFI_STATUS GetNextStatus; + EFI_STATUS GetDataStatus; + EFI_IPSEC_CONFIG_SELECTOR *Selector; + VOID *Data; + UINTN SelectorSize; + UINTN DataSize; + BOOLEAN FirstGetNext; + + FirstGetNext = TRUE; + SelectorSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR); + Selector = AllocateZeroPool (SelectorSize); + + DataSize = 0; + Data = NULL; + + while (TRUE) { + GetNextStatus = mIpSecConfig->GetNextSelector ( + mIpSecConfig, + DataType, + &SelectorSize, + Selector + ); + if (GetNextStatus == EFI_BUFFER_TOO_SMALL) { + gBS->FreePool (Selector); + Selector = FirstGetNext ? AllocateZeroPool (SelectorSize) : AllocatePool (SelectorSize); + + GetNextStatus = mIpSecConfig->GetNextSelector ( + mIpSecConfig, + DataType, + &SelectorSize, + Selector + ); + } + + if (EFI_ERROR (GetNextStatus)) { + break; + } + + FirstGetNext = FALSE; + + GetDataStatus = mIpSecConfig->GetData ( + mIpSecConfig, + DataType, + Selector, + &DataSize, + Data + ); + if (GetDataStatus == EFI_BUFFER_TOO_SMALL) { + if (Data != NULL) { + gBS->FreePool (Data); + } + + Data = AllocateZeroPool (DataSize); + GetDataStatus = mIpSecConfig->GetData ( + mIpSecConfig, + DataType, + Selector, + &DataSize, + Data + ); + } + + ASSERT_EFI_ERROR (GetDataStatus); + + if (EFI_ERROR (Routine (Selector, Data, Context))) { + break; + } + } + + if (Data != NULL) { + gBS->FreePool (Data); + } + + if (Selector != NULL) { + gBS->FreePool (Selector); + } + + return EFI_SUCCESS; +} + diff --git a/Core/NetworkPkg/Application/IpsecConfig/ForEach.h b/Core/NetworkPkg/Application/IpsecConfig/ForEach.h new file mode 100644 index 0000000000..fc309300c0 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/ForEach.h @@ -0,0 +1,54 @@ +/** @file + The internal structure and function declaration of the implementation + to go through each entry in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _FOREACH_H_ +#define _FOREACH_H_ + +/** + The prototype for the DumpSpdEntry()/DumpSadEntry()/DumpPadEntry(). + Print EFI_IPSEC_CONFIG_SELECTOR and corresponding content. + + @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union. + @param[in] Data The pointer to the corresponding data. + @param[in] Context The pointer to the Index in SPD/SAD/PAD Database. + + @retval EFI_SUCCESS Dump SPD/SAD/PAD information successfully. +**/ +typedef +EFI_STATUS +(*VISIT_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context + ); + +/** + Enumerate all entry in the database to execute a specified operation according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] Routine The pointer to function of a specified operation. + @param[in] Context The pointer to the context of a function. + + @retval EFI_SUCCESS Execute specified operation successfully. +**/ +EFI_STATUS +ForeachPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN VISIT_POLICY_ENTRY Routine, + IN VOID *Context + ); + +#endif diff --git a/Core/NetworkPkg/Application/IpsecConfig/Helper.c b/Core/NetworkPkg/Application/IpsecConfig/Helper.c new file mode 100644 index 0000000000..ae867ceee7 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Helper.c @@ -0,0 +1,420 @@ +/** @file + The assistant function implementation for IpSecConfig application. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecConfig.h" +#include "Helper.h" + +/** + Helper function called to change an input parameter in the string format to a number. + + @param[in] FlagStr The pointer to the flag string. + @param[in] Maximum Greatest value number. + @param[in, out] ValuePtr The pointer to the input parameter in string format. + @param[in] ByteCount The valid byte count + @param[in] Map The pointer to the STR2INT table. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[in] FormatMask The bit mask. + BIT 0 set indicates the value of a flag might be a number. + BIT 1 set indicates the value of a flag might be a string that needs to be looked up. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The input parameter can't be found. + @retval EFI_INVALID_PARAMETER The input parameter is an invalid input. +**/ +EFI_STATUS +GetNumber ( + IN CHAR16 *FlagStr, + IN UINT64 Maximum, + IN OUT VOID *ValuePtr, + IN UINTN ByteCount, + IN STR2INT *Map, + IN LIST_ENTRY *ParamPackage, + IN UINT32 FormatMask + ) +{ + EFI_STATUS Status; + UINT64 Value64; + BOOLEAN Converted; + UINTN Index; + CONST CHAR16 *ValueStr; + + ASSERT (FormatMask & (FORMAT_NUMBER | FORMAT_STRING)); + + Converted = FALSE; + Value64 = 0; + ValueStr = ShellCommandLineGetValue (ParamPackage, FlagStr); + + if (ValueStr == NULL) { + return EFI_NOT_FOUND; + } else { + // + // Try to convert to integer directly if MaybeNumber is TRUE. + // + if ((FormatMask & FORMAT_NUMBER) != 0) { + Value64 = StrToUInteger (ValueStr, &Status); + if (!EFI_ERROR (Status)) { + // + // Convert successfully. + // + if (Value64 > Maximum) { + // + // But the result is invalid + // + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + FlagStr, + ValueStr + ); + return EFI_INVALID_PARAMETER; + } + + Converted = TRUE; + } + } + + if (!Converted && ((FormatMask & FORMAT_STRING) != 0)) { + // + // Convert falied, so use String->Integer map. + // + ASSERT (Map != NULL); + Value64 = MapStringToInteger (ValueStr, Map); + if (Value64 == (UINT32) -1) { + // + // Cannot find the string in the map. + // + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + FlagStr, + ValueStr + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ACCEPT_PARAMETERS), mHiiHandle); + for (Index = 0; Map[Index].String != NULL; Index++) { + Print (L" %s", Map[Index].String); + } + + Print (L"\n"); + return EFI_INVALID_PARAMETER; + } + } + + CopyMem (ValuePtr, &Value64, ByteCount); + return EFI_SUCCESS; + } +} + +/** + Helper function called to convert a string containing an Ipv4 or Ipv6 Internet Protocol address + into a proper address for the EFI_IP_ADDRESS structure. + + @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address. + @param[out] Ip The pointer to the EFI_IP_ADDRESS structure to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EfiInetAddr2 ( + IN CHAR16 *Ptr, + OUT EFI_IP_ADDRESS *Ip + ) +{ + EFI_STATUS Status; + + if ((Ptr == NULL) || (Ip == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Parse the input address as Ipv4 Address first. + // + Status = NetLibStrToIp4 (Ptr, &Ip->v4); + if (!EFI_ERROR (Status)) { + return Status; + } + + Status = NetLibStrToIp6 (Ptr, &Ip->v6); + return Status; +} + +/** + Helper function called to calculate the prefix length associated with the string + containing an Ipv4 or Ipv6 Internet Protocol address. + + @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address. + @param[out] Addr The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval Others Other mistake case. +**/ +EFI_STATUS +EfiInetAddrRange ( + IN CHAR16 *Ptr, + OUT EFI_IP_ADDRESS_INFO *Addr + ) +{ + EFI_STATUS Status; + + if ((Ptr == NULL) || (Addr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = NetLibStrToIp4 (Ptr, &Addr->Address.v4); + if (!EFI_ERROR (Status)) { + if ((UINT32)(*Addr->Address.v4.Addr) == 0) { + Addr->PrefixLength = 0; + } else { + Addr->PrefixLength = 32; + } + return Status; + } + + Status = NetLibStrToIp6andPrefix (Ptr, &Addr->Address.v6, &Addr->PrefixLength); + if (!EFI_ERROR (Status) && (Addr->PrefixLength == 0xFF)) { + Addr->PrefixLength = 128; + } + + return Status; +} + +/** + Helper function called to calculate the port range associated with the string. + + @param[in] Ptr The pointer to the string containing a port and range. + @param[out] Port The pointer to the Port to contain the result. + @param[out] PortRange The pointer to the PortRange to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval Others Other mistake case. +**/ +EFI_STATUS +EfiInetPortRange ( + IN CHAR16 *Ptr, + OUT UINT16 *Port, + OUT UINT16 *PortRange + ) +{ + CHAR16 *BreakPtr; + CHAR16 Ch; + EFI_STATUS Status; + + for (BreakPtr = Ptr; (*BreakPtr != L'\0') && (*BreakPtr != L':'); BreakPtr++) { + ; + } + + Ch = *BreakPtr; + *BreakPtr = L'\0'; + *Port = (UINT16) StrToUInteger (Ptr, &Status); + *BreakPtr = Ch; + if (EFI_ERROR (Status)) { + return Status; + } + + *PortRange = 0; + if (*BreakPtr == L':') { + BreakPtr++; + *PortRange = (UINT16) StrToUInteger (BreakPtr, &Status); + if (EFI_ERROR (Status)) { + return Status; + } + + if (*PortRange < *Port) { + return EFI_INVALID_PARAMETER; + } + + *PortRange = (UINT16) (*PortRange - *Port); + } + + return EFI_SUCCESS; +} + +/** + Helper function called to transfer a string to an unsigned integer. + + @param[in] Str The pointer to the string. + @param[out] Status The operation status. + + @return The integer value of converted Str. +**/ +UINT64 +StrToUInteger ( + IN CONST CHAR16 *Str, + OUT EFI_STATUS *Status + ) +{ + UINT64 Value; + UINT64 NewValue; + CHAR16 *StrTail; + CHAR16 Char; + UINTN Base; + UINTN Len; + + Base = 10; + Value = 0; + *Status = EFI_ABORTED; + + // + // Skip leading white space. + // + while ((*Str != 0) && (*Str == ' ')) { + Str++; + } + // + // For NULL Str, just return. + // + if (*Str == 0) { + return 0; + } + // + // Skip white space in tail. + // + Len = StrLen (Str); + StrTail = (CHAR16 *) (Str + Len - 1); + while (*StrTail == ' ') { + *StrTail = 0; + StrTail--; + } + + Len = StrTail - Str + 1; + + // + // Check hex prefix '0x'. + // + if ((Len >= 2) && (*Str == '0') && ((*(Str + 1) == 'x') || (*(Str + 1) == 'X'))) { + Str += 2; + Len -= 2; + Base = 16; + } + + if (Len == 0) { + return 0; + } + // + // Convert the string to value. + // + for (; Str <= StrTail; Str++) { + + Char = *Str; + + if (Base == 16) { + if (RShiftU64 (Value, 60) != 0) { + // + // Overflow here x16. + // + return 0; + } + + NewValue = LShiftU64 (Value, 4); + } else { + if (RShiftU64 (Value, 61) != 0) { + // + // Overflow here x8. + // + return 0; + } + + NewValue = LShiftU64 (Value, 3); + Value = LShiftU64 (Value, 1); + NewValue += Value; + if (NewValue < Value) { + // + // Overflow here. + // + return 0; + } + } + + Value = NewValue; + + if ((Base == 16) && (Char >= 'a') && (Char <= 'f')) { + Char = (CHAR16) (Char - 'a' + 'A'); + } + + if ((Base == 16) && (Char >= 'A') && (Char <= 'F')) { + Value += (Char - 'A') + 10; + } else if ((Char >= '0') && (Char <= '9')) { + Value += (Char - '0'); + } else { + // + // Unexpected Char encountered. + // + return 0; + } + } + + *Status = EFI_SUCCESS; + return Value; +} + +/** + Helper function called to transfer a string to an unsigned integer according to the map table. + + @param[in] Str The pointer to the string. + @param[in] Map The pointer to the map table. + + @return The integer value of converted Str. If not found, then return -1. +**/ +UINT32 +MapStringToInteger ( + IN CONST CHAR16 *Str, + IN STR2INT *Map + ) +{ + STR2INT *Item; + + for (Item = Map; Item->String != NULL; Item++) { + if (StrCmp (Item->String, Str) == 0) { + return Item->Integer; + } + } + + return (UINT32) -1; +} + +/** + Helper function called to transfer an unsigned integer to a string according to the map table. + + @param[in] Integer The pointer to the string. + @param[in] Map The pointer to the map table. + + @return The converted Str. If not found, then return NULL. +**/ +CHAR16 * +MapIntegerToString ( + IN UINT32 Integer, + IN STR2INT *Map + ) +{ + STR2INT *Item; + + for (Item = Map; Item->String != NULL; Item++) { + if (Integer == Item->Integer) { + return Item->String; + } + } + + return NULL; +} diff --git a/Core/NetworkPkg/Application/IpsecConfig/Helper.h b/Core/NetworkPkg/Application/IpsecConfig/Helper.h new file mode 100644 index 0000000000..d893145a76 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Helper.h @@ -0,0 +1,143 @@ +/** @file + The assistant function declaration for IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _HELPER_H_ +#define _HELPER_H_ + +#define FORMAT_NUMBER 0x1 +#define FORMAT_STRING 0x2 + +/** + Helper function called to change input parameter in string format to number. + + @param[in] FlagStr The pointer to the flag string. + @param[in] Maximum most value number. + @param[in, out] ValuePtr The pointer to the input parameter in string format. + @param[in] ByteCount The valid byte count + @param[in] Map The pointer to the STR2INT table. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[in] FormatMask The bit mask. + BIT 0 set indicates the value of flag might be number. + BIT 1 set indicates the value of flag might be a string that needs to be looked up. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The input parameter can't be found. + @retval EFI_INVALID_PARAMETER The input parameter is an invalid input. +**/ +EFI_STATUS +GetNumber ( + IN CHAR16 *FlagStr, + IN UINT64 Maximum, + IN OUT VOID *ValuePtr, + IN UINTN ByteCount, + IN STR2INT *Map, + IN LIST_ENTRY *ParamPackage, + IN UINT32 FormatMask + ); + +/** + Helper function called to convert a string containing an (Ipv4) Internet Protocol dotted address + into a proper address for the EFI_IP_ADDRESS structure. + + @param[in] Ptr The pointer to the string containing an (Ipv4) Internet Protocol dotted address. + @param[out] Ip The pointer to the Ip address structure to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. +**/ +EFI_STATUS +EfiInetAddr2 ( + IN CHAR16 *Ptr, + OUT EFI_IP_ADDRESS *Ip + ); + +/** + Helper function called to calculate the prefix length associated with the string + containing an Ipv4 or Ipv6 Internet Protocol address. + + @param[in] Ptr The pointer to the string containing an Ipv4 or Ipv6 Internet Protocol address. + @param[out] Addr The pointer to the EFI_IP_ADDRESS_INFO structure to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval Others Other mistake case. +**/ +EFI_STATUS +EfiInetAddrRange ( + IN CHAR16 *Ptr, + OUT EFI_IP_ADDRESS_INFO *Addr + ); + +/** + Helper function called to calculate the port range associated with the string. + + @param[in] Ptr The pointer to the string containing a port and range. + @param[out] Port The pointer to the Port to contain the result. + @param[out] PortRange The pointer to the PortRange to contain the result. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval Others Other mistake case. +**/ +EFI_STATUS +EfiInetPortRange ( + IN CHAR16 *Ptr, + OUT UINT16 *Port, + OUT UINT16 *PortRange + ); + +/** + Helper function called to transfer a string to an unsigned integer. + + @param[in] Str The pointer to the string. + @param[out] Status The operation status. + + @return The integer value of a converted str. +**/ +UINT64 +StrToUInteger ( + IN CONST CHAR16 *Str, + OUT EFI_STATUS *Status + ); + +/** + Helper function called to transfer a string to an unsigned integer according to the map table. + + @param[in] Str The pointer to the string. + @param[in] Map The pointer to the map table. + + @return The integer value of converted str. If not found, then return -1. +**/ +UINT32 +MapStringToInteger ( + IN CONST CHAR16 *Str, + IN STR2INT *Map + ); + +/** + Helper function called to transfer an unsigned integer to a string according to the map table. + + @param[in] Integer The pointer to the string. + @param[in] Map The pointer to the map table. + + @return The converted str. If not found, then return NULL. +**/ +CHAR16 * +MapIntegerToString ( + IN UINT32 Integer, + IN STR2INT *Map + ); + +#endif diff --git a/Core/NetworkPkg/Application/IpsecConfig/Indexer.c b/Core/NetworkPkg/Application/IpsecConfig/Indexer.c new file mode 100644 index 0000000000..353b22e06a --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Indexer.c @@ -0,0 +1,255 @@ +/** @file + The implementation of construct ENTRY_INDEXER in IpSecConfig application. + + Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecConfig.h" +#include "Indexer.h" +#include "Helper.h" + +/** + Fill in SPD_ENTRY_INDEXER through ParamPackage list. + + @param[in, out] Indexer The pointer to the SPD_ENTRY_INDEXER structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Filled in SPD_ENTRY_INDEXER successfully. +**/ +EFI_STATUS +ConstructSpdIndexer ( + IN OUT SPD_ENTRY_INDEXER *Indexer, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + UINT64 Value64; + CONST CHAR16 *ValueStr; + + ValueStr = NULL; + + if (ShellCommandLineGetFlag (ParamPackage, L"-i")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e"); + } else { + return EFI_INVALID_PARAMETER; + } + + if (ValueStr == NULL) { + return EFI_INVALID_PARAMETER; + } + + Value64 = StrToUInteger (ValueStr, &Status); + if (!EFI_ERROR (Status)) { + Indexer->Index = (UINTN) Value64; + ZeroMem (Indexer->Name, MAX_PEERID_LEN); + } else { + UnicodeStrToAsciiStrS (ValueStr, (CHAR8 *) Indexer->Name, MAX_PEERID_LEN); + } + + return EFI_SUCCESS; +} + +/** + Fill in SAD_ENTRY_INDEXER through ParamPackage list. + + @param[in, out] Indexer The pointer to the SAD_ENTRY_INDEXER structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Filled in SPD_ENTRY_INDEXER successfully. + @retval EFI_INVALID_PARAMETER The mistaken user input in ParamPackage list. +**/ +EFI_STATUS +ConstructSadIndexer ( + IN OUT SAD_ENTRY_INDEXER *Indexer, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + EFI_STATUS Status1; + UINT64 Value64; + CONST CHAR16 *ValueStr; + + ValueStr = NULL; + + if (ShellCommandLineGetFlag (ParamPackage, L"-i")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e"); + } else { + return EFI_INVALID_PARAMETER; + } + + if (ValueStr == NULL) { + return EFI_INVALID_PARAMETER; + } + + Value64 = StrToUInteger (ValueStr, &Status); + if (!EFI_ERROR (Status)) { + Indexer->Index = (UINTN) Value64; + ZeroMem (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID)); + } else { + if ((!ShellCommandLineGetFlag (ParamPackage, L"--lookup-spi")) || + (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-ipsec-proto")) || + (!ShellCommandLineGetFlag (ParamPackage, L"--lookup-dest"))) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--lookup-spi --lookup-ipsec-proto --lookup-dest" + ); + return EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lookup-spi", + (UINT32) -1, + &Indexer->SaId.Spi, + sizeof (UINT32), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + Status1 = GetNumber ( + L"--lookup-ipsec-proto", + 0, + &Indexer->SaId.Proto, + sizeof (EFI_IPSEC_PROTOCOL_TYPE), + mMapIpSecProtocol, + ParamPackage, + FORMAT_STRING + ); + + if (EFI_ERROR (Status) || EFI_ERROR (Status1)) { + return EFI_INVALID_PARAMETER; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-dest"); + ASSERT (ValueStr != NULL); + + Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &Indexer->SaId.DestAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--lookup-dest", + ValueStr + ); + return EFI_INVALID_PARAMETER; + } + } + + return EFI_SUCCESS; +} + +/** + Fill in PAD_ENTRY_INDEXER through ParamPackage list. + + @param[in, out] Indexer The pointer to the PAD_ENTRY_INDEXER structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Filled in PAD_ENTRY_INDEXER successfully. + @retval EFI_INVALID_PARAMETER The mistaken user input in ParamPackage list. +**/ +EFI_STATUS +ConstructPadIndexer ( + IN OUT PAD_ENTRY_INDEXER *Indexer, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + UINT64 Value64; + CONST CHAR16 *ValueStr; + + ValueStr = NULL; + + if (ShellCommandLineGetFlag (ParamPackage, L"-i")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-d"); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e"); + } else { + return EFI_INVALID_PARAMETER; + } + + if (ValueStr == NULL) { + return EFI_INVALID_PARAMETER; + } + + Value64 = StrToUInteger (ValueStr, &Status); + + if (!EFI_ERROR (Status)) { + Indexer->Index = (UINTN) Value64; + ZeroMem (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID)); + } else { + + if (ShellCommandLineGetFlag (ParamPackage, L"--lookup-peer-address")) { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-address"); + ASSERT (ValueStr != NULL); + + Indexer->PadId.PeerIdValid = FALSE; + Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &Indexer->PadId.Id.IpAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--lookup-peer-address", + ValueStr + ); + return EFI_INVALID_PARAMETER; + } + } else { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--lookup-peer-id"); + if (ValueStr == NULL) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--lookup-peer-address --lookup-peer-id" + ); + return EFI_INVALID_PARAMETER; + } + + Indexer->PadId.PeerIdValid = TRUE; + ZeroMem (Indexer->PadId.Id.PeerId, MAX_PEERID_LEN); + StrnCpyS ((CHAR16 *) Indexer->PadId.Id.PeerId, MAX_PEERID_LEN / sizeof (CHAR16), ValueStr, MAX_PEERID_LEN / sizeof (CHAR16) - 1); + } + } + + return EFI_SUCCESS; +} + +CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[] = { + (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSpdIndexer, + (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructSadIndexer, + (CONSTRUCT_POLICY_ENTRY_INDEXER) ConstructPadIndexer +}; diff --git a/Core/NetworkPkg/Application/IpsecConfig/Indexer.h b/Core/NetworkPkg/Application/IpsecConfig/Indexer.h new file mode 100644 index 0000000000..58c0689021 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Indexer.h @@ -0,0 +1,58 @@ +/** @file + The internal structure and function declaration to construct ENTRY_INDEXER in + IpSecConfig application. + + Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _INDEXER_H_ +#define _INDEXER_H_ + +typedef struct { + UINT8 Name[MAX_PEERID_LEN]; + UINTN Index; // Used only if Name buffer is filled with zero. +} SPD_ENTRY_INDEXER; + +typedef struct { + EFI_IPSEC_SA_ID SaId; + UINTN Index; +} SAD_ENTRY_INDEXER; + +typedef struct { + EFI_IPSEC_PAD_ID PadId; + UINTN Index; +} PAD_ENTRY_INDEXER; + +typedef union { + SPD_ENTRY_INDEXER Spd; + SAD_ENTRY_INDEXER Sad; + PAD_ENTRY_INDEXER Pad; +} POLICY_ENTRY_INDEXER; + +/** + The prototype for the ConstructSpdIndexer()/ConstructSadIndexer()/ConstructPadIndexer(). + Fill in SPD_ENTRY_INDEXER/SAD_ENTRY_INDEXER/PAD_ENTRY_INDEXER through ParamPackage list. + + @param[in, out] Indexer The pointer to the POLICY_ENTRY_INDEXER union. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Filled in POLICY_ENTRY_INDEXER successfully. +**/ +typedef +EFI_STATUS +(* CONSTRUCT_POLICY_ENTRY_INDEXER) ( + IN POLICY_ENTRY_INDEXER *Indexer, + IN LIST_ENTRY *ParamPackage +); + +extern CONSTRUCT_POLICY_ENTRY_INDEXER mConstructPolicyEntryIndexer[]; +#endif diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.c b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.c new file mode 100644 index 0000000000..e4f6057f40 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.c @@ -0,0 +1,810 @@ +/** @file + The main process for IpSecConfig application. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include + +#include + +#include "IpSecConfig.h" +#include "Dump.h" +#include "Indexer.h" +#include "PolicyEntryOperation.h" +#include "Delete.h" +#include "Helper.h" + +// +// Used for ShellCommandLineParseEx only +// and to ensure user inputs are in valid format +// +SHELL_PARAM_ITEM mIpSecConfigParamList[] = { + { L"-p", TypeValue }, + { L"-a", TypeValue }, + { L"-i", TypeValue }, + { L"-e", TypeValue }, + { L"-d", TypeValue }, + { L"-f", TypeFlag }, + { L"-l", TypeFlag }, + { L"-enable", TypeFlag }, + { L"-disable", TypeFlag }, + { L"-status", TypeFlag }, + { L"-?", TypeFlag }, + + // + // SPD Selector + // + { L"--local", TypeValue }, + { L"--remote", TypeValue }, + { L"--proto", TypeValue }, + { L"--local-port", TypeValue }, + { L"--remote-port", TypeValue }, + { L"--icmp-type", TypeValue }, + { L"--icmp-code", TypeValue }, + + // + // SPD Data + // + { L"--name", TypeValue }, + { L"--packet-flag", TypeValue }, + { L"--action", TypeValue }, + { L"--lifebyte", TypeValue }, + { L"--lifetime-soft", TypeValue }, + { L"--lifetime", TypeValue }, + { L"--mode", TypeValue }, + { L"--tunnel-local", TypeValue }, + { L"--tunnel-remote", TypeValue }, + { L"--dont-fragment", TypeValue }, + { L"--ipsec-proto", TypeValue }, + { L"--auth-algo", TypeValue }, + { L"--encrypt-algo", TypeValue }, + + { L"--ext-sequence", TypeFlag }, + { L"--sequence-overflow", TypeFlag }, + { L"--fragment-check", TypeFlag }, + { L"--ext-sequence-", TypeFlag }, + { L"--sequence-overflow-", TypeFlag }, + { L"--fragment-check-", TypeFlag }, + + // + // SA ID + // --ipsec-proto + // + { L"--spi", TypeValue }, + { L"--tunnel-dest", TypeValue }, + { L"--tunnel-source", TypeValue }, + { L"--lookup-spi", TypeValue }, + { L"--lookup-ipsec-proto", TypeValue }, + { L"--lookup-dest", TypeValue }, + + // + // SA DATA + // --mode + // --auth-algo + // --encrypt-algo + // + { L"--sequence-number", TypeValue }, + { L"--antireplay-window", TypeValue }, + { L"--auth-key", TypeValue }, + { L"--encrypt-key", TypeValue }, + { L"--path-mtu", TypeValue }, + + // + // PAD ID + // + { L"--peer-id", TypeValue }, + { L"--peer-address", TypeValue }, + { L"--auth-proto", TypeValue }, + { L"--auth-method", TypeValue }, + { L"--ike-id", TypeValue }, + { L"--ike-id-", TypeValue }, + { L"--auth-data", TypeValue }, + { L"--revocation-data", TypeValue }, + { L"--lookup-peer-id", TypeValue }, + { L"--lookup-peer-address", TypeValue }, + + { NULL, TypeMax }, +}; + +// +// -P +// +STR2INT mMapPolicy[] = { + { L"SPD", IPsecConfigDataTypeSpd }, + { L"SAD", IPsecConfigDataTypeSad }, + { L"PAD", IPsecConfigDataTypePad }, + { NULL, 0 }, +}; + +// +// --proto +// +STR2INT mMapIpProtocol[] = { + { L"TCP", EFI_IP4_PROTO_TCP }, + { L"UDP", EFI_IP4_PROTO_UDP }, + { L"ICMP", EFI_IP4_PROTO_ICMP }, + { NULL, 0 }, +}; + +// +// --action +// +STR2INT mMapIpSecAction[] = { + { L"Bypass", EfiIPsecActionBypass }, + { L"Discard", EfiIPsecActionDiscard }, + { L"Protect", EfiIPsecActionProtect }, + { NULL, 0 }, +}; + +// +// --mode +// +STR2INT mMapIpSecMode[] = { + { L"Transport", EfiIPsecTransport }, + { L"Tunnel", EfiIPsecTunnel }, + { NULL, 0 }, +}; + +// +// --dont-fragment +// +STR2INT mMapDfOption[] = { + { L"clear", EfiIPsecTunnelClearDf }, + { L"set", EfiIPsecTunnelSetDf }, + { L"copy", EfiIPsecTunnelCopyDf }, + { NULL, 0 }, +}; + +// +// --ipsec-proto +// +STR2INT mMapIpSecProtocol[] = { + { L"AH", EfiIPsecAH }, + { L"ESP", EfiIPsecESP }, + { NULL, 0 }, +}; + +// +// --auth-algo +// +STR2INT mMapAuthAlgo[] = { + { L"NONE", IPSEC_AALG_NONE }, + { L"MD5HMAC", IPSEC_AALG_MD5HMAC }, + { L"SHA1HMAC", IPSEC_AALG_SHA1HMAC }, + { L"SHA2-256HMAC", IPSEC_AALG_SHA2_256HMAC }, + { L"SHA2-384HMAC", IPSEC_AALG_SHA2_384HMAC }, + { L"SHA2-512HMAC", IPSEC_AALG_SHA2_512HMAC }, + { L"AES-XCBC-MAC", IPSEC_AALG_AES_XCBC_MAC }, + { L"NULL", IPSEC_AALG_NULL }, + { NULL, 0 }, +}; + +// +// --encrypt-algo +// +STR2INT mMapEncAlgo[] = { + { L"NONE", IPSEC_EALG_NONE }, + { L"DESCBC", IPSEC_EALG_DESCBC }, + { L"3DESCBC", IPSEC_EALG_3DESCBC }, + { L"CASTCBC", IPSEC_EALG_CASTCBC }, + { L"BLOWFISHCBC", IPSEC_EALG_BLOWFISHCBC }, + { L"NULL", IPSEC_EALG_NULL }, + { L"AESCBC", IPSEC_EALG_AESCBC }, + { L"AESCTR", IPSEC_EALG_AESCTR }, + { L"AES-CCM-ICV8", IPSEC_EALG_AES_CCM_ICV8 }, + { L"AES-CCM-ICV12",IPSEC_EALG_AES_CCM_ICV12 }, + { L"AES-CCM-ICV16",IPSEC_EALG_AES_CCM_ICV16 }, + { L"AES-GCM-ICV8", IPSEC_EALG_AES_GCM_ICV8 }, + { L"AES-GCM-ICV12",IPSEC_EALG_AES_GCM_ICV12 }, + { L"AES-GCM-ICV16",IPSEC_EALG_AES_GCM_ICV16 }, + { NULL, 0 }, +}; + +// +// --auth-proto +// +STR2INT mMapAuthProto[] = { + { L"IKEv1", EfiIPsecAuthProtocolIKEv1 }, + { L"IKEv2", EfiIPsecAuthProtocolIKEv2 }, + { NULL, 0 }, +}; + +// +// --auth-method +// +STR2INT mMapAuthMethod[] = { + { L"PreSharedSecret", EfiIPsecAuthMethodPreSharedSecret }, + { L"Certificates", EfiIPsecAuthMethodCertificates }, + { NULL, 0 }, +}; + +EFI_IPSEC2_PROTOCOL *mIpSec; +EFI_IPSEC_CONFIG_PROTOCOL *mIpSecConfig; +EFI_HII_HANDLE mHiiHandle; +CHAR16 mAppName[] = L"IpSecConfig"; + +// +// Used for IpSecConfigRetriveCheckListByName only to check the validation of user input +// +VAR_CHECK_ITEM mIpSecConfigVarCheckList[] = { + { L"-enable", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-disable", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-status", BIT(1)|BIT(0), BIT(1), BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-p", BIT(1), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + + { L"-a", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-i", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-d", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-e", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-l", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + { L"-f", BIT(0), 0, BIT(2)|BIT(1)|BIT(0), 0 }, + + { L"-?", BIT(0), BIT(0), BIT(2)|BIT(1)|BIT(0), 0 }, + + // + // SPD Selector + // + { L"--local", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--remote", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--proto", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--local-port", 0, 0, BIT(2)|BIT(1), BIT(0) }, + { L"--remote-port", 0, 0, BIT(2)|BIT(1), BIT(0) }, + { L"--icmp-type", 0, 0, BIT(2)|BIT(1), BIT(1) }, + { L"--icmp-code", 0, 0, BIT(2)|BIT(1), BIT(1) }, + + // + // SPD Data + // + { L"--name", 0, 0, BIT(2), 0 }, + { L"--packet-flag", 0, 0, BIT(2), 0 }, + { L"--action", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--lifebyte", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--lifetime-soft", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--lifetime", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--mode", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--tunnel-local", 0, 0, BIT(2), 0 }, + { L"--tunnel-remote", 0, 0, BIT(2), 0 }, + { L"--dont-fragment", 0, 0, BIT(2), 0 }, + { L"--ipsec-proto", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--auth-algo", 0, 0, BIT(2)|BIT(1), 0 }, + { L"--encrypt-algo", 0, 0, BIT(2)|BIT(1), 0 }, + + { L"--ext-sequence", 0, 0, BIT(2), BIT(2) }, + { L"--sequence-overflow", 0, 0, BIT(2), BIT(2) }, + { L"--fragment-check", 0, 0, BIT(2), BIT(2) }, + { L"--ext-sequence-", 0, 0, BIT(2), BIT(3) }, + { L"--sequence-overflow-", 0, 0, BIT(2), BIT(3) }, + { L"--fragment-check-", 0, 0, BIT(2), BIT(3) }, + + // + // SA ID + // --ipsec-proto + // + { L"--spi", 0, 0, BIT(1), 0 }, + { L"--tunnel-dest", 0, 0, BIT(1), 0 }, + { L"--tunnel-source", 0, 0, BIT(1), 0 }, + { L"--lookup-spi", 0, 0, BIT(1), 0 }, + { L"--lookup-ipsec-proto", 0, 0, BIT(1), 0 }, + { L"--lookup-dest", 0, 0, BIT(1), 0 }, + + // + // SA DATA + // --mode + // --auth-algo + // --encrypt-algo + // + { L"--sequence-number", 0, 0, BIT(1), 0 }, + { L"--antireplay-window", 0, 0, BIT(1), 0 }, + { L"--auth-key", 0, 0, BIT(1), 0 }, + { L"--encrypt-key", 0, 0, BIT(1), 0 }, + { L"--path-mtu", 0, 0, BIT(1), 0 }, + + // + // The example to add a PAD: + // "-A --peer-id Mike [--peer-address 10.23.2.2] --auth-proto IKE1/IKE2 + // --auth-method PreSharedSeceret/Certificate --ike-id + // --auth-data 343343 --revocation-data 2342432" + // The example to delete a PAD: + // "-D * --lookup-peer-id Mike [--lookup-peer-address 10.23.2.2]" + // "-D 1" + // The example to edit a PAD: + // "-E * --lookup-peer-id Mike --auth-method Certificate" + + // + // PAD ID + // + { L"--peer-id", 0, 0, BIT(0), BIT(4) }, + { L"--peer-address", 0, 0, BIT(0), BIT(5) }, + { L"--auth-proto", 0, 0, BIT(0), 0 }, + { L"--auth-method", 0, 0, BIT(0), 0 }, + { L"--IKE-ID", 0, 0, BIT(0), BIT(6) }, + { L"--IKE-ID-", 0, 0, BIT(0), BIT(7) }, + { L"--auth-data", 0, 0, BIT(0), 0 }, + { L"--revocation-data", 0, 0, BIT(0), 0 }, + { L"--lookup-peer-id", 0, 0, BIT(0), BIT(4) }, + { L"--lookup-peer-address",0, 0, BIT(0), BIT(5) }, + + { NULL, 0, 0, 0, 0 }, +}; + +/** + The function to allocate the proper sized buffer for various + EFI interfaces. + + @param[in, out] Status Current status. + @param[in, out] Buffer Current allocated buffer, or NULL. + @param[in] BufferSize Current buffer size needed + + @retval TRUE If the buffer was reallocated and the caller should try the API again. + @retval FALSE If the buffer was not reallocated successfully. +**/ +BOOLEAN +GrowBuffer ( + IN OUT EFI_STATUS *Status, + IN OUT VOID **Buffer, + IN UINTN BufferSize + ) +{ + BOOLEAN TryAgain; + + ASSERT (Status != NULL); + ASSERT (Buffer != NULL); + + // + // If this is an initial request, buffer will be null with a new buffer size. + // + if ((NULL == *Buffer) && (BufferSize != 0)) { + *Status = EFI_BUFFER_TOO_SMALL; + } + + // + // If the status code is "buffer too small", resize the buffer. + // + TryAgain = FALSE; + if (*Status == EFI_BUFFER_TOO_SMALL) { + + if (*Buffer != NULL) { + FreePool (*Buffer); + } + + *Buffer = AllocateZeroPool (BufferSize); + + if (*Buffer != NULL) { + TryAgain = TRUE; + } else { + *Status = EFI_OUT_OF_RESOURCES; + } + } + + // + // If there's an error, free the buffer. + // + if (!TryAgain && EFI_ERROR (*Status) && (*Buffer != NULL)) { + FreePool (*Buffer); + *Buffer = NULL; + } + + return TryAgain; +} + +/** + Function returns an array of handles that support the requested protocol + in a buffer allocated from a pool. + + @param[in] SearchType Specifies which handle(s) are to be returned. + @param[in] Protocol Provides the protocol to search by. + This parameter is only valid for SearchType ByProtocol. + + @param[in] SearchKey Supplies the search key depending on the SearchType. + @param[in, out] NoHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested array of + handles that support Protocol. + + @retval EFI_SUCCESS The resulting array of handles was returned. + @retval Others Other mistake case. +**/ +EFI_STATUS +LocateHandle ( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol OPTIONAL, + IN VOID *SearchKey OPTIONAL, + IN OUT UINTN *NoHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + ASSERT (NoHandles != NULL); + ASSERT (Buffer != NULL); + + // + // Initialize for GrowBuffer loop. + // + Status = EFI_SUCCESS; + *Buffer = NULL; + BufferSize = 50 * sizeof (EFI_HANDLE); + + // + // Call the real function. + // + while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) { + Status = gBS->LocateHandle ( + SearchType, + Protocol, + SearchKey, + &BufferSize, + *Buffer + ); + } + + *NoHandles = BufferSize / sizeof (EFI_HANDLE); + if (EFI_ERROR (Status)) { + *NoHandles = 0; + } + + return Status; +} + +/** + Find the first instance of this protocol in the system and return its interface. + + @param[in] ProtocolGuid The guid of the protocol. + @param[out] Interface The pointer to the first instance of the protocol. + + @retval EFI_SUCCESS A protocol instance matching ProtocolGuid was found. + @retval Others A protocol instance matching ProtocolGuid was not found. +**/ +EFI_STATUS +LocateProtocol ( + IN EFI_GUID *ProtocolGuid, + OUT VOID **Interface + ) + +{ + EFI_STATUS Status; + UINTN NumberHandles; + UINTN Index; + EFI_HANDLE *Handles; + + *Interface = NULL; + Handles = NULL; + NumberHandles = 0; + + Status = LocateHandle (ByProtocol, ProtocolGuid, NULL, &NumberHandles, &Handles); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "LibLocateProtocol: Handle not found\n")); + return Status; + } + + for (Index = 0; Index < NumberHandles; Index++) { + ASSERT (Handles != NULL); + Status = gBS->HandleProtocol ( + Handles[Index], + ProtocolGuid, + Interface + ); + + if (!EFI_ERROR (Status)) { + break; + } + } + + if (Handles != NULL) { + FreePool (Handles); + } + + return Status; +} + +/** + Helper function called to check the conflicted flags. + + @param[in] CheckList The pointer to the VAR_CHECK_ITEM table. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS No conflicted flags. + @retval EFI_INVALID_PARAMETER The input parameter is erroroneous or there are some conflicted flags. +**/ +EFI_STATUS +IpSecConfigRetriveCheckListByName ( + IN VAR_CHECK_ITEM *CheckList, + IN LIST_ENTRY *ParamPackage +) +{ + + LIST_ENTRY *Node; + VAR_CHECK_ITEM *Item; + UINT32 Attribute1; + UINT32 Attribute2; + UINT32 Attribute3; + UINT32 Attribute4; + UINT32 Index; + + Attribute1 = 0; + Attribute2 = 0; + Attribute3 = 0; + Attribute4 = 0; + Index = 0; + Item = mIpSecConfigVarCheckList; + + if ((ParamPackage == NULL) || (CheckList == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Enumerate through the list of parameters that are input by user. + // + for (Node = GetFirstNode (ParamPackage); !IsNull (ParamPackage, Node); Node = GetNextNode (ParamPackage, Node)) { + if (((SHELL_PARAM_PACKAGE *) Node)->Name != NULL) { + // + // Enumerate the check list that defines the conflicted attributes of each flag. + // + for (; Item->VarName != NULL; Item++) { + if (StrCmp (((SHELL_PARAM_PACKAGE *) Node)->Name, Item->VarName) == 0) { + Index++; + if (Index == 1) { + Attribute1 = Item->Attribute1; + Attribute2 = Item->Attribute2; + Attribute3 = Item->Attribute3; + Attribute4 = Item->Attribute4; + } else { + Attribute1 &= Item->Attribute1; + Attribute2 |= Item->Attribute2; + Attribute3 &= Item->Attribute3; + Attribute4 |= Item->Attribute4; + if (Attribute1 != 0) { + return EFI_INVALID_PARAMETER; + } + + if (Attribute2 != 0) { + if ((Index == 2) && (StrCmp (Item->VarName, L"-p") == 0)) { + continue; + } + + return EFI_INVALID_PARAMETER; + } + + if (Attribute3 == 0) { + return EFI_INVALID_PARAMETER; + } + if (((Attribute4 & 0xFF) == 0x03) || ((Attribute4 & 0xFF) == 0x0C) || + ((Attribute4 & 0xFF) == 0x30) || ((Attribute4 & 0xFF) == 0xC0)) { + return EFI_INVALID_PARAMETER; + } + } + break; + } + } + + Item = mIpSecConfigVarCheckList; + } + } + + return EFI_SUCCESS; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for IpSecConfig application that parse the command line input and call an IpSecConfig process. + + @param[in] ImageHandle The image handle of this application. + @param[in] SystemTable The pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +EFIAPI +InitializeIpSecConfig ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_IPSEC_CONFIG_DATA_TYPE DataType; + UINT8 Value; + LIST_ENTRY *ParamPackage; + CONST CHAR16 *ValueStr; + CHAR16 *ProblemParam; + UINTN NonOptionCount; + + // + // Register our string package with HII and return the handle to it. + // + mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, IpSecConfigStrings, NULL); + ASSERT (mHiiHandle != NULL); + + Status = ShellCommandLineParseEx (mIpSecConfigParamList, &ParamPackage, &ProblemParam, TRUE, FALSE); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, ProblemParam); + goto Done; + } + + Status = IpSecConfigRetriveCheckListByName (mIpSecConfigVarCheckList, ParamPackage); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_MISTAKEN_OPTIONS), mHiiHandle); + goto Done; + } + + Status = LocateProtocol (&gEfiIpSecConfigProtocolGuid, (VOID **) &mIpSecConfig); + if (EFI_ERROR (Status) || mIpSecConfig == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName); + goto Done; + } + + Status = LocateProtocol (&gEfiIpSec2ProtocolGuid, (VOID **) &mIpSec); + if (EFI_ERROR (Status) || mIpSec == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT), mHiiHandle, mAppName); + goto Done; + } + + // + // Enable IPsec. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-enable")) { + if (!(mIpSec->DisabledFlag)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_ENABLE), mHiiHandle, mAppName); + } else { + // + // Set enable flag. + // + Value = IPSEC_STATUS_ENABLED; + Status = gRT->SetVariable ( + IPSECCONFIG_STATUS_NAME, + &gEfiIpSecConfigProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (Value), + &Value + ); + if (!EFI_ERROR (Status)) { + mIpSec->DisabledFlag = FALSE; + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_SUCCESS), mHiiHandle, mAppName); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ENABLE_FAILED), mHiiHandle, mAppName); + } + } + + goto Done; + } + + // + // Disable IPsec. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-disable")) { + if (mIpSec->DisabledFlag) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_DISABLE), mHiiHandle, mAppName); + } else { + // + // Set disable flag; however, leave it to be disabled in the callback function of DisabledEvent. + // + gBS->SignalEvent (mIpSec->DisabledEvent); + if (mIpSec->DisabledFlag) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_SUCCESS), mHiiHandle, mAppName); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_DISABLE_FAILED), mHiiHandle, mAppName); + } + } + + goto Done; + } + + // + //IPsec Status. + // + if (ShellCommandLineGetFlag (ParamPackage, L"-status")) { + if (mIpSec->DisabledFlag) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_DISABLE), mHiiHandle, mAppName); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_STATUS_ENABLE), mHiiHandle, mAppName); + } + goto Done; + } + + // + // Try to get policy database type. + // + DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) - 1; + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-p"); + if (ValueStr != NULL) { + DataType = (EFI_IPSEC_CONFIG_DATA_TYPE) MapStringToInteger (ValueStr, mMapPolicy); + if (DataType == -1) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle, mAppName, ValueStr); + goto Done; + } + } + + if (ShellCommandLineGetFlag (ParamPackage, L"-?")) { + if (DataType == -1) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_HELP), mHiiHandle); + goto Done; + } + + switch (DataType) { + case IPsecConfigDataTypeSpd: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SPD_HELP), mHiiHandle); + break; + + case IPsecConfigDataTypeSad: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_SAD_HELP), mHiiHandle); + break; + + case IPsecConfigDataTypePad: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_PAD_HELP), mHiiHandle); + break; + + default: + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_DB), mHiiHandle); + break; + } + + goto Done; + } + + NonOptionCount = ShellCommandLineGetCount (ParamPackage); + if ((NonOptionCount - 1) > 0) { + ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32) (NonOptionCount - 1)); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_REDUNDANCY_MANY), mHiiHandle, mAppName, ValueStr); + goto Done; + } + + if (DataType == -1) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_DB), mHiiHandle, mAppName); + goto Done; + } + + if (ShellCommandLineGetFlag (ParamPackage, L"-a")) { + Status = AddOrInsertPolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-i")) { + Status = AddOrInsertPolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-e")) { + Status = EditPolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-d")) { + Status = FlushOrDeletePolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-f")) { + Status = FlushOrDeletePolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else if (ShellCommandLineGetFlag (ParamPackage, L"-l")) { + Status = ListPolicyEntry (DataType, ParamPackage); + if (EFI_ERROR (Status)) { + goto Done; + } + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_UNKNOWN_OPERATION), mHiiHandle, mAppName); + goto Done; + } + +Done: + ShellCommandLineFreeVarList (ParamPackage); + HiiRemovePackages (mHiiHandle); + + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.h b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.h new file mode 100644 index 0000000000..17044fef79 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.h @@ -0,0 +1,150 @@ +/** @file + The internal structure and function declaration in IpSecConfig application. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IPSEC_CONFIG_H_ +#define _IPSEC_CONFIG_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define IPSECCONFIG_STATUS_NAME L"IpSecStatus" + +#define BIT(x) (UINT32) (1 << (x)) + +#define IPSEC_STATUS_DISABLED 0x0 +#define IPSEC_STATUS_ENABLED 0x1 + +#define EFI_IP4_PROTO_ICMP 0x1 +#define EFI_IP4_PROTO_TCP 0x6 +#define EFI_IP4_PROTO_UDP 0x11 + +#define EFI_IPSEC_ANY_PROTOCOL 0xFFFF +#define EFI_IPSEC_ANY_PORT 0 + +/// +/// IPsec Authentication Algorithm Definition +/// The number value definition is aligned to IANA assignment +/// +#define IPSEC_AALG_NONE 0x00 +#define IPSEC_AALG_MD5HMAC 0x01 +#define IPSEC_AALG_SHA1HMAC 0x02 +#define IPSEC_AALG_SHA2_256HMAC 0x05 +#define IPSEC_AALG_SHA2_384HMAC 0x06 +#define IPSEC_AALG_SHA2_512HMAC 0x07 +#define IPSEC_AALG_AES_XCBC_MAC 0x09 +#define IPSEC_AALG_NULL 0xFB + +/// +/// IPsec Encryption Algorithm Definition +/// The number value definition is aligned to IANA assignment +/// +#define IPSEC_EALG_NONE 0x00 +#define IPSEC_EALG_DESCBC 0x02 +#define IPSEC_EALG_3DESCBC 0x03 +#define IPSEC_EALG_CASTCBC 0x06 +#define IPSEC_EALG_BLOWFISHCBC 0x07 +#define IPSEC_EALG_NULL 0x0B +#define IPSEC_EALG_AESCBC 0x0C +#define IPSEC_EALG_AESCTR 0x0D +#define IPSEC_EALG_AES_CCM_ICV8 0x0E +#define IPSEC_EALG_AES_CCM_ICV12 0x0F +#define IPSEC_EALG_AES_CCM_ICV16 0x10 +#define IPSEC_EALG_AES_GCM_ICV8 0x12 +#define IPSEC_EALG_AES_GCM_ICV12 0x13 +#define IPSEC_EALG_AES_GCM_ICV16 0x14 + +typedef struct { + CHAR16 *VarName; + UINT32 Attribute1; + UINT32 Attribute2; + UINT32 Attribute3; + UINT32 Attribute4; +} VAR_CHECK_ITEM; + +typedef struct { + LIST_ENTRY Link; + CHAR16 *Name; + SHELL_PARAM_TYPE Type; + CHAR16 *Value; + UINTN OriginalPosition; +} SHELL_PARAM_PACKAGE; + +typedef struct { + CHAR16 *String; + UINT32 Integer; +} STR2INT; + +extern EFI_IPSEC_CONFIG_PROTOCOL *mIpSecConfig; +extern EFI_HII_HANDLE mHiiHandle; +extern CHAR16 mAppName[]; + +// +// -P +// +extern STR2INT mMapPolicy[]; + +// +// --proto +// +extern STR2INT mMapIpProtocol[]; + +// +// --action +// +extern STR2INT mMapIpSecAction[]; + +// +// --mode +// +extern STR2INT mMapIpSecMode[]; + +// +// --dont-fragment +// +extern STR2INT mMapDfOption[]; + +// +// --ipsec-proto +// +extern STR2INT mMapIpSecProtocol[]; +// +// --auth-algo +// +extern STR2INT mMapAuthAlgo[]; + +// +// --encrypt-algo +// +extern STR2INT mMapEncAlgo[]; +// +// --auth-proto +// +extern STR2INT mMapAuthProto[]; + +// +// --auth-method +// +extern STR2INT mMapAuthMethod[]; + +#endif diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf new file mode 100644 index 0000000000..52cf6b0341 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf @@ -0,0 +1,68 @@ +## @file +# Shell application IpSecConfig. +# +# This application is used to set and retrieve security and policy related information +# for the EFI IPsec protocol driver. +# +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = IpSecConfig + FILE_GUID = 0922E604-F5EC-42ef-980D-A35E9A2B1844 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeIpSecConfig + MODULE_UNI_FILE = IpSecConfig.uni + +[Sources] + IpSecConfigStrings.uni + IpSecConfig.c + IpSecConfig.h + Dump.c + Dump.h + Indexer.c + Indexer.h + Match.c + Match.h + Delete.h + Delete.c + Helper.c + Helper.h + ForEach.c + ForEach.h + PolicyEntryOperation.c + PolicyEntryOperation.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiApplicationEntryPoint + BaseMemoryLib + ShellLib + MemoryAllocationLib + DebugLib + HiiLib + NetLib + UefiLib + +[Protocols] + gEfiIpSec2ProtocolGuid ##CONSUMES + gEfiIpSecConfigProtocolGuid ##CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + IpSecConfigExtra.uni diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.uni b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.uni new file mode 100644 index 0000000000..4dd8d10d36 Binary files /dev/null and b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.uni differ diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni new file mode 100644 index 0000000000..bdc200e25e Binary files /dev/null and b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni differ diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni new file mode 100644 index 0000000000..4ebccd48b3 Binary files /dev/null and b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni differ diff --git a/Core/NetworkPkg/Application/IpsecConfig/Match.c b/Core/NetworkPkg/Application/IpsecConfig/Match.c new file mode 100644 index 0000000000..2ee763e607 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Match.c @@ -0,0 +1,163 @@ +/** @file + The implementation of match policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecConfig.h" +#include "Indexer.h" +#include "Match.h" + +/** + Private function to validate a buffer that should be filled with zero. + + @param[in] Memory The pointer to the buffer. + @param[in] Size The size of the buffer. + + @retval TRUE The memory is filled with zero. + @retval FALSE The memory isn't filled with zero. +**/ +BOOLEAN +IsMemoryZero ( + IN VOID *Memory, + IN UINTN Size + ) +{ + UINTN Index; + + for (Index = 0; Index < Size; Index++) { + if (*((UINT8 *) Memory + Index) != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Find the matching SPD with Indexer. + + @param[in] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in] Data The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure. + + @retval TRUE The matched SPD is found. + @retval FALSE The matched SPD is not found. +**/ +BOOLEAN +MatchSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *Selector, + IN EFI_IPSEC_SPD_DATA *Data, + IN SPD_ENTRY_INDEXER *Indexer + ) +{ + BOOLEAN Match; + + Match = FALSE; + if (!IsMemoryZero (Indexer->Name, MAX_PEERID_LEN)) { + if ((Data->Name != NULL) && (AsciiStrCmp ((CHAR8 *) Indexer->Name, (CHAR8 *) Data->Name) == 0)) { + Match = TRUE; + } + } else { + if (Indexer->Index == 0) { + Match = TRUE; + } + + Indexer->Index--; + } + + return Match; +} + +/** + Find the matching SAD with Indexer. + + @param[in] SaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[in] Data The pointer to the EFI_IPSEC_SA_DATA2 structure. + @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure. + + @retval TRUE The matched SAD is found. + @retval FALSE The matched SAD is not found. +**/ +BOOLEAN +MatchSadEntry ( + IN EFI_IPSEC_SA_ID *SaId, + IN EFI_IPSEC_SA_DATA2 *Data, + IN SAD_ENTRY_INDEXER *Indexer + ) +{ + BOOLEAN Match; + + Match = FALSE; + if (!IsMemoryZero (&Indexer->SaId, sizeof (EFI_IPSEC_SA_ID))) { + Match = (BOOLEAN) (CompareMem (&Indexer->SaId, SaId, sizeof (EFI_IPSEC_SA_ID)) == 0); + } else { + if (Indexer->Index == 0) { + Match = TRUE; + } + Indexer->Index--; + } + + return Match; +} + +/** + Find the matching PAD with Indexer. + + @param[in] PadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[in] Data The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] Indexer The pointer to the SPD_ENTRY_INDEXER structure. + + @retval TRUE The matched PAD is found. + @retval FALSE The matched PAD is not found. +**/ +BOOLEAN +MatchPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN EFI_IPSEC_PAD_DATA *Data, + IN PAD_ENTRY_INDEXER *Indexer + ) +{ + BOOLEAN Match; + + Match = FALSE; + if (!IsMemoryZero (&Indexer->PadId, sizeof (EFI_IPSEC_PAD_ID))) { + Match = (BOOLEAN) ((Indexer->PadId.PeerIdValid == PadId->PeerIdValid) && + ((PadId->PeerIdValid && + (StrCmp ( + (CONST CHAR16 *) Indexer->PadId.Id.PeerId, + (CONST CHAR16 *) PadId->Id.PeerId + ) == 0)) || + ((!PadId->PeerIdValid) && + (Indexer->PadId.Id.IpAddress.PrefixLength == PadId->Id.IpAddress.PrefixLength) && + (CompareMem ( + &Indexer->PadId.Id.IpAddress.Address, + &PadId->Id.IpAddress.Address, + sizeof (EFI_IP_ADDRESS) + ) == 0)))); + } else { + if (Indexer->Index == 0) { + Match = TRUE; + } + + Indexer->Index--; + } + + return Match; +} + +MATCH_POLICY_ENTRY mMatchPolicyEntry[] = { + (MATCH_POLICY_ENTRY) MatchSpdEntry, + (MATCH_POLICY_ENTRY) MatchSadEntry, + (MATCH_POLICY_ENTRY) MatchPadEntry +}; + diff --git a/Core/NetworkPkg/Application/IpsecConfig/Match.h b/Core/NetworkPkg/Application/IpsecConfig/Match.h new file mode 100644 index 0000000000..1d73c5cfbc --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/Match.h @@ -0,0 +1,41 @@ +/** @file + The internal structure and function declaration of + match policy entry function in IpSecConfig application. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _MATCH_H_ +#define _MATCH_H_ + +/** + The prototype for the MatchSpdEntry()/MatchSadEntry()/MatchPadEntry(). + The functionality is to find the matching SPD/SAD/PAD with Indexer. + + @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union. + @param[in] Data The pointer to corresponding Data. + @param[in] Indexer The pointer to the POLICY_ENTRY_INDEXER union. + + @retval TRUE The matched SPD/SAD/PAD is found. + @retval FALSE The matched SPD/SAD/PAD is not found. +**/ +typedef +BOOLEAN +(* MATCH_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN POLICY_ENTRY_INDEXER *Indexer + ); + +extern MATCH_POLICY_ENTRY mMatchPolicyEntry[]; + +#endif diff --git a/Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c b/Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c new file mode 100644 index 0000000000..6af9a4a547 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c @@ -0,0 +1,2081 @@ +/** @file + The implementation of policy entry operation function in IpSecConfig application. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecConfig.h" +#include "Indexer.h" +#include "Match.h" +#include "Helper.h" +#include "ForEach.h" +#include "PolicyEntryOperation.h" + +/** + Fill in EFI_IPSEC_SPD_SELECTOR through ParamPackage list. + + @param[out] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[in, out] Mask The pointer to the Mask. + + @retval EFI_SUCCESS Fill in EFI_IPSEC_SPD_SELECTOR successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CreateSpdSelector ( + OUT EFI_IPSEC_SPD_SELECTOR *Selector, + IN LIST_ENTRY *ParamPackage, + IN OUT UINT32 *Mask + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + CONST CHAR16 *ValueStr; + + Status = EFI_SUCCESS; + ReturnStatus = EFI_SUCCESS; + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local"); + if (ValueStr != NULL) { + Selector->LocalAddressCount = 1; + Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->LocalAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--local", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= LOCAL; + } + } + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote"); + if (ValueStr != NULL) { + Selector->RemoteAddressCount = 1; + Status = EfiInetAddrRange ((CHAR16 *) ValueStr, Selector->RemoteAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--remote", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= REMOTE; + } + } + + Selector->NextLayerProtocol = EFI_IPSEC_ANY_PROTOCOL; + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + Status = GetNumber ( + L"--proto", + (UINT16) -1, + &Selector->NextLayerProtocol, + sizeof (UINT16), + mMapIpProtocol, + ParamPackage, + FORMAT_NUMBER | FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= PROTO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Selector->LocalPort = EFI_IPSEC_ANY_PORT; + Selector->RemotePort = EFI_IPSEC_ANY_PORT; + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--local-port"); + if (ValueStr != NULL) { + Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->LocalPort, &Selector->LocalPortRange); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--local-port", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= LOCAL_PORT; + } + } + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--remote-port"); + if (ValueStr != NULL) { + Status = EfiInetPortRange ((CHAR16 *) ValueStr, &Selector->RemotePort, &Selector->RemotePortRange); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--remote-port", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= REMOTE_PORT; + } + } + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + Status = GetNumber ( + L"--icmp-type", + (UINT8) -1, + &Selector->LocalPort, + sizeof (UINT16), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= ICMP_TYPE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the member in EFI_IPSEC_SPD_SELECTOR. + // + Status = GetNumber ( + L"--icmp-code", + (UINT8) -1, + &Selector->RemotePort, + sizeof (UINT16), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= ICMP_CODE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + return ReturnStatus; +} + +/** + Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA through ParamPackage list. + + @param[out] Selector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[out] Data The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[out] Mask The pointer to the Mask. + @param[in] CreateNew The switch to create new. + + @retval EFI_SUCCESS Fill in EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CreateSpdEntry ( + OUT EFI_IPSEC_SPD_SELECTOR **Selector, + OUT EFI_IPSEC_SPD_DATA **Data, + IN LIST_ENTRY *ParamPackage, + OUT UINT32 *Mask, + IN BOOLEAN CreateNew + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + CONST CHAR16 *ValueStr; + UINTN DataSize; + + Status = EFI_SUCCESS; + *Mask = 0; + + *Selector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR) + 2 * sizeof (EFI_IP_ADDRESS_INFO)); + ASSERT (*Selector != NULL); + + (*Selector)->LocalAddress = (EFI_IP_ADDRESS_INFO *) (*Selector + 1); + (*Selector)->RemoteAddress = (*Selector)->LocalAddress + 1; + + ReturnStatus = CreateSpdSelector (*Selector, ParamPackage, Mask); + + // + // SPD DATA + // NOTE: Allocate enough memory and add padding for different arch. + // + DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SPD_DATA)); + DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_PROCESS_POLICY)); + DataSize += sizeof (EFI_IPSEC_TUNNEL_OPTION); + + *Data = AllocateZeroPool (DataSize); + ASSERT (*Data != NULL); + + (*Data)->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER ( + (*Data + 1), + sizeof (UINTN) + ); + (*Data)->ProcessingPolicy->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER ( + ((*Data)->ProcessingPolicy + 1), + sizeof (UINTN) + ); + + + // + // Convert user imput from string to integer, and fill in the Name in EFI_IPSEC_SPD_DATA. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--name"); + if (ValueStr != NULL) { + UnicodeStrToAsciiStrS (ValueStr, (CHAR8 *) (*Data)->Name, sizeof ((*Data)->Name)); + *Mask |= NAME; + } + + // + // Convert user imput from string to integer, and fill in the PackageFlag in EFI_IPSEC_SPD_DATA. + // + Status = GetNumber ( + L"--packet-flag", + (UINT8) -1, + &(*Data)->PackageFlag, + sizeof (UINT32), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= PACKET_FLAG; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the Action in EFI_IPSEC_SPD_DATA. + // + Status = GetNumber ( + L"--action", + (UINT8) -1, + &(*Data)->Action, + sizeof (UINT32), + mMapIpSecAction, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= ACTION; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the ExtSeqNum in EFI_IPSEC_SPD_DATA. + // + if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence")) { + (*Data)->ProcessingPolicy->ExtSeqNum = TRUE; + *Mask |= EXT_SEQUENCE; + } else if (ShellCommandLineGetFlag (ParamPackage, L"--ext-sequence-")) { + (*Data)->ProcessingPolicy->ExtSeqNum = FALSE; + *Mask |= EXT_SEQUENCE; + } + + // + // Convert user imput from string to integer, and fill in the SeqOverflow in EFI_IPSEC_SPD_DATA. + // + if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow")) { + (*Data)->ProcessingPolicy->SeqOverflow = TRUE; + *Mask |= SEQUENCE_OVERFLOW; + } else if (ShellCommandLineGetFlag (ParamPackage, L"--sequence-overflow-")) { + (*Data)->ProcessingPolicy->SeqOverflow = FALSE; + *Mask |= SEQUENCE_OVERFLOW; + } + + // + // Convert user imput from string to integer, and fill in the FragCheck in EFI_IPSEC_SPD_DATA. + // + if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check")) { + (*Data)->ProcessingPolicy->FragCheck = TRUE; + *Mask |= FRAGMENT_CHECK; + } else if (ShellCommandLineGetFlag (ParamPackage, L"--fragment-check-")) { + (*Data)->ProcessingPolicy->FragCheck = FALSE; + *Mask |= FRAGMENT_CHECK; + } + + // + // Convert user imput from string to integer, and fill in the ProcessingPolicy in EFI_IPSEC_SPD_DATA. + // + Status = GetNumber ( + L"--lifebyte", + (UINT64) -1, + &(*Data)->ProcessingPolicy->SaLifetime.ByteCount, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFEBYTE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lifetime", + (UINT64) -1, + &(*Data)->ProcessingPolicy->SaLifetime.HardLifetime, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFETIME; + } + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lifetime-soft", + (UINT64) -1, + &(*Data)->ProcessingPolicy->SaLifetime.SoftLifetime, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFETIME_SOFT; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + (*Data)->ProcessingPolicy->Mode = EfiIPsecTransport; + Status = GetNumber ( + L"--mode", + 0, + &(*Data)->ProcessingPolicy->Mode, + sizeof (UINT32), + mMapIpSecMode, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= MODE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-local"); + if (ValueStr != NULL) { + Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->LocalTunnelAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--tunnel-local", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= TUNNEL_LOCAL; + } + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-remote"); + if (ValueStr != NULL) { + Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->ProcessingPolicy->TunnelOption->RemoteTunnelAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--tunnel-remote", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= TUNNEL_REMOTE; + } + } + + (*Data)->ProcessingPolicy->TunnelOption->DF = EfiIPsecTunnelCopyDf; + Status = GetNumber ( + L"--dont-fragment", + 0, + &(*Data)->ProcessingPolicy->TunnelOption->DF, + sizeof (UINT32), + mMapDfOption, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= DONT_FRAGMENT; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + (*Data)->ProcessingPolicy->Proto = EfiIPsecESP; + Status = GetNumber ( + L"--ipsec-proto", + 0, + &(*Data)->ProcessingPolicy->Proto, + sizeof (UINT32), + mMapIpSecProtocol, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= IPSEC_PROTO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--encrypt-algo", + 0, + &(*Data)->ProcessingPolicy->EncAlgoId, + sizeof (UINT8), + mMapEncAlgo, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= ENCRYPT_ALGO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--auth-algo", + 0, + &(*Data)->ProcessingPolicy->AuthAlgoId, + sizeof (UINT8), + mMapAuthAlgo, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= AUTH_ALGO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Cannot check Mode against EfiIPsecTunnel, because user may want to change tunnel_remote only so the Mode is not set. + // + if ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE | DONT_FRAGMENT)) == 0) { + (*Data)->ProcessingPolicy->TunnelOption = NULL; + } + + if ((*Mask & (EXT_SEQUENCE | SEQUENCE_OVERFLOW | FRAGMENT_CHECK | LIFEBYTE | + LIFETIME_SOFT | LIFETIME | MODE | TUNNEL_LOCAL | TUNNEL_REMOTE | + DONT_FRAGMENT | IPSEC_PROTO | AUTH_ALGO | ENCRYPT_ALGO)) == 0) { + if ((*Data)->Action != EfiIPsecActionProtect) { + // + // User may not provide additional parameter for Protect action, so we cannot simply set ProcessingPolicy to NULL. + // + (*Data)->ProcessingPolicy = NULL; + } + } + + if (CreateNew) { + if ((*Mask & (LOCAL | REMOTE | PROTO | ACTION)) != (LOCAL | REMOTE | PROTO | ACTION)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--local --remote --proto --action" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else if (((*Data)->Action == EfiIPsecActionProtect) && + ((*Data)->ProcessingPolicy->Mode == EfiIPsecTunnel) && + ((*Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE))) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--tunnel-local --tunnel-remote" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } + } + + return ReturnStatus; +} + +/** + Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA2 through ParamPackage list. + + @param[out] SaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[out] Data The pointer to the EFI_IPSEC_SA_DATA2 structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[out] Mask The pointer to the Mask. + @param[in] CreateNew The switch to create new. + + @retval EFI_SUCCESS Fill in EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA2 successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CreateSadEntry ( + OUT EFI_IPSEC_SA_ID **SaId, + OUT EFI_IPSEC_SA_DATA2 **Data, + IN LIST_ENTRY *ParamPackage, + OUT UINT32 *Mask, + IN BOOLEAN CreateNew + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINTN AuthKeyLength; + UINTN EncKeyLength; + CONST CHAR16 *ValueStr; + CHAR8 *AsciiStr; + UINTN DataSize; + + Status = EFI_SUCCESS; + ReturnStatus = EFI_SUCCESS; + *Mask = 0; + AuthKeyLength = 0; + EncKeyLength = 0; + + *SaId = AllocateZeroPool (sizeof (EFI_IPSEC_SA_ID)); + ASSERT (*SaId != NULL); + + // + // Convert user imput from string to integer, and fill in the Spi in EFI_IPSEC_SA_ID. + // + Status = GetNumber (L"--spi", (UINT32) -1, &(*SaId)->Spi, sizeof (UINT32), NULL, ParamPackage, FORMAT_NUMBER); + if (!EFI_ERROR (Status)) { + *Mask |= SPI; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the Proto in EFI_IPSEC_SA_ID. + // + Status = GetNumber ( + L"--ipsec-proto", + 0, + &(*SaId)->Proto, + sizeof (EFI_IPSEC_PROTOCOL_TYPE), + mMapIpSecProtocol, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= IPSEC_PROTO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in EFI_IPSEC_SA_DATA2. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key"); + if (ValueStr != NULL) { + AuthKeyLength = StrLen (ValueStr); + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key"); + if (ValueStr != NULL) { + EncKeyLength = StrLen (ValueStr); + } + + // + // EFI_IPSEC_SA_DATA2: + // +------------ + // | EFI_IPSEC_SA_DATA2 + // +----------------------- + // | AuthKey + // +------------------------- + // | EncKey + // +------------------------- + // | SpdSelector + // + // Notes: To make sure the address alignment add padding after each data if needed. + // + DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA2)); + DataSize = ALIGN_VARIABLE (DataSize + AuthKeyLength); + DataSize = ALIGN_VARIABLE (DataSize + EncKeyLength); + DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IPSEC_SPD_SELECTOR)); + DataSize = ALIGN_VARIABLE (DataSize + sizeof (EFI_IP_ADDRESS_INFO)); + DataSize += sizeof (EFI_IP_ADDRESS_INFO); + + + + *Data = AllocateZeroPool (DataSize); + ASSERT (*Data != NULL); + + (*Data)->ManualSet = TRUE; + (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER (((*Data) + 1), sizeof (UINTN)); + (*Data)->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.AuthKey + AuthKeyLength), + sizeof (UINTN) + ); + (*Data)->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER ( + ((UINT8 *) (*Data)->AlgoInfo.EspAlgoInfo.EncKey + EncKeyLength), + sizeof (UINTN) + ); + (*Data)->SpdSelector->LocalAddress = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER ( + ((UINT8 *) (*Data)->SpdSelector + sizeof (EFI_IPSEC_SPD_SELECTOR)), + sizeof (UINTN)); + (*Data)->SpdSelector->RemoteAddress = (EFI_IP_ADDRESS_INFO *) ALIGN_POINTER ( + (*Data)->SpdSelector->LocalAddress + 1, + sizeof (UINTN) + ); + + (*Data)->Mode = EfiIPsecTransport; + Status = GetNumber ( + L"--mode", + 0, + &(*Data)->Mode, + sizeof (EFI_IPSEC_MODE), + mMapIpSecMode, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= MODE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // According to RFC 4303-3.3.3. The first packet sent using a given SA + // will contain a sequence number of 1. + // + (*Data)->SNCount = 1; + Status = GetNumber ( + L"--sequence-number", + (UINT64) -1, + &(*Data)->SNCount, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= SEQUENCE_NUMBER; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + (*Data)->AntiReplayWindows = 0; + Status = GetNumber ( + L"--antireplay-window", + (UINT8) -1, + &(*Data)->AntiReplayWindows, + sizeof (UINT8), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= SEQUENCE_NUMBER; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--encrypt-algo", + 0, + &(*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId, + sizeof (UINT8), + mMapEncAlgo, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= ENCRYPT_ALGO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--encrypt-key"); + if (ValueStr != NULL ) { + (*Data)->AlgoInfo.EspAlgoInfo.EncKeyLength = EncKeyLength; + AsciiStr = AllocateZeroPool (EncKeyLength + 1); + ASSERT (AsciiStr != NULL); + UnicodeStrToAsciiStrS (ValueStr, AsciiStr, EncKeyLength + 1); + CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.EncKey, AsciiStr, EncKeyLength); + FreePool (AsciiStr); + *Mask |= ENCRYPT_KEY; + } else { + (*Data)->AlgoInfo.EspAlgoInfo.EncKey = NULL; + } + + Status = GetNumber ( + L"--auth-algo", + 0, + &(*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId, + sizeof (UINT8), + mMapAuthAlgo, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= AUTH_ALGO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-key"); + if (ValueStr != NULL) { + (*Data)->AlgoInfo.EspAlgoInfo.AuthKeyLength = AuthKeyLength; + AsciiStr = AllocateZeroPool (AuthKeyLength + 1); + ASSERT (AsciiStr != NULL); + UnicodeStrToAsciiStrS (ValueStr, AsciiStr, AuthKeyLength + 1); + CopyMem ((*Data)->AlgoInfo.EspAlgoInfo.AuthKey, AsciiStr, AuthKeyLength); + FreePool (AsciiStr); + *Mask |= AUTH_KEY; + } else { + (*Data)->AlgoInfo.EspAlgoInfo.AuthKey = NULL; + } + + Status = GetNumber ( + L"--lifebyte", + (UINT64) -1, + &(*Data)->SaLifetime.ByteCount, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFEBYTE; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lifetime", + (UINT64) -1, + &(*Data)->SaLifetime.HardLifetime, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFETIME; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--lifetime-soft", + (UINT64) -1, + &(*Data)->SaLifetime.SoftLifetime, + sizeof (UINT64), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= LIFETIME_SOFT; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--path-mtu", + (UINT32) -1, + &(*Data)->PathMTU, + sizeof (UINT32), + NULL, + ParamPackage, + FORMAT_NUMBER + ); + if (!EFI_ERROR (Status)) { + *Mask |= PATH_MTU; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + // + // Convert user imput from string to integer, and fill in the DestAddress in EFI_IPSEC_SA_ID. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-dest"); + if (ValueStr != NULL) { + Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->TunnelDestinationAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--tunnel-dest", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= DEST; + } + } + + // + // Convert user input from string to integer, and fill in the DestAddress in EFI_IPSEC_SA_ID. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--tunnel-source"); + if (ValueStr != NULL) { + Status = EfiInetAddr2 ((CHAR16 *) ValueStr, &(*Data)->TunnelSourceAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--tunnel-source", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= SOURCE; + } + } + + // + // If it is TunnelMode, then check if the tunnel-source and --tunnel-dest are set + // + if ((*Data)->Mode == EfiIPsecTunnel) { + if ((*Mask & (DEST|SOURCE)) != (DEST|SOURCE)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--tunnel-source --tunnel-dest" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } + } + ReturnStatus = CreateSpdSelector ((*Data)->SpdSelector, ParamPackage, Mask); + + if (CreateNew) { + if ((*Mask & (SPI|IPSEC_PROTO|LOCAL|REMOTE)) != (SPI|IPSEC_PROTO|LOCAL|REMOTE)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--spi --ipsec-proto --local --remote" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + if ((*SaId)->Proto == EfiIPsecAH) { + if ((*Mask & AUTH_ALGO) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--auth-algo" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else if ((*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId != IPSEC_AALG_NONE && (*Mask & AUTH_KEY) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--auth-key" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } + } else { + if ((*Mask & (ENCRYPT_ALGO|AUTH_ALGO)) != (ENCRYPT_ALGO|AUTH_ALGO) ) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-algo --auth-algo" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else if ((*Data)->AlgoInfo.EspAlgoInfo.EncAlgoId != IPSEC_EALG_NONE && (*Mask & ENCRYPT_KEY) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-key" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else if ((*Data)->AlgoInfo.EspAlgoInfo.AuthAlgoId != IPSEC_AALG_NONE && (*Mask & AUTH_KEY) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--auth-key" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } + } + } + } + + return ReturnStatus; +} + +/** + Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA through ParamPackage list. + + @param[out] PadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[out] Data The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[out] Mask The pointer to the Mask. + @param[in] CreateNew The switch to create new. + + @retval EFI_SUCCESS Fill in EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CreatePadEntry ( + OUT EFI_IPSEC_PAD_ID **PadId, + OUT EFI_IPSEC_PAD_DATA **Data, + IN LIST_ENTRY *ParamPackage, + OUT UINT32 *Mask, + IN BOOLEAN CreateNew + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + SHELL_FILE_HANDLE FileHandle; + UINT64 FileSize; + UINTN AuthDataLength; + UINTN RevocationDataLength; + UINTN DataLength; + UINTN Index; + CONST CHAR16 *ValueStr; + UINTN DataSize; + + Status = EFI_SUCCESS; + ReturnStatus = EFI_SUCCESS; + *Mask = 0; + AuthDataLength = 0; + RevocationDataLength = 0; + + *PadId = AllocateZeroPool (sizeof (EFI_IPSEC_PAD_ID)); + ASSERT (*PadId != NULL); + + // + // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_ID. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-address"); + if (ValueStr != NULL) { + (*PadId)->PeerIdValid = FALSE; + Status = EfiInetAddrRange ((CHAR16 *) ValueStr, &(*PadId)->Id.IpAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE), + mHiiHandle, + mAppName, + L"--peer-address", + ValueStr + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + *Mask |= PEER_ADDRESS; + } + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--peer-id"); + if (ValueStr != NULL) { + (*PadId)->PeerIdValid = TRUE; + StrnCpyS ((CHAR16 *) (*PadId)->Id.PeerId, MAX_PEERID_LEN / sizeof (CHAR16), ValueStr, MAX_PEERID_LEN / sizeof (CHAR16) - 1); + *Mask |= PEER_ID; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data"); + if (ValueStr != NULL) { + if (ValueStr[0] == L'@') { + // + // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat" + // + Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED), + mHiiHandle, + mAppName, + &ValueStr[1] + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + Status = ShellGetFileSize (FileHandle, &FileSize); + ShellCloseFile (&FileHandle); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED), + mHiiHandle, + mAppName, + &ValueStr[1] + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else { + AuthDataLength = (UINTN) FileSize; + } + } + } else { + AuthDataLength = StrLen (ValueStr); + } + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data"); + if (ValueStr != NULL) { + RevocationDataLength = (StrLen (ValueStr) + 1) * sizeof (CHAR16); + } + + // + // Allocate Buffer for Data. Add padding after each struct to make sure the alignment + // in different Arch. + // + DataSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA)); + DataSize = ALIGN_VARIABLE (DataSize + AuthDataLength); + DataSize += RevocationDataLength; + + *Data = AllocateZeroPool (DataSize); + ASSERT (*Data != NULL); + + (*Data)->AuthData = (VOID *) ALIGN_POINTER ((*Data + 1), sizeof (UINTN)); + (*Data)->RevocationData = (VOID *) ALIGN_POINTER (((UINT8 *) (*Data + 1) + AuthDataLength), sizeof (UINTN)); + (*Data)->AuthProtocol = EfiIPsecAuthProtocolIKEv1; + + // + // Convert user imput from string to integer, and fill in EFI_IPSEC_PAD_DATA. + // + Status = GetNumber ( + L"--auth-proto", + 0, + &(*Data)->AuthProtocol, + sizeof (EFI_IPSEC_AUTH_PROTOCOL_TYPE), + mMapAuthProto, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= AUTH_PROTO; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + Status = GetNumber ( + L"--auth-method", + 0, + &(*Data)->AuthMethod, + sizeof (EFI_IPSEC_AUTH_METHOD), + mMapAuthMethod, + ParamPackage, + FORMAT_STRING + ); + if (!EFI_ERROR (Status)) { + *Mask |= AUTH_METHOD; + } + + if (Status == EFI_INVALID_PARAMETER) { + ReturnStatus = EFI_INVALID_PARAMETER; + } + + if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id")) { + (*Data)->IkeIdFlag = TRUE; + *Mask |= IKE_ID; + } + + if (ShellCommandLineGetFlag (ParamPackage, L"--ike-id-")) { + (*Data)->IkeIdFlag = FALSE; + *Mask |= IKE_ID; + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--auth-data"); + if (ValueStr != NULL) { + if (ValueStr[0] == L'@') { + // + // Input is a file: --auth-data "@fs1:\My Certificates\tom.dat" + // + + Status = ShellOpenFileByName (&ValueStr[1], &FileHandle, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED), + mHiiHandle, + mAppName, + &ValueStr[1] + ); + ReturnStatus = EFI_INVALID_PARAMETER; + (*Data)->AuthData = NULL; + } else { + DataLength = AuthDataLength; + Status = ShellReadFile (FileHandle, &DataLength, (*Data)->AuthData); + ShellCloseFile (&FileHandle); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_FILE_OPEN_FAILED), + mHiiHandle, + mAppName, + &ValueStr[1] + ); + ReturnStatus = EFI_INVALID_PARAMETER; + (*Data)->AuthData = NULL; + } else { + ASSERT (DataLength == AuthDataLength); + *Mask |= AUTH_DATA; + } + } + } else { + for (Index = 0; Index < AuthDataLength; Index++) { + ((CHAR8 *) (*Data)->AuthData)[Index] = (CHAR8) ValueStr[Index]; + } + (*Data)->AuthDataSize = AuthDataLength; + *Mask |= AUTH_DATA; + } + } + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"--revocation-data"); + if (ValueStr != NULL) { + CopyMem ((*Data)->RevocationData, ValueStr, RevocationDataLength); + (*Data)->RevocationDataSize = RevocationDataLength; + *Mask |= REVOCATION_DATA; + } else { + (*Data)->RevocationData = NULL; + } + + if (CreateNew) { + if ((*Mask & (PEER_ID | PEER_ADDRESS)) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--peer-id --peer-address" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } else if ((*Mask & (AUTH_METHOD | AUTH_DATA)) != (AUTH_METHOD | AUTH_DATA)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--auth-method --auth-data" + ); + ReturnStatus = EFI_INVALID_PARAMETER; + } + } + + return ReturnStatus; +} + +CREATE_POLICY_ENTRY mCreatePolicyEntry[] = { + (CREATE_POLICY_ENTRY) CreateSpdEntry, + (CREATE_POLICY_ENTRY) CreateSadEntry, + (CREATE_POLICY_ENTRY) CreatePadEntry +}; + +/** + Combine old SPD entry with new SPD entry. + + @param[in, out] OldSelector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in, out] OldData The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] NewSelector The pointer to the EFI_IPSEC_SPD_SELECTOR structure. + @param[in] NewData The pointer to the EFI_IPSEC_SPD_DATA structure. + @param[in] Mask The pointer to the Mask. + @param[out] CreateNew The switch to create new. + + @retval EFI_SUCCESS Combined successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CombineSpdEntry ( + IN OUT EFI_IPSEC_SPD_SELECTOR *OldSelector, + IN OUT EFI_IPSEC_SPD_DATA *OldData, + IN EFI_IPSEC_SPD_SELECTOR *NewSelector, + IN EFI_IPSEC_SPD_DATA *NewData, + IN UINT32 Mask, + OUT BOOLEAN *CreateNew + ) +{ + + // + // Process Selector + // + *CreateNew = FALSE; + if ((Mask & LOCAL) == 0) { + NewSelector->LocalAddressCount = OldSelector->LocalAddressCount; + NewSelector->LocalAddress = OldSelector->LocalAddress; + } else if ((NewSelector->LocalAddressCount != OldSelector->LocalAddressCount) || + (CompareMem (NewSelector->LocalAddress, OldSelector->LocalAddress, NewSelector->LocalAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) { + *CreateNew = TRUE; + } + + if ((Mask & REMOTE) == 0) { + NewSelector->RemoteAddressCount = OldSelector->RemoteAddressCount; + NewSelector->RemoteAddress = OldSelector->RemoteAddress; + } else if ((NewSelector->RemoteAddressCount != OldSelector->RemoteAddressCount) || + (CompareMem (NewSelector->RemoteAddress, OldSelector->RemoteAddress, NewSelector->RemoteAddressCount * sizeof (EFI_IP_ADDRESS_INFO)) != 0)) { + *CreateNew = TRUE; + } + + if ((Mask & PROTO) == 0) { + NewSelector->NextLayerProtocol = OldSelector->NextLayerProtocol; + } else if (NewSelector->NextLayerProtocol != OldSelector->NextLayerProtocol) { + *CreateNew = TRUE; + } + + switch (NewSelector->NextLayerProtocol) { + case EFI_IP4_PROTO_TCP: + case EFI_IP4_PROTO_UDP: + if ((Mask & LOCAL_PORT) == 0) { + NewSelector->LocalPort = OldSelector->LocalPort; + NewSelector->LocalPortRange = OldSelector->LocalPortRange; + } else if ((NewSelector->LocalPort != OldSelector->LocalPort) || + (NewSelector->LocalPortRange != OldSelector->LocalPortRange)) { + *CreateNew = TRUE; + } + + if ((Mask & REMOTE_PORT) == 0) { + NewSelector->RemotePort = OldSelector->RemotePort; + NewSelector->RemotePortRange = OldSelector->RemotePortRange; + } else if ((NewSelector->RemotePort != OldSelector->RemotePort) || + (NewSelector->RemotePortRange != OldSelector->RemotePortRange)) { + *CreateNew = TRUE; + } + break; + + case EFI_IP4_PROTO_ICMP: + if ((Mask & ICMP_TYPE) == 0) { + NewSelector->LocalPort = OldSelector->LocalPort; + } else if (NewSelector->LocalPort != OldSelector->LocalPort) { + *CreateNew = TRUE; + } + + if ((Mask & ICMP_CODE) == 0) { + NewSelector->RemotePort = OldSelector->RemotePort; + } else if (NewSelector->RemotePort != OldSelector->RemotePort) { + *CreateNew = TRUE; + } + break; + } + // + // Process Data + // + if ((Mask & NAME) != 0) { + AsciiStrCpyS ((CHAR8 *) OldData->Name, MAX_PEERID_LEN, (CHAR8 *) NewData->Name); + } + + if ((Mask & PACKET_FLAG) != 0) { + OldData->PackageFlag = NewData->PackageFlag; + } + + if ((Mask & ACTION) != 0) { + OldData->Action = NewData->Action; + } + + if (OldData->Action != EfiIPsecActionProtect) { + OldData->ProcessingPolicy = NULL; + } else { + // + // Protect + // + if (OldData->ProcessingPolicy == NULL) { + // + // Just point to new data if originally NULL. + // + OldData->ProcessingPolicy = NewData->ProcessingPolicy; + if (OldData->ProcessingPolicy->Mode == EfiIPsecTunnel && + (Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE) + ) { + // + // Change to Protect action and Tunnel mode, but without providing local/remote tunnel address. + // + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--tunnel-local --tunnel-remote" + ); + return EFI_INVALID_PARAMETER; + } + } else { + // + // Modify some of the data. + // + if ((Mask & EXT_SEQUENCE) != 0) { + OldData->ProcessingPolicy->ExtSeqNum = NewData->ProcessingPolicy->ExtSeqNum; + } + + if ((Mask & SEQUENCE_OVERFLOW) != 0) { + OldData->ProcessingPolicy->SeqOverflow = NewData->ProcessingPolicy->SeqOverflow; + } + + if ((Mask & FRAGMENT_CHECK) != 0) { + OldData->ProcessingPolicy->FragCheck = NewData->ProcessingPolicy->FragCheck; + } + + if ((Mask & LIFEBYTE) != 0) { + OldData->ProcessingPolicy->SaLifetime.ByteCount = NewData->ProcessingPolicy->SaLifetime.ByteCount; + } + + if ((Mask & LIFETIME_SOFT) != 0) { + OldData->ProcessingPolicy->SaLifetime.SoftLifetime = NewData->ProcessingPolicy->SaLifetime.SoftLifetime; + } + + if ((Mask & LIFETIME) != 0) { + OldData->ProcessingPolicy->SaLifetime.HardLifetime = NewData->ProcessingPolicy->SaLifetime.HardLifetime; + } + + if ((Mask & MODE) != 0) { + OldData->ProcessingPolicy->Mode = NewData->ProcessingPolicy->Mode; + } + + if ((Mask & IPSEC_PROTO) != 0) { + OldData->ProcessingPolicy->Proto = NewData->ProcessingPolicy->Proto; + } + + if ((Mask & AUTH_ALGO) != 0) { + OldData->ProcessingPolicy->AuthAlgoId = NewData->ProcessingPolicy->AuthAlgoId; + } + + if ((Mask & ENCRYPT_ALGO) != 0) { + OldData->ProcessingPolicy->EncAlgoId = NewData->ProcessingPolicy->EncAlgoId; + } + + if (OldData->ProcessingPolicy->Mode != EfiIPsecTunnel) { + OldData->ProcessingPolicy->TunnelOption = NULL; + } else { + if (OldData->ProcessingPolicy->TunnelOption == NULL) { + // + // Set from Transport mode to Tunnel mode, should ensure TUNNEL_LOCAL & TUNNEL_REMOTE both exists. + // + if ((Mask & (TUNNEL_LOCAL | TUNNEL_REMOTE)) != (TUNNEL_LOCAL | TUNNEL_REMOTE)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--tunnel-local --tunnel-remote" + ); + return EFI_INVALID_PARAMETER; + } + + OldData->ProcessingPolicy->TunnelOption = NewData->ProcessingPolicy->TunnelOption; + } else { + if ((Mask & TUNNEL_LOCAL) != 0) { + CopyMem ( + &OldData->ProcessingPolicy->TunnelOption->LocalTunnelAddress, + &NewData->ProcessingPolicy->TunnelOption->LocalTunnelAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + + if ((Mask & TUNNEL_REMOTE) != 0) { + CopyMem ( + &OldData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress, + &NewData->ProcessingPolicy->TunnelOption->RemoteTunnelAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + + if ((Mask & DONT_FRAGMENT) != 0) { + OldData->ProcessingPolicy->TunnelOption->DF = NewData->ProcessingPolicy->TunnelOption->DF; + } + } + } + } + } + + return EFI_SUCCESS; +} + +/** + Combine old SAD entry with new SAD entry. + + @param[in, out] OldSaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[in, out] OldData The pointer to the EFI_IPSEC_SA_DATA2 structure. + @param[in] NewSaId The pointer to the EFI_IPSEC_SA_ID structure. + @param[in] NewData The pointer to the EFI_IPSEC_SA_DATA2 structure. + @param[in] Mask The pointer to the Mask. + @param[out] CreateNew The switch to create new. + + @retval EFI_SUCCESS Combined successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CombineSadEntry ( + IN OUT EFI_IPSEC_SA_ID *OldSaId, + IN OUT EFI_IPSEC_SA_DATA2 *OldData, + IN EFI_IPSEC_SA_ID *NewSaId, + IN EFI_IPSEC_SA_DATA2 *NewData, + IN UINT32 Mask, + OUT BOOLEAN *CreateNew + ) +{ + + *CreateNew = FALSE; + + if ((Mask & SPI) == 0) { + NewSaId->Spi = OldSaId->Spi; + } else if (NewSaId->Spi != OldSaId->Spi) { + *CreateNew = TRUE; + } + + if ((Mask & IPSEC_PROTO) == 0) { + NewSaId->Proto = OldSaId->Proto; + } else if (NewSaId->Proto != OldSaId->Proto) { + *CreateNew = TRUE; + } + + if ((Mask & DEST) == 0) { + CopyMem (&NewData->TunnelDestinationAddress, &OldData->TunnelDestinationAddress, sizeof (EFI_IP_ADDRESS)); + } else if (CompareMem (&NewData->TunnelDestinationAddress, &OldData->TunnelDestinationAddress, sizeof (EFI_IP_ADDRESS)) != 0) { + *CreateNew = TRUE; + } + + if ((Mask & SOURCE) == 0) { + CopyMem (&NewData->TunnelSourceAddress, &OldData->TunnelSourceAddress, sizeof (EFI_IP_ADDRESS)); + } else if (CompareMem (&NewData->TunnelSourceAddress, &OldData->TunnelSourceAddress, sizeof (EFI_IP_ADDRESS)) != 0) { + *CreateNew = TRUE; + } + // + // Process SA_DATA. + // + if ((Mask & MODE) != 0) { + OldData->Mode = NewData->Mode; + } + + if ((Mask & SEQUENCE_NUMBER) != 0) { + OldData->SNCount = NewData->SNCount; + } + + if ((Mask & ANTIREPLAY_WINDOW) != 0) { + OldData->AntiReplayWindows = NewData->AntiReplayWindows; + } + + if ((Mask & AUTH_ALGO) != 0) { + OldData->AlgoInfo.EspAlgoInfo.AuthAlgoId = NewData->AlgoInfo.EspAlgoInfo.AuthAlgoId; + } + + if ((Mask & AUTH_KEY) != 0) { + OldData->AlgoInfo.EspAlgoInfo.AuthKey = NewData->AlgoInfo.EspAlgoInfo.AuthKey; + OldData->AlgoInfo.EspAlgoInfo.AuthKeyLength = NewData->AlgoInfo.EspAlgoInfo.AuthKeyLength; + } + + if ((Mask & ENCRYPT_ALGO) != 0) { + OldData->AlgoInfo.EspAlgoInfo.EncAlgoId = NewData->AlgoInfo.EspAlgoInfo.EncAlgoId; + } + + if ((Mask & ENCRYPT_KEY) != 0) { + OldData->AlgoInfo.EspAlgoInfo.EncKey = NewData->AlgoInfo.EspAlgoInfo.EncKey; + OldData->AlgoInfo.EspAlgoInfo.EncKeyLength = NewData->AlgoInfo.EspAlgoInfo.EncKeyLength; + } + + if (NewSaId->Proto == EfiIPsecAH) { + if ((Mask & (ENCRYPT_ALGO | ENCRYPT_KEY)) != 0) { + // + // Should not provide encrypt_* if AH. + // + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_UNWANTED_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-algo --encrypt-key" + ); + return EFI_INVALID_PARAMETER; + } + } + + if (NewSaId->Proto == EfiIPsecESP && OldSaId->Proto == EfiIPsecAH) { + // + // AH -> ESP + // Should provide encrypt_algo at least. + // + if ((Mask & ENCRYPT_ALGO) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-algo" + ); + return EFI_INVALID_PARAMETER; + } + + // + // Encrypt_key should be provided if algorithm is not NONE. + // + if (NewData->AlgoInfo.EspAlgoInfo.EncAlgoId != IPSEC_EALG_NONE && (Mask & ENCRYPT_KEY) == 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_PARAMETER), + mHiiHandle, + mAppName, + L"--encrypt-algo" + ); + return EFI_INVALID_PARAMETER; + } + } + + if ((Mask & LIFEBYTE) != 0) { + OldData->SaLifetime.ByteCount = NewData->SaLifetime.ByteCount; + } + + if ((Mask & LIFETIME_SOFT) != 0) { + OldData->SaLifetime.SoftLifetime = NewData->SaLifetime.SoftLifetime; + } + + if ((Mask & LIFETIME) != 0) { + OldData->SaLifetime.HardLifetime = NewData->SaLifetime.HardLifetime; + } + + if ((Mask & PATH_MTU) != 0) { + OldData->PathMTU = NewData->PathMTU; + } + // + // Process SpdSelector. + // + if (OldData->SpdSelector == NULL) { + if ((Mask & (LOCAL | REMOTE | PROTO | LOCAL_PORT | REMOTE_PORT | ICMP_TYPE | ICMP_CODE)) != 0) { + if ((Mask & (LOCAL | REMOTE | PROTO)) != (LOCAL | REMOTE | PROTO)) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS), + mHiiHandle, + mAppName, + L"--local --remote --proto" + ); + return EFI_INVALID_PARAMETER; + } + + OldData->SpdSelector = NewData->SpdSelector; + } + } else { + if ((Mask & LOCAL) != 0) { + OldData->SpdSelector->LocalAddressCount = NewData->SpdSelector->LocalAddressCount; + OldData->SpdSelector->LocalAddress = NewData->SpdSelector->LocalAddress; + } + + if ((Mask & REMOTE) != 0) { + OldData->SpdSelector->RemoteAddressCount = NewData->SpdSelector->RemoteAddressCount; + OldData->SpdSelector->RemoteAddress = NewData->SpdSelector->RemoteAddress; + } + + if ((Mask & PROTO) != 0) { + OldData->SpdSelector->NextLayerProtocol = NewData->SpdSelector->NextLayerProtocol; + } + + if (OldData->SpdSelector != NULL) { + switch (OldData->SpdSelector->NextLayerProtocol) { + case EFI_IP4_PROTO_TCP: + case EFI_IP4_PROTO_UDP: + if ((Mask & LOCAL_PORT) != 0) { + OldData->SpdSelector->LocalPort = NewData->SpdSelector->LocalPort; + } + + if ((Mask & REMOTE_PORT) != 0) { + OldData->SpdSelector->RemotePort = NewData->SpdSelector->RemotePort; + } + break; + + case EFI_IP4_PROTO_ICMP: + if ((Mask & ICMP_TYPE) != 0) { + OldData->SpdSelector->LocalPort = (UINT8) NewData->SpdSelector->LocalPort; + } + + if ((Mask & ICMP_CODE) != 0) { + OldData->SpdSelector->RemotePort = (UINT8) NewData->SpdSelector->RemotePort; + } + break; + } + } + } + + return EFI_SUCCESS; +} + +/** + Combine old PAD entry with new PAD entry. + + @param[in, out] OldPadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[in, out] OldData The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] NewPadId The pointer to the EFI_IPSEC_PAD_ID structure. + @param[in] NewData The pointer to the EFI_IPSEC_PAD_DATA structure. + @param[in] Mask The pointer to the Mask. + @param[out] CreateNew The switch to create new. + + @retval EFI_SUCCESS Combined successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +EFI_STATUS +CombinePadEntry ( + IN OUT EFI_IPSEC_PAD_ID *OldPadId, + IN OUT EFI_IPSEC_PAD_DATA *OldData, + IN EFI_IPSEC_PAD_ID *NewPadId, + IN EFI_IPSEC_PAD_DATA *NewData, + IN UINT32 Mask, + OUT BOOLEAN *CreateNew + ) +{ + + *CreateNew = FALSE; + + if ((Mask & (PEER_ID | PEER_ADDRESS)) == 0) { + CopyMem (NewPadId, OldPadId, sizeof (EFI_IPSEC_PAD_ID)); + } else { + if ((Mask & PEER_ID) != 0) { + if (OldPadId->PeerIdValid) { + if (StrCmp ((CONST CHAR16 *) OldPadId->Id.PeerId, (CONST CHAR16 *) NewPadId->Id.PeerId) != 0) { + *CreateNew = TRUE; + } + } else { + *CreateNew = TRUE; + } + } else { + // + // MASK & PEER_ADDRESS + // + if (OldPadId->PeerIdValid) { + *CreateNew = TRUE; + } else { + if ((CompareMem (&OldPadId->Id.IpAddress.Address, &NewPadId->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0) || + (OldPadId->Id.IpAddress.PrefixLength != NewPadId->Id.IpAddress.PrefixLength)) { + *CreateNew = TRUE; + } + } + } + } + + if ((Mask & AUTH_PROTO) != 0) { + OldData->AuthProtocol = NewData->AuthProtocol; + } + + if ((Mask & AUTH_METHOD) != 0) { + OldData->AuthMethod = NewData->AuthMethod; + } + + if ((Mask & IKE_ID) != 0) { + OldData->IkeIdFlag = NewData->IkeIdFlag; + } + + if ((Mask & AUTH_DATA) != 0) { + OldData->AuthDataSize = NewData->AuthDataSize; + OldData->AuthData = NewData->AuthData; + } + + if ((Mask & REVOCATION_DATA) != 0) { + OldData->RevocationDataSize = NewData->RevocationDataSize; + OldData->RevocationData = NewData->RevocationData; + } + + return EFI_SUCCESS; +} + +COMBINE_POLICY_ENTRY mCombinePolicyEntry[] = { + (COMBINE_POLICY_ENTRY) CombineSpdEntry, + (COMBINE_POLICY_ENTRY) CombineSadEntry, + (COMBINE_POLICY_ENTRY) CombinePadEntry +}; + +/** + Edit entry information in the database. + + @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure. + @param[in] Data The pointer to the data. + @param[in] Context The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure. + + @retval EFI_SUCCESS Continue the iteration. + @retval EFI_ABORTED Abort the iteration. +**/ +EFI_STATUS +EditOperatePolicyEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN EDIT_POLICY_ENTRY_CONTEXT *Context + ) +{ + EFI_STATUS Status; + BOOLEAN CreateNew; + + if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) { + ASSERT (Context->DataType < 3); + + Status = mCombinePolicyEntry[Context->DataType] ( + Selector, + Data, + Context->Selector, + Context->Data, + Context->Mask, + &CreateNew + ); + if (!EFI_ERROR (Status)) { + if (CreateNew) { + // + // Insert new entry before old entry + // + Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Context->Selector, + Data, + Selector + ); + ASSERT_EFI_ERROR (Status); + // + // Delete old entry + // + Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Selector, + NULL, + NULL + ); + ASSERT_EFI_ERROR (Status); + } else { + Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Context->Selector, + Data, + NULL + ); + } + } + + Context->Status = Status; + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Edit entry information in database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Edit entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval Others Some mistaken case. +**/ +EFI_STATUS +EditPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + EDIT_POLICY_ENTRY_CONTEXT Context; + CONST CHAR16 *ValueStr; + + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-e"); + if (ValueStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr); + return EFI_NOT_FOUND; + } + + Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage); + if (!EFI_ERROR (Status)) { + Context.DataType = DataType; + Context.Status = EFI_NOT_FOUND; + Status = mCreatePolicyEntry[DataType] (&Context.Selector, &Context.Data, ParamPackage, &Context.Mask, FALSE); + if (!EFI_ERROR (Status)) { + ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) EditOperatePolicyEntry, &Context); + Status = Context.Status; + } + + if (Context.Selector != NULL) { + gBS->FreePool (Context.Selector); + } + + if (Context.Data != NULL) { + gBS->FreePool (Context.Data); + } + } + + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr); + } else if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_EDIT_FAILED), mHiiHandle, mAppName); + } + + return Status; + +} + +/** + Insert entry information in database. + + @param[in] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR structure. + @param[in] Data The pointer to the data. + @param[in] Context The pointer to the INSERT_POLICY_ENTRY_CONTEXT structure. + + @retval EFI_SUCCESS Continue the iteration. + @retval EFI_ABORTED Abort the iteration. +**/ +EFI_STATUS +InsertPolicyEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN INSERT_POLICY_ENTRY_CONTEXT *Context + ) +{ + // + // Found the entry which we want to insert before. + // + if (mMatchPolicyEntry[Context->DataType] (Selector, Data, &Context->Indexer)) { + + Context->Status = mIpSecConfig->SetData ( + mIpSecConfig, + Context->DataType, + Context->Selector, + Context->Data, + Selector + ); + // + // Abort the iteration after the insertion. + // + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Insert or add entry information in database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Insert or add entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval EFI_BUFFER_TOO_SMALL The entry already existed. + @retval EFI_UNSUPPORTED The operation is not supported. + @retval Others Some mistaken case. +**/ +EFI_STATUS +AddOrInsertPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ) +{ + EFI_STATUS Status; + EFI_IPSEC_CONFIG_SELECTOR *Selector; + VOID *Data; + INSERT_POLICY_ENTRY_CONTEXT Context; + UINT32 Mask; + UINTN DataSize; + CONST CHAR16 *ValueStr; + + Status = mCreatePolicyEntry[DataType] (&Selector, &Data, ParamPackage, &Mask, TRUE); + if (!EFI_ERROR (Status)) { + // + // Find if the Selector to be inserted already exists. + // + DataSize = 0; + Status = mIpSecConfig->GetData ( + mIpSecConfig, + DataType, + Selector, + &DataSize, + NULL + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_ALREADY_EXISTS), mHiiHandle, mAppName); + } else if (ShellCommandLineGetFlag (ParamPackage, L"-a")) { + Status = mIpSecConfig->SetData ( + mIpSecConfig, + DataType, + Selector, + Data, + NULL + ); + } else { + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-i"); + if (ValueStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED), mHiiHandle, mAppName, ValueStr); + return EFI_NOT_FOUND; + } + + Status = mConstructPolicyEntryIndexer[DataType] (&Context.Indexer, ParamPackage); + if (!EFI_ERROR (Status)) { + Context.DataType = DataType; + Context.Status = EFI_NOT_FOUND; + Context.Selector = Selector; + Context.Data = Data; + + ForeachPolicyEntry (DataType, (VISIT_POLICY_ENTRY) InsertPolicyEntry, &Context); + Status = Context.Status; + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INDEX_NOT_FOUND), mHiiHandle, mAppName, ValueStr); + } + } + } + + gBS->FreePool (Selector); + gBS->FreePool (Data); + } + + if (Status == EFI_UNSUPPORTED) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_UNSUPPORT), mHiiHandle, mAppName); + } else if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IPSEC_CONFIG_INSERT_FAILED), mHiiHandle, mAppName); + } + + return Status; +} diff --git a/Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h b/Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h new file mode 100644 index 0000000000..4514d2f8d2 --- /dev/null +++ b/Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h @@ -0,0 +1,159 @@ +/** @file + The function declaration of policy entry operation in IpSecConfig application. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _POLICY_ENTRY_OPERATION_H_ +#define _POLICY_ENTRY_OPERATION_H_ + +#define LOCAL BIT(0) +#define REMOTE BIT(1) +#define PROTO BIT(2) +#define LOCAL_PORT BIT(3) +#define REMOTE_PORT BIT(4) +#define ICMP_TYPE BIT(5) +#define ICMP_CODE BIT(6) +#define NAME BIT(7) +#define PACKET_FLAG BIT(8) +#define ACTION BIT(9) +#define EXT_SEQUENCE BIT(10) +#define SEQUENCE_OVERFLOW BIT(11) +#define FRAGMENT_CHECK BIT(12) +#define LIFEBYTE BIT(13) +#define LIFETIME_SOFT BIT(14) +#define LIFETIME BIT(15) +#define MODE BIT(16) +#define TUNNEL_LOCAL BIT(17) +#define TUNNEL_REMOTE BIT(18) +#define DONT_FRAGMENT BIT(19) +#define IPSEC_PROTO BIT(20) +#define AUTH_ALGO BIT(21) +#define ENCRYPT_ALGO BIT(22) +#define SPI BIT(23) +#define DEST BIT(24) +#define SEQUENCE_NUMBER BIT(25) +#define ANTIREPLAY_WINDOW BIT(26) +#define AUTH_KEY BIT(27) +#define ENCRYPT_KEY BIT(28) +#define PATH_MTU BIT(29) +#define SOURCE BIT(30) + +#define PEER_ID BIT(0) +#define PEER_ADDRESS BIT(1) +#define AUTH_PROTO BIT(2) +#define AUTH_METHOD BIT(3) +#define IKE_ID BIT(4) +#define AUTH_DATA BIT(5) +#define REVOCATION_DATA BIT(6) + +typedef struct { + EFI_IPSEC_CONFIG_DATA_TYPE DataType; + EFI_IPSEC_CONFIG_SELECTOR *Selector; // Data to be inserted. + VOID *Data; + UINT32 Mask; + POLICY_ENTRY_INDEXER Indexer; + EFI_STATUS Status; // Indicate whether insertion succeeds. +} EDIT_POLICY_ENTRY_CONTEXT; + +typedef struct { + EFI_IPSEC_CONFIG_DATA_TYPE DataType; + EFI_IPSEC_CONFIG_SELECTOR *Selector; // Data to be inserted. + VOID *Data; + POLICY_ENTRY_INDEXER Indexer; + EFI_STATUS Status; // Indicate whether insertion succeeds. +} INSERT_POLICY_ENTRY_CONTEXT; + +/** + The prototype for the CreateSpdEntry()/CreateSadEntry()/CreatePadEntry(). + Fill in EFI_IPSEC_CONFIG_SELECTOR and corresponding data thru ParamPackage list. + + @param[out] Selector The pointer to the EFI_IPSEC_CONFIG_SELECTOR union. + @param[out] Data The pointer to corresponding data. + @param[in] ParamPackage The pointer to the ParamPackage list. + @param[out] Mask The pointer to the Mask. + @param[in] CreateNew The switch to create new. + + @retval EFI_SUCCESS Filled in EFI_IPSEC_CONFIG_SELECTOR and corresponding data successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +typedef +EFI_STATUS +(*CREATE_POLICY_ENTRY) ( + OUT EFI_IPSEC_CONFIG_SELECTOR **Selector, + OUT VOID **Data, + IN LIST_ENTRY *ParamPackage, + OUT UINT32 *Mask, + IN BOOLEAN CreateNew + ); + +/** + The prototype for the CombineSpdEntry()/CombineSadEntry()/CombinePadEntry(). + Combine old SPD/SAD/PAD entry with new SPD/SAD/PAD entry. + + @param[in, out] OldSelector The pointer to the old EFI_IPSEC_CONFIG_SELECTOR union. + @param[in, out] OldData The pointer to the corresponding old data. + @param[in] NewSelector The pointer to the new EFI_IPSEC_CONFIG_SELECTOR union. + @param[in] NewData The pointer to the corresponding new data. + @param[in] Mask The pointer to the Mask. + @param[out] CreateNew The switch to create new. + + @retval EFI_SUCCESS Combined successfully. + @retval EFI_INVALID_PARAMETER Invalid user input parameter. + +**/ +typedef +EFI_STATUS +(* COMBINE_POLICY_ENTRY) ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *OldSelector, + IN OUT VOID *OldData, + IN EFI_IPSEC_CONFIG_SELECTOR *NewSelector, + IN VOID *NewData, + IN UINT32 Mask, + OUT BOOLEAN *CreateNew + ); + +/** + Insert or add entry information in database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Insert or add entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval EFI_BUFFER_TOO_SMALL The entry already existed. + @retval EFI_UNSUPPORTED The operation is not supported./ + @retval Others Some mistaken case. +**/ +EFI_STATUS +AddOrInsertPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ); + +/** + Edit entry information in the database according to datatype. + + @param[in] DataType The value of EFI_IPSEC_CONFIG_DATA_TYPE. + @param[in] ParamPackage The pointer to the ParamPackage list. + + @retval EFI_SUCCESS Edit entry information successfully. + @retval EFI_NOT_FOUND Can't find the specified entry. + @retval Others Some mistaken case. +**/ +EFI_STATUS +EditPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN LIST_ENTRY *ParamPackage + ); +#endif diff --git a/Core/NetworkPkg/Application/Ping6/Ia32/Tsc.c b/Core/NetworkPkg/Application/Ping6/Ia32/Tsc.c new file mode 100644 index 0000000000..e2eae99077 --- /dev/null +++ b/Core/NetworkPkg/Application/Ping6/Ia32/Tsc.c @@ -0,0 +1,28 @@ +/** @file + The implement to read TSC in IA32 platform. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +/** + Reads and returns the current value of the Time Stamp Counter (TSC). + + @return The current value of TSC. + +**/ +UINT64 +ReadTime () +{ + return AsmReadTsc (); +} diff --git a/Core/NetworkPkg/Application/Ping6/Ipf/Itc.c b/Core/NetworkPkg/Application/Ping6/Ipf/Itc.c new file mode 100644 index 0000000000..131e5c0e30 --- /dev/null +++ b/Core/NetworkPkg/Application/Ping6/Ipf/Itc.c @@ -0,0 +1,28 @@ +/** @file + The implement to read ITC in IA64 platform. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +/** + Reads and returns the current value of the Interval Timer Counter Register (ITC). + + @return The current value of ITC. + +**/ +UINT64 +ReadTime () +{ + return AsmReadItc (); +} diff --git a/Core/NetworkPkg/Application/Ping6/Ping6.c b/Core/NetworkPkg/Application/Ping6/Ping6.c new file mode 100644 index 0000000000..596ee3b007 --- /dev/null +++ b/Core/NetworkPkg/Application/Ping6/Ping6.c @@ -0,0 +1,1178 @@ +/** @file + The implementation for Ping6 application. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Ping6.h" + +SHELL_PARAM_ITEM Ping6ParamList[] = { + { + L"-l", + TypeValue + }, + { + L"-n", + TypeValue + }, + { + L"-s", + TypeValue + }, + { + L"-?", + TypeFlag + }, + { + NULL, + TypeMax + }, +}; + +// +// Global Variables in Ping6 application. +// +EFI_HII_HANDLE mHiiHandle; +CONST CHAR16 *mIp6DstString; +CONST CHAR16 *mIp6SrcString; +UINT64 mFrequency = 0; +/** + Get and calculate the frequency in tick/ms. + The result is saved in the globle variable mFrequency + + @retval EFI_SUCCESS Calculated the frequency successfully. + @retval Others Failed to calculate the frequency. + +**/ +EFI_STATUS +Ping6GetFrequency ( + VOID + ) +{ + EFI_STATUS Status; + EFI_CPU_ARCH_PROTOCOL *Cpu; + UINT64 CurrentTick; + UINT64 TimerPeriod; + + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Cpu->GetTimerValue (Cpu, 0, &CurrentTick, &TimerPeriod); + + if (EFI_ERROR (Status)) { + // + // For NT32 Simulator only. 358049 is a similar value to keep timer granularity. + // Set the timer period by ourselves. + // + TimerPeriod = (UINT64) NTTIMERPERIOD; + } + // + // The timer period is in femtosecond (1 femtosecond is 1e-15 second). + // So 1e+12 is divided by timer period to produce the freq in tick/ms. + // + mFrequency = DivU64x64Remainder (1000000000000ULL, TimerPeriod, NULL); + + return EFI_SUCCESS; +} + +/** + Get and calculate the duration in ms. + + @param[in] Begin The start point of time. + @param[in] End The end point of time. + + @return The duration in ms. + +**/ +UINT64 +Ping6CalculateTick ( + IN UINT64 Begin, + IN UINT64 End + ) +{ + ASSERT (End > Begin); + return DivU64x64Remainder (End - Begin, mFrequency, NULL); +} + +/** + Destroy IPING6_ICMP6_TX_INFO, and recollect the memory. + + @param[in] TxInfo The pointer to PING6_ICMP6_TX_INFO. + +**/ +VOID +Ping6DestroyTxInfo ( + IN PING6_ICMP6_TX_INFO *TxInfo + ) +{ + EFI_IP6_TRANSMIT_DATA *TxData; + EFI_IP6_FRAGMENT_DATA *FragData; + UINTN Index; + + ASSERT (TxInfo != NULL); + + if (TxInfo->Token != NULL) { + + if (TxInfo->Token->Event != NULL) { + gBS->CloseEvent (TxInfo->Token->Event); + } + + TxData = TxInfo->Token->Packet.TxData; + if (TxData != NULL) { + + if (TxData->OverrideData != NULL) { + FreePool (TxData->OverrideData); + } + + if (TxData->ExtHdrs != NULL) { + FreePool (TxData->ExtHdrs); + } + + for (Index = 0; Index < TxData->FragmentCount; Index++) { + FragData = TxData->FragmentTable[Index].FragmentBuffer; + if (FragData != NULL) { + FreePool (FragData); + } + } + } + + FreePool (TxInfo->Token); + } + + FreePool (TxInfo); +} + +/** + Match the request, and reply with SequenceNum/TimeStamp. + + @param[in] Private The pointer to PING6_PRIVATE_DATA. + @param[in] Packet The pointer to ICMP6_ECHO_REQUEST_REPLY. + + @retval EFI_SUCCESS The match is successful. + @retval EFI_NOT_FOUND The reply can't be matched with any request. + +**/ +EFI_STATUS +Ping6MatchEchoReply ( + IN PING6_PRIVATE_DATA *Private, + IN ICMP6_ECHO_REQUEST_REPLY *Packet + ) +{ + PING6_ICMP6_TX_INFO *TxInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { + TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link); + + if ((TxInfo->SequenceNum == Packet->SequenceNum) && (TxInfo->TimeStamp == Packet->TimeStamp)) { + Private->RxCount++; + RemoveEntryList (&TxInfo->Link); + Ping6DestroyTxInfo (TxInfo); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + The original intention is to send a request. + Currently, the application retransmits an icmp6 echo request packet + per second in sendnumber times that is specified by the user. + Because nothing can be done here, all things move to the timer rountine. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to Context. + +**/ +VOID +EFIAPI +Ping6OnEchoRequestSent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ +} + +/** + receive reply, match and print reply infomation. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to context. + +**/ +VOID +EFIAPI +Ping6OnEchoReplyReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + PING6_PRIVATE_DATA *Private; + EFI_IP6_COMPLETION_TOKEN *RxToken; + EFI_IP6_RECEIVE_DATA *RxData; + ICMP6_ECHO_REQUEST_REPLY *Reply; + UINT32 PayLoad; + UINT64 Rtt; + CHAR8 Near; + + Private = (PING6_PRIVATE_DATA *) Context; + + if (Private->Status == EFI_ABORTED) { + return; + } + + RxToken = &Private->RxToken; + RxData = RxToken->Packet.RxData; + Reply = RxData->FragmentTable[0].FragmentBuffer; + PayLoad = RxData->DataLength; + + if (RxData->Header->NextHeader != IP6_ICMP) { + goto ON_EXIT; + } + + if (!IP6_IS_MULTICAST (&Private->DstAddress) && + !EFI_IP6_EQUAL (&RxData->Header->SourceAddress, &Private->DstAddress)) { + goto ON_EXIT; + } + + if ((Reply->Type != ICMP_V6_ECHO_REPLY) || (Reply->Code != 0)) { + goto ON_EXIT; + } + + if (PayLoad != Private->BufferSize) { + goto ON_EXIT; + } + // + // Check whether the reply matches the sent request before. + // + Status = Ping6MatchEchoReply (Private, Reply); + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + // + // Display statistics on this icmp6 echo reply packet. + // + Rtt = Ping6CalculateTick (Reply->TimeStamp, ReadTime ()); + if (Rtt != 0) { + Near = (CHAR8) '='; + } else { + Near = (CHAR8) '<'; + } + + Private->RttSum += Rtt; + Private->RttMin = Private->RttMin > Rtt ? Rtt : Private->RttMin; + Private->RttMax = Private->RttMax < Rtt ? Rtt : Private->RttMax; + + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_PING6_REPLY_INFO), + mHiiHandle, + PayLoad, + mIp6DstString, + Reply->SequenceNum, + RxData->Header->HopLimit, + Near, + Rtt + ); + +ON_EXIT: + + if (Private->RxCount < Private->SendNum) { + // + // Continue to receive icmp6 echo reply packets. + // + RxToken->Status = EFI_ABORTED; + + Status = Private->Ip6->Receive (Private->Ip6, RxToken); + + if (EFI_ERROR (Status)) { + Private->Status = EFI_ABORTED; + } + } else { + // + // All reply have already been received from the dest host. + // + Private->Status = EFI_SUCCESS; + } + // + // Singal to recycle the each rxdata here, not at the end of process. + // + gBS->SignalEvent (RxData->RecycleSignal); +} + +/** + Initial EFI_IP6_COMPLETION_TOKEN. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + @param[in] TimeStamp The TimeStamp of request. + @param[in] SequenceNum The SequenceNum of request. + + @return The pointer of EFI_IP6_COMPLETION_TOKEN. + +**/ +EFI_IP6_COMPLETION_TOKEN * +Ping6GenerateToken ( + IN PING6_PRIVATE_DATA *Private, + IN UINT64 TimeStamp, + IN UINT16 SequenceNum + ) +{ + EFI_STATUS Status; + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_IP6_TRANSMIT_DATA *TxData; + ICMP6_ECHO_REQUEST_REPLY *Request; + + Request = AllocateZeroPool (Private->BufferSize); + + if (Request == NULL) { + return NULL; + } + // + // Assembly icmp6 echo request packet. + // + Request->Type = ICMP_V6_ECHO_REQUEST; + Request->Code = 0; + Request->SequenceNum = SequenceNum; + Request->TimeStamp = TimeStamp; + Request->Identifier = 0; + // + // Leave check sum to ip6 layer, since it has no idea of source address + // selection. + // + Request->Checksum = 0; + + TxData = AllocateZeroPool (sizeof (EFI_IP6_TRANSMIT_DATA)); + + if (TxData == NULL) { + FreePool (Request); + return NULL; + } + // + // Assembly ipv6 token for transmit. + // + TxData->OverrideData = 0; + TxData->ExtHdrsLength = 0; + TxData->ExtHdrs = NULL; + TxData->DataLength = Private->BufferSize; + TxData->FragmentCount = 1; + TxData->FragmentTable[0].FragmentBuffer = (VOID *) Request; + TxData->FragmentTable[0].FragmentLength = Private->BufferSize; + + Token = AllocateZeroPool (sizeof (EFI_IP6_COMPLETION_TOKEN)); + + if (Token == NULL) { + FreePool (Request); + FreePool (TxData); + return NULL; + } + + Token->Status = EFI_ABORTED; + Token->Packet.TxData = TxData; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ping6OnEchoRequestSent, + Private, + &Token->Event + ); + + if (EFI_ERROR (Status)) { + FreePool (Request); + FreePool (TxData); + FreePool (Token); + return NULL; + } + + return Token; +} + +/** + Transmit the EFI_IP6_COMPLETION_TOKEN. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + + @retval EFI_SUCCESS Transmitted successfully. + @retval EFI_OUT_OF_RESOURCES No memory is available on the platform. + @retval others Transmitted unsuccessfully. + +**/ +EFI_STATUS +Ping6SendEchoRequest ( + IN PING6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + PING6_ICMP6_TX_INFO *TxInfo; + + TxInfo = AllocateZeroPool (sizeof (PING6_ICMP6_TX_INFO)); + + if (TxInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxInfo->TimeStamp = ReadTime (); + TxInfo->SequenceNum = (UINT16) (Private->TxCount + 1); + + TxInfo->Token = Ping6GenerateToken ( + Private, + TxInfo->TimeStamp, + TxInfo->SequenceNum + ); + + if (TxInfo->Token == NULL) { + Ping6DestroyTxInfo (TxInfo); + return EFI_OUT_OF_RESOURCES; + } + + Status = Private->Ip6->Transmit (Private->Ip6, TxInfo->Token); + + if (EFI_ERROR (Status)) { + Ping6DestroyTxInfo (TxInfo); + return Status; + } + + InsertTailList (&Private->TxList, &TxInfo->Link); + Private->TxCount++; + + return EFI_SUCCESS; +} + +/** + Place a completion token into the receive packet queue to receive the echo reply. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + + @retval EFI_SUCCESS Put the token into the receive packet queue successfully. + @retval others Put the token into the receive packet queue unsuccessfully. + +**/ +EFI_STATUS +Ping6ReceiveEchoReply ( + IN PING6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + ZeroMem (&Private->RxToken, sizeof (EFI_IP6_COMPLETION_TOKEN)); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ping6OnEchoReplyReceived, + Private, + &Private->RxToken.Event + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Private->RxToken.Status = EFI_NOT_READY; + + return Private->Ip6->Receive (Private->Ip6, &Private->RxToken); +} + +/** + Remove the timeout request from the list. + + @param[in] Event A EFI_EVENT type event. + @param[in] Context The pointer to Context. + +**/ +VOID +EFIAPI +Ping6OnTimerRoutine ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + PING6_PRIVATE_DATA *Private; + PING6_ICMP6_TX_INFO *TxInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + UINT64 Time; + + Private = (PING6_PRIVATE_DATA *) Context; + + // + // Retransmit icmp6 echo request packets per second in sendnumber times. + // + if (Private->TxCount < Private->SendNum) { + + Status = Ping6SendEchoRequest (Private); + if (Private->TxCount != 0){ + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SEND_REQUEST), mHiiHandle, Private->TxCount + 1); + } + } + } + // + // Check whether any icmp6 echo request in the list timeout. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { + TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link); + Time = Ping6CalculateTick (TxInfo->TimeStamp, ReadTime ()); + + // + // Remove the timeout echo request from txlist. + // + if (Time > PING6_DEFAULT_TIMEOUT) { + + if (EFI_ERROR (TxInfo->Token->Status)) { + Private->Ip6->Cancel (Private->Ip6, TxInfo->Token); + } + // + // Remove the timeout icmp6 echo request from list. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_TIMEOUT), mHiiHandle, TxInfo->SequenceNum); + + RemoveEntryList (&TxInfo->Link); + Ping6DestroyTxInfo (TxInfo); + + if (IsListEmpty (&Private->TxList) && (Private->TxCount == Private->SendNum)) { + // + // All the left icmp6 echo request in the list timeout. + // + Private->Status = EFI_TIMEOUT; + } + } + } +} + +/** + Create a valid IP6 instance. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + + @retval EFI_SUCCESS Create a valid IP6 instance successfully. + @retval EFI_ABORTED Locate handle with ip6 service binding protocol unsuccessfully. + @retval EFI_INVALID_PARAMETER The source address is unspecified when the destination address is a link -ocal address. + @retval EFI_OUT_OF_RESOURCES No memory is available on the platform. + @retval EFI_NOT_FOUND The source address is not found. +**/ +EFI_STATUS +Ping6CreateIp6Instance ( + IN PING6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN HandleIndex; + UINTN HandleNum; + EFI_HANDLE *HandleBuffer; + EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_IP6_CONFIG_DATA Ip6Config; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + UINTN IfInfoSize; + EFI_IPv6_ADDRESS *Addr; + UINTN AddrIndex; + + HandleBuffer = NULL; + Ip6Sb = NULL; + IfInfo = NULL; + IfInfoSize = 0; + + // + // Locate all the handles with ip6 service binding protocol. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + &HandleNum, + &HandleBuffer + ); + if (EFI_ERROR (Status) || (HandleNum == 0)) { + return EFI_ABORTED; + } + // + // Source address is required when pinging a link-local address on multi- + // interfaces host. + // + if (NetIp6IsLinkLocalAddr (&Private->DstAddress) && + NetIp6IsUnspecifiedAddr (&Private->SrcAddress) && + (HandleNum > 1)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SOURCE), mHiiHandle); + Status = EFI_INVALID_PARAMETER; + goto ON_ERROR; + } + // + // For each ip6 protocol, check interface addresses list. + // + for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) { + + Ip6Sb = NULL; + IfInfo = NULL; + IfInfoSize = 0; + + Status = gBS->HandleProtocol ( + HandleBuffer[HandleIndex], + &gEfiIp6ServiceBindingProtocolGuid, + (VOID **) &Ip6Sb + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (NetIp6IsUnspecifiedAddr (&Private->SrcAddress)) { + // + // No need to match interface address. + // + break; + } else { + // + // Ip6config protocol and ip6 service binding protocol are installed + // on the same handle. + // + Status = gBS->HandleProtocol ( + HandleBuffer[HandleIndex], + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Ip6Cfg + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + // + // Get the interface information size. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeInterfaceInfo, + &IfInfoSize, + NULL + ); + + if (Status != EFI_BUFFER_TOO_SMALL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + + IfInfo = AllocateZeroPool (IfInfoSize); + + if (IfInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + // + // Get the interface info. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeInterfaceInfo, + &IfInfoSize, + IfInfo + ); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6CFG_GETDATA), mHiiHandle, Status); + goto ON_ERROR; + } + // + // Check whether the source address is one of the interface addresses. + // + for (AddrIndex = 0; AddrIndex < IfInfo->AddressInfoCount; AddrIndex++) { + + Addr = &(IfInfo->AddressInfo[AddrIndex].Address); + if (EFI_IP6_EQUAL (&Private->SrcAddress, Addr)) { + // + // Match a certain interface address. + // + break; + } + } + + if (AddrIndex < IfInfo->AddressInfoCount) { + // + // Found a nic handle with right interface address. + // + break; + } + } + + FreePool (IfInfo); + IfInfo = NULL; + } + // + // No exact interface address matched. + // + + if (HandleIndex == HandleNum) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_SOURCE_NOT_FOUND), mHiiHandle, mIp6SrcString); + Status = EFI_NOT_FOUND; + goto ON_ERROR; + } + + Private->NicHandle = HandleBuffer[HandleIndex]; + + ASSERT (Ip6Sb != NULL); + Status = Ip6Sb->CreateChild (Ip6Sb, &Private->Ip6ChildHandle); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip6ChildHandle, + &gEfiIp6ProtocolGuid, + (VOID **) &Private->Ip6, + Private->ImageHandle, + Private->Ip6ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + ZeroMem (&Ip6Config, sizeof (EFI_IP6_CONFIG_DATA)); + + // + // Configure the ip6 instance for icmp6 packet exchange. + // + Ip6Config.DefaultProtocol = 58; + Ip6Config.AcceptAnyProtocol = FALSE; + Ip6Config.AcceptIcmpErrors = TRUE; + Ip6Config.AcceptPromiscuous = FALSE; + Ip6Config.TrafficClass = 0; + Ip6Config.HopLimit = 128; + Ip6Config.FlowLabel = 0; + Ip6Config.ReceiveTimeout = 0; + Ip6Config.TransmitTimeout = 0; + + IP6_COPY_ADDRESS (&Ip6Config.StationAddress, &Private->SrcAddress); + + IP6_COPY_ADDRESS (&Ip6Config.DestinationAddress, &Private->DstAddress); + + Status = Private->Ip6->Configure (Private->Ip6, &Ip6Config); + + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_IP6_CONFIG), mHiiHandle, Status); + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + if (IfInfo != NULL) { + FreePool (IfInfo); + } + + if ((Ip6Sb != NULL) && (Private->Ip6ChildHandle != NULL)) { + Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle); + } + + return Status; +} + +/** + Destroy the IP6 instance. + + @param[in] Private The pointer of PING6_PRIVATE_DATA. + +**/ +VOID +Ping6DestroyIp6Instance ( + IN PING6_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *Ip6Sb; + + gBS->CloseProtocol ( + Private->Ip6ChildHandle, + &gEfiIp6ProtocolGuid, + Private->ImageHandle, + Private->Ip6ChildHandle + ); + + Status = gBS->HandleProtocol ( + Private->NicHandle, + &gEfiIp6ServiceBindingProtocolGuid, + (VOID **) &Ip6Sb + ); + + if (!EFI_ERROR(Status)) { + Ip6Sb->DestroyChild (Ip6Sb, Private->Ip6ChildHandle); + } +} + +/** + The Ping6 Process. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SendNumber The send request count. + @param[in] BufferSize The send buffer size. + @param[in] SrcAddress The source IPv6 address. + @param[in] DstAddress The destination IPv6 address. + + @retval EFI_SUCCESS The ping6 processed successfullly. + @retval others The ping6 processed unsuccessfully. + +**/ +EFI_STATUS +Ping6 ( + IN EFI_HANDLE ImageHandle, + IN UINT32 SendNumber, + IN UINT32 BufferSize, + IN EFI_IPv6_ADDRESS *SrcAddress, + IN EFI_IPv6_ADDRESS *DstAddress + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Key; + PING6_PRIVATE_DATA *Private; + PING6_ICMP6_TX_INFO *TxInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + Private = AllocateZeroPool (sizeof (PING6_PRIVATE_DATA)); + + ASSERT (Private != NULL); + + Private->ImageHandle = ImageHandle; + Private->SendNum = SendNumber; + Private->BufferSize = BufferSize; + Private->RttMin = ~((UINT64 )(0x0)); + Private->Status = EFI_NOT_READY; + + InitializeListHead (&Private->TxList); + + IP6_COPY_ADDRESS (&Private->SrcAddress, SrcAddress); + IP6_COPY_ADDRESS (&Private->DstAddress, DstAddress); + + // + // Open and configure a ip6 instance for ping6. + // + Status = Ping6CreateIp6Instance (Private); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Print the command line itself. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_START), mHiiHandle, mIp6DstString, Private->BufferSize); + // + // Create a ipv6 token to receive the first icmp6 echo reply packet. + // + Status = Ping6ReceiveEchoReply (Private); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Create and start timer to send icmp6 echo request packet per second. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ping6OnTimerRoutine, + Private, + &Private->Timer + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Create a ipv6 token to send the first icmp6 echo request packet. + // + Status = Ping6SendEchoRequest (Private); + // + // EFI_NOT_READY for IPsec is enable and IKE is not established. + // + if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) { + if(Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_NOSOURCE_INDOMAIN), mHiiHandle, mIp6DstString); + } + + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + Private->Timer, + TimerPeriodic, + PING6_ONE_SECOND + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Control the ping6 process by two factors: + // 1. Hot key + // 2. Private->Status + // 2.1. success means all icmp6 echo request packets get reply packets. + // 2.2. timeout means the last icmp6 echo reply request timeout to get reply. + // 2.3. noready means ping6 process is on-the-go. + // + while (Private->Status == EFI_NOT_READY) { + Private->Ip6->Poll (Private->Ip6); + + // + // Terminate the ping6 process by 'esc' or 'ctl-c'. + // + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + + if (!EFI_ERROR(Status)) { + if ((Key.UnicodeChar == 0x1b) || (Key.UnicodeChar == 0x03) || + ((Key.UnicodeChar == 0) && (Key.ScanCode == SCAN_ESC))) { + goto ON_STAT; + } + } + } + +ON_STAT: + // + // Display the statistics in all. + // + gBS->SetTimer (Private->Timer, TimerCancel, 0); + + if (Private->TxCount != 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_PING6_STAT), + mHiiHandle, + Private->TxCount, + Private->RxCount, + (100 * (Private->TxCount - Private->RxCount)) / Private->TxCount, + Private->RttSum + ); + } + + if (Private->RxCount != 0) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_PING6_RTT), + mHiiHandle, + Private->RttMin, + Private->RttMax, + DivU64x64Remainder (Private->RttSum, Private->RxCount, NULL) + ); + } + +ON_EXIT: + + if (Private != NULL) { + Private->Status = EFI_ABORTED; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->TxList) { + TxInfo = BASE_CR (Entry, PING6_ICMP6_TX_INFO, Link); + + Status = Private->Ip6->Cancel (Private->Ip6, TxInfo->Token); + + RemoveEntryList (&TxInfo->Link); + Ping6DestroyTxInfo (TxInfo); + } + + if (Private->Timer != NULL) { + gBS->CloseEvent (Private->Timer); + } + + if (Private->Ip6 != NULL) { + Status = Private->Ip6->Cancel (Private->Ip6, &Private->RxToken); + } + + if (Private->RxToken.Event != NULL) { + gBS->CloseEvent (Private->RxToken.Event); + } + + if (Private->Ip6ChildHandle != NULL) { + Ping6DestroyIp6Instance (Private); + } + + FreePool (Private); + } + + return Status; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for the Ping6 application that parses the command line input and calls the Ping6 process. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETETR Input parameters combination is invalid. + @retval Others Some errors occur. + +**/ +EFI_STATUS +EFIAPI +InitializePing6 ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS DstAddress; + EFI_IPv6_ADDRESS SrcAddress; + UINT64 BufferSize; + UINTN SendNumber; + LIST_ENTRY *ParamPackage; + CONST CHAR16 *ValueStr; + CONST CHAR16 *ValueStrPtr; + UINTN NonOptionCount; + + // + // Register our string package with HII and return the handle to it. + // + mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, Ping6Strings, NULL); + ASSERT (mHiiHandle != NULL); + + Status = ShellCommandLineParseEx (Ping6ParamList, &ParamPackage, NULL, TRUE, FALSE); + if (EFI_ERROR(Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle); + goto ON_EXIT; + } + + if (ShellCommandLineGetFlag (ParamPackage, L"-?")) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_HELP), mHiiHandle); + goto ON_EXIT; + } + + SendNumber = 10; + BufferSize = 16; + + // + // Parse the paramter of count number. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-n"); + ValueStrPtr = ValueStr; + if (ValueStr != NULL) { + SendNumber = ShellStrToUintn (ValueStrPtr); + + // + // ShellStrToUintn will return 0 when input is 0 or an invalid input string. + // + if ((SendNumber == 0) || (SendNumber > PING6_MAX_SEND_NUMBER)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_SEND_NUMBER), mHiiHandle, ValueStr); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + // + // Parse the paramter of buffer size. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-l"); + ValueStrPtr = ValueStr; + if (ValueStr != NULL) { + BufferSize = ShellStrToUintn (ValueStrPtr); + + // + // ShellStrToUintn will return 0 when input is 0 or an invalid input string. + // + if ((BufferSize < 16) || (BufferSize > PING6_MAX_BUFFER_SIZE)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_BUFFER_SIZE), mHiiHandle, ValueStr); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + + ZeroMem (&SrcAddress, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&DstAddress, sizeof (EFI_IPv6_ADDRESS)); + + // + // Parse the paramter of source ip address. + // + ValueStr = ShellCommandLineGetValue (ParamPackage, L"-s"); + ValueStrPtr = ValueStr; + if (ValueStr != NULL) { + mIp6SrcString = ValueStr; + Status = NetLibStrToIp6 (ValueStrPtr, &SrcAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + // + // Parse the paramter of destination ip address. + // + NonOptionCount = ShellCommandLineGetCount(ParamPackage); + ValueStr = ShellCommandLineGetRawValue (ParamPackage, (UINT32)(NonOptionCount-1)); + if (NonOptionCount != 2) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_INPUT), mHiiHandle); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + ValueStrPtr = ValueStr; + if (ValueStr != NULL) { + mIp6DstString = ValueStr; + Status = NetLibStrToIp6 (ValueStrPtr, &DstAddress); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_PING6_INVALID_IP), mHiiHandle, ValueStr); + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + // + // Get frequency to calculate the time from ticks. + // + Status = Ping6GetFrequency (); + + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + // + // Enter into ping6 process. + // + Status = Ping6 ( + ImageHandle, + (UINT32)SendNumber, + (UINT32)BufferSize, + &SrcAddress, + &DstAddress + ); + +ON_EXIT: + ShellCommandLineFreeVarList (ParamPackage); + HiiRemovePackages (mHiiHandle); + return Status; +} diff --git a/Core/NetworkPkg/Application/Ping6/Ping6.h b/Core/NetworkPkg/Application/Ping6/Ping6.h new file mode 100644 index 0000000000..b152ff18bc --- /dev/null +++ b/Core/NetworkPkg/Application/Ping6/Ping6.h @@ -0,0 +1,87 @@ +/** @file + The interface function declaration of shell application Ping6 (Ping for v6 series). + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _PING6_H_ +#define _PING6_H_ + +#define PING6_DEFAULT_TIMEOUT 5000 +#define PING6_MAX_SEND_NUMBER 10000 +#define PING6_MAX_BUFFER_SIZE 32768 +#define PING6_ONE_SECOND 10000000 + +// +// A similar amount of time that passes in femtoseconds +// for each increment of TimerValue. It is for NT32 only. +// +#define NTTIMERPERIOD 358049 + +#pragma pack(1) + +typedef struct _ICMP6_ECHO_REQUEST_REPLY { + UINT8 Type; + UINT8 Code; + UINT16 Checksum; + UINT16 Identifier; + UINT16 SequenceNum; + UINT64 TimeStamp; + UINT8 Data[1]; +} ICMP6_ECHO_REQUEST_REPLY; + +#pragma pack() + +typedef struct _PING6_ICMP6_TX_INFO { + LIST_ENTRY Link; + UINT16 SequenceNum; + UINT64 TimeStamp; + EFI_IP6_COMPLETION_TOKEN *Token; +} PING6_ICMP6_TX_INFO; + +typedef struct _PING6_PRIVATE_DATA { + EFI_HANDLE ImageHandle; + EFI_HANDLE NicHandle; + EFI_HANDLE Ip6ChildHandle; + EFI_IP6_PROTOCOL *Ip6; + EFI_EVENT Timer; + + EFI_STATUS Status; + LIST_ENTRY TxList; + EFI_IP6_COMPLETION_TOKEN RxToken; + UINT16 RxCount; + UINT16 TxCount; + UINT64 RttSum; + UINT64 RttMin; + UINT64 RttMax; + UINT32 SequenceNum; + + EFI_IPv6_ADDRESS SrcAddress; + EFI_IPv6_ADDRESS DstAddress; + UINT32 SendNum; + UINT32 BufferSize; +} PING6_PRIVATE_DATA; + +/** + Reads and returns the current value of register. + In IA64, the register is the Interval Timer Vector (ITV). + In X86(IA32/X64), the register is the Time Stamp Counter (TSC) + + @return The current value of the register. + +**/ +UINT64 +ReadTime ( + VOID + ); + +#endif diff --git a/Core/NetworkPkg/Application/Ping6/Ping6.inf b/Core/NetworkPkg/Application/Ping6/Ping6.inf new file mode 100644 index 0000000000..f8851b98b0 --- /dev/null +++ b/Core/NetworkPkg/Application/Ping6/Ping6.inf @@ -0,0 +1,70 @@ +## @file +# Shell application Ping6. +# +# It is an shell application which is used to Ping the target host with IPv6 stack. +# +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = Ping6 + FILE_GUID = F35F733F-5235-4d7b-83FA-97780CEBCB20 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = InitializePing6 + MODULE_UNI_FILE = Ping6.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + Ping6.c + Ping6Strings.uni + Ping6.h + +[Sources.IA32] + Ia32/Tsc.c + +[Sources.X64] + X64/Tsc.c + +[Sources.IPF] + Ipf/Itc.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + BaseLib + UefiBootServicesTableLib + UefiApplicationEntryPoint + BaseMemoryLib + ShellLib + MemoryAllocationLib + DebugLib + HiiLib + NetLib + +[Protocols] + gEfiCpuArchProtocolGuid ## CONSUMES + gEfiIp6ProtocolGuid ## CONSUMES + gEfiIp6ServiceBindingProtocolGuid ## CONSUMES + gEfiIp6ConfigProtocolGuid ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + Ping6Extra.uni diff --git a/Core/NetworkPkg/Application/Ping6/Ping6.uni b/Core/NetworkPkg/Application/Ping6/Ping6.uni new file mode 100644 index 0000000000..8a1bd3a679 Binary files /dev/null and b/Core/NetworkPkg/Application/Ping6/Ping6.uni differ diff --git a/Core/NetworkPkg/Application/Ping6/Ping6Extra.uni b/Core/NetworkPkg/Application/Ping6/Ping6Extra.uni new file mode 100644 index 0000000000..8963ea71b1 Binary files /dev/null and b/Core/NetworkPkg/Application/Ping6/Ping6Extra.uni differ diff --git a/Core/NetworkPkg/Application/Ping6/Ping6Strings.uni b/Core/NetworkPkg/Application/Ping6/Ping6Strings.uni new file mode 100644 index 0000000000..ad9f739f82 Binary files /dev/null and b/Core/NetworkPkg/Application/Ping6/Ping6Strings.uni differ diff --git a/Core/NetworkPkg/Application/Ping6/X64/Tsc.c b/Core/NetworkPkg/Application/Ping6/X64/Tsc.c new file mode 100644 index 0000000000..b3e7bdbb96 --- /dev/null +++ b/Core/NetworkPkg/Application/Ping6/X64/Tsc.c @@ -0,0 +1,28 @@ +/** @file + The implement to read TSC in X64 platform. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +/** + Reads and returns the current value of Time Stamp Counter (TSC). + + @return The current value of TSC + +**/ +UINT64 +ReadTime () +{ + return AsmReadTsc (); +} diff --git a/Core/NetworkPkg/Application/VConfig/VConfig.c b/Core/NetworkPkg/Application/VConfig/VConfig.c new file mode 100644 index 0000000000..ba1720705c --- /dev/null +++ b/Core/NetworkPkg/Application/VConfig/VConfig.c @@ -0,0 +1,668 @@ +/** @file + Shell application for VLAN configuration. + + Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define INVALID_NIC_INDEX 0xffff +#define INVALID_VLAN_ID 0xffff + +// +// This is the generated String package data for all .UNI files. +// This data array is ready to be used as input of HiiAddPackages() to +// create a packagelist (which contains Form packages, String packages, etc). +// +extern UINT8 VConfigStrings[]; + +EFI_HANDLE mImageHandle = NULL; +EFI_HII_HANDLE mHiiHandle = NULL; + +SHELL_PARAM_ITEM mParamList[] = { + { + L"-l", + TypeValue + }, + { + L"-a", + TypeMaxValue + }, + { + L"-d", + TypeValue + }, + { + NULL, + TypeMax + } +}; + +/** + Locate the network interface handle buffer. + + @param[out] NumberOfHandles Pointer to the number of handles. + @param[out] HandleBuffer Pointer to the buffer to store the returned handles. + +**/ +VOID +LocateNicHandleBuffer ( + OUT UINTN *NumberOfHandles, + OUT EFI_HANDLE **HandleBuffer + ) +{ + EFI_STATUS Status; + + *NumberOfHandles = 0; + *HandleBuffer = NULL; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiVlanConfigProtocolGuid, + NULL, + NumberOfHandles, + HandleBuffer + ); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_LOCATE_FAIL), mHiiHandle, Status); + } +} + +/** + Extract the decimal index from the network interface name. + + @param[in] Name Name of the network interface. + + @retval INVALID_NIC_INDEX Failed to extract the network interface index. + @return others The network interface index. + +**/ +UINTN +NicNameToIndex ( + IN CHAR16 *Name + ) +{ + CHAR16 *Str; + + Str = Name + 3; + if ((StrnCmp (Name, L"eth", 3) != 0) || (*Str == 0)) { + return INVALID_NIC_INDEX; + } + + while (*Str != 0) { + if ((*Str < L'0') || (*Str > L'9')) { + return INVALID_NIC_INDEX; + } + + Str++; + } + + return (UINT16) StrDecimalToUintn (Name + 3); +} + +/** + Find network interface device handle by its name. + + @param[in] Name Name of the network interface. + + @retval NULL Cannot find the network interface. + @return others Handle of the network interface. + +**/ +EFI_HANDLE +NicNameToHandle ( + IN CHAR16 *Name + ) +{ + UINTN NumberOfHandles; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_HANDLE Handle; + + // + // Find all NIC handles. + // + LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer); + if (NumberOfHandles == 0) { + return NULL; + } + + Index = NicNameToIndex (Name); + if (Index >= NumberOfHandles) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_IF), mHiiHandle, Name); + Handle = NULL; + } else { + Handle = HandleBuffer[Index]; + } + + FreePool (HandleBuffer); + return Handle; +} + +/** + Open VlanConfig protocol from a handle. + + @param[in] Handle The handle to open the VlanConfig protocol. + + @return The VlanConfig protocol interface. + +**/ +EFI_VLAN_CONFIG_PROTOCOL * +OpenVlanConfigProtocol ( + IN EFI_HANDLE Handle + ) +{ + EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; + + VlanConfig = NULL; + gBS->OpenProtocol ( + Handle, + &gEfiVlanConfigProtocolGuid, + (VOID **) &VlanConfig, + mImageHandle, + Handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + return VlanConfig; +} + +/** + Close VlanConfig protocol of a handle. + + @param[in] Handle The handle to close the VlanConfig protocol. + +**/ +VOID +CloseVlanConfigProtocol ( + IN EFI_HANDLE Handle + ) +{ + gBS->CloseProtocol ( + Handle, + &gEfiVlanConfigProtocolGuid, + mImageHandle, + Handle + ); +} + +/** + Display VLAN configuration of a network interface. + + @param[in] Handle Handle of the network interface. + @param[in] NicIndex Index of the network interface. + +**/ +VOID +ShowNicVlanInfo ( + IN EFI_HANDLE Handle, + IN UINTN NicIndex + ) +{ + CHAR16 *MacStr; + EFI_STATUS Status; + UINTN Index; + EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; + UINT16 NumberOfVlan; + EFI_VLAN_FIND_DATA *VlanData; + + VlanConfig = OpenVlanConfigProtocol (Handle); + if (VlanConfig == NULL) { + return ; + } + + MacStr = NULL; + Status = NetLibGetMacString (Handle, mImageHandle, &MacStr); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_MAC_FAIL), mHiiHandle, Status); + goto Exit; + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_ETH_MAC), mHiiHandle, NicIndex, MacStr); + + Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VLAN), mHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_FIND_FAIL), mHiiHandle, Status); + } + + goto Exit; + } + + for (Index = 0; Index < NumberOfVlan; Index++) { + ShellPrintHiiEx ( + -1, + -1, + NULL, + STRING_TOKEN (STR_VCONFIG_VLAN_DISPLAY), + mHiiHandle, + VlanData[Index].VlanId, + VlanData[Index].Priority + ); + } + + FreePool (VlanData); + +Exit: + CloseVlanConfigProtocol (Handle); + + if (MacStr != NULL) { + FreePool (MacStr); + } +} + +/** + Display the VLAN configuration of all, or a specified network interface. + + @param[in] Name Name of the network interface. If NULL, the VLAN + configuration of all network will be displayed. + +**/ +VOID +DisplayVlan ( + IN CHAR16 *Name OPTIONAL + ) +{ + UINTN NumberOfHandles; + EFI_HANDLE *HandleBuffer; + UINTN Index; + EFI_HANDLE NicHandle; + + if (Name != NULL) { + // + // Display specified NIC + // + NicHandle = NicNameToHandle (Name); + if (NicHandle == NULL) { + return ; + } + + ShowNicVlanInfo (NicHandle, 0); + return ; + } + + // + // Find all NIC handles + // + LocateNicHandleBuffer (&NumberOfHandles, &HandleBuffer); + if (NumberOfHandles == 0) { + return ; + } + + for (Index = 0; Index < NumberOfHandles; Index++) { + ShowNicVlanInfo (HandleBuffer[Index], Index); + } + + FreePool (HandleBuffer); +} + +/** + Convert a NULL-terminated unicode decimal VLAN ID string to VLAN ID. + + @param[in] String Pointer to VLAN ID string from user input. + + @retval Value translated from String, or INVALID_VLAN_ID is string is invalid. + +**/ +UINT16 +StrToVlanId ( + IN CHAR16 *String + ) +{ + CHAR16 *Str; + + if (String == NULL) { + return INVALID_VLAN_ID; + } + + Str = String; + while ((*Str >= '0') && (*Str <= '9')) { + Str++; + } + + if (*Str != 0) { + return INVALID_VLAN_ID; + } + + return (UINT16) StrDecimalToUintn (String); +} + +/** + Add a VLAN device. + + @param[in] ParamStr Parameter string from user input. + +**/ +VOID +AddVlan ( + IN CHAR16 *ParamStr + ) +{ + CHAR16 *Name; + CHAR16 *VlanIdStr; + CHAR16 *PriorityStr; + CHAR16 *StrPtr; + BOOLEAN IsSpace; + UINTN VlanId; + UINTN Priority; + EFI_HANDLE Handle; + EFI_HANDLE VlanHandle; + EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; + EFI_STATUS Status; + + VlanConfig = NULL; + Priority = 0; + + if (ParamStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle); + return ; + } + + StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr); + if (StrPtr == NULL) { + return ; + } + + Name = StrPtr; + VlanIdStr = NULL; + PriorityStr = NULL; + IsSpace = FALSE; + while (*StrPtr != 0) { + if (*StrPtr == L' ') { + *StrPtr = 0; + IsSpace = TRUE; + } else { + if (IsSpace) { + // + // Start of a parameter. + // + if (VlanIdStr == NULL) { + // + // 2nd parameter is VLAN ID. + // + VlanIdStr = StrPtr; + } else if (PriorityStr == NULL) { + // + // 3rd parameter is Priority. + // + PriorityStr = StrPtr; + } else { + // + // Ignore else parameters. + // + break; + } + } + + IsSpace = FALSE; + } + + StrPtr++; + } + + Handle = NicNameToHandle (Name); + if (Handle == NULL) { + goto Exit; + } + + VlanConfig = OpenVlanConfigProtocol (Handle); + if (VlanConfig == NULL) { + goto Exit; + } + + // + // Check VLAN ID. + // + if ((VlanIdStr == NULL) || (*VlanIdStr == 0)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle); + goto Exit; + } + + VlanId = StrToVlanId (VlanIdStr); + if (VlanId > 4094) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr); + goto Exit; + } + + // + // Check Priority. + // + if ((PriorityStr != NULL) && (*PriorityStr != 0)) { + Priority = StrDecimalToUintn (PriorityStr); + if (Priority > 7) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_PRIORITY), mHiiHandle, PriorityStr); + goto Exit; + } + } + + // + // Set VLAN + // + Status = VlanConfig->Set (VlanConfig, (UINT16) VlanId, (UINT8) Priority); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_FAIL), mHiiHandle, Status); + goto Exit; + } + + // + // Connect the VLAN device. + // + VlanHandle = NetLibGetVlanHandle (Handle, (UINT16) VlanId); + if (VlanHandle != NULL) { + gBS->ConnectController (VlanHandle, NULL, NULL, TRUE); + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_SET_SUCCESS), mHiiHandle); + +Exit: + if (VlanConfig != NULL) { + CloseVlanConfigProtocol (Handle); + } + + FreePool (Name); +} + +/** + Remove a VLAN device. + + @param[in] ParamStr Parameter string from user input. + +**/ +VOID +DeleteVlan ( + IN CHAR16 *ParamStr + ) +{ + CHAR16 *Name; + CHAR16 *VlanIdStr; + CHAR16 *StrPtr; + UINTN VlanId; + EFI_HANDLE Handle; + EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; + EFI_STATUS Status; + UINT16 NumberOfVlan; + EFI_VLAN_FIND_DATA *VlanData; + + VlanConfig = NULL; + + if (ParamStr == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_IF), mHiiHandle); + return ; + } + + StrPtr = AllocateCopyPool (StrSize (ParamStr), ParamStr); + if (StrPtr == NULL) { + return ; + } + + Name = StrPtr; + VlanIdStr = NULL; + while (*StrPtr != 0) { + if (*StrPtr == L'.') { + *StrPtr = 0; + VlanIdStr = StrPtr + 1; + break; + } + + StrPtr++; + } + + Handle = NicNameToHandle (Name); + if (Handle == NULL) { + goto Exit; + } + + VlanConfig = OpenVlanConfigProtocol (Handle); + if (VlanConfig == NULL) { + goto Exit; + } + + // + // Check VLAN ID + // + if (VlanIdStr == NULL || *VlanIdStr == 0) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_VID), mHiiHandle); + goto Exit; + } + + VlanId = StrToVlanId (VlanIdStr); + if (VlanId > 4094) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_INVALID_VID), mHiiHandle, VlanIdStr); + goto Exit; + } + + // + // Delete VLAN. + // + Status = VlanConfig->Remove (VlanConfig, (UINT16) VlanId); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NOT_FOUND), mHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_FAIL), mHiiHandle, Status); + } + + goto Exit; + } + + // + // Check whether this is the last VLAN to remove. + // + Status = VlanConfig->Find (VlanConfig, NULL, &NumberOfVlan, &VlanData); + if (EFI_ERROR (Status)) { + // + // This is the last VLAN to remove, try to connect the controller handle. + // + gBS->ConnectController (Handle, NULL, NULL, TRUE); + } else { + FreePool (VlanData); + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_REMOVE_SUCCESS), mHiiHandle); + +Exit: + if (VlanConfig != NULL) { + CloseVlanConfigProtocol (Handle); + } + + FreePool (Name); +} + +/** + The actual entry point for the application. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point executed successfully. + @retval other Some error occur when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +VlanConfigMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + LIST_ENTRY *List; + CONST CHAR16 *Str; + + mImageHandle = ImageHandle; + + // + // Register our string package to HII database. + // + mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, ImageHandle, VConfigStrings, NULL); + if (mHiiHandle == NULL) { + return EFI_SUCCESS; + } + + List = NULL; + ShellCommandLineParseEx (mParamList, &List, NULL, FALSE, FALSE); + if (List == NULL) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle); + goto Exit; + } + + if (ShellCommandLineGetFlag (List, L"-?")) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_HELP), mHiiHandle); + goto Exit; + } + + if (ShellCommandLineGetFlag (List, L"-l")) { + Str = ShellCommandLineGetValue (List, L"-l"); + DisplayVlan ((CHAR16 *) Str); + goto Exit; + } + + if (ShellCommandLineGetFlag (List, L"-a")) { + Str = ShellCommandLineGetValue (List, L"-a"); + AddVlan ((CHAR16 *) Str); + goto Exit; + } + + if (ShellCommandLineGetFlag (List, L"-d")) { + Str = ShellCommandLineGetValue (List, L"-d"); + DeleteVlan ((CHAR16 *) Str); + goto Exit; + } + + // + // No valid argument till now. + // + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_VCONFIG_NO_ARG), mHiiHandle); + +Exit: + if (List != NULL) { + ShellCommandLineFreeVarList (List); + } + + // + // Remove our string package from HII database. + // + HiiRemovePackages (mHiiHandle); + + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/Application/VConfig/VConfig.inf b/Core/NetworkPkg/Application/VConfig/VConfig.inf new file mode 100644 index 0000000000..7067e0cd81 --- /dev/null +++ b/Core/NetworkPkg/Application/VConfig/VConfig.inf @@ -0,0 +1,53 @@ +## @file +# Shell application VLAN configuration. +# +# It is shell application which is used to get and set VLAN configuration. +# +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VConfig + FILE_GUID = 87E36301-0406-44db-AAF3-9E0E591F3725 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = VlanConfigMain + MODULE_UNI_FILE = VConfig.uni + +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + VConfigStrings.uni + VConfig.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint + UefiBootServicesTableLib + UefiLib + ShellLib + NetLib + MemoryAllocationLib + HiiLib + +[Protocols] + gEfiVlanConfigProtocolGuid ## CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + VConfigExtra.uni diff --git a/Core/NetworkPkg/Application/VConfig/VConfig.uni b/Core/NetworkPkg/Application/VConfig/VConfig.uni new file mode 100644 index 0000000000..e9db9bb687 Binary files /dev/null and b/Core/NetworkPkg/Application/VConfig/VConfig.uni differ diff --git a/Core/NetworkPkg/Application/VConfig/VConfigExtra.uni b/Core/NetworkPkg/Application/VConfig/VConfigExtra.uni new file mode 100644 index 0000000000..4faf8ffae1 Binary files /dev/null and b/Core/NetworkPkg/Application/VConfig/VConfigExtra.uni differ diff --git a/Core/NetworkPkg/Application/VConfig/VConfigStrings.uni b/Core/NetworkPkg/Application/VConfig/VConfigStrings.uni new file mode 100644 index 0000000000..5cab8ba543 Binary files /dev/null and b/Core/NetworkPkg/Application/VConfig/VConfigStrings.uni differ diff --git a/Core/NetworkPkg/Contributions.txt b/Core/NetworkPkg/Contributions.txt new file mode 100644 index 0000000000..f87cbd73c6 --- /dev/null +++ b/Core/NetworkPkg/Contributions.txt @@ -0,0 +1,218 @@ + +====================== += Code Contributions = +====================== + +To make a contribution to a TianoCore project, follow these steps. +1. Create a change description in the format specified below to + use in the source control commit log. +2. Your commit message must include your "Signed-off-by" signature, + and "Contributed-under" message. +3. Your "Contributed-under" message explicitly states that the + contribution is made under the terms of the specified + contribution agreement. Your "Contributed-under" message + must include the name of contribution agreement and version. + For example: Contributed-under: TianoCore Contribution Agreement 1.0 + The "TianoCore Contribution Agreement" is included below in + this document. +4. Submit your code to the TianoCore project using the process + that the project documents on its web page. If the process is + not documented, then submit the code on development email list + for the project. +5. It is preferred that contributions are submitted using the same + copyright license as the base project. When that is not possible, + then contributions using the following licenses can be accepted: + * BSD (2-clause): http://opensource.org/licenses/BSD-2-Clause + * BSD (3-clause): http://opensource.org/licenses/BSD-3-Clause + * MIT: http://opensource.org/licenses/MIT + * Python-2.0: http://opensource.org/licenses/Python-2.0 + * Zlib: http://opensource.org/licenses/Zlib + + Contributions of code put into the public domain can also be + accepted. + + Contributions using other licenses might be accepted, but further + review will be required. + +===================================================== += Change Description / Commit Message / Patch Email = +===================================================== + +Your change description should use the standard format for a +commit message, and must include your "Signed-off-by" signature +and the "Contributed-under" message. + +== Sample Change Description / Commit Message = + +=== Start of sample patch email message === + +From: Contributor Name +Subject: [PATCH] CodeModule: Brief-single-line-summary + +Full-commit-message + +Contributed-under: TianoCore Contribution Agreement 1.0 +Signed-off-by: Contributor Name +--- + +An extra message for the patch email which will not be considered part +of the commit message can be added here. + +Patch content inline or attached + +=== End of sample patch email message === + +=== Notes for sample patch email === + +* The first line of commit message is taken from the email's subject + line following [PATCH]. The remaining portion of the commit message + is the email's content until the '---' line. +* git format-patch is one way to create this format + +=== Definitions for sample patch email === + +* "CodeModule" is a short idenfier for the affected code. For + example MdePkg, or MdeModulePkg UsbBusDxe. +* "Brief-single-line-summary" is a short summary of the change. +* The entire first line should be less than ~70 characters. +* "Full-commit-message" a verbose multiple line comment describing + the change. Each line should be less than ~70 characters. +* "Contributed-under" explicitely states that the contribution is + made under the terms of the contribtion agreement. This + agreement is included below in this document. +* "Signed-off-by" is the contributor's signature identifying them + by their real/legal name and their email address. + +======================================== += TianoCore Contribution Agreement 1.0 = +======================================== + +INTEL CORPORATION ("INTEL") MAKES AVAILABLE SOFTWARE, DOCUMENTATION, +INFORMATION AND/OR OTHER MATERIALS FOR USE IN THE TIANOCORE OPEN SOURCE +PROJECT (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE +TERMS AND CONDITIONS OF THIS AGREEMENT BETWEEN YOU AND INTEL AND/OR THE +TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR +REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE OF THE +CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS +OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED +BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS +AGREEMENT AND THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE +AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT +USE THE CONTENT. + +Unless otherwise indicated, all Content made available on the TianoCore +site is provided to you under the terms and conditions of the BSD +License ("BSD"). A copy of the BSD License is available at +http://opensource.org/licenses/bsd-license.php +or when applicable, in the associated License.txt file. + +Certain other content may be made available under other licenses as +indicated in or with such Content. (For example, in a License.txt file.) + +You accept and agree to the following terms and conditions for Your +present and future Contributions submitted to TianoCore site. Except +for the license granted to Intel hereunder, You reserve all right, +title, and interest in and to Your Contributions. + +== SECTION 1: Definitions == +* "You" or "Contributor" shall mean the copyright owner or legal + entity authorized by the copyright owner that is making a + Contribution hereunder. All other entities that control, are + controlled by, or are under common control with that entity are + considered to be a single Contributor. For the purposes of this + definition, "control" means (i) the power, direct or indirect, to + cause the direction or management of such entity, whether by + contract or otherwise, or (ii) ownership of fifty percent (50%) + or more of the outstanding shares, or (iii) beneficial ownership + of such entity. +* "Contribution" shall mean any original work of authorship, + including any modifications or additions to an existing work, + that is intentionally submitted by You to the TinaoCore site for + inclusion in, or documentation of, any of the Content. For the + purposes of this definition, "submitted" means any form of + electronic, verbal, or written communication sent to the + TianoCore site or its representatives, including but not limited + to communication on electronic mailing lists, source code + control systems, and issue tracking systems that are managed by, + or on behalf of, the TianoCore site for the purpose of + discussing and improving the Content, but excluding + communication that is conspicuously marked or otherwise + designated in writing by You as "Not a Contribution." + +== SECTION 2: License for Contributions == +* Contributor hereby agrees that redistribution and use of the + Contribution in source and binary forms, with or without + modification, are permitted provided that the following + conditions are met: +** Redistributions of source code must retain the Contributor's + copyright notice, this list of conditions and the following + disclaimer. +** Redistributions in binary form must reproduce the Contributor's + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. +* Disclaimer. None of the names of Contributor, Intel, or the names + of their respective contributors may be used to endorse or + promote products derived from this software without specific + prior written permission. +* Contributor grants a license (with the right to sublicense) under + claims of Contributor's patents that Contributor can license that + are infringed by the Contribution (as delivered by Contributor) to + make, use, distribute, sell, offer for sale, and import the + Contribution and derivative works thereof solely to the minimum + extent necessary for licensee to exercise the granted copyright + license; this patent license applies solely to those portions of + the Contribution that are unmodified. No hardware per se is + licensed. +* EXCEPT AS EXPRESSLY SET FORTH IN SECTION 3 BELOW, THE + CONTRIBUTION IS PROVIDED BY THE CONTRIBUTOR "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + CONTRIBUTOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE + CONTRIBUTION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + +== SECTION 3: Representations == +* You represent that You are legally entitled to grant the above + license. If your employer(s) has rights to intellectual property + that You create that includes Your Contributions, You represent + that You have received permission to make Contributions on behalf + of that employer, that Your employer has waived such rights for + Your Contributions. +* You represent that each of Your Contributions is Your original + creation (see Section 4 for submissions on behalf of others). + You represent that Your Contribution submissions include complete + details of any third-party license or other restriction + (including, but not limited to, related patents and trademarks) + of which You are personally aware and which are associated with + any part of Your Contributions. + +== SECTION 4: Third Party Contributions == +* Should You wish to submit work that is not Your original creation, + You may submit it to TianoCore site separately from any + Contribution, identifying the complete details of its source + and of any license or other restriction (including, but not + limited to, related patents, trademarks, and license agreements) + of which You are personally aware, and conspicuously marking the + work as "Submitted on behalf of a third-party: [named here]". + +== SECTION 5: Miscellaneous == +* Applicable Laws. Any claims arising under or relating to this + Agreement shall be governed by the internal substantive laws of + the State of Delaware or federal courts located in Delaware, + without regard to principles of conflict of laws. +* Language. This Agreement is in the English language only, which + language shall be controlling in all respects, and all versions + of this Agreement in any other language shall be for accommodation + only and shall not be binding. All communications and notices made + or given pursuant to this Agreement, and all documentation and + support to be provided, unless otherwise noted, shall be in the + English language. + diff --git a/Core/NetworkPkg/Dhcp6Dxe/ComponentName.c b/Core/NetworkPkg/Dhcp6Dxe/ComponentName.c new file mode 100644 index 0000000000..927a7fe7ec --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/ComponentName.c @@ -0,0 +1,441 @@ +/** @file + UEFI Component Name(2) protocol implementation for Dhcp6 driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Dhcp6Impl.h" + + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that attempt to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that attempts to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language, from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName = { + Dhcp6ComponentNameGetDriverName, + Dhcp6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Dhcp6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Dhcp6ComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDhcp6DriverNameTable[] = { + { + "eng;en", + L"DHCP6 Protocol Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gDhcp6ControllerNameTable = NULL; + +CHAR16 *mDhcp6ControllerName[] = { + L"DHCPv6 (State=0, Init)", + L"DHCPv6 (State=1, Selecting)", + L"DHCPv6 (State=2, Requesting)", + L"DHCPv6 (State=3, Declining)", + L"DHCPv6 (State=4, Confirming)", + L"DHCPv6 (State=5, Releasing)", + L"DHCPv6 (State=6, Bound)", + L"DHCPv6 (State=7, Renewing)", + L"DHCPv6 (State=8, Rebinding)" +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDhcp6DriverNameTable, + DriverName, + (BOOLEAN)(This == &gDhcp6ComponentName) + ); +} + +/** + Update the component name for the Dhcp6 child handle. + + @param Dhcp6[in] A pointer to the EFI_DHCP6_PROTOCOL. + + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + +**/ +EFI_STATUS +UpdateName ( + IN EFI_DHCP6_PROTOCOL *Dhcp6 + ) +{ + EFI_STATUS Status; + EFI_DHCP6_MODE_DATA Dhcp6ModeData; + CHAR16 *HandleName; + + if (Dhcp6 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer. + // + Status = Dhcp6->GetModeData (Dhcp6, &Dhcp6ModeData, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + if (gDhcp6ControllerNameTable != NULL) { + FreeUnicodeStringTable (gDhcp6ControllerNameTable); + gDhcp6ControllerNameTable = NULL; + } + + if (Dhcp6ModeData.Ia == NULL) { + HandleName = L"DHCPv6 (No configured IA)"; + } else { + if (Dhcp6ModeData.Ia->State > Dhcp6Rebinding) { + return EFI_DEVICE_ERROR; + } + HandleName = mDhcp6ControllerName[Dhcp6ModeData.Ia->State]; + } + + Status = AddUnicodeString2 ( + "eng", + gDhcp6ComponentName.SupportedLanguages, + &gDhcp6ControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gDhcp6ComponentName2.SupportedLanguages, + &gDhcp6ControllerNameTable, + HandleName, + FALSE + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in the + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language, from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PROTOCOL *Dhcp6; + + // + // Only provide names for child handles. + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiUdp6ProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiDhcp6ProtocolGuid, + (VOID **)&Dhcp6, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateName (Dhcp6); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gDhcp6ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gDhcp6ComponentName) + ); +} + diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c new file mode 100644 index 0000000000..de53a1a9b4 --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c @@ -0,0 +1,819 @@ +/** @file + Driver Binding functions and Service Binding functions + implementationfor for Dhcp6 Driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Dhcp6Impl.h" + + +EFI_DRIVER_BINDING_PROTOCOL gDhcp6DriverBinding = { + Dhcp6DriverBindingSupported, + Dhcp6DriverBindingStart, + Dhcp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL gDhcp6ServiceBindingTemplate = { + Dhcp6ServiceBindingCreateChild, + Dhcp6ServiceBindingDestroyChild +}; + +/** + Configure the default Udp6Io to receive all the DHCP6 traffic + on this network interface. + + @param[in] UdpIo The pointer to Udp6Io to be configured. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS The Udp6Io is successfully configured. + @retval Others Failed to configure the Udp6Io. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ConfigureUdpIo ( + IN UDP_IO *UdpIo, + IN VOID *Context + ) +{ + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA *Config; + + Udp6 = UdpIo->Protocol.Udp6; + Config = &(UdpIo->Config.Udp6); + + ZeroMem (Config, sizeof (EFI_UDP6_CONFIG_DATA)); + + // + // Set Udp6 configure data for the Dhcp6 instance. + // + Config->AcceptPromiscuous = FALSE; + Config->AcceptAnyPort = FALSE; + Config->AllowDuplicatePort = FALSE; + Config->TrafficClass = 0; + Config->HopLimit = 128; + Config->ReceiveTimeout = 0; + Config->TransmitTimeout = 0; + + // + // Configure an endpoint of client(0, 546), server(0, 0), the addresses + // will be overridden later. Note that we MUST not limit RemotePort. + // More details, refer to RFC 3315 section 5.2. + // + Config->StationPort = DHCP6_PORT_CLIENT; + Config->RemotePort = 0; + + return Udp6->Configure (Udp6, Config);; +} + + +/** + Destroy the Dhcp6 service. The Dhcp6 service may be partly initialized, + or partly destroyed. If a resource is destroyed, it is marked as such in + case the destroy failed and being called again later. + + @param[in, out] Service The pointer to Dhcp6 service to be destroyed. + +**/ +VOID +Dhcp6DestroyService ( + IN OUT DHCP6_SERVICE *Service + ) +{ + // + // All children instances should have been already destroyed here. + // + ASSERT (Service->NumOfChild == 0); + + if (Service->ClientId != NULL) { + FreePool (Service->ClientId); + } + + if (Service->UdpIo != NULL) { + UdpIoFreeIo (Service->UdpIo); + } + + FreePool (Service); +} + + +/** + Create a new Dhcp6 service for the Nic controller. + + @param[in] Controller The controller to be installed DHCP6 service + binding protocol. + @param[in] ImageHandle The image handle of the Dhcp6 driver. + @param[out] Service The return pointer of the new Dhcp6 service. + + @retval EFI_SUCCESS The Dhcp6 service is created successfully. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + +**/ +EFI_STATUS +Dhcp6CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT DHCP6_SERVICE **Service + ) +{ + DHCP6_SERVICE *Dhcp6Srv; + EFI_STATUS Status; + + *Service = NULL; + Dhcp6Srv = AllocateZeroPool (sizeof (DHCP6_SERVICE)); + + if (Dhcp6Srv == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Open the SNP protocol to get mode data later. + // + Dhcp6Srv->Snp = NULL; + NetLibGetSnpHandle (Controller, &Dhcp6Srv->Snp); + if (Dhcp6Srv->Snp == NULL) { + FreePool (Dhcp6Srv); + return EFI_DEVICE_ERROR; + } + + // + // Initialize the fields of the new Dhcp6 service. + // + Dhcp6Srv->Signature = DHCP6_SERVICE_SIGNATURE; + Dhcp6Srv->Controller = Controller; + Dhcp6Srv->Image = ImageHandle; + Dhcp6Srv->Xid = (0xffffff & NET_RANDOM (NetRandomInitSeed ())); + + CopyMem ( + &Dhcp6Srv->ServiceBinding, + &gDhcp6ServiceBindingTemplate, + sizeof (EFI_SERVICE_BINDING_PROTOCOL) + ); + + // + // Locate Ip6->Ip6Config and store it for get IP6 Duplicate Address Detection transmits. + // + Status = gBS->HandleProtocol ( + Controller, + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Dhcp6Srv->Ip6Cfg + ); + if (EFI_ERROR (Status)) { + FreePool (Dhcp6Srv); + return Status; + } + + // + // Generate client Duid: If SMBIOS system UUID is located, generate DUID in DUID-UUID format. + // Otherwise, in DUID-LLT format. + // + Dhcp6Srv->ClientId = Dhcp6GenerateClientId (Dhcp6Srv->Snp->Mode); + + if (Dhcp6Srv->ClientId == NULL) { + FreePool (Dhcp6Srv); + return EFI_DEVICE_ERROR; + } + + // + // Create an Udp6Io for stateful transmit/receive of each Dhcp6 instance. + // + Dhcp6Srv->UdpIo = UdpIoCreateIo ( + Controller, + ImageHandle, + Dhcp6ConfigureUdpIo, + UDP_IO_UDP6_VERSION, + NULL + ); + + if (Dhcp6Srv->UdpIo == NULL) { + FreePool (Dhcp6Srv->ClientId); + FreePool (Dhcp6Srv); + return EFI_DEVICE_ERROR; + } + + InitializeListHead (&Dhcp6Srv->Child); + + *Service = Dhcp6Srv; + + return EFI_SUCCESS; +} + + +/** + Destroy the Dhcp6 instance and recycle the resources. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + +**/ +VOID +Dhcp6DestroyInstance ( + IN OUT DHCP6_INSTANCE *Instance + ) +{ + // + // Clean up the retry list first. + // + Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL); + gBS->CloseEvent (Instance->Timer); + + // + // Clean up the current configure data. + // + if (Instance->Config != NULL) { + Dhcp6CleanupConfigData (Instance->Config); + FreePool (Instance->Config); + } + + // + // Clean up the current Ia. + // + if (Instance->IaCb.Ia != NULL) { + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + FreePool (Instance->IaCb.Ia); + } + + if (Instance->Unicast != NULL) { + FreePool (Instance->Unicast); + } + + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + FreePool (Instance); +} + + +/** + Create the Dhcp6 instance and initialize it. + + @param[in] Service The pointer to the Dhcp6 service. + @param[out] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS The Dhcp6 instance is created. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +Dhcp6CreateInstance ( + IN DHCP6_SERVICE *Service, + OUT DHCP6_INSTANCE **Instance + ) +{ + EFI_STATUS Status; + DHCP6_INSTANCE *Dhcp6Ins; + + *Instance = NULL; + Dhcp6Ins = AllocateZeroPool (sizeof (DHCP6_INSTANCE)); + + if (Dhcp6Ins == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize the fields of the new Dhcp6 instance. + // + Dhcp6Ins->Signature = DHCP6_INSTANCE_SIGNATURE; + Dhcp6Ins->UdpSts = EFI_ALREADY_STARTED; + Dhcp6Ins->Service = Service; + Dhcp6Ins->InDestroy = FALSE; + Dhcp6Ins->MediaPresent = TRUE; + + CopyMem ( + &Dhcp6Ins->Dhcp6, + &gDhcp6ProtocolTemplate, + sizeof (EFI_DHCP6_PROTOCOL) + ); + + InitializeListHead (&Dhcp6Ins->TxList); + InitializeListHead (&Dhcp6Ins->InfList); + + // + // There is a timer for each Dhcp6 instance, which is used to track the + // lease time of Ia and the retransmisson time of all sent packets. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Dhcp6OnTimerTick, + Dhcp6Ins, + &Dhcp6Ins->Timer + ); + + if (EFI_ERROR (Status)) { + FreePool (Dhcp6Ins); + return Status; + } + + *Instance = Dhcp6Ins; + + return EFI_SUCCESS; +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DestroyChildEntry ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + DHCP6_INSTANCE *Instance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = NET_LIST_USER_STRUCT_S (Entry, DHCP6_INSTANCE, Link, DHCP6_INSTANCE_SIGNATURE); + ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context; + + return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); +} + + +/** + Entry point of the DHCP6 driver to install various protocols. + + @param[in] ImageHandle The handle of the UEFI image file. + @param[in] SystemTable The pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others Unexpected error occurs. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gDhcp6DriverBinding, + ImageHandle, + &gDhcp6ComponentName, + &gDhcp6ComponentName2 + ); +} + + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); +} + + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + DHCP6_SERVICE *Service; + + // + // Check the Dhcp6 serivce whether already started. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Create and initialize the Dhcp6 service. + // + Status = Dhcp6CreateService ( + ControllerHandle, + This->DriverBindingHandle, + &Service + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Service != NULL); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Service->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + Dhcp6DestroyService (Service); + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + DHCP6_SERVICE *Service; + LIST_ENTRY *List; + UINTN ListLength; + + // + // Find and check the Nic handle by the controller handle. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid); + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Service = DHCP6_SERVICE_FROM_THIS (ServiceBinding); + if (!IsListEmpty (&Service->Child)) { + // + // Destroy all the children instances before destory the service. + // + List = &Service->Child; + Status = NetDestroyLinkList ( + List, + Dhcp6DestroyChildEntry, + ServiceBinding, + &ListLength + ); + if (EFI_ERROR (Status) || ListLength != 0) { + Status = EFI_DEVICE_ERROR; + } + } + + if (NumberOfChildren == 0 && !IsListEmpty (&Service->Child)) { + Status = EFI_DEVICE_ERROR; + } + + if (NumberOfChildren == 0 && IsListEmpty (&Service->Child)) { + // + // Destroy the service itself if no child instance left. + // + Status = gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + ServiceBinding + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Dhcp6DestroyService (Service); + Status = EFI_SUCCESS; + } + +ON_EXIT: + return Status; +} + + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing + UEFI handle, then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + DHCP6_SERVICE *Service; + DHCP6_INSTANCE *Instance; + VOID *Udp6; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + Service = DHCP6_SERVICE_FROM_THIS (This); + + Status = Dhcp6CreateInstance (Service, &Instance); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Instance != NULL); + + // + // Start the timer when the instance is ready to use. + // + Status = gBS->SetTimer ( + Instance->Timer, + TimerPeriodic, + TICKS_PER_SECOND + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the DHCP6 protocol onto ChildHandle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiDhcp6ProtocolGuid, + &Instance->Dhcp6, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->Handle = *ChildHandle; + + // + // Open the UDP6 protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + Service->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + gDhcp6DriverBinding.DriverBindingHandle, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR (Status)) { + + gBS->UninstallMultipleProtocolInterfaces ( + Instance->Handle, + &gEfiDhcp6ProtocolGuid, + &Instance->Dhcp6, + NULL + ); + goto ON_ERROR; + } + + // + // Add into the children list of its parent service. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&Service->Child, &Instance->Link); + Service->NumOfChild++; + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +ON_ERROR: + + Dhcp6DestroyInstance (Instance); + return Status; +} + + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Dhcp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_DHCP6_PROTOCOL *Dhcp6; + DHCP6_SERVICE *Service; + DHCP6_INSTANCE *Instance; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Dhcp6, + gDhcp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (Dhcp6); + Service = DHCP6_SERVICE_FROM_THIS (This); + + if (Instance->Service != Service) { + return EFI_INVALID_PARAMETER; + } + + if (Instance->InDestroy) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->InDestroy = TRUE; + + Status = gBS->CloseProtocol ( + Service->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gDhcp6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + if (EFI_ERROR (Status)) { + Instance->InDestroy = FALSE; + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // Uninstall the MTFTP6 protocol first to enable a top down destruction. + // + gBS->RestoreTPL (OldTpl); + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiDhcp6ProtocolGuid, + Dhcp6 + ); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (EFI_ERROR (Status)) { + Instance->InDestroy = FALSE; + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // Remove it from the children list of its parent service. + // + RemoveEntryList (&Instance->Link); + Service->NumOfChild--; + + gBS->RestoreTPL (OldTpl); + + Dhcp6DestroyInstance (Instance); + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h new file mode 100644 index 0000000000..bec47a0679 --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h @@ -0,0 +1,156 @@ +/** @file + Driver Binding functions and Service Binding functions + declaration for Dhcp6 Driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_DHCP6_DRIVER_H__ +#define __EFI_DHCP6_DRIVER_H__ + +#include + +extern EFI_COMPONENT_NAME_PROTOCOL gDhcp6ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gDhcp6ComponentName2; +extern EFI_UNICODE_STRING_TABLE *gDhcp6ControllerNameTable; + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start(), it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Dhcp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Dhcp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Dhcp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf new file mode 100644 index 0000000000..24cb9a9b64 --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf @@ -0,0 +1,82 @@ +## @file +# Client-side DHCPv6 services. +# +# This driver produces EFI DHCPv6 Protocol which is used to get IPv6 addresses +# and other configuration parameters from DHCPv6 servers. +# +# (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Dhcp6Dxe + FILE_GUID = 95E3669D-34BE-4775-A651-7EA41B69D89E + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Dhcp6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = Dhcp6Dxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gDhcp6DriverBinding +# COMPONENT_NAME = gDhcp6ComponentName +# COMPONENT_NAME2 = gDhcp6ComponentName2 +# + +[Sources] + Dhcp6Driver.c + Dhcp6Driver.h + Dhcp6Impl.c + Dhcp6Impl.h + Dhcp6Io.c + Dhcp6Io.h + Dhcp6Utility.c + Dhcp6Utility.h + ComponentName.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + NetworkPkg/NetworkPkg.dec + +[LibraryClasses] + UefiLib + BaseLib + BaseMemoryLib + MemoryAllocationLib + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + DebugLib + NetLib + UdpIoLib + + +[Protocols] + gEfiUdp6ServiceBindingProtocolGuid ## TO_START + gEfiUdp6ProtocolGuid ## TO_START + gEfiDhcp6ServiceBindingProtocolGuid ## BY_START + gEfiDhcp6ProtocolGuid ## BY_START + gEfiIp6ConfigProtocolGuid ## TO_START + + +[Pcd] + gEfiNetworkPkgTokenSpaceGuid.PcdDhcp6UidType ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + Dhcp6DxeExtra.uni diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.uni b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.uni new file mode 100644 index 0000000000..ce60a4758f Binary files /dev/null and b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.uni differ diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni new file mode 100644 index 0000000000..78f057400d Binary files /dev/null and b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni differ diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c new file mode 100644 index 0000000000..d8c0ad0d24 --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c @@ -0,0 +1,1216 @@ +/** @file + This EFI_DHCP6_PROTOCOL interface implementation. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Dhcp6Impl.h" + +// +// Well-known multi-cast address defined in section-24.1 of rfc-3315 +// +// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2 +// ALL_DHCP_Servers address: FF05::1:3 +// +EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}}; +EFI_IPv6_ADDRESS mAllDhcpServersAddress = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}}; + +EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = { + EfiDhcp6GetModeData, + EfiDhcp6Configure, + EfiDhcp6Start, + EfiDhcp6InfoRequest, + EfiDhcp6RenewRebind, + EfiDhcp6Decline, + EfiDhcp6Release, + EfiDhcp6Stop, + EfiDhcp6Parse +}; + +/** + Starts the DHCPv6 standard S.A.R.R. process. + + The Start() function starts the DHCPv6 standard process. This function can + be called only when the state of Dhcp6 instance is in the Dhcp6Init state. + If the DHCP process completes successfully, the state of the Dhcp6 instance + will be transferred through Dhcp6Selecting and Dhcp6Requesting to the + Dhcp6Bound state. + Refer to rfc-3315 for precise state transitions during this process. At the + time when each event occurs in this process, the callback function that was set + by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this + opportunity to control the process. + + @param[in] This The pointer to Dhcp6 protocol. + + @retval EFI_SUCCESS The DHCPv6 standard process has started, or it has + completed when CompletionEvent is NULL. + @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no + response was received from the server within the + specified timeout value. + @retval EFI_ABORTED The user aborted the DHCPv6 process. + @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6 + standard process. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NO_MEDIA There was a media error. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Start ( + IN EFI_DHCP6_PROTOCOL *This + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + // + // The instance has already been started. + // + if (Instance->IaCb.Ia->State != Dhcp6Init) { + return EFI_ALREADY_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + + // + // Send the solicit message to start S.A.R.R process. + // + Status = Dhcp6SendSolicitMsg (Instance); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateful exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchronous call. + // + if (Instance->Config->IaInfoEvent == NULL) { + + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; + +ON_ERROR: + + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Stops the DHCPv6 standard S.A.R.R. process. + + The Stop() function is used to stop the DHCPv6 standard process. After this + function is called successfully, the state of Dhcp6 instance is transferred + into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called + before DHCPv6 standard process can be started again. This function can be + called when the Dhcp6 instance is in any state. + + @param[in] This The pointer to the Dhcp6 protocol. + + @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Stop ( + IN EFI_DHCP6_PROTOCOL *This + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + EFI_UDP6_PROTOCOL *Udp6; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + Udp6 = Service->UdpIo->Protocol.Udp6; + Status = EFI_SUCCESS; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return Status; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + // + // No valid REPLY message received yet, cleanup this instance directly. + // + if (Instance->IaCb.Ia->State == Dhcp6Init || + Instance->IaCb.Ia->State == Dhcp6Selecting || + Instance->IaCb.Ia->State == Dhcp6Requesting + ) { + goto ON_EXIT; + } + + // + // Release the current ready Ia. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->UdpSts = EFI_ALREADY_STARTED; + Status = Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia); + gBS->RestoreTPL (OldTpl); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (Instance->Config->IaInfoEvent == NULL) { + ASSERT (Udp6 != NULL); + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Udp6->Poll (Udp6); + } + Status = Instance->UdpSts; + } + +ON_EXIT: + // + // Clean up the session data for the released Ia. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Dhcp6CleanupSession (Instance, EFI_SUCCESS); + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Returns the current operating mode data for the Dhcp6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the Dhcp6 instance. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data. + @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data. + + @retval EFI_SUCCESS The mode data was returned. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance was not + configured. +**/ +EFI_STATUS +EFIAPI +EfiDhcp6GetModeData ( + IN EFI_DHCP6_PROTOCOL *This, + OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL, + OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_DHCP6_IA *Ia; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + UINT32 IaSize; + UINT32 IdSize; + + if (This == NULL || (Dhcp6ModeData == NULL && Dhcp6ConfigData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + if (Instance->Config == NULL && Dhcp6ConfigData != NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Service->ClientId != NULL); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // User needs a copy of instance config data. + // + if (Dhcp6ConfigData != NULL) { + ZeroMem (Dhcp6ConfigData, sizeof(EFI_DHCP6_CONFIG_DATA)); + // + // Duplicate config data, including all reference buffers. + // + if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) { + goto ON_ERROR; + } + } + + // + // User need a copy of instance mode data. + // + if (Dhcp6ModeData != NULL) { + ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA)); + // + // Duplicate a copy of EFI_DHCP6_DUID for client Id. + // + IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length); + + Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize); + if (Dhcp6ModeData->ClientId == NULL) { + goto ON_ERROR; + } + + CopyMem ( + Dhcp6ModeData->ClientId, + Service->ClientId, + IdSize + ); + + Ia = Instance->IaCb.Ia; + if (Ia != NULL) { + // + // Duplicate a copy of EFI_DHCP6_IA for configured Ia. + // + IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS); + + Dhcp6ModeData->Ia = AllocateZeroPool (IaSize); + if (Dhcp6ModeData->Ia == NULL) { + goto ON_ERROR; + } + + CopyMem ( + Dhcp6ModeData->Ia, + Ia, + IaSize + ); + + // + // Duplicate a copy of reply packet if has. + // + if (Ia->ReplyPacket != NULL) { + Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size); + if (Dhcp6ModeData->Ia->ReplyPacket == NULL) { + goto ON_ERROR; + } + CopyMem ( + Dhcp6ModeData->Ia->ReplyPacket, + Ia->ReplyPacket, + Ia->ReplyPacket->Size + ); + } + } + } + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_ERROR: + + if (Dhcp6ConfigData != NULL) { + Dhcp6CleanupConfigData (Dhcp6ConfigData); + } + if (Dhcp6ModeData != NULL) { + Dhcp6CleanupModeData (Dhcp6ModeData); + } + gBS->RestoreTPL (OldTpl); + + return EFI_OUT_OF_RESOURCES; +} + + +/** + Initializes, changes, or resets the operational settings for the Dhcp6 instance. + + The Configure() function is used to initialize or clean up the configuration + data of the Dhcp6 instance: + - When Dhcp6CfgData is not NULL and Configure() is called successfully, the + configuration data will be initialized in the Dhcp6 instance, and the state + of the configured IA will be transferred into Dhcp6Init. + - When Dhcp6CfgData is NULL and Configure() is called successfully, the + configuration data will be cleaned up and no IA will be associated with + the Dhcp6 instance. + To update the configuration data for an Dhcp6 instance, the original data + must be cleaned up before setting the new configuration data. + + @param[in] This The pointer to the Dhcp6 protocol + @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA. + + @retval EFI_SUCCESS The Dhcp6 is configured successfully with the + Dhcp6Init state, or cleaned up the original + configuration setting. + @retval EFI_ACCESS_DENIED The Dhcp6 instance was already configured. + The Dhcp6 instance has already started the + DHCPv6 S.A.R.R when Dhcp6CfgData is NULL. + @retval EFI_INVALID_PARAMETER Some of the parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Configure ( + IN EFI_DHCP6_PROTOCOL *This, + IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + LIST_ENTRY *Entry; + DHCP6_INSTANCE *Other; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + UINTN Index; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // Check the parameter of configure data. + // + if (Dhcp6CfgData != NULL) { + if (Dhcp6CfgData->OptionCount > 0 && Dhcp6CfgData->OptionList == NULL) { + return EFI_INVALID_PARAMETER; + } + if (Dhcp6CfgData->OptionList != NULL) { + for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) { + if (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId || + Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit || + Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept || + Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana || + Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata + ) { + return EFI_INVALID_PARAMETER; + } + } + } + + if (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA && + Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA + ) { + return EFI_INVALID_PARAMETER; + } + + if (Dhcp6CfgData->IaInfoEvent == NULL && Dhcp6CfgData->SolicitRetransmission == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Dhcp6CfgData->SolicitRetransmission != NULL && + Dhcp6CfgData->SolicitRetransmission->Mrc == 0 && + Dhcp6CfgData->SolicitRetransmission->Mrd == 0 + ) { + return EFI_INVALID_PARAMETER; + } + + // + // Make sure the (IaId, IaType) is unique over all the instances. + // + NET_LIST_FOR_EACH (Entry, &Service->Child) { + Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link); + if (Other->IaCb.Ia != NULL && + Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type && + Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId + ) { + return EFI_INVALID_PARAMETER; + } + } + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (Dhcp6CfgData != NULL) { + // + // It's not allowed to configure one instance twice without configure null. + // + if (Instance->Config != NULL) { + gBS->RestoreTPL (OldTpl); + return EFI_ACCESS_DENIED; + } + + // + // Duplicate config data including all reference buffers. + // + Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA)); + if (Instance->Config == NULL) { + gBS->RestoreTPL (OldTpl); + return EFI_OUT_OF_RESOURCES; + } + + Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData); + if (EFI_ERROR(Status)) { + FreePool (Instance->Config); + gBS->RestoreTPL (OldTpl); + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize the Ia descriptor from the config data, and leave the other + // fields of the Ia as default value 0. + // + Instance->IaCb.Ia = AllocateZeroPool (sizeof(EFI_DHCP6_IA)); + if (Instance->IaCb.Ia == NULL) { + Dhcp6CleanupConfigData (Instance->Config); + FreePool (Instance->Config); + gBS->RestoreTPL (OldTpl); + return EFI_OUT_OF_RESOURCES; + } + CopyMem ( + &Instance->IaCb.Ia->Descriptor, + &Dhcp6CfgData->IaDescriptor, + sizeof(EFI_DHCP6_IA_DESCRIPTOR) + ); + + } else { + + if (Instance->Config == NULL) { + ASSERT (Instance->IaCb.Ia == NULL); + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + } + + // + // It's not allowed to configure a started instance as null. + // + if (Instance->IaCb.Ia->State != Dhcp6Init) { + gBS->RestoreTPL (OldTpl); + return EFI_ACCESS_DENIED; + } + + Dhcp6CleanupConfigData (Instance->Config); + FreePool (Instance->Config); + Instance->Config = NULL; + + FreePool (Instance->IaCb.Ia); + Instance->IaCb.Ia = NULL; + } + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Request configuration information without the assignment of any + Ia addresses of the client. + + The InfoRequest() function is used to request configuration information + without the assignment of any IPv6 address of the client. The client sends + out an Information Request packet to obtain the required configuration + information, and DHCPv6 server responds with a Reply packet containing + the information for the client. The received Reply packet will be passed + to the user by ReplyCallback function. If the user returns EFI_NOT_READY from + ReplyCallback, the Dhcp6 instance will continue to receive other Reply + packets unless timeout according to the Retransmission parameter. + Otherwise, the Information Request exchange process will be finished + successfully if user returns EFI_SUCCESS from ReplyCallback. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client + Identifier option and include it into Information Request + packet. Otherwise, Client Identifier option will not be included. + @param[in] OptionRequest The pointer to the buffer of option request options. + @param[in] OptionCount The option number in the OptionList. + @param[in] OptionList The list of appended options. + @param[in] Retransmission The pointer to the retransmission of the message. + @param[in] TimeoutEvent The event of timeout. + @param[in] ReplyCallback The callback function when the reply was received. + @param[in] CallbackContext The pointer to the parameter passed to the callback. + + @retval EFI_SUCCESS The DHCPv6 information request exchange process + completed when TimeoutEvent is NULL. Information + Request packet has been sent to DHCPv6 server when + TimeoutEvent is not NULL. + @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed + because of no response, or not all requested-options + are responded by DHCPv6 servers when Timeout happened. + @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted + by user. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6InfoRequest ( + IN EFI_DHCP6_PROTOCOL *This, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL, + IN EFI_DHCP6_RETRANSMISSION *Retransmission, + IN EFI_EVENT TimeoutEvent OPTIONAL, + IN EFI_DHCP6_INFO_CALLBACK ReplyCallback, + IN VOID *CallbackContext OPTIONAL + ) +{ + EFI_STATUS Status; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + UINTN Index; + EFI_EVENT Timer; + EFI_STATUS TimerStatus; + UINTN GetMappingTimeOut; + + if (This == NULL || OptionRequest == NULL || Retransmission == NULL || ReplyCallback == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Retransmission != NULL && Retransmission->Mrc == 0 && Retransmission->Mrd == 0) { + return EFI_INVALID_PARAMETER; + } + + if (OptionCount > 0 && OptionList == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (OptionList != NULL) { + for (Index = 0; Index < OptionCount; Index++) { + if (OptionList[Index]->OpCode == Dhcp6OptClientId || OptionList[Index]->OpCode == Dhcp6OptRequestOption) { + return EFI_INVALID_PARAMETER; + } + } + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + Status = Dhcp6StartInfoRequest ( + Instance, + SendClientId, + OptionRequest, + OptionCount, + OptionList, + Retransmission, + TimeoutEvent, + ReplyCallback, + CallbackContext + ); + if (Status == EFI_NO_MAPPING) { + // + // The link local address is not ready, wait for some time and restart + // the DHCP6 information request process. + // + Status = Dhcp6GetMappingTimeOut(Service->Ip6Cfg, &GetMappingTimeOut); + if (EFI_ERROR(Status)) { + return Status; + } + + Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Start the timer, wait for link local address DAD to finish. + // + Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Timer); + return Status; + } + + do { + TimerStatus = gBS->CheckEvent (Timer); + if (!EFI_ERROR (TimerStatus)) { + Status = Dhcp6StartInfoRequest ( + Instance, + SendClientId, + OptionRequest, + OptionCount, + OptionList, + Retransmission, + TimeoutEvent, + ReplyCallback, + CallbackContext + ); + } + } while (TimerStatus == EFI_NOT_READY); + + gBS->CloseEvent (Timer); + } + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (TimeoutEvent == NULL) { + + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; +} + + +/** + Manually extend the valid and preferred lifetimes for the IPv6 addresses + of the configured IA and update other configuration parameters by sending a + Renew or Rebind packet. + + The RenewRebind() function is used to manually extend the valid and preferred + lifetimes for the IPv6 addresses of the configured IA, and update other + configuration parameters by sending Renew or Rebind packet. + - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound, + it sends Renew packet to the previously DHCPv6 server and transfer the + state of the configured IA to Dhcp6Renewing. If valid Reply packet received, + the state transfers to Dhcp6Bound and the valid and preferred timer restarts. + If fails, the state transfers to Dhcp6Bound, but the timer continues. + - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound, + it will send a Rebind packet. If valid Reply packet is received, the state transfers + to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state + transfers to Dhcp6Init, and the IA can't be used. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state. + Otherwise, Renew packet will be sent and enter Dhcp6Renewing state. + + @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process has + completed and at least one IPv6 address of the + configured IA has been bound again when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL. + The EFI DHCPv6 Protocol instance has sent Renew + or Rebind packet when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ALREADY_STARTED The state of the configured IA has already entered + Dhcp6Renewing when RebindRequest is FALSE. + The state of the configured IA has already entered + Dhcp6Rebinding when RebindRequest is TRUE. + @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted + by the user. + @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed + because of no response. + @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured + IA after the DHCPv6 renew/rebind exchange process. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6RenewRebind ( + IN EFI_DHCP6_PROTOCOL *This, + IN BOOLEAN RebindRequest + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + // + // The instance has already entered renewing or rebinding state. + // + if ((Instance->IaCb.Ia->State == Dhcp6Rebinding && RebindRequest) || + (Instance->IaCb.Ia->State == Dhcp6Renewing && !RebindRequest) + ) { + return EFI_ALREADY_STARTED; + } + + if (Instance->IaCb.Ia->State != Dhcp6Bound) { + return EFI_ACCESS_DENIED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + + // + // Send renew/rebind message to start exchange process. + // + Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateful exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (Instance->Config->IaInfoEvent == NULL) { + + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; + +ON_ERROR: + + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Inform that one or more addresses assigned by a server are already + in use by another node. + + The Decline() function is used to manually decline the assignment of + IPv6 addresses, which have been already used by another node. If all + IPv6 addresses of the configured IA are declined through this function, + the state of the IA will switch through Dhcp6Declining to Dhcp6Init. + Otherwise, the state of the IA will restore to Dhcp6Bound after the + declining process. The Decline() can only be called when the IA is in + Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, + this function is a blocking operation. It will return after the + declining process finishes, or aborted by user. + + @param[in] This The pointer to EFI_DHCP6_PROTOCOL. + @param[in] AddressCount The number of declining addresses. + @param[in] Addresses The pointer to the buffer stored the declining + addresses. + + @retval EFI_SUCCESS The DHCPv6 decline exchange process completed + when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL. + The Dhcp6 instance sent Decline packet when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ABORTED The DHCPv6 decline exchange process aborted by user. + @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with + the configured IA for this instance. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Decline ( + IN EFI_DHCP6_PROTOCOL *This, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_DHCP6_IA *DecIa; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL || AddressCount == 0 || Addresses == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + if (Instance->IaCb.Ia->State != Dhcp6Bound) { + return EFI_ACCESS_DENIED; + } + + // + // Check whether all the declined addresses belongs to the configured Ia. + // + Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses); + + if (EFI_ERROR(Status)) { + return Status; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + + // + // Deprive of all the declined addresses from the configured Ia, and create a + // DeclineIa used to create decline message. + // + DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses); + + if (DecIa == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Send the decline message to start exchange process. + // + Status = Dhcp6SendDeclineMsg (Instance, DecIa); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateful exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + FreePool (DecIa); + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (Instance->Config->IaInfoEvent == NULL) { + + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (DecIa != NULL) { + FreePool (DecIa); + } + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Release one or more addresses associated with the configured Ia + for current instance. + + The Release() function is used to manually release one or more + IPv6 addresses. If AddressCount is zero, it will release all IPv6 + addresses of the configured IA. If all IPv6 addresses of the IA are + released through this function, the state of the IA will switch + through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the + IA will restore to Dhcp6Bound after the releasing process. + The Release() can only be called when the IA is in Dhcp6Bound state. + If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is + a blocking operation. It will return after the releasing process + finishes, or is aborted by user. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] AddressCount The number of releasing addresses. + @param[in] Addresses The pointer to the buffer stored the releasing + addresses. + + @retval EFI_SUCCESS The DHCPv6 release exchange process + completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent + was NULL. The Dhcp6 instance was sent Release + packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent + was not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ABORTED The DHCPv6 release exchange process aborted by user. + @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with + the configured IA for this instance. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Release ( + IN EFI_DHCP6_PROTOCOL *This, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_DHCP6_IA *RelIa; + DHCP6_INSTANCE *Instance; + DHCP6_SERVICE *Service; + + if (This == NULL || (AddressCount != 0 && Addresses == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = DHCP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + + // + // The instance hasn't been configured. + // + if (Instance->Config == NULL) { + return EFI_ACCESS_DENIED; + } + + ASSERT (Instance->IaCb.Ia != NULL); + + if (Instance->IaCb.Ia->State != Dhcp6Bound) { + return EFI_ACCESS_DENIED; + } + + // + // Check whether all the released addresses belongs to the configured Ia. + // + Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses); + + if (EFI_ERROR(Status)) { + return Status; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + + // + // Deprive of all the released addresses from the configured Ia, and create a + // ReleaseIa used to create release message. + // + RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses); + + if (RelIa == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Send the release message to start exchange process. + // + Status = Dhcp6SendReleaseMsg (Instance, RelIa); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateful exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + FreePool (RelIa); + gBS->RestoreTPL (OldTpl); + + // + // Poll udp out of the net tpl if synchoronus call. + // + if (Instance->Config->IaInfoEvent == NULL) { + while (Instance->UdpSts == EFI_ALREADY_STARTED) { + Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); + } + return Instance->UdpSts; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (RelIa != NULL) { + FreePool (RelIa); + } + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + Parse the option data in the Dhcp6 packet. + + The Parse() function is used to retrieve the option list in the DHCPv6 packet. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] Packet The pointer to the Dhcp6 packet. + @param[in, out] OptionCount The number of option in the packet. + @param[out] PacketOptionList The array of pointers to each option in the packet. + + @retval EFI_SUCCESS The packet was successfully parsed. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options + that were found in the Packet. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Parse ( + IN EFI_DHCP6_PROTOCOL *This, + IN EFI_DHCP6_PACKET *Packet, + IN OUT UINT32 *OptionCount, + OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL + ) +{ + UINT32 OptCnt; + UINT32 OptLen; + UINT16 DataLen; + UINT8 *Start; + UINT8 *End; + + if (This == NULL || Packet == NULL || OptionCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*OptionCount != 0 && PacketOptionList == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Packet->Length > Packet->Size || Packet->Length < sizeof (EFI_DHCP6_HEADER)) { + return EFI_INVALID_PARAMETER; + } + + // + // The format of Dhcp6 option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | option-code | option-len (option data) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | option-data | + // | (option-len octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + OptCnt = 0; + OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER); + Start = Packet->Dhcp6.Option; + End = Start + OptLen; + + // + // Calculate the number of option in the packet. + // + while (Start < End) { + DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen; + Start += (NTOHS (DataLen) + 4); + OptCnt++; + } + + // + // It will return buffer too small if pass-in option count is smaller than the + // actual count of options in the packet. + // + if (OptCnt > *OptionCount) { + *OptionCount = OptCnt; + return EFI_BUFFER_TOO_SMALL; + } + + ZeroMem ( + PacketOptionList, + (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *)) + ); + + OptCnt = 0; + Start = Packet->Dhcp6.Option; + + while (Start < End) { + + PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *) Start; + DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen; + Start += (NTOHS (DataLen) + 4); + OptCnt++; + } + + return EFI_SUCCESS; +} + diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h new file mode 100644 index 0000000000..e3e7553163 --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h @@ -0,0 +1,605 @@ +/** @file + Dhcp6 internal data structure and definition declaration. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_DHCP6_IMPL_H__ +#define __EFI_DHCP6_IMPL_H__ + + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct _DHCP6_IA_CB DHCP6_IA_CB; +typedef struct _DHCP6_INF_CB DHCP6_INF_CB; +typedef struct _DHCP6_TX_CB DHCP6_TX_CB; +typedef struct _DHCP6_SERVICE DHCP6_SERVICE; +typedef struct _DHCP6_INSTANCE DHCP6_INSTANCE; + +#include "Dhcp6Utility.h" +#include "Dhcp6Io.h" +#include "Dhcp6Driver.h" + +#define DHCP6_SERVICE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'S') +#define DHCP6_INSTANCE_SIGNATURE SIGNATURE_32 ('D', 'H', '6', 'I') + +// +// Transmit parameters of solicit message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_SOL_MAX_DELAY 1 +#define DHCP6_SOL_IRT 1 +#define DHCP6_SOL_MRC 0 +#define DHCP6_SOL_MRT 120 +#define DHCP6_SOL_MRD 0 +// +// Transmit parameters of request message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_REQ_IRT 1 +#define DHCP6_REQ_MRC 10 +#define DHCP6_REQ_MRT 30 +#define DHCP6_REQ_MRD 0 +// +// Transmit parameters of confirm message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_CNF_MAX_DELAY 1 +#define DHCP6_CNF_IRT 1 +#define DHCP6_CNF_MRC 0 +#define DHCP6_CNF_MRT 4 +#define DHCP6_CNF_MRD 10 +// +// Transmit parameters of renew message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_REN_IRT 10 +#define DHCP6_REN_MRC 0 +#define DHCP6_REN_MRT 600 +#define DHCP6_REN_MRD 0 +// +// Transmit parameters of rebind message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_REB_IRT 10 +#define DHCP6_REB_MRC 0 +#define DHCP6_REB_MRT 600 +#define DHCP6_REB_MRD 0 +// +// Transmit parameters of information request message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_INF_MAX_DELAY 1 +#define DHCP6_INF_IRT 1 +#define DHCP6_INF_MRC 0 +#define DHCP6_INF_MRT 120 +#define DHCP6_INF_MRD 0 +// +// Transmit parameters of release message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_REL_IRT 1 +#define DHCP6_REL_MRC 5 +#define DHCP6_REL_MRT 0 +#define DHCP6_REL_MRD 0 +// +// Transmit parameters of decline message, refers to section-5.5 of rfc-3315. +// +#define DHCP6_DEC_IRT 1 +#define DHCP6_DEC_MRC 5 +#define DHCP6_DEC_MRT 0 +#define DHCP6_DEC_MRD 0 + +#define DHCP6_PACKET_ALL 0 +#define DHCP6_PACKET_STATEFUL 1 +#define DHCP6_PACKET_STATELESS 2 + +#define DHCP6_BASE_PACKET_SIZE 1024 + +#define DHCP6_PORT_CLIENT 546 +#define DHCP6_PORT_SERVER 547 + +#define DHCP6_INSTANCE_FROM_THIS(Instance) CR ((Instance), DHCP6_INSTANCE, Dhcp6, DHCP6_INSTANCE_SIGNATURE) +#define DHCP6_SERVICE_FROM_THIS(Service) CR ((Service), DHCP6_SERVICE, ServiceBinding, DHCP6_SERVICE_SIGNATURE) + +extern EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress; +extern EFI_IPv6_ADDRESS mAllDhcpServersAddress; +extern EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate; + +// +// Enumeration of Dhcp6 message type, refers to section-5.3 of rfc-3315. +// +typedef enum { + Dhcp6MsgSolicit = 1, + Dhcp6MsgAdvertise = 2, + Dhcp6MsgRequest = 3, + Dhcp6MsgConfirm = 4, + Dhcp6MsgRenew = 5, + Dhcp6MsgRebind = 6, + Dhcp6MsgReply = 7, + Dhcp6MsgRelease = 8, + Dhcp6MsgDecline = 9, + Dhcp6MsgReconfigure = 10, + Dhcp6MsgInfoRequest = 11 +} DHCP6_MSG_TYPE; + +// +// Enumeration of option code in Dhcp6 packet, refers to section-24.3 of rfc-3315. +// +typedef enum { + Dhcp6OptClientId = 1, + Dhcp6OptServerId = 2, + Dhcp6OptIana = 3, + Dhcp6OptIata = 4, + Dhcp6OptIaAddr = 5, + Dhcp6OptRequestOption = 6, + Dhcp6OptPreference = 7, + Dhcp6OptElapsedTime = 8, + Dhcp6OptReplayMessage = 9, + Dhcp6OptAuthentication = 11, + Dhcp6OptServerUnicast = 12, + Dhcp6OptStatusCode = 13, + Dhcp6OptRapidCommit = 14, + Dhcp6OptUserClass = 15, + Dhcp6OptVendorClass = 16, + Dhcp6OptVendorInfo = 17, + Dhcp6OptInterfaceId = 18, + Dhcp6OptReconfigMessage = 19, + Dhcp6OptReconfigureAccept = 20 +} DHCP6_OPT_CODE; + +// +// Enumeration of status code recorded by IANA, refers to section-24.4 of rfc-3315. +// +typedef enum { + Dhcp6StsSuccess = 0, + Dhcp6StsUnspecFail = 1, + Dhcp6StsNoAddrsAvail = 2, + Dhcp6StsNoBinding = 3, + Dhcp6StsNotOnLink = 4, + Dhcp6StsUseMulticast = 5 +} DHCP6_STS_CODE; + +// +// Enumeration of Duid type recorded by IANA, refers to section-24.5 of rfc-3315. +// +typedef enum { + Dhcp6DuidTypeLlt = 1, + Dhcp6DuidTypeEn = 2, + Dhcp6DuidTypeLl = 3, + Dhcp6DuidTypeUuid = 4 +} DHCP6_DUID_TYPE; + +// +// Control block for each IA. +// +struct _DHCP6_IA_CB { + EFI_DHCP6_IA *Ia; + UINT32 T1; + UINT32 T2; + UINT32 AllExpireTime; + UINT32 LeaseTime; +}; + +// +// Control block for each transmitted message. +// +struct _DHCP6_TX_CB { + LIST_ENTRY Link; + UINT32 Xid; + EFI_DHCP6_PACKET *TxPacket; + EFI_DHCP6_RETRANSMISSION RetryCtl; + UINT32 RetryCnt; + UINT32 RetryExp; + UINT32 RetryLos; + UINT32 TickTime; + UINT16 *Elapsed; + BOOLEAN SolicitRetry; +}; + +// +// Control block for each info-request message. +// +struct _DHCP6_INF_CB { + LIST_ENTRY Link; + UINT32 Xid; + EFI_DHCP6_INFO_CALLBACK ReplyCallback; + VOID *CallbackContext; + EFI_EVENT TimeoutEvent; +}; + +// +// Control block for Dhcp6 instance, it's per configuration data. +// +struct _DHCP6_INSTANCE { + UINT32 Signature; + EFI_HANDLE Handle; + DHCP6_SERVICE *Service; + LIST_ENTRY Link; + EFI_DHCP6_PROTOCOL Dhcp6; + EFI_EVENT Timer; + EFI_DHCP6_CONFIG_DATA *Config; + EFI_DHCP6_IA *CacheIa; + DHCP6_IA_CB IaCb; + LIST_ENTRY TxList; + LIST_ENTRY InfList; + EFI_DHCP6_PACKET *AdSelect; + UINT8 AdPref; + EFI_IPv6_ADDRESS *Unicast; + volatile EFI_STATUS UdpSts; + BOOLEAN InDestroy; + BOOLEAN MediaPresent; + // + // StartTime is used to calculate the 'elapsed-time' option. Refer to RFC3315, + // the elapsed-time is amount of time since the client began its current DHCP transaction. + // + UINT64 StartTime; +}; + +// +// Control block for Dhcp6 service, it's per Nic handle. +// +struct _DHCP6_SERVICE { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_HANDLE Image; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_DHCP6_DUID *ClientId; + UDP_IO *UdpIo; + UINT32 Xid; + LIST_ENTRY Child; + UINTN NumOfChild; +}; + +/** + Starts the DHCPv6 standard S.A.R.R. process. + + The Start() function starts the DHCPv6 standard process. This function can + be called only when the state of Dhcp6 instance is in the Dhcp6Init state. + If the DHCP process completes successfully, the state of the Dhcp6 instance + will be transferred through Dhcp6Selecting and Dhcp6Requesting to the + Dhcp6Bound state. + Refer to rfc-3315 for precise state transitions during this process. At the + time when each event occurs in this process, the callback function that was set + by EFI_DHCP6_PROTOCOL.Configure() will be called and the user can take this + opportunity to control the process. + + @param[in] This The pointer to Dhcp6 protocol. + + @retval EFI_SUCCESS The DHCPv6 standard process has started, or it + completed when CompletionEvent was NULL. + @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no + response was received from the server within the + specified timeout value. + @retval EFI_ABORTED The user aborted the DHCPv6 process. + @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6 + standard process. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Start ( + IN EFI_DHCP6_PROTOCOL *This + ); + +/** + Stops the DHCPv6 standard S.A.R.R. process. + + The Stop() function is used to stop the DHCPv6 standard process. After this + function is called successfully, the state of Dhcp6 instance is transferred + into the Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called + before DHCPv6 standard process can be started again. This function can be + called when the Dhcp6 instance is in any state. + + @param[in] This The pointer to the Dhcp6 protocol. + + @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Stop ( + IN EFI_DHCP6_PROTOCOL *This + ); + +/** + Returns the current operating mode data for the Dhcp6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the Dhcp6 instance. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data. + @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data. + + @retval EFI_SUCCESS The mode data was returned. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance has not + been configured when Dhcp6ConfigData is + not NULL. +**/ +EFI_STATUS +EFIAPI +EfiDhcp6GetModeData ( + IN EFI_DHCP6_PROTOCOL *This, + OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL, + OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL + ); + +/** + Initializes, changes, or resets the operational settings for the Dhcp6 instance. + + The Configure() function is used to initialize or clean up the configuration + data of the Dhcp6 instance: + - When Dhcp6CfgData is not NULL and Configure() is called successfully, the + configuration data will be initialized in the Dhcp6 instance and the state + of the configured IA will be transferred into Dhcp6Init. + - When Dhcp6CfgData is NULL and Configure() is called successfully, the + configuration data will be cleaned up and no IA will be associated with + the Dhcp6 instance. + To update the configuration data for an Dhcp6 instance, the original data + must be cleaned up before setting the new configuration data. + + @param[in] This The pointer to the Dhcp6 protocol + @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA. + + @retval EFI_SUCCESS The Dhcp6 is configured successfully with the + Dhcp6Init state, or cleaned up the original + configuration setting. + @retval EFI_ACCESS_DENIED The Dhcp6 instance has been already configured + when Dhcp6CfgData is not NULL. + The Dhcp6 instance has already started the + DHCPv6 S.A.R.R when Dhcp6CfgData is NULL. + @retval EFI_INVALID_PARAMETER Some of the parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Configure ( + IN EFI_DHCP6_PROTOCOL *This, + IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL + ); + +/** + Request configuration information without the assignment of any + Ia addresses of the client. + + The InfoRequest() function is used to request configuration information + without the assignment of any IPv6 address of the client. Client sends + out Information Request packet to obtain the required configuration + information, and DHCPv6 server responds with Reply packet containing + the information for the client. The received Reply packet will be passed + to the user by ReplyCallback function. If user returns EFI_NOT_READY from + ReplyCallback, the Dhcp6 instance will continue to receive other Reply + packets unless timeout according to the Retransmission parameter. + Otherwise, the Information Request exchange process will be finished + successfully if user returns EFI_SUCCESS from ReplyCallback. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client + Identifier option and include it into Information Request + packet. Otherwise, Client Identifier option will not be included. + @param[in] OptionRequest The pointer to the buffer of option request options. + @param[in] OptionCount The option number in the OptionList. + @param[in] OptionList The list of appended options. + @param[in] Retransmission The pointer to the retransmission of the message. + @param[in] TimeoutEvent The event of timeout. + @param[in] ReplyCallback The callback function when a reply was received. + @param[in] CallbackContext The pointer to the parameter passed to the callback. + + @retval EFI_SUCCESS The DHCPv6 information request exchange process + completed when TimeoutEvent is NULL. Information + Request packet has been sent to DHCPv6 server when + TimeoutEvent is not NULL. + @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed + because of no response, or not all requested-options + are responded to by DHCPv6 servers when Timeout happened. + @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted + by the user. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6InfoRequest ( + IN EFI_DHCP6_PROTOCOL *This, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL, + IN EFI_DHCP6_RETRANSMISSION *Retransmission, + IN EFI_EVENT TimeoutEvent OPTIONAL, + IN EFI_DHCP6_INFO_CALLBACK ReplyCallback, + IN VOID *CallbackContext OPTIONAL + ); + +/** + Manually extend the valid and preferred lifetimes for the IPv6 addresses + of the configured IA and update other configuration parameters by sending + Renew or Rebind packet. + + The RenewRebind() function is used to manually extend the valid and preferred + lifetimes for the IPv6 addresses of the configured IA and update other + configuration parameters by sending a Renew or Rebind packet. + - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound, + it will send Renew packet to the previously DHCPv6 server and transfer the + state of the configured IA to Dhcp6Renewing. If valid Reply packet received, + the state transfers to Dhcp6Bound and the valid and preferred timer restarts. + If fails, the state transfers to Dhcp6Bound but the timer continues. + - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound, + it will send a Rebind packet. If a valid Reply packet is received, the state transfers + to Dhcp6Bound, and the valid and preferred timer restarts. If it fails, the state + transfers to Dhcp6Init, and the IA can't be used. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state. + Otherwise, Renew packet will be sent and enter Dhcp6Renewing state. + + @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process + completed and at least one IPv6 address of the + configured IA was bound again when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL. + The EFI DHCPv6 Protocol instance has sent Renew + or Rebind packet when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ALREADY_STARTED The state of the configured IA has already entered + Dhcp6Renewing when RebindRequest is FALSE. + The state of the configured IA has already entered + Dhcp6Rebinding when RebindRequest is TRUE. + @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted + by user. + @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed + because of no response. + @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured + IA after the DHCPv6 renew/rebind exchange process. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6RenewRebind ( + IN EFI_DHCP6_PROTOCOL *This, + IN BOOLEAN RebindRequest + ); + +/** + Inform that one or more addresses assigned by a server are already + in use by another node. + + The Decline() function is used to manually decline the assignment of + IPv6 addresses, which have been already used by another node. If all + IPv6 addresses of the configured IA are declined through this function, + the state of the IA will switch through Dhcp6Declining to Dhcp6Init. + Otherwise, the state of the IA will restore to Dhcp6Bound after the + declining process. The Decline() can only be called when the IA is in + Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, + this function is a blocking operation. It will return after the + declining process finishes, or aborted by user. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] AddressCount The number of declining addresses. + @param[in] Addresses The pointer to the buffer stored the declining + addresses. + + @retval EFI_SUCCESS The DHCPv6 decline exchange process completed + when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL. + The Dhcp6 instance has sent Decline packet when + EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ABORTED The DHCPv6 decline exchange process was aborted by the user. + @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with + the configured IA for this instance. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Decline ( + IN EFI_DHCP6_PROTOCOL *This, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ); + +/** + Release one or more addresses associated with the configured Ia + for the current instance. + + The Release() function is used to manually release the one or more + IPv6 address. If AddressCount is zero, it will release all IPv6 + addresses of the configured IA. If all IPv6 addresses of the IA are + released through this function, the state of the IA will switch + through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the + IA will restore to Dhcp6Bound after the releasing process. + The Release() can only be called when the IA is in a Dhcp6Bound state. + If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is + a blocking operation. It will return after the releasing process + finishes, or aborted by user. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] AddressCount The number of releasing addresses. + @param[in] Addresses The pointer to the buffer stored the releasing + addresses. + @retval EFI_SUCCESS The DHCPv6 release exchange process has + completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent + is NULL. The Dhcp6 instance has sent Release + packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent + is not NULL. + @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the + state of the configured IA is not in Dhcp6Bound. + @retval EFI_ABORTED The DHCPv6 release exchange process was aborted by the user. + @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with + the configured IA for this instance. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Release ( + IN EFI_DHCP6_PROTOCOL *This, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ); + +/** + Parse the option data in the Dhcp6 packet. + + The Parse() function is used to retrieve the option list in the DHCPv6 packet. + + @param[in] This The pointer to the Dhcp6 protocol. + @param[in] Packet The pointer to the Dhcp6 packet. + @param[in, out] OptionCount The number of option in the packet. + @param[out] PacketOptionList The array of pointers to the each option in the packet. + + @retval EFI_SUCCESS The packet was successfully parsed. + @retval EFI_INVALID_PARAMETER Some parameter is NULL. + @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options + that were found in the Packet. + +**/ +EFI_STATUS +EFIAPI +EfiDhcp6Parse ( + IN EFI_DHCP6_PROTOCOL *This, + IN EFI_DHCP6_PACKET *Packet, + IN OUT UINT32 *OptionCount, + OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL + ); + +#endif diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c new file mode 100644 index 0000000000..b4e0007926 --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c @@ -0,0 +1,3170 @@ +/** @file + Dhcp6 internal functions implementation. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Dhcp6Impl.h" + + +/** + Enqueue the packet into the retry list in case of timeout. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the Dhcp6 packet to retry. + @param[in] Elapsed The pointer to the elapsed time value in the packet. + @param[in] RetryCtl The pointer to the transmission control of the packet. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS Successfully enqueued the packet into the retry list according + to its message type. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected message type. + +**/ +EFI_STATUS +Dhcp6EnqueueRetry ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet, + IN UINT16 *Elapsed, + IN EFI_DHCP6_RETRANSMISSION *RetryCtl OPTIONAL + ) +{ + DHCP6_TX_CB *TxCb; + DHCP6_IA_CB *IaCb; + + ASSERT (Packet != NULL); + + IaCb = &Instance->IaCb; + TxCb = AllocateZeroPool (sizeof (DHCP6_TX_CB)); + + if (TxCb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Save tx packet pointer, and it will be destroyed when reply received. + // + TxCb->TxPacket = Packet; + TxCb->Xid = Packet->Dhcp6.Header.TransactionId; + + // + // Save pointer to elapsed-time value so we can update it on retransmits. + // + TxCb->Elapsed = Elapsed; + + // + // Calculate the retransmission according to the the message type. + // + switch (Packet->Dhcp6.Header.MessageType) { + case Dhcp6MsgSolicit: + // + // Calculate the retransmission threshold value for solicit packet. + // Use the default value by rfc-3315 if user doesn't configure. + // + if (RetryCtl == NULL) { + TxCb->RetryCtl.Irt = DHCP6_SOL_IRT; + TxCb->RetryCtl.Mrc = DHCP6_SOL_MRC; + TxCb->RetryCtl.Mrt = DHCP6_SOL_MRT; + TxCb->RetryCtl.Mrd = DHCP6_SOL_MRD; + } else { + TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_SOL_IRT; + TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_SOL_MRC; + TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_SOL_MRT; + TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_SOL_MRD; + } + + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + FALSE + ); + break; + + case Dhcp6MsgRequest: + // + // Calculate the retransmission threshold value for request packet. + // + TxCb->RetryCtl.Irt = DHCP6_REQ_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REQ_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REQ_MRT; + TxCb->RetryCtl.Mrd = DHCP6_REQ_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgConfirm: + // + // Calculate the retransmission threshold value for confirm packet. + // + TxCb->RetryCtl.Irt = DHCP6_CNF_IRT; + TxCb->RetryCtl.Mrc = DHCP6_CNF_MRC; + TxCb->RetryCtl.Mrt = DHCP6_CNF_MRT; + TxCb->RetryCtl.Mrd = DHCP6_CNF_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgRenew: + // + // Calculate the retransmission threshold value for renew packet. + // + TxCb->RetryCtl.Irt = DHCP6_REB_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REB_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REB_MRT; + TxCb->RetryCtl.Mrd = IaCb->T2 - IaCb->T1; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgRebind: + // + // Calculate the retransmission threshold value for rebind packet. + // + TxCb->RetryCtl.Irt = DHCP6_REN_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REN_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REN_MRT; + TxCb->RetryCtl.Mrd = IaCb->AllExpireTime - IaCb->T2; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgDecline: + // + // Calculate the retransmission threshold value for decline packet. + // + TxCb->RetryCtl.Irt = DHCP6_DEC_IRT; + TxCb->RetryCtl.Mrc = DHCP6_DEC_MRC; + TxCb->RetryCtl.Mrt = DHCP6_DEC_MRT; + TxCb->RetryCtl.Mrd = DHCP6_DEC_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgRelease: + // + // Calculate the retransmission threshold value for release packet. + // + TxCb->RetryCtl.Irt = DHCP6_REL_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REL_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REL_MRT; + TxCb->RetryCtl.Mrd = DHCP6_REL_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgInfoRequest: + // + // Calculate the retransmission threshold value for info-request packet. + // Use the default value by rfc-3315 if user doesn't configure. + // + if (RetryCtl == NULL) { + TxCb->RetryCtl.Irt = DHCP6_INF_IRT; + TxCb->RetryCtl.Mrc = DHCP6_INF_MRC; + TxCb->RetryCtl.Mrt = DHCP6_INF_MRT; + TxCb->RetryCtl.Mrd = DHCP6_INF_MRD; + } else { + TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_INF_IRT; + TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_INF_MRC; + TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_INF_MRT; + TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_INF_MRD; + } + + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + default: + // + // Unexpected message type. + // + return EFI_DEVICE_ERROR; + } + + // + // Insert into the retransmit list of the instance. + // + InsertTailList (&Instance->TxList, &TxCb->Link); + + return EFI_SUCCESS; +} + + +/** + Dequeue the packet from retry list if reply received or timeout at last. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] PacketXid The packet transaction id to match. + @param[in] NeedSignal If TRUE, then an timeout event need be signaled when it is existed. + Otherwise, this parameter is ignored. + + @retval EFI_SUCCESS Successfully dequeued the packet into retry list . + @retval EFI_NOT_FOUND There is no xid matched in retry list. + +**/ +EFI_STATUS +Dhcp6DequeueRetry ( + IN DHCP6_INSTANCE *Instance, + IN UINT32 PacketXid, + IN BOOLEAN NeedSignal + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + DHCP6_TX_CB *TxCb; + DHCP6_INF_CB *InfCb; + + // + // Seek the retransmit node in the retransmit list by packet xid. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + ASSERT(TxCb->TxPacket); + + if (TxCb->Xid == PacketXid) { + + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + + // + // Seek the info-request node in the info-request list by packet xid. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) { + + InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link); + + if (InfCb->Xid == PacketXid) { + // + // Remove the info-request node, and signal the event if timeout. + // + if (InfCb->TimeoutEvent != NULL && NeedSignal) { + gBS->SignalEvent (InfCb->TimeoutEvent); + } + + RemoveEntryList (&InfCb->Link); + FreePool (InfCb); + } + } + } + // + // Remove the retransmit node. + // + RemoveEntryList (&TxCb->Link); + ASSERT(TxCb->TxPacket); + FreePool (TxCb->TxPacket); + FreePool (TxCb); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Clean up the specific nodes in the retry list. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Scope The scope of cleanup nodes. + +**/ +VOID +Dhcp6CleanupRetry ( + IN DHCP6_INSTANCE *Instance, + IN UINT32 Scope + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + DHCP6_TX_CB *TxCb; + DHCP6_INF_CB *InfCb; + + // + // Clean up all the stateful messages from the retransmit list. + // + if (Scope == DHCP6_PACKET_STATEFUL || Scope == DHCP6_PACKET_ALL) { + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + ASSERT(TxCb->TxPacket); + + if (TxCb->TxPacket->Dhcp6.Header.MessageType != Dhcp6MsgInfoRequest) { + RemoveEntryList (&TxCb->Link); + FreePool (TxCb->TxPacket); + FreePool (TxCb); + } + } + } + + // + // Clean up all the stateless messages from the retransmit list. + // + if (Scope == DHCP6_PACKET_STATELESS || Scope == DHCP6_PACKET_ALL) { + + // + // Clean up all the retransmit list for stateless messages. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + ASSERT(TxCb->TxPacket); + + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + RemoveEntryList (&TxCb->Link); + FreePool (TxCb->TxPacket); + FreePool (TxCb); + } + } + + // + // Clean up all the info-request messages list. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) { + + InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link); + + if (InfCb->TimeoutEvent != NULL) { + gBS->SignalEvent (InfCb->TimeoutEvent); + } + RemoveEntryList (&InfCb->Link); + FreePool (InfCb); + } + } +} + +/** + Check whether the TxCb is still a valid control block in the instance's retry list. + + @param[in] Instance The pointer to DHCP6_INSTANCE. + @param[in] TxCb The control block for a transmitted message. + + @retval TRUE The control block is in Instance's retry list. + @retval FALSE The control block is NOT in Instance's retry list. + +**/ +BOOLEAN +Dhcp6IsValidTxCb ( + IN DHCP6_INSTANCE *Instance, + IN DHCP6_TX_CB *TxCb + ) +{ + LIST_ENTRY *Entry; + + NET_LIST_FOR_EACH (Entry, &Instance->TxList) { + if (TxCb == NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link)) { + return TRUE; + } + } + + return FALSE; +} + +/** + Clean up the session of the instance stateful exchange. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + @param[in] Status The return status from udp. + +**/ +VOID +Dhcp6CleanupSession ( + IN OUT DHCP6_INSTANCE *Instance, + IN EFI_STATUS Status + ) +{ + UINTN Index; + EFI_DHCP6_IA *Ia; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + // + // Clean up the retransmit list for stateful messages. + // + Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATEFUL); + + if (Instance->Unicast != NULL) { + FreePool (Instance->Unicast); + } + + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + + // + // Reinitialize the Ia fields of the instance. + // + Instance->UdpSts = Status; + Instance->AdSelect = NULL; + Instance->AdPref = 0; + Instance->Unicast = NULL; + Instance->IaCb.T1 = 0; + Instance->IaCb.T2 = 0; + Instance->IaCb.AllExpireTime = 0; + Instance->IaCb.LeaseTime = 0; + + // + // Clear start time + // + Instance->StartTime = 0; + + Ia = Instance->IaCb.Ia; + Ia->State = Dhcp6Init; + Ia->ReplyPacket = NULL; + + // + // Set the addresses as zero lifetime, and then the notify + // function in Ip6Config will remove these timeout address. + // + for (Index = 0; Index < Ia->IaAddressCount; Index++) { + Ia->IaAddress[Index].PreferredLifetime = 0; + Ia->IaAddress[Index].ValidLifetime = 0; + } + + // + // + // Signal the Ia information updated event to informal user. + // + if (Instance->Config->IaInfoEvent != NULL) { + gBS->SignalEvent (Instance->Config->IaInfoEvent); + } +} + + +/** + Callback to user when Dhcp6 transmit/receive occurs. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Event The current Dhcp6 event. + @param[in, out] Packet The pointer to the packet sending or received. + + @retval EFI_SUCCESS The user function returns success. + @retval EFI_NOT_READY Direct the caller to continue collecting the offer. + @retval EFI_ABORTED The user function ask it to abort. + +**/ +EFI_STATUS +EFIAPI +Dhcp6CallbackUser ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_EVENT Event, + IN OUT EFI_DHCP6_PACKET **Packet + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *NewPacket; + EFI_DHCP6_CALLBACK Callback; + VOID *Context; + + ASSERT (Packet != NULL); + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + + NewPacket = NULL; + Status = EFI_SUCCESS; + Callback = Instance->Config->Dhcp6Callback; + Context = Instance->Config->CallbackContext; + + // + // Callback to user with the new message if has. + // + if (Callback != NULL) { + + Status = Callback ( + &Instance->Dhcp6, + Context, + Instance->IaCb.Ia->State, + Event, + *Packet, + &NewPacket + ); + // + // Updated the new packet from user to replace the original one. + // + if (NewPacket != NULL) { + ASSERT (*Packet != NULL); + FreePool (*Packet); + *Packet = NewPacket; + } + } + + return Status; +} + + +/** + Update Ia according to the new reply message. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to reply messages. + + @retval EFI_SUCCESS Updated the Ia information successfully. + @retval EFI_DEVICE_ERROR An unexpected error. + +**/ +EFI_STATUS +Dhcp6UpdateIaInfo ( + IN OUT DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 *Option; + UINT8 *IaInnerOpt; + UINT16 IaInnerLen; + UINT16 StsCode; + UINT32 T1; + UINT32 T2; + + ASSERT (Instance->Config != NULL); + // + // If the reply was received in reponse to a solicit with rapid commit option, + // request, renew or rebind message, the client updates the information it has + // recorded about IAs from the IA options contained in the reply message: + // 1. record the T1 and T2 times + // 2. add any new addresses in the IA + // 3. discard any addresses from the IA, that have a valid lifetime of 0 + // 4. update lifetimes for any addresses that alread recorded + // 5. leave unchanged any information about addresses + // + // See details in the section-18.1.8 of rfc-3315. + // + Option = Dhcp6SeekIaOption ( + Packet->Dhcp6.Option, + Packet->Length - sizeof (EFI_DHCP6_HEADER), + &Instance->Config->IaDescriptor + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // The format of the IA_NA option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_NA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T1 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T2 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_NA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // The format of the IA_TA option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_TA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_TA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len + IaId) = 8 + // sizeof (option-code + option-len + IaId + T1) = 12 + // sizeof (option-code + option-len + IaId + T1 + T2) = 16 + // + // The inner options still start with 2 bytes option-code and 2 bytes option-len. + // + if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) { + T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8))); + T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12))); + // + // Refer to RFC3155 Chapter 22.4. If a client receives an IA_NA with T1 greater than T2, + // and both T1 and T2 are greater than 0, the client discards the IA_NA option and processes + // the remainder of the message as though the server had not included the invalid IA_NA option. + // + if (T1 > T2 && T2 > 0) { + return EFI_DEVICE_ERROR; + } + IaInnerOpt = Option + 16; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12); + } else { + T1 = 0; + T2 = 0; + IaInnerOpt = Option + 8; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 4); + } + + // + // The format of the Status Code option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_STATUS_CODE | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | status-code | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + // . . + // . status-message . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len) = 4 + // + StsCode = Dhcp6StsSuccess; + Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode); + + if (Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + // + // Generate control block for the Ia. + // + Status = Dhcp6GenerateIaCb ( + Instance, + IaInnerOpt, + IaInnerLen, + T1, + T2 + ); + + return Status; +} + + + +/** + Seek StatusCode Option in package. A Status Code option may appear in the + options field of a DHCP message and/or in the options field of another option. + See details in section 22.13, RFC3315. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to reply messages. + @param[out] Option The pointer to status code option. + + @retval EFI_SUCCESS Seek status code option successfully. + @retval EFI_DEVICE_ERROR An unexpected error. + +**/ +EFI_STATUS +Dhcp6SeekStsOption ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet, + OUT UINT8 **Option + ) +{ + UINT8 *IaInnerOpt; + UINT16 IaInnerLen; + UINT16 StsCode; + + // + // Seek StatusCode option directly in DHCP message body. That is, search in + // non-encapsulated option fields. + // + *Option = Dhcp6SeekOption ( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptStatusCode + ); + + if (*Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + // + // Seek in encapsulated options, IA_NA and IA_TA. + // + *Option = Dhcp6SeekIaOption ( + Packet->Dhcp6.Option, + Packet->Length - sizeof (EFI_DHCP6_HEADER), + &Instance->Config->IaDescriptor + ); + if (*Option == NULL) { + return EFI_SUCCESS; + } + + // + // The format of the IA_NA option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_NA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T1 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T2 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_NA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // The format of the IA_TA option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_TA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_TA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len + IaId) = 8 + // sizeof (option-code + option-len + IaId + T1) = 12 + // sizeof (option-code + option-len + IaId + T1 + T2) = 16 + // + // The inner options still start with 2 bytes option-code and 2 bytes option-len. + // + if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) { + IaInnerOpt = *Option + 16; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 12); + } else { + IaInnerOpt = *Option + 8; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 4); + } + + // + // The format of the Status Code option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_STATUS_CODE | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | status-code | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + // . . + // . status-message . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len) = 4 + // + *Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode); + if (*Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; +} + + +/** + Transmit Dhcp6 message by udpio. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to transmit message. + @param[in] Elapsed The pointer to the elapsed time value to fill in. + + @retval EFI_SUCCESS Successfully transmitted the packet. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Dhcp6TransmitPacket ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet, + IN UINT16 *Elapsed + ) +{ + EFI_STATUS Status; + NET_BUF *Wrap; + NET_FRAGMENT Frag; + UDP_END_POINT EndPt; + DHCP6_SERVICE *Service; + + Service = Instance->Service; + + // + // Wrap it into a netbuf then send it. + // + Frag.Bulk = (UINT8 *) &Packet->Dhcp6.Header; + Frag.Len = Packet->Length; + + // + // Do not register free packet here, which will be handled in retry list. + // + Wrap = NetbufFromExt (&Frag, 1, 0, 0, Dhcp6DummyExtFree, NULL); + + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Multicast the Dhcp6 message, unless get the unicast server address by option. + // + ZeroMem (&EndPt, sizeof (UDP_END_POINT)); + + if (Instance->Unicast != NULL) { + CopyMem ( + &EndPt.RemoteAddr, + Instance->Unicast, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + CopyMem ( + &EndPt.RemoteAddr, + &mAllDhcpRelayAndServersAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } + + EndPt.RemotePort = DHCP6_PORT_SERVER; + EndPt.LocalPort = DHCP6_PORT_CLIENT; + + // + // Update the elapsed time value. + // + if (Elapsed != NULL) { + SetElapsedTime (Elapsed, Instance); + } + + // + // Send out the message by the configured Udp6Io. + // + Status = UdpIoSendDatagram ( + Service->UdpIo, + Wrap, + &EndPt, + NULL, + Dhcp6OnTransmitted, + NULL + ); + + if (EFI_ERROR (Status)) { + NetbufFree (Wrap); + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Create the solicit message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the solicit message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the solicit message. + +**/ +EFI_STATUS +Dhcp6SendSolicitMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + DHCP6_SERVICE *Service; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + Service = Instance->Service; + ClientId = Service->ClientId; + UserLen = 0; + + ASSERT (Service->ClientId != NULL); + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + + // + // Calculate the added length of customized option list. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgSolicit; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for solicit message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2, + Packet->Dhcp6.Header.MessageType + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendSolicit, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send solicit packet with the state transition from Dhcp6init to + // Dhcp6selecting. + // + Instance->IaCb.Ia->State = Dhcp6Selecting; + // + // Clear initial time for current transaction. + // + Instance->StartTime = 0; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry ( + Instance, + Packet, + Elapsed, + Instance->Config->SolicitRetransmission + ); +} + +/** + Configure some parameter to initiate SolicitMsg. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the solicit message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the solicit message. + +**/ +EFI_STATUS +Dhcp6InitSolicitMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + Instance->IaCb.T1 = 0; + Instance->IaCb.T2 = 0; + Instance->IaCb.Ia->IaAddressCount = 0; + + return Dhcp6SendSolicitMsg (Instance); +} + + +/** + Create the request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the request message. + +**/ +EFI_STATUS +Dhcp6SendRequestMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + ASSERT(Instance->AdSelect != NULL); + ASSERT(Instance->Config != NULL); + ASSERT(Instance->IaCb.Ia != NULL); + ASSERT(Instance->Service != NULL); + + Service = Instance->Service; + ClientId = Service->ClientId; + + ASSERT(ClientId != NULL); + + // + // Get the server Id from the selected advertisement message. + // + Option = Dhcp6SeekOption ( + Instance->AdSelect->Dhcp6.Option, + Instance->AdSelect->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + // + // Calculate the added length of customized option list. + // + UserLen = 0; + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgRequest; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for request message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2, + Packet->Dhcp6.Header.MessageType + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendRequest, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send request packet with the state transition from Dhcp6selecting to + // Dhcp6requesting. + // + Instance->IaCb.Ia->State = Dhcp6Requesting; + // + // Clear initial time for current transaction. + // + Instance->StartTime = 0; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the decline message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] DecIa The pointer to the decline Ia. + + @retval EFI_SUCCESS Created and sent the decline message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the decline message. + +**/ +EFI_STATUS +Dhcp6SendDeclineMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *DecIa + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET *LastReply; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT16 Length; + + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + ASSERT (Instance->Service != NULL); + + Service = Instance->Service; + ClientId = Service->ClientId; + LastReply = Instance->IaCb.Ia->ReplyPacket; + + ASSERT (ClientId != NULL); + ASSERT (LastReply != NULL); + + // + // Get the server Id from the last reply message. + // + Option = Dhcp6SeekOption ( + LastReply->Dhcp6.Option, + LastReply->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // EFI_DHCP6_DUID contains a length field of 2 bytes. + // + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgDecline; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for rebind/renew message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + + Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0, Packet->Dhcp6.Header.MessageType); + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendDecline, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send decline packet with the state transition from Dhcp6bound to + // Dhcp6declining. + // + Instance->IaCb.Ia->State = Dhcp6Declining; + // + // Clear initial time for current transaction. + // + Instance->StartTime = 0; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the release message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RelIa The pointer to the release Ia. + + @retval EFI_SUCCESS Created and sent the release message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the release message. + +**/ +EFI_STATUS +Dhcp6SendReleaseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *RelIa + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET *LastReply; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT16 Length; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + Service = Instance->Service; + ClientId = Service->ClientId; + LastReply = Instance->IaCb.Ia->ReplyPacket; + + ASSERT(ClientId); + ASSERT(LastReply); + + // + // Get the server Id from the last reply message. + // + Option = Dhcp6SeekOption ( + LastReply->Dhcp6.Option, + LastReply->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgRelease; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for rebind/renew message + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + // + // ServerId is extracted from packet, it's network order. + // + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0, Packet->Dhcp6.Header.MessageType); + + // + // Determine the size/length of packet + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendRelease, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send release packet with the state transition from Dhcp6bound to + // Dhcp6releasing. + // + Instance->IaCb.Ia->State = Dhcp6Releasing; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the renew/rebind message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RebindRequest If TRUE, it is a Rebind type message. + Otherwise, it is a Renew type message. + + @retval EFI_SUCCESS Created and sent the renew/rebind message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the renew/rebind message. + +**/ +EFI_STATUS +Dhcp6SendRenewRebindMsg ( + IN DHCP6_INSTANCE *Instance, + IN BOOLEAN RebindRequest + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET *LastReply; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + EFI_DHCP6_STATE State; + EFI_DHCP6_EVENT Event; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + Service = Instance->Service; + ClientId = Service->ClientId; + + ASSERT(ClientId); + + // + // Calculate the added length of customized option list. + // + UserLen = 0; + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = RebindRequest ? Dhcp6MsgRebind : Dhcp6MsgRenew; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for rebind/renew message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2, + Packet->Dhcp6.Header.MessageType + ); + + if (!RebindRequest) { + // + // Get the server Id from the last reply message and + // insert it for rebind request. + // + LastReply = Instance->IaCb.Ia->ReplyPacket; + ASSERT (LastReply); + + Option = Dhcp6SeekOption ( + LastReply->Dhcp6.Option, + LastReply->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + FreePool (Packet); + return EFI_DEVICE_ERROR; + } + + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + } + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + State = (RebindRequest) ? Dhcp6Rebinding : Dhcp6Renewing; + Event = (RebindRequest) ? Dhcp6EnterRebinding : Dhcp6EnterRenewing; + + Status = Dhcp6CallbackUser (Instance, Event, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send renew/rebind packet with the state transition from Dhcp6bound to + // Dhcp6renew/rebind. + // And sync the lease time when send renew/rebind, in case that user send + // renew/rebind actively. + // + Instance->IaCb.Ia->State = State; + Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1; + // + // Clear initial time for current transaction. + // + Instance->StartTime = 0; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + +/** + Start the information request process. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] SendClientId If TRUE, the client identifier option will be included in + information request message. Otherwise, the client identifier + option will not be included. + @param[in] OptionRequest The pointer to the option request option. + @param[in] OptionCount The number options in the OptionList. + @param[in] OptionList The array pointers to the appended options. + @param[in] Retransmission The pointer to the retransmission control. + @param[in] TimeoutEvent The event of timeout. + @param[in] ReplyCallback The callback function when the reply was received. + @param[in] CallbackContext The pointer to the parameter passed to the callback. + + @retval EFI_SUCCESS Start the info-request process successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_NO_MAPPING No source address is available for use. + @retval Others Failed to start the info-request process. + +**/ +EFI_STATUS +Dhcp6StartInfoRequest ( + IN DHCP6_INSTANCE *Instance, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL, + IN EFI_DHCP6_RETRANSMISSION *Retransmission, + IN EFI_EVENT TimeoutEvent OPTIONAL, + IN EFI_DHCP6_INFO_CALLBACK ReplyCallback, + IN VOID *CallbackContext OPTIONAL + ) +{ + EFI_STATUS Status; + DHCP6_INF_CB *InfCb; + DHCP6_SERVICE *Service; + EFI_TPL OldTpl; + + Service = Instance->Service; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance->UdpSts = EFI_ALREADY_STARTED; + // + // Create and initialize the control block for the info-request. + // + InfCb = AllocateZeroPool (sizeof(DHCP6_INF_CB)); + + if (InfCb == NULL) { + gBS->RestoreTPL (OldTpl); + return EFI_OUT_OF_RESOURCES; + } + + InfCb->ReplyCallback = ReplyCallback; + InfCb->CallbackContext = CallbackContext; + InfCb->TimeoutEvent = TimeoutEvent; + + InsertTailList (&Instance->InfList, &InfCb->Link); + + // + // Send the info-request message to start exchange process. + // + Status = Dhcp6SendInfoRequestMsg ( + Instance, + InfCb, + SendClientId, + OptionRequest, + OptionCount, + OptionList, + Retransmission + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Register receive callback for the stateless exchange process. + // + Status = UdpIoRecvDatagram( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +ON_ERROR: + gBS->RestoreTPL (OldTpl); + RemoveEntryList (&InfCb->Link); + FreePool (InfCb); + + return Status; +} + +/** + Create the information request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] InfCb The pointer to the information request control block. + @param[in] SendClientId If TRUE, the client identifier option will be included in + information request message. Otherwise, the client identifier + option will not be included. + @param[in] OptionRequest The pointer to the option request option. + @param[in] OptionCount The number options in the OptionList. + @param[in] OptionList The array pointers to the appended options. + @param[in] Retransmission The pointer to the retransmission control. + + @retval EFI_SUCCESS Created and sent the info-request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the info-request message. + +**/ +EFI_STATUS +Dhcp6SendInfoRequestMsg ( + IN DHCP6_INSTANCE *Instance, + IN DHCP6_INF_CB *InfCb, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[], + IN EFI_DHCP6_RETRANSMISSION *Retransmission + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + DHCP6_SERVICE *Service; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + ASSERT(OptionRequest); + + Service = Instance->Service; + ClientId = Service->ClientId; + UserLen = NTOHS (OptionRequest->OpLen) + 4; + + ASSERT(ClientId); + + // + // Calculate the added length of customized option list. + // + for (Index = 0; Index < OptionCount; Index++) { + UserLen += (NTOHS (OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgInfoRequest; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + InfCb->Xid = Packet->Dhcp6.Header.TransactionId; + + // + // Assembly Dhcp6 options for info-request message. + // + Cursor = Packet->Dhcp6.Option; + + if (SendClientId) { + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + } + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendOption ( + Cursor, + OptionRequest->OpCode, + OptionRequest->OpLen, + OptionRequest->Data + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < OptionCount; Index++) { + + UserOpt = OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Clear initial time for current transaction. + // + Instance->StartTime = 0; + + // + // Send info-request packet with no state. + // + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, Retransmission); +} + + +/** + Create the Confirm message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the confirm message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the confirm message. + +**/ +EFI_STATUS +Dhcp6SendConfirmMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINT8 *Cursor; + UINTN Index; + UINT16 Length; + UINT32 UserLen; + EFI_STATUS Status; + DHCP6_SERVICE *Service; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + UINT16 *Elapsed; + + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + ASSERT (Instance->Service != NULL); + + Service = Instance->Service; + ClientId = Service->ClientId; + ASSERT (ClientId != NULL); + + // + // Calculate the added length of customized option list. + // + UserLen = 0; + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize common fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgConfirm; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for solicit message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2, + Packet->Dhcp6.Header.MessageType + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption ( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendConfirm, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send confirm packet with the state transition from Dhcp6Bound to + // Dhcp6Confirming. + // + Instance->IaCb.Ia->State = Dhcp6Confirming; + // + // Clear initial time for current transaction. + // + Instance->StartTime = 0; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + + +/** + Handle with the Dhcp6 reply message. + + @param[in] Instance The pointer to Dhcp6 instance. + @param[in] Packet The pointer to the Dhcp6 reply message. + + @retval EFI_SUCCESS Processed the reply message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to process the reply message. + +**/ +EFI_STATUS +Dhcp6HandleReplyMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 *Option; + UINT16 StsCode; + + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + ASSERT (Packet != NULL); + + Status = EFI_SUCCESS; + + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) { + return EFI_DEVICE_ERROR; + } + + // + // If the client subsequently receives a valid reply message that includes a + // rapid commit option since send a solicit with rapid commit option before, + // preocess the reply message and discard any reply messages received in + // response to the request message. + // See details in the section-17.1.4 of rfc-3315. + // + Option = Dhcp6SeekOption ( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptRapidCommit + ); + + if ((Option != NULL && !Instance->Config->RapidCommit) || (Option == NULL && Instance->Config->RapidCommit)) { + return EFI_DEVICE_ERROR; + } + + // + // As to a valid reply packet in response to a request/renew/rebind packet, + // ignore the packet if not contains the Ia option + // + if (Instance->IaCb.Ia->State == Dhcp6Requesting || + Instance->IaCb.Ia->State == Dhcp6Renewing || + Instance->IaCb.Ia->State == Dhcp6Rebinding + ) { + + Option = Dhcp6SeekIaOption ( + Packet->Dhcp6.Option, + Packet->Length, + &Instance->Config->IaDescriptor + ); + if (Option == NULL) { + return EFI_SUCCESS; + } + } + + // + // Callback to user with the received packet and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdReply, &Packet); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // When receive a valid reply packet in response to a decline/release packet, + // the client considers the decline/release event completed regardless of the + // status code. + // + if (Instance->IaCb.Ia->State == Dhcp6Declining || Instance->IaCb.Ia->State == Dhcp6Releasing) { + + if (Instance->IaCb.Ia->IaAddressCount != 0) { + Instance->IaCb.Ia->State = Dhcp6Bound; + } else { + ASSERT (Instance->IaCb.Ia->ReplyPacket); + FreePool (Instance->IaCb.Ia->ReplyPacket); + Instance->IaCb.Ia->ReplyPacket = NULL; + Instance->IaCb.Ia->State = Dhcp6Init; + } + + // + // For sync, set the success flag out of polling in decline/release. + // + Instance->UdpSts = EFI_SUCCESS; + + // + // For async, signal the Ia event to inform Ia infomation update. + // + if (Instance->Config->IaInfoEvent != NULL) { + gBS->SignalEvent (Instance->Config->IaInfoEvent); + } + + // + // Reset start time for next exchange. + // + Instance->StartTime = 0; + + Status = EFI_SUCCESS; + goto ON_EXIT; + } + + // + // Upon the receipt of a valid reply packet in response to a solicit, request, + // confirm, renew and rebind, the behavior depends on the status code option. + // See the details in the section-18.1.8 of rfc-3315. + // + Option = NULL; + Status = Dhcp6SeekStsOption ( + Instance, + Packet, + &Option + ); + + if (!EFI_ERROR (Status)) { + // + // No status code or no error status code means succeed to reply. + // + Status = Dhcp6UpdateIaInfo (Instance, Packet); + if (!EFI_ERROR (Status)) { + // + // Reset start time for next exchange. + // + Instance->StartTime = 0; + + // + // Set bound state and store the reply packet. + // + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + + Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size); + + if (Instance->IaCb.Ia->ReplyPacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size); + + Instance->IaCb.Ia->State = Dhcp6Bound; + + // + // For sync, set the success flag out of polling in start/renewrebind. + // + Instance->UdpSts = EFI_SUCCESS; + + // + // Maybe this is a new round DHCP process due to some reason, such as NotOnLink + // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that + // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP + // consumers can be notified to flush old address. + // + Dhcp6AppendCacheIa (Instance); + + // + // For async, signal the Ia event to inform Ia infomation update. + // + if (Instance->Config->IaInfoEvent != NULL) { + gBS->SignalEvent (Instance->Config->IaInfoEvent); + } + } else if (Status == EFI_NOT_FOUND) { + // + // Refer to RFC3315 Chapter 18.1.8, for each IA in the original Renew or Rebind message, + // the client sends a Renew or Rebind if the IA is not in the Reply message. + // Return EFI_SUCCESS so we can continue to restart the Renew/Rebind process. + // + return EFI_SUCCESS; + } + + goto ON_EXIT; + + } else if (Option != NULL) { + // + // Any error status code option is found. + // + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4))); + switch (StsCode) { + case Dhcp6StsUnspecFail: + // + // It indicates the server is unable to process the message due to an + // unspecified failure condition, so just retry if possible. + // + break; + + case Dhcp6StsUseMulticast: + // + // It indicates the server receives a message via unicast from a client + // to which the server has not sent a unicast option, so retry it by + // multi-cast address. + // + if (Instance->Unicast != NULL) { + FreePool (Instance->Unicast); + Instance->Unicast = NULL; + } + break; + + case Dhcp6StsNotOnLink: + if (Instance->IaCb.Ia->State == Dhcp6Confirming) { + // + // Before initiate new round DHCP, cache the current IA. + // + Status = Dhcp6CacheIa (Instance); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Restart S.A.R.R process to acquire new address. + // + Status = Dhcp6InitSolicitMsg (Instance); + if (EFI_ERROR (Status)) { + return Status; + } + } + break; + + case Dhcp6StsNoBinding: + if (Instance->IaCb.Ia->State == Dhcp6Renewing || Instance->IaCb.Ia->State == Dhcp6Rebinding) { + // + // Refer to RFC3315 Chapter 18.1.8, for each IA in the original Renew or Rebind message, the client + // sends a Request message if the IA contained a Status Code option with the NoBinding status. + // + Status = Dhcp6SendRequestMsg(Instance); + if (EFI_ERROR (Status)) { + return Status; + } + } + break; + + default: + // + // The other status code, just restart solicitation. + // + break; + } + } + + return EFI_SUCCESS; + +ON_EXIT: + + if (!EFI_ERROR(Status)) { + Status = Dhcp6DequeueRetry ( + Instance, + Packet->Dhcp6.Header.TransactionId, + FALSE + ); + } + + return Status; +} + + +/** + Select the appointed Dhcp6 advertisement message. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] AdSelect The pointer to the selected Dhcp6 advertisement message. + + @retval EFI_SUCCESS Selected the right advertisement message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to select the advertise message. + +**/ +EFI_STATUS +Dhcp6SelectAdvertiseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *AdSelect + ) +{ + EFI_STATUS Status; + UINT8 *Option; + + ASSERT (AdSelect != NULL); + + // + // Callback to user with the selected advertisement packet, and the user + // might overwrite it. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SelectAdvertise, &AdSelect); + + if (EFI_ERROR (Status)) { + return Status; + } + + Instance->AdSelect = AdSelect; + + // + // Dequeue the sent packet for the retransmission since advertisement selected. + // + Status = Dhcp6DequeueRetry ( + Instance, + AdSelect->Dhcp6.Header.TransactionId, + FALSE + ); + + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Check whether there is server unicast option in the selected advertise + // packet, and update it. + // + Option = Dhcp6SeekOption( + AdSelect->Dhcp6.Option, + AdSelect->Length - 4, + Dhcp6OptServerUnicast + ); + + if (Option != NULL) { + + Instance->Unicast = AllocateZeroPool (sizeof(EFI_IPv6_ADDRESS)); + + if (Instance->Unicast == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->Unicast, Option + 4, sizeof(EFI_IPv6_ADDRESS)); + } + + // + // Update the information of the Ia by the selected advertisement message. + // + Status = Dhcp6UpdateIaInfo (Instance, AdSelect); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Send the request message to continue the S.A.R.R. process. + // + return Dhcp6SendRequestMsg (Instance); +} + + +/** + Handle with the Dhcp6 advertisement message. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the Dhcp6 advertisement message. + + @retval EFI_SUCCESS Processed the advertisement message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to process the advertise message. + +**/ +EFI_STATUS +Dhcp6HandleAdvertiseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 *Option; + BOOLEAN Timeout; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + Timeout = FALSE; + + // + // If the client does receives a valid reply message that includes a rapid + // commit option since a solicit with rapid commit optioin sent before, select + // this reply message. Or else, process the advertise messages as normal. + // See details in the section-17.1.4 of rfc-3315. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptRapidCommit + ); + + if (Option != NULL && Instance->Config->RapidCommit && Packet->Dhcp6.Header.MessageType == Dhcp6MsgReply) { + + return Dhcp6HandleReplyMsg (Instance, Packet); + } + + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise) { + return EFI_DEVICE_ERROR; + } + + // + // Client must ignore any advertise message that includes a status code option + // containing the value noaddrsavail, with the exception that the client may + // display the associated status message to the user. + // See the details in the section-17.1.3 of rfc-3315. + // + Status = Dhcp6SeekStsOption ( + Instance, + Packet, + &Option + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // Callback to user with the received packet and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdAdvertise, &Packet); + + if (!EFI_ERROR (Status)) { + // + // Success means user choose the current advertisement packet. + // + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + // + // Store the selected advertisement packet and set a flag. + // + Instance->AdSelect = AllocateZeroPool (Packet->Size); + + if (Instance->AdSelect == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->AdSelect, Packet, Packet->Size); + + Instance->AdPref = 0xff; + + } else if (Status == EFI_NOT_READY) { + // + // Not_ready means user wants to continue to receive more advertise packets. + // + if (Instance->AdPref == 0xff && Instance->AdSelect == NULL) { + // + // It's a tricky point. The timer routine set adpref as 0xff if the first + // rt timeout and no advertisement received, which means any advertisement + // received will be selected after the first rt. + // + Timeout = TRUE; + } + + // + // Check whether the current packet has a 255 preference option or not. + // Take non-preference option as 0 value. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptPreference + ); + + if (Instance->AdSelect == NULL || (Option != NULL && *(Option + 4) > Instance->AdPref)) { + // + // No advertisements received before or preference is more than other + // advertisements received before. Then store the new packet and the + // preference value. + // + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + Instance->AdSelect = AllocateZeroPool (Packet->Size); + + if (Instance->AdSelect == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->AdSelect, Packet, Packet->Size); + + if (Option != NULL) { + Instance->AdPref = *(Option + 4); + } + } else { + // + // Non-preference and other advertisements received before or current + // preference is less than other advertisements received before. + // Leave the packet alone. + } + + } else { + // + // Other error status means termination. + // + return Status; + } + + // + // Client must collect advertise messages as more as possible until the first + // RT has elapsed, or get a highest preference 255 advertise. + // See details in the section-17.1.2 of rfc-3315. + // + if (Instance->AdPref == 0xff || Timeout) { + Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect); + } + + return Status; +} + + +/** + The Dhcp6 stateful exchange process routine. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the received Dhcp6 message. + +**/ +VOID +Dhcp6HandleStateful ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + EFI_DHCP6_DUID *ClientId; + DHCP6_SERVICE *Service; + UINT8 *Option; + + Service = Instance->Service; + ClientId = Service->ClientId; + Status = EFI_SUCCESS; + + if (Instance->Config == NULL) { + goto ON_CONTINUE; + } + + ASSERT (ClientId); + ASSERT (Instance->Config); + ASSERT (Instance->IaCb.Ia); + + // + // Discard the packet if not advertisement or reply packet. + // + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise && Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) { + goto ON_CONTINUE; + } + + // + // Check whether include client Id or not. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptClientId + ); + + if (Option == NULL || CompareMem (Option + 4, ClientId->Duid, ClientId->Length) != 0) { + goto ON_CONTINUE; + } + + // + // Check whether include server Id or not. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptServerId + ); + + if (Option == NULL) { + goto ON_CONTINUE; + } + + switch (Instance->IaCb.Ia->State) { + case Dhcp6Selecting: + // + // Handle the advertisement message when in the Dhcp6Selecting state. + // Do not need check return status, if failed, just continue to the next. + // + Dhcp6HandleAdvertiseMsg (Instance, Packet); + break; + + case Dhcp6Requesting: + case Dhcp6Confirming: + case Dhcp6Renewing: + case Dhcp6Rebinding: + case Dhcp6Releasing: + case Dhcp6Declining: + // + // Handle the reply message when in the Dhcp6Requesting, Dhcp6Renewing + // Dhcp6Rebinding, Dhcp6Releasing and Dhcp6Declining state. + // If failed here, it should reset the current session. + // + Status = Dhcp6HandleReplyMsg (Instance, Packet); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + break; + default: + // + // Other state has not supported yet. + // + break; + } + +ON_CONTINUE: + // + // Continue to receive the following Dhcp6 message. + // + Status = UdpIoRecvDatagram ( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp6CleanupSession (Instance, Status); + } +} + + +/** + The Dhcp6 stateless exchange process routine. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the received Dhcp6 message. + +**/ +VOID +Dhcp6HandleStateless ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + DHCP6_SERVICE *Service; + DHCP6_INF_CB *InfCb; + UINT8 *Option; + BOOLEAN IsMatched; + + Service = Instance->Service; + Status = EFI_SUCCESS; + IsMatched = FALSE; + InfCb = NULL; + + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) { + goto ON_EXIT; + } + + // + // Check whether it's a desired Info-request message by Xid. + // + while (!IsListEmpty (&Instance->InfList)) { + InfCb = NET_LIST_HEAD (&Instance->InfList, DHCP6_INF_CB, Link); + if (InfCb->Xid == Packet->Dhcp6.Header.TransactionId) { + IsMatched = TRUE; + break; + } + } + + if (!IsMatched) { + goto ON_EXIT; + } + + // + // Check whether include server Id or not. + // + Option = Dhcp6SeekOption ( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptServerId + ); + + if (Option == NULL) { + goto ON_EXIT; + } + + // + // Callback to user with the received packet and check the user's feedback. + // + Status = InfCb->ReplyCallback ( + &Instance->Dhcp6, + InfCb->CallbackContext, + Packet + ); + + if (Status == EFI_NOT_READY) { + // + // Success or aborted will both stop this info-request exchange process, + // but not ready means user wants to continue to receive reply. + // + goto ON_EXIT; + } + + // + // Dequeue the sent packet from the txlist if the xid matched, and ignore + // if no xid matched. + // + Dhcp6DequeueRetry ( + Instance, + Packet->Dhcp6.Header.TransactionId, + FALSE + ); + + // + // For sync, set the status out of polling for info-request. + // + Instance->UdpSts = Status; + +ON_EXIT: + + Status = UdpIoRecvDatagram ( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status)) { + Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATELESS); + } +} + + +/** + The receive callback function for Dhcp6 exchange process. + + @param[in] Udp6Wrap The pointer to the received net buffer. + @param[in] EndPoint The pointer to the udp end point. + @param[in] IoStatus The return status from udp io. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6ReceivePacket ( + IN NET_BUF *Udp6Wrap, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + EFI_DHCP6_HEADER *Head; + EFI_DHCP6_PACKET *Packet; + DHCP6_SERVICE *Service; + DHCP6_INSTANCE *Instance; + DHCP6_TX_CB *TxCb; + UINT32 Size; + BOOLEAN IsDispatched; + BOOLEAN IsStateless; + LIST_ENTRY *Entry1; + LIST_ENTRY *Next1; + LIST_ENTRY *Entry2; + LIST_ENTRY *Next2; + EFI_STATUS Status; + + ASSERT (Udp6Wrap != NULL); + ASSERT (Context != NULL); + + Service = (DHCP6_SERVICE *) Context; + Instance = NULL; + Packet = NULL; + IsDispatched = FALSE; + IsStateless = FALSE; + + if (EFI_ERROR (IoStatus)) { + return ; + } + + // + // Copy the net buffer received from upd6 to a Dhcp6 packet. + // + Size = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize; + Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size); + + if (Packet == NULL) { + goto ON_CONTINUE; + } + + Packet->Size = Size; + Head = &Packet->Dhcp6.Header; + Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head); + + if (Packet->Length == 0) { + goto ON_CONTINUE; + } + + // + // Dispatch packet to right instance by transaction id. + // + NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) { + + Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link); + + NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link); + + if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) { + // + // Find the corresponding packet in tx list, and check it whether belongs + // to stateful exchange process. + // + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + IsStateless = TRUE; + } + IsDispatched = TRUE; + break; + } + } + + if (IsDispatched) { + break; + } + } + + // + // Skip this packet if not dispatched to any instance. + // + if (!IsDispatched) { + goto ON_CONTINUE; + } + + // + // Dispatch the received packet ot the right instance. + // + if (IsStateless) { + Dhcp6HandleStateless (Instance, Packet); + } else { + Dhcp6HandleStateful (Instance, Packet); + } + +ON_CONTINUE: + + if (!IsDispatched) { + Status = UdpIoRecvDatagram ( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + if (EFI_ERROR (Status)) { + NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) { + Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link); + Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL); + } + } + } + + NetbufFree (Udp6Wrap); + + if (Packet != NULL) { + FreePool (Packet); + } +} + +/** + Detect Link movement for specified network device. + + This routine will try to invoke Snp->GetStatus() to get the media status. + If media present status switches from unpresent to present, a link movement + is detected. Note that the underlying UNDI driver may not support reporting + media status from GET_STATUS command. If that, fail to detect link movement. + + @param[in] Instance The pointer to DHCP6_INSTANCE. + + @retval TRUE A link movement is detected. + @retval FALSE A link movement is not detected. + +**/ +BOOLEAN +Dhcp6LinkMovDetect ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINT32 InterruptStatus; + BOOLEAN MediaPresent; + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + + ASSERT (Instance != NULL); + Snp = Instance->Service->Snp; + MediaPresent = Instance->MediaPresent; + + // + // Check whether SNP support media detection + // + if (!Snp->Mode->MediaPresentSupported) { + return FALSE; + } + + // + // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data + // + Status = Snp->GetStatus (Snp, &InterruptStatus, NULL); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Instance->MediaPresent = Snp->Mode->MediaPresent; + // + // Media transimit Unpresent to Present means new link movement is detected. + // + if (!MediaPresent && Instance->MediaPresent) { + return TRUE; + } + return FALSE; +} + + +/** + The timer routine of the Dhcp6 instance for each second. + + @param[in] Event The timer event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + DHCP6_INSTANCE *Instance; + DHCP6_TX_CB *TxCb; + DHCP6_IA_CB *IaCb; + UINT32 LossTime; + EFI_STATUS Status; + + ASSERT (Context != NULL); + + Instance = (DHCP6_INSTANCE *) Context; + + // + // 1. Loop the tx list, count live time of every tx packet to check whether + // need re-transmit or not. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + + TxCb->TickTime++; + + if (TxCb->TickTime > TxCb->RetryExp) { + // + // Handle the first rt in the transmission of solicit specially. + // + if ((TxCb->RetryCnt == 0 || TxCb->SolicitRetry) && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) { + if (Instance->AdSelect == NULL) { + // + // Set adpref as 0xff here to indicate select any advertisement + // afterwards. + // + Instance->AdPref = 0xff; + } else { + // + // Select the advertisement received before. + // + Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect); + if (Status == EFI_ABORTED) { + goto ON_CLOSE; + } else if (EFI_ERROR (Status)) { + TxCb->RetryCnt++; + } + return; + } + } + // + // Increase the retry count for the packet and add up the total loss time. + // + TxCb->RetryCnt++; + TxCb->RetryLos += TxCb->RetryExp; + + // + // Check whether overflow the max retry count limit for this packet + // + if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) { + Status = EFI_NO_RESPONSE; + goto ON_CLOSE; + } + + // + // Check whether overflow the max retry duration for this packet + // + if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) { + Status = EFI_NO_RESPONSE; + goto ON_CLOSE; + } + + // + // Re-calculate retry expire timeout for the next time. + // + // Firstly, Check the new calculated time whether overflow the max retry + // expire time. + // + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryExp, + FALSE, + TRUE + ); + + if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) { + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Mrt, + TRUE, + TRUE + ); + } + + // + // Secondly, Check the new calculated time whether overflow the max retry + // duration time. + // + LossTime = TxCb->RetryLos + TxCb->RetryExp; + if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) { + TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos; + } + + // + // Reset the tick time for the next retransmission + // + TxCb->TickTime = 0; + + // + // Retransmit the last sent packet again. + // + Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed); + TxCb->SolicitRetry = FALSE; + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) { + TxCb->SolicitRetry = TRUE; + } + } + } + + // + // 2. Check the configured Ia, count lease time of every valid Ia to check + // whether need to renew or rebind this Ia. + // + IaCb = &Instance->IaCb; + + if (Instance->Config == NULL || IaCb->Ia == NULL) { + return; + } + + if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) { + + IaCb->LeaseTime++; + + if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) { + // + // Exceed t2, send rebind packet to extend the Ia lease. + // + Dhcp6SendRenewRebindMsg (Instance, TRUE); + + } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) { + + // + // Exceed t1, send renew packet to extend the Ia lease. + // + Dhcp6SendRenewRebindMsg (Instance, FALSE); + } + } + + // + // 3. In any situation when a client may have moved to a new link, the + // client MUST initiate a Confirm/Reply message exchange. + // + if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) { + Dhcp6SendConfirmMsg (Instance); + } + + return; + + ON_CLOSE: + + if (Dhcp6IsValidTxCb (Instance, TxCb) && + TxCb->TxPacket != NULL && + (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest || + TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew || + TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm) + ) { + // + // The failure of renew/Confirm will still switch to the bound state. + // + if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) || + (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) { + ASSERT (Instance->IaCb.Ia); + Instance->IaCb.Ia->State = Dhcp6Bound; + } + // + // The failure of info-request will return no response. + // + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + Instance->UdpSts = EFI_NO_RESPONSE; + } + Dhcp6DequeueRetry ( + Instance, + TxCb->Xid, + TRUE + ); + } else { + // + // The failure of the others will terminate current state machine if timeout. + // + Dhcp6CleanupSession (Instance, Status); + } +} diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h new file mode 100644 index 0000000000..b0b12a79fe --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h @@ -0,0 +1,227 @@ +/** @file + Dhcp6 internal functions declaration. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_DHCP6_IO_H__ +#define __EFI_DHCP6_IO_H__ + + +/** + Clean up the specific nodes in the retry list. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Scope The scope of cleanup nodes. + +**/ +VOID +Dhcp6CleanupRetry ( + IN DHCP6_INSTANCE *Instance, + IN UINT32 Scope + ); + +/** + Clean up the session of the instance stateful exchange. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + @param[in] Status The return status from udp. + +**/ +VOID +Dhcp6CleanupSession ( + IN OUT DHCP6_INSTANCE *Instance, + IN EFI_STATUS Status + ); + +/** + Create the solicit message and send it. + + @param[in] Instance The pointer to Dhcp6 instance. + + @retval EFI_SUCCESS Create and send the solicit message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the solicit message. + +**/ +EFI_STATUS +Dhcp6SendSolicitMsg ( + IN DHCP6_INSTANCE *Instance + ); + +/** + Create the request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Create and send the request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the request message. + +**/ +EFI_STATUS +Dhcp6SendRequestMsg ( + IN DHCP6_INSTANCE *Instance + ); + +/** + Create the renew/rebind message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RebindRequest If TRUE, it is a Rebind type message. + Otherwise, it is a Renew type message. + + @retval EFI_SUCCESS Create and send the renew/rebind message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the renew/rebind message. + +**/ +EFI_STATUS +Dhcp6SendRenewRebindMsg ( + IN DHCP6_INSTANCE *Instance, + IN BOOLEAN RebindRequest + ); + +/** + Create the decline message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] DecIa The pointer to the decline Ia. + + @retval EFI_SUCCESS Create and send the decline message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the decline message. + +**/ +EFI_STATUS +Dhcp6SendDeclineMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *DecIa + ); + +/** + Create the release message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RelIa The pointer to the release Ia. + + @retval EFI_SUCCESS Create and send the release message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the release message. + +**/ +EFI_STATUS +Dhcp6SendReleaseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *RelIa + ); + +/** + Start the information request process. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] SendClientId If TRUE, the client identifier option will be included in + information request message. Otherwise, the client identifier + option will not be included. + @param[in] OptionRequest The pointer to the option request option. + @param[in] OptionCount The number options in the OptionList. + @param[in] OptionList The array pointers to the appended options. + @param[in] Retransmission The pointer to the retransmission control. + @param[in] TimeoutEvent The event of timeout. + @param[in] ReplyCallback The callback function when the reply was received. + @param[in] CallbackContext The pointer to the parameter passed to the callback. + + @retval EFI_SUCCESS Start the info-request process successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_NO_MAPPING No source address is available for use. + @retval Others Failed to start the info-request process. + +**/ +EFI_STATUS +Dhcp6StartInfoRequest ( + IN DHCP6_INSTANCE *Instance, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL, + IN EFI_DHCP6_RETRANSMISSION *Retransmission, + IN EFI_EVENT TimeoutEvent OPTIONAL, + IN EFI_DHCP6_INFO_CALLBACK ReplyCallback, + IN VOID *CallbackContext OPTIONAL + ); + +/** + Create the information request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] InfCb The pointer to the information request control block. + @param[in] SendClientId If TRUE, the client identifier option will be included in + information request message. Otherwise, the client identifier + option will not be included. + @param[in] OptionRequest The pointer to the option request option. + @param[in] OptionCount The number options in the OptionList. + @param[in] OptionList The array pointers to the appended options. + @param[in] Retransmission The pointer to the retransmission control. + + @retval EFI_SUCCESS Create and send the info-request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the info-request message. + +**/ +EFI_STATUS +Dhcp6SendInfoRequestMsg ( + IN DHCP6_INSTANCE *Instance, + IN DHCP6_INF_CB *InfCb, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[], + IN EFI_DHCP6_RETRANSMISSION *Retransmission + ); + +/** + The receive callback function for the Dhcp6 exchange process. + + @param[in] Udp6Wrap The pointer to the received net buffer. + @param[in] EndPoint The pointer to the udp end point. + @param[in] IoStatus The return status from udp io. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6ReceivePacket ( + IN NET_BUF *Udp6Wrap, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + +/** + The timer routine of the Dhcp6 instance for each second. + + @param[in] Event The timer event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +#endif diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c new file mode 100644 index 0000000000..2525a32ff1 --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c @@ -0,0 +1,1330 @@ +/** @file + Dhcp6 support functions implementation. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Dhcp6Impl.h" + + +/** + Generate client Duid in the format of Duid-llt. + + @param[in] Mode The pointer to the mode of SNP. + + @retval NULL If it failed to generate a client Id. + @retval others The pointer to the new client id. + +**/ +EFI_DHCP6_DUID * +Dhcp6GenerateClientId ( + IN EFI_SIMPLE_NETWORK_MODE *Mode + ) +{ + EFI_STATUS Status; + EFI_DHCP6_DUID *Duid; + EFI_TIME Time; + UINT32 Stamp; + EFI_GUID Uuid; + + + // + // Attempt to get client Id from variable to keep it constant. + // See details in section-9 of rfc-3315. + // + GetVariable2 (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid, (VOID**)&Duid, NULL); + if (Duid != NULL) { + return Duid; + } + + // + // The format of client identifier option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_CLIENTID | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // . . + // . DUID . + // . (variable length) . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // If System UUID is found from SMBIOS Table, use DUID-UUID type. + // + if ((PcdGet8 (PcdDhcp6UidType) == Dhcp6DuidTypeUuid) && !EFI_ERROR (NetLibGetSystemGuid (&Uuid))) { + // + // + // The format of DUID-UUID: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | DUID-Type (4) | UUID (128 bits) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + // | | + // | | + // | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + + // + // sizeof (option-len + Duid-type + UUID-size) = 20 bytes + // + Duid = AllocateZeroPool (2 + 2 + sizeof (EFI_GUID)); + if (Duid == NULL) { + return NULL; + } + + // + // sizeof (Duid-type + UUID-size) = 18 bytes + // + Duid->Length = (UINT16) (18); + + // + // Set the Duid-type and copy UUID. + // + WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeUuid)); + + CopyMem (Duid->Duid + 2, &Uuid, sizeof(EFI_GUID)); + + } else { + + // + // + // The format of DUID-LLT: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Duid type (1) | hardware type (16 bits) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time (32 bits) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // . . + // . link-layer address (variable length) . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month. + // + gRT->GetTime (&Time, NULL); + Stamp = (UINT32) + ( + (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) * + 60 + + Time.Second + ); + + // + // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes + // + Duid = AllocateZeroPool (10 + Mode->HwAddressSize); + if (Duid == NULL) { + return NULL; + } + + // + // sizeof (Duid-type + hardware-type + time) = 8 bytes + // + Duid->Length = (UINT16) (Mode->HwAddressSize + 8); + + // + // Set the Duid-type, hardware-type, time and copy the hardware address. + // + WriteUnaligned16 ((UINT16 *) ((UINT8 *) Duid + OFFSET_OF (EFI_DHCP6_DUID, Duid)), HTONS (Dhcp6DuidTypeLlt)); + WriteUnaligned16 ((UINT16 *) ((UINT8 *) Duid + OFFSET_OF (EFI_DHCP6_DUID, Duid) + 2), HTONS (NET_IFTYPE_ETHERNET)); + WriteUnaligned32 ((UINT32 *) ((UINT8 *) Duid + OFFSET_OF (EFI_DHCP6_DUID, Duid) + 4), HTONL (Stamp)); + + CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize); + } + + Status = gRT->SetVariable ( + L"ClientId", + &gEfiDhcp6ServiceBindingProtocolGuid, + (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS), + Duid->Length + 2, + (VOID *) Duid + ); + if (EFI_ERROR (Status)) { + FreePool (Duid); + return NULL; + } + + return Duid; +} + + +/** + Copy the Dhcp6 configure data. + + @param[in] DstCfg The pointer to the destination configure data. + @param[in] SorCfg The pointer to the source configure data. + + @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6CopyConfigData ( + IN EFI_DHCP6_CONFIG_DATA *DstCfg, + IN EFI_DHCP6_CONFIG_DATA *SorCfg + ) +{ + UINTN Index; + UINTN OptionListSize; + UINTN OptionSize; + + CopyMem (DstCfg, SorCfg, sizeof (EFI_DHCP6_CONFIG_DATA)); + + // + // Allocate another buffer for solicitretransmission, and copy it. + // + if (SorCfg->SolicitRetransmission != NULL) { + + DstCfg->SolicitRetransmission = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION)); + + if (DstCfg->SolicitRetransmission == NULL) { + // + // Error will be handled out of this function. + // + return EFI_OUT_OF_RESOURCES; + } + + CopyMem ( + DstCfg->SolicitRetransmission, + SorCfg->SolicitRetransmission, + sizeof (EFI_DHCP6_RETRANSMISSION) + ); + } + + if (SorCfg->OptionList != NULL && SorCfg->OptionCount != 0) { + + OptionListSize = SorCfg->OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *); + DstCfg->OptionList = AllocateZeroPool (OptionListSize); + + if (DstCfg->OptionList == NULL) { + // + // Error will be handled out of this function. + // + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < SorCfg->OptionCount; Index++) { + + OptionSize = NTOHS (SorCfg->OptionList[Index]->OpLen) + 4; + DstCfg->OptionList[Index] = AllocateZeroPool (OptionSize); + + if (DstCfg->OptionList[Index] == NULL) { + // + // Error will be handled out of this function. + // + return EFI_OUT_OF_RESOURCES; + } + + CopyMem ( + DstCfg->OptionList[Index], + SorCfg->OptionList[Index], + OptionSize + ); + } + } + + return EFI_SUCCESS; +} + + +/** + Clean up the configure data. + + @param[in, out] CfgData The pointer to the configure data. + +**/ +VOID +Dhcp6CleanupConfigData ( + IN OUT EFI_DHCP6_CONFIG_DATA *CfgData + ) +{ + UINTN Index; + + ASSERT (CfgData != NULL); + // + // Clean up all fields in config data including the reference buffers, but do + // not free the config data buffer itself. + // + if (CfgData->OptionList != NULL) { + for (Index = 0; Index < CfgData->OptionCount; Index++) { + if (CfgData->OptionList[Index] != NULL) { + FreePool (CfgData->OptionList[Index]); + } + } + FreePool (CfgData->OptionList); + } + + if (CfgData->SolicitRetransmission != NULL) { + FreePool (CfgData->SolicitRetransmission); + } + + ZeroMem (CfgData, sizeof (EFI_DHCP6_CONFIG_DATA)); +} + + +/** + Clean up the mode data. + + @param[in, out] ModeData The pointer to the mode data. + +**/ +VOID +Dhcp6CleanupModeData ( + IN OUT EFI_DHCP6_MODE_DATA *ModeData + ) +{ + ASSERT (ModeData != NULL); + // + // Clean up all fields in mode data including the reference buffers, but do + // not free the mode data buffer itself. + // + if (ModeData->ClientId != NULL) { + FreePool (ModeData->ClientId); + } + + if (ModeData->Ia != NULL) { + + if (ModeData->Ia->ReplyPacket != NULL) { + FreePool (ModeData->Ia->ReplyPacket); + } + FreePool (ModeData->Ia); + } + + ZeroMem (ModeData, sizeof (EFI_DHCP6_MODE_DATA)); +} + + +/** + Calculate the expire time by the algorithm defined in rfc. + + @param[in] Base The base value of the time. + @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time. + @param[in] NeedSigned If TRUE, the the signed factor is needed. + + @return Expire The calculated result for the new expire time. + +**/ +UINT32 +Dhcp6CalculateExpireTime ( + IN UINT32 Base, + IN BOOLEAN IsFirstRt, + IN BOOLEAN NeedSigned + ) +{ + EFI_TIME Time; + BOOLEAN Signed; + UINT32 Seed; + UINT32 Expire; + + // + // Take the 10bits of microsecond in system time as a uniform distribution. + // Take the 10th bit as a flag to determine it's signed or not. + // + gRT->GetTime (&Time, NULL); + Seed = ((Time.Nanosecond >> 10) & DHCP6_10_BIT_MASK); + Signed = (BOOLEAN) ((((Time.Nanosecond >> 9) & 0x01) != 0) ? TRUE : FALSE); + Signed = (BOOLEAN) (NeedSigned ? Signed : FALSE); + + // + // Calculate expire by the following algo: + // 1. base + base * (-0.1 ~ 0) for the first solicit + // 2. base + base * (-0.1 ~ 0.1) for the first other messages + // 3. 2 * base + base * (-0.1 ~ 0.1) for the subsequent all messages + // 4. base + base * (-0.1 ~ 0) for the more than mrt timeout + // + // The (Seed / 0x3ff / 10) is used to a random range (0, 0.1). + // + if (IsFirstRt && Signed) { + + Expire = Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10); + + } else if (IsFirstRt && !Signed) { + + Expire = Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10); + + } else if (!IsFirstRt && Signed) { + + Expire = 2 * Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10); + + } else { + + Expire = 2 * Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10); + } + + Expire = (Expire != 0) ? Expire : 1; + + return Expire; +} + + +/** + Calculate the lease time by the algorithm defined in rfc. + + @param[in] IaCb The pointer to the Ia control block. + +**/ +VOID +Dhcp6CalculateLeaseTime ( + IN DHCP6_IA_CB *IaCb + ) +{ + UINT32 MinLt; + UINT32 MaxLt; + UINTN Index; + + ASSERT (IaCb->Ia->IaAddressCount > 0); + + MinLt = (UINT32) (-1); + MaxLt = 0; + + // + // Calculate minlt as min of all valid life time, and maxlt as max of all + // valid life time. + // + for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) { + MinLt = MIN (MinLt, IaCb->Ia->IaAddress[Index].ValidLifetime); + MaxLt = MAX (MinLt, IaCb->Ia->IaAddress[Index].ValidLifetime); + } + + // + // Take 50% minlt as t1, and 80% maxlt as t2 if Dhcp6 server doesn't offer + // such information. + // + IaCb->T1 = (IaCb->T1 != 0) ? IaCb->T1 : (UINT32)(MinLt * 5 / 10); + IaCb->T2 = (IaCb->T2 != 0) ? IaCb->T2 : (UINT32)(MinLt * 8 / 10); + IaCb->AllExpireTime = MaxLt; + IaCb->LeaseTime = 0; +} + + +/** + Check whether the addresses are all included by the configured Ia. + + @param[in] Ia The pointer to the Ia. + @param[in] AddressCount The number of addresses. + @param[in] Addresses The pointer to the addresses buffer. + + @retval EFI_SUCCESS The addresses are all included by the configured IA. + @retval EFI_NOT_FOUND The addresses are not included by the configured IA. + +**/ +EFI_STATUS +Dhcp6CheckAddress ( + IN EFI_DHCP6_IA *Ia, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ) +{ + UINTN Index1; + UINTN Index2; + BOOLEAN Found; + + // + // Check whether the addresses are all included by the configured IA. And it + // will return success if address count is zero, which means all addresses. + // + for (Index1 = 0; Index1 < AddressCount; Index1++) { + + Found = FALSE; + + for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) { + + if (CompareMem ( + &Addresses[Index1], + &Ia->IaAddress[Index2], + sizeof (EFI_IPv6_ADDRESS) + ) == 0) { + + Found = TRUE; + break; + } + } + + if (!Found) { + return EFI_NOT_FOUND; + } + } + + return EFI_SUCCESS; +} + + +/** + Deprive the addresses from current Ia, and generate another eliminated Ia. + + @param[in] Ia The pointer to the Ia. + @param[in] AddressCount The number of addresses. + @param[in] Addresses The pointer to the addresses buffer. + + @retval NULL If it failed to generate the deprived Ia. + @retval others The pointer to the deprived Ia. + +**/ +EFI_DHCP6_IA * +Dhcp6DepriveAddress ( + IN EFI_DHCP6_IA *Ia, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ) +{ + EFI_DHCP6_IA *IaCopy; + UINTN IaCopySize; + UINTN Index1; + UINTN Index2; + BOOLEAN Found; + + if (AddressCount == 0) { + // + // It means release all Ia addresses if address count is zero. + // + AddressCount = Ia->IaAddressCount; + } + + ASSERT (AddressCount != 0); + + IaCopySize = sizeof (EFI_DHCP6_IA) + (AddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + IaCopy = AllocateZeroPool (IaCopySize); + + if (IaCopy == NULL) { + return NULL; + } + + if (AddressCount == Ia->IaAddressCount) { + // + // If release all Ia addresses, just copy the configured Ia and then set + // its address count as zero. + // We may decline/release part of addresses at the begining. So it's a + // forwarding step to update address infor for decline/release, while the + // other infor such as Ia state will be updated when receiving reply. + // + CopyMem (IaCopy, Ia, IaCopySize); + Ia->IaAddressCount = 0; + return IaCopy; + } + + CopyMem (IaCopy, Ia, sizeof (EFI_DHCP6_IA)); + + // + // Move the addresses from the Ia of instance to the deprived Ia. + // + for (Index1 = 0; Index1 < AddressCount; Index1++) { + + Found = FALSE; + + for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) { + + if (CompareMem ( + &Addresses[Index1], + &Ia->IaAddress[Index2], + sizeof (EFI_IPv6_ADDRESS) + ) == 0) { + // + // Copy the deprived address to the copy of Ia + // + CopyMem ( + &IaCopy->IaAddress[Index1], + &Ia->IaAddress[Index2], + sizeof (EFI_DHCP6_IA_ADDRESS) + ); + // + // Delete the deprived address from the instance Ia + // + if (Index2 + 1 < Ia->IaAddressCount) { + CopyMem ( + &Ia->IaAddress[Index2], + &Ia->IaAddress[Index2 + 1], + (Ia->IaAddressCount - Index2 - 1) * sizeof (EFI_DHCP6_IA_ADDRESS) + ); + } + Found = TRUE; + break; + } + } + ASSERT (Found == TRUE); + } + + Ia->IaAddressCount -= AddressCount; + IaCopy->IaAddressCount = AddressCount; + + return IaCopy; +} + + +/** + The dummy ext buffer free callback routine. + + @param[in] Arg The pointer to the parameter. + +**/ +VOID +EFIAPI +Dhcp6DummyExtFree ( + IN VOID *Arg + ) +{ +} + + +/** + The callback routine once message transmitted. + + @param[in] Wrap The pointer to the received net buffer. + @param[in] EndPoint The pointer to the udp end point. + @param[in] IoStatus The return status from udp io. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6OnTransmitted ( + IN NET_BUF *Wrap, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + NetbufFree (Wrap); +} + + +/** + Append the option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the buffer. + @param[in] OptType The option type. + @param[in] OptLen The length of option contents. + @param[in] Data The pointer to the option content. + + @return Buf The position to append the next option. + +**/ +UINT8 * +Dhcp6AppendOption ( + IN OUT UINT8 *Buf, + IN UINT16 OptType, + IN UINT16 OptLen, + IN UINT8 *Data + ) +{ + // + // The format of Dhcp6 option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | option-code | option-len (option data) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | option-data | + // | (option-len octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + ASSERT (OptLen != 0); + + WriteUnaligned16 ((UINT16 *) Buf, OptType); + Buf += 2; + WriteUnaligned16 ((UINT16 *) Buf, OptLen); + Buf += 2; + CopyMem (Buf, Data, NTOHS (OptLen)); + Buf += NTOHS (OptLen); + + return Buf; +} + +/** + Append the appointed IA Address option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the position to append. + @param[in] IaAddr The pointer to the IA Address. + @param[in] MessageType Message type of DHCP6 package. + + @return Buf The position to append the next option. + +**/ +UINT8 * +Dhcp6AppendIaAddrOption ( + IN OUT UINT8 *Buf, + IN EFI_DHCP6_IA_ADDRESS *IaAddr, + IN UINT32 MessageType +) +{ + + // The format of the IA Address option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IAADDR | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // | IPv6 address | + // | | + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | preferred-lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | valid-lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // . . + // . IAaddr-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // + // Fill the value of Ia Address option type + // + WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptIaAddr)); + Buf += 2; + + WriteUnaligned16 ((UINT16 *) Buf, HTONS (sizeof (EFI_DHCP6_IA_ADDRESS))); + Buf += 2; + + CopyMem (Buf, &IaAddr->IpAddress, sizeof(EFI_IPv6_ADDRESS)); + Buf += sizeof(EFI_IPv6_ADDRESS); + + // + // Fill the value of preferred-lifetime and valid-lifetime. + // According to RFC3315 Chapter 18.1.2, the preferred-lifetime and valid-lifetime fields + // should set to 0 when initiate a Confirm message. + // + if (MessageType != Dhcp6MsgConfirm) { + WriteUnaligned32 ((UINT32 *) Buf, HTONL (IaAddr->PreferredLifetime)); + } + Buf += 4; + + if (MessageType != Dhcp6MsgConfirm) { + WriteUnaligned32 ((UINT32 *) Buf, HTONL (IaAddr->ValidLifetime)); + } + Buf += 4; + + return Buf; +} + + +/** + Append the appointed Ia option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the position to append. + @param[in] Ia The pointer to the Ia. + @param[in] T1 The time of T1. + @param[in] T2 The time of T2. + @param[in] MessageType Message type of DHCP6 package. + + @return Buf The position to append the next Ia option. + +**/ +UINT8 * +Dhcp6AppendIaOption ( + IN OUT UINT8 *Buf, + IN EFI_DHCP6_IA *Ia, + IN UINT32 T1, + IN UINT32 T2, + IN UINT32 MessageType + ) +{ + UINT8 *AddrOpt; + UINT16 *Len; + UINTN Index; + + // + // The format of IA_NA and IA_TA option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IA_NA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T1 (only for IA_NA) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T2 (only for IA_NA) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_NA-options/IA_TA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Fill the value of Ia option type + // + WriteUnaligned16 ((UINT16 *) Buf, HTONS (Ia->Descriptor.Type)); + Buf += 2; + + // + // Fill the len of Ia option later, keep the pointer first + // + Len = (UINT16 *) Buf; + Buf += 2; + + // + // Fill the value of iaid + // + WriteUnaligned32 ((UINT32 *) Buf, HTONL (Ia->Descriptor.IaId)); + Buf += 4; + + // + // Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified. + // + if (Ia->Descriptor.Type == Dhcp6OptIana) { + WriteUnaligned32 ((UINT32 *) Buf, HTONL ((T1 != 0) ? T1 : 0xffffffff)); + Buf += 4; + WriteUnaligned32 ((UINT32 *) Buf, HTONL ((T2 != 0) ? T2 : 0xffffffff)); + Buf += 4; + } + + // + // Fill all the addresses belong to the Ia + // + for (Index = 0; Index < Ia->IaAddressCount; Index++) { + AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS); + Buf = Dhcp6AppendIaAddrOption (Buf, (EFI_DHCP6_IA_ADDRESS *) AddrOpt, MessageType); + } + + // + // Fill the value of Ia option length + // + *Len = HTONS ((UINT16) (Buf - (UINT8 *) Len - 2)); + + return Buf; +} + +/** + Append the appointed Elapsed time option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the position to append. + @param[in] Instance The pointer to the Dhcp6 instance. + @param[out] Elapsed The pointer to the elapsed time value in + the generated packet. + + @return Buf The position to append the next Ia option. + +**/ +UINT8 * +Dhcp6AppendETOption ( + IN OUT UINT8 *Buf, + IN DHCP6_INSTANCE *Instance, + OUT UINT16 **Elapsed + ) +{ + // + // The format of elapsed time option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_ELAPSED_TIME | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | elapsed-time | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Fill the value of elapsed-time option type. + // + WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptElapsedTime)); + Buf += 2; + + // + // Fill the len of elapsed-time option, which is fixed. + // + WriteUnaligned16 ((UINT16 *) Buf, HTONS(2)); + Buf += 2; + + // + // Fill in elapsed time value with 0 value for now. The actual value is + // filled in later just before the packet is transmitted. + // + WriteUnaligned16 ((UINT16 *) Buf, HTONS(0)); + *Elapsed = (UINT16 *) Buf; + Buf += 2; + + return Buf; +} + +/** + Set the elapsed time based on the given instance and the pointer to the + elapsed time option. + + @param[in] Elapsed The pointer to the position to append. + @param[in] Instance The pointer to the Dhcp6 instance. + +**/ +VOID +SetElapsedTime ( + IN UINT16 *Elapsed, + IN DHCP6_INSTANCE *Instance + ) +{ + EFI_TIME Time; + UINT64 CurrentStamp; + UINT64 ElapsedTimeValue; + + // + // Generate a time stamp of the centiseconds from 2000/1/1, assume 30day/month. + // + gRT->GetTime (&Time, NULL); + CurrentStamp = (UINT64) + ( + ((((((Time.Year - 2000) * 360 + + (Time.Month - 1)) * 30 + + (Time.Day - 1)) * 24 + Time.Hour) * 60 + + Time.Minute) * 60 + Time.Second) * 100 + + DivU64x32(Time.Nanosecond, 10000000) + ); + + // + // Sentinel value of 0 means that this is the first DHCP packet that we are + // sending and that we need to initialize the value. First DHCP message + // gets 0 elapsed-time. Otherwise, calculate based on StartTime. + // + if (Instance->StartTime == 0) { + ElapsedTimeValue = 0; + Instance->StartTime = CurrentStamp; + } else { + ElapsedTimeValue = CurrentStamp - Instance->StartTime; + + // + // If elapsed time cannot fit in two bytes, set it to 0xffff. + // + if (ElapsedTimeValue > 0xffff) { + ElapsedTimeValue = 0xffff; + } + } + WriteUnaligned16 (Elapsed, HTONS((UINT16) ElapsedTimeValue)); +} + + +/** + Seek the address of the first byte of the option header. + + @param[in] Buf The pointer to the buffer. + @param[in] SeekLen The length to seek. + @param[in] OptType The option type. + + @retval NULL If it failed to seek the option. + @retval others The position to the option. + +**/ +UINT8 * +Dhcp6SeekOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN UINT16 OptType + ) +{ + UINT8 *Cursor; + UINT8 *Option; + UINT16 DataLen; + UINT16 OpCode; + + Option = NULL; + Cursor = Buf; + + // + // The format of Dhcp6 option refers to Dhcp6AppendOption(). + // + while (Cursor < Buf + SeekLen) { + OpCode = ReadUnaligned16 ((UINT16 *) Cursor); + if (OpCode == HTONS (OptType)) { + Option = Cursor; + break; + } + DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2))); + Cursor += (DataLen + 4); + } + + return Option; +} + + +/** + Seek the address of the first byte of the Ia option header. + + @param[in] Buf The pointer to the buffer. + @param[in] SeekLen The length to seek. + @param[in] IaDesc The pointer to the Ia descriptor. + + @retval NULL If it failed to seek the Ia option. + @retval others The position to the Ia option. + +**/ +UINT8 * +Dhcp6SeekIaOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc + ) +{ + UINT8 *Cursor; + UINT8 *Option; + UINT16 DataLen; + UINT16 OpCode; + UINT32 IaId; + + // + // The format of IA_NA and IA_TA option refers to Dhcp6AppendIaOption(). + // + Option = NULL; + Cursor = Buf; + + while (Cursor < Buf + SeekLen) { + OpCode = ReadUnaligned16 ((UINT16 *) Cursor); + IaId = ReadUnaligned32 ((UINT32 *) (Cursor + 4)); + if (OpCode == HTONS (IaDesc->Type) && IaId == HTONL (IaDesc->IaId)) { + Option = Cursor; + break; + } + DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2))); + Cursor += (DataLen + 4); + } + + return Option; +} + +/** + Check whether the incoming IPv6 address in IaAddr is one of the maintained + addresses in the IA control blcok. + + @param[in] IaAddr The pointer to the IA Address to be checked. + @param[in] CurrentIa The pointer to the IA in IA control block. + + @retval TRUE Yes, this Address is already in IA control block. + @retval FALSE No, this Address is NOT in IA control block. + +**/ +BOOLEAN +Dhcp6AddrIsInCurrentIa ( + IN EFI_DHCP6_IA_ADDRESS *IaAddr, + IN EFI_DHCP6_IA *CurrentIa + ) +{ + UINT32 Index; + + ASSERT (IaAddr != NULL && CurrentIa != NULL); + + for (Index = 0; Index < CurrentIa->IaAddressCount; Index++) { + if (EFI_IP6_EQUAL(&IaAddr->IpAddress, &CurrentIa->IaAddress[Index].IpAddress)) { + return TRUE; + } + } + return FALSE; +} + +/** + Parse the address option and update the address infomation. + + @param[in] CurrentIa The pointer to the Ia Address in control blcok. + @param[in] IaInnerOpt The pointer to the buffer. + @param[in] IaInnerLen The length to parse. + @param[out] AddrNum The number of addresses. + @param[in, out] AddrBuf The pointer to the address buffer. + +**/ +VOID +Dhcp6ParseAddrOption ( + IN EFI_DHCP6_IA *CurrentIa, + IN UINT8 *IaInnerOpt, + IN UINT16 IaInnerLen, + OUT UINT32 *AddrNum, + IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf + ) +{ + UINT8 *Cursor; + UINT16 DataLen; + UINT16 OpCode; + UINT32 ValidLt; + UINT32 PreferredLt; + EFI_DHCP6_IA_ADDRESS *IaAddr; + + // + // The format of the IA Address option: + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPTION_IAADDR | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // | IPv6 address | + // | | + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | preferred-lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | valid-lifetime | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // . . + // . IAaddr-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Two usage model: + // + // 1. Pass addrbuf == null, to get the addrnum over the Ia inner options. + // 2. Pass addrbuf != null, to resolve the addresses over the Ia inner + // options to the addrbuf. + // + + Cursor = IaInnerOpt; + *AddrNum = 0; + + while (Cursor < IaInnerOpt + IaInnerLen) { + // + // Refer to RFC3315 Chapter 18.1.8, we need to update lifetimes for any addresses in the IA option + // that the client already has recorded in the IA, and discard the Ia address option with 0 valid time. + // + OpCode = ReadUnaligned16 ((UINT16 *) Cursor); + PreferredLt = NTOHL (ReadUnaligned32 ((UINT32 *) (Cursor + 20))); + ValidLt = NTOHL (ReadUnaligned32 ((UINT32 *) (Cursor + 24))); + IaAddr = (EFI_DHCP6_IA_ADDRESS *) (Cursor + 4); + if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt >= PreferredLt && + (Dhcp6AddrIsInCurrentIa(IaAddr, CurrentIa) || ValidLt !=0)) { + if (AddrBuf != NULL) { + CopyMem (AddrBuf, IaAddr, sizeof (EFI_DHCP6_IA_ADDRESS)); + AddrBuf->PreferredLifetime = PreferredLt; + AddrBuf->ValidLifetime = ValidLt; + AddrBuf = (EFI_DHCP6_IA_ADDRESS *) ((UINT8 *) AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS)); + } + (*AddrNum)++; + } + DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2))); + Cursor += (DataLen + 4); + } +} + + +/** + Create a control blcok for the Ia according to the corresponding options. + + @param[in] Instance The pointer to DHCP6 Instance. + @param[in] IaInnerOpt The pointer to the inner options in the Ia option. + @param[in] IaInnerLen The length of all the inner options in the Ia option. + @param[in] T1 T1 time in the Ia option. + @param[in] T2 T2 time in the Ia option. + + @retval EFI_NOT_FOUND No valid IA option is found. + @retval EFI_SUCCESS Create an IA control block successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + +**/ +EFI_STATUS +Dhcp6GenerateIaCb ( + IN DHCP6_INSTANCE *Instance, + IN UINT8 *IaInnerOpt, + IN UINT16 IaInnerLen, + IN UINT32 T1, + IN UINT32 T2 + ) +{ + UINT32 AddrNum; + UINT32 IaSize; + EFI_DHCP6_IA *Ia; + + if (Instance->IaCb.Ia == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // Calculate the number of addresses for this Ia, excluding the addresses with + // the value 0 of valid lifetime. + // + Dhcp6ParseAddrOption (Instance->IaCb.Ia, IaInnerOpt, IaInnerLen, &AddrNum, NULL); + + if (AddrNum == 0) { + return EFI_NOT_FOUND; + } + + // + // Allocate for new IA. + // + IaSize = sizeof (EFI_DHCP6_IA) + (AddrNum - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + Ia = AllocateZeroPool (IaSize); + + if (Ia == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Fill up this new IA fields. + // + Ia->State = Instance->IaCb.Ia->State; + Ia->IaAddressCount = AddrNum; + CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR)); + Dhcp6ParseAddrOption (Instance->IaCb.Ia, IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress); + + // + // Free original IA resource. + // + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + FreePool (Instance->IaCb.Ia); + + + ZeroMem (&Instance->IaCb, sizeof (DHCP6_IA_CB)); + + // + // Update IaCb to use new IA. + // + Instance->IaCb.Ia = Ia; + + // + + // Fill in IaCb fields. Such as T1, T2, AllExpireTime and LeaseTime. + // + Instance->IaCb.T1 = T1; + Instance->IaCb.T2 = T2; + Dhcp6CalculateLeaseTime (&Instance->IaCb); + + return EFI_SUCCESS; +} + + +/** + Cache the current IA configuration information. + + @param[in] Instance The pointer to DHCP6 Instance. + + @retval EFI_SUCCESS Cache the current IA successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6CacheIa ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINTN IaSize; + EFI_DHCP6_IA *Ia; + + Ia = Instance->IaCb.Ia; + + if ((Instance->CacheIa == NULL) && (Ia != NULL)) { + // + // Cache the current IA. + // + IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + + Instance->CacheIa = AllocateZeroPool (IaSize); + if (Instance->CacheIa == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Instance->CacheIa, Ia, IaSize); + } + return EFI_SUCCESS; +} + +/** + Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0. + + @param[in] Instance The pointer to DHCP6 instance. + +**/ +VOID +Dhcp6AppendCacheIa ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINT8 *Ptr; + UINTN Index; + UINTN IaSize; + UINTN NewIaSize; + EFI_DHCP6_IA *Ia; + EFI_DHCP6_IA *NewIa; + EFI_DHCP6_IA *CacheIa; + + Ia = Instance->IaCb.Ia; + CacheIa = Instance->CacheIa; + + if ((CacheIa != NULL) && (CacheIa->IaAddressCount != 0)) { + // + // There are old addresses existing. Merge with current addresses. + // + NewIaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount + CacheIa->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + NewIa = AllocateZeroPool (NewIaSize); + if (NewIa == NULL) { + return; + } + + IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS); + CopyMem (NewIa, Ia, IaSize); + + // + // Clear old address.ValidLifetime + // + for (Index = 0; Index < CacheIa->IaAddressCount; Index++) { + CacheIa->IaAddress[Index].ValidLifetime = 0; + } + + NewIa->IaAddressCount += CacheIa->IaAddressCount; + Ptr = (UINT8*)&NewIa->IaAddress[Ia->IaAddressCount]; + CopyMem (Ptr, CacheIa->IaAddress, CacheIa->IaAddressCount * sizeof (EFI_DHCP6_IA_ADDRESS)); + + // + // Migrate to the NewIa and free previous. + // + FreePool (Instance->CacheIa); + FreePool (Instance->IaCb.Ia); + Instance->CacheIa = NULL; + Instance->IaCb.Ia = NewIa; + } +} + +/** + Calculate the Dhcp6 get mapping timeout by adding additinal delay to the IP6 DAD transmits count. + + @param[in] Ip6Cfg The pointer to Ip6 config protocol. + @param[out] TimeOut The time out value in 100ns units. + + @retval EFI_INVALID_PARAMETER Input parameters are invalid. + @retval EFI_SUCCESS Calculate the time out value successfully. +**/ +EFI_STATUS +Dhcp6GetMappingTimeOut ( + IN EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg, + OUT UINTN *TimeOut + ) +{ + EFI_STATUS Status; + UINTN DataSize; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + + if (Ip6Cfg == NULL || TimeOut == NULL) { + return EFI_INVALID_PARAMETER; + } + + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &DadXmits + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *TimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + DHCP6_DAD_ADDITIONAL_DELAY; + + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h new file mode 100644 index 0000000000..8c3151e306 --- /dev/null +++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h @@ -0,0 +1,360 @@ +/** @file + Dhcp6 support functions declaration. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_DHCP6_UTILITY_H__ +#define __EFI_DHCP6_UTILITY_H__ + + +#define DHCP6_10_BIT_MASK 0x3ff +#define DHCP6_DAD_ADDITIONAL_DELAY 30000000 // 3 seconds + +/** + Generate client Duid in the format of Duid-llt. + + @param[in] Mode The pointer to the mode of SNP. + + @retval NULL if failed to generate client Id. + @retval Others The pointer to the new client id. + +**/ +EFI_DHCP6_DUID * +Dhcp6GenerateClientId ( + IN EFI_SIMPLE_NETWORK_MODE *Mode + ); + +/** + Copy the Dhcp6 configure data. + + @param[in] DstCfg The pointer to the destination configure data. + @param[in] SorCfg The pointer to the source configure data. + + @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6CopyConfigData ( + IN EFI_DHCP6_CONFIG_DATA *DstCfg, + IN EFI_DHCP6_CONFIG_DATA *SorCfg + ); + +/** + Clean up the configure data. + + @param[in, out] CfgData The pointer to the configure data. + +**/ +VOID +Dhcp6CleanupConfigData ( + IN OUT EFI_DHCP6_CONFIG_DATA *CfgData + ); + +/** + Clean up the mode data. + + @param[in, out] ModeData The pointer to the mode data. + +**/ +VOID +Dhcp6CleanupModeData ( + IN OUT EFI_DHCP6_MODE_DATA *ModeData + ); + +/** + Calculate the expire time by the algorithm defined in rfc. + + @param[in] Base The base value of the time. + @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time. + @param[in] NeedSigned If TRUE, the the signed factor is needed. + + @return Expire The calculated result for the new expire time. + +**/ +UINT32 +Dhcp6CalculateExpireTime ( + IN UINT32 Base, + IN BOOLEAN IsFirstRt, + IN BOOLEAN NeedSigned + ); + +/** + Calculate the lease time by the algorithm defined in rfc. + + @param[in] IaCb The pointer to the Ia control block. + +**/ +VOID +Dhcp6CalculateLeaseTime ( + IN DHCP6_IA_CB *IaCb + ); + +/** + Check whether the addresses are all included by the configured Ia. + + @param[in] Ia The pointer to the Ia. + @param[in] AddressCount The number of addresses. + @param[in] Addresses The pointer to the addresses buffer. + + @retval EFI_SUCCESS The addresses are all included by the configured IA. + @retval EFI_NOT_FOUND The addresses are not included by the configured IA. + +**/ +EFI_STATUS +Dhcp6CheckAddress ( + IN EFI_DHCP6_IA *Ia, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ); + +/** + Deprive the addresses from current Ia, and generate another eliminated Ia. + + @param[in] Ia The pointer to the Ia. + @param[in] AddressCount The number of addresses. + @param[in] Addresses The pointer to the addresses buffer. + + @retval NULL If failed to generate the deprived Ia. + @retval others The pointer to the deprived Ia. + +**/ +EFI_DHCP6_IA * +Dhcp6DepriveAddress ( + IN EFI_DHCP6_IA *Ia, + IN UINT32 AddressCount, + IN EFI_IPv6_ADDRESS *Addresses + ); + +/** + The dummy ext buffer free callback routine. + + @param[in] Arg The pointer to the parameter. + +**/ +VOID +EFIAPI +Dhcp6DummyExtFree ( + IN VOID *Arg + ); + +/** + The callback routine once message transmitted. + + @param[in] Wrap The pointer to the received net buffer. + @param[in] EndPoint The pointer to the udp end point. + @param[in] IoStatus The return status from udp io. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6OnTransmitted ( + IN NET_BUF *Wrap, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + +/** + Append the appointed option to the buf, and move the buf to the end. + + @param[in, out] Buf The pointer to buffer. + @param[in] OptType The option type. + @param[in] OptLen The lenght of option content.s + @param[in] Data The pointer to the option content. + + @return Buf The position to append the next option. + +**/ +UINT8 * +Dhcp6AppendOption ( + IN OUT UINT8 *Buf, + IN UINT16 OptType, + IN UINT16 OptLen, + IN UINT8 *Data + ); + +/** + Append the Ia option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the position to append. + @param[in] Ia The pointer to the Ia. + @param[in] T1 The time of T1. + @param[in] T2 The time of T2. + @param[in] MessageType Message type of DHCP6 package. + + @return Buf The position to append the next Ia option. + +**/ +UINT8 * +Dhcp6AppendIaOption ( + IN OUT UINT8 *Buf, + IN EFI_DHCP6_IA *Ia, + IN UINT32 T1, + IN UINT32 T2, + IN UINT32 MessageType + ); + +/** + Append the appointed Elapsed time option to Buf, and move Buf to the end. + + @param[in, out] Buf The pointer to the position to append. + @param[in] Instance The pointer to the Dhcp6 instance. + @param[out] Elapsed The pointer to the elapsed time value in + the generated packet. + + @return Buf The position to append the next Ia option. + +**/ +UINT8 * +Dhcp6AppendETOption ( + IN OUT UINT8 *Buf, + IN DHCP6_INSTANCE *Instance, + OUT UINT16 **Elapsed + ); + +/** + Set the elapsed time based on the given instance and the pointer to the + elapsed time option. + + @param[in] Elapsed The pointer to the position to append. + @param[in] Instance The pointer to the Dhcp6 instance. +**/ +VOID +SetElapsedTime ( + IN UINT16 *Elapsed, + IN DHCP6_INSTANCE *Instance + ); + +/** + Seek the address of the first byte of the option header. + + @param[in] Buf The pointer to buffer. + @param[in] SeekLen The length to seek. + @param[in] OptType The option type. + + @retval NULL If failed to seek the option. + @retval others The position to the option. + +**/ +UINT8 * +Dhcp6SeekOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN UINT16 OptType + ); + +/** + Seek the address of the first byte of the Ia option header. + + @param[in] Buf The pointer to the buffer. + @param[in] SeekLen The length to seek. + @param[in] IaDesc The pointer to the Ia descriptor. + + @retval NULL If failed to seek the Ia option. + @retval others The position to the Ia option. + +**/ +UINT8 * +Dhcp6SeekIaOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc + ); + +/** + Parse the address option and update the address info. + + @param[in] CurrentIa The pointer to the Ia Address in control blcok. + @param[in] IaInnerOpt The pointer to the buffer. + @param[in] IaInnerLen The length to parse. + @param[out] AddrNum The number of addresses. + @param[in, out] AddrBuf The pointer to the address buffer. + +**/ +VOID +Dhcp6ParseAddrOption ( + IN EFI_DHCP6_IA *CurrentIa, + IN UINT8 *IaInnerOpt, + IN UINT16 IaInnerLen, + OUT UINT32 *AddrNum, + IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf + ); + +/** + Create a control blcok for the Ia according to the corresponding options. + + @param[in] Instance The pointer to DHCP6 Instance. + @param[in] IaInnerOpt The pointer to the inner options in the Ia option. + @param[in] IaInnerLen The length of all the inner options in the Ia option. + @param[in] T1 T1 time in the Ia option. + @param[in] T2 T2 time in the Ia option. + + @retval EFI_NOT_FOUND No valid IA option is found. + @retval EFI_SUCCESS Create an IA control block successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + +**/ +EFI_STATUS +Dhcp6GenerateIaCb ( + IN DHCP6_INSTANCE *Instance, + IN UINT8 *IaInnerOpt, + IN UINT16 IaInnerLen, + IN UINT32 T1, + IN UINT32 T2 + ); + + +/** + Cache the current IA configuration information. + + @param[in] Instance The pointer to DHCP6 Instance. + + @retval EFI_SUCCESS Cache the current IA successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + +**/ +EFI_STATUS +Dhcp6CacheIa ( + IN DHCP6_INSTANCE *Instance + ); + + +/** + Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0. + + @param[in] Instance The pointer to DHCP6 instance. + +**/ +VOID +Dhcp6AppendCacheIa ( + IN DHCP6_INSTANCE *Instance + ); + +/** + Calculate the Dhcp6 get mapping timeout by adding additinal delay to the IP6 DAD transmits count. + + @param[in] Ip6Cfg The pointer to Ip6 config protocol. + @param[out] TimeOut The time out value in 100ns units. + + @retval EFI_INVALID_PARAMETER Input parameters are invalid. + @retval EFI_SUCCESS Calculate the time out value successfully. +**/ +EFI_STATUS +Dhcp6GetMappingTimeOut ( + IN EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg, + OUT UINTN *TimeOut + ); +#endif diff --git a/Core/NetworkPkg/DnsDxe/ComponentName.c b/Core/NetworkPkg/DnsDxe/ComponentName.c new file mode 100644 index 0000000000..d95ed92d8a --- /dev/null +++ b/Core/NetworkPkg/DnsDxe/ComponentName.c @@ -0,0 +1,443 @@ +/** @file +Implementation of EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL protocol. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DnsImpl.h" + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DnsComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DnsComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +/// +/// Component Name Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME_PROTOCOL gDnsComponentName = { + DnsComponentNameGetDriverName, + DnsComponentNameGetControllerName, + "eng" +}; + +/// +/// Component Name 2 Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME2_PROTOCOL gDnsComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) DnsComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) DnsComponentNameGetControllerName, + "en" +}; + +/// +/// Table of driver names +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_UNICODE_STRING_TABLE mDnsDriverNameTable[] = { + { "eng;en", (CHAR16 *)L"DNS Network Service Driver" }, + { NULL, NULL } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gDnsControllerNameTable = NULL; + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DnsComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDnsDriverNameTable, + DriverName, + (BOOLEAN)(This == &gDnsComponentName) + ); +} + +/** + Update the component name for the Dns4 child handle. + + @param Dns4 A pointer to the EFI_DNS4_PROTOCOL. + + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + +**/ +EFI_STATUS +UpdateDns4Name ( + EFI_DNS4_PROTOCOL *Dns4 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[80]; + EFI_DNS4_MODE_DATA ModeData; + + if (Dns4 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer as: + // DNSv4 (StationIp=?, LocalPort=?) + // + Status = Dns4->GetModeData (Dns4, &ModeData); + if (EFI_ERROR (Status)) { + return Status; + } + + UnicodeSPrint ( + HandleName, + sizeof (HandleName), + L"DNSv4 (StationIp=%d.%d.%d.%d, LocalPort=%d)", + ModeData.DnsConfigData.StationIp.Addr[0], + ModeData.DnsConfigData.StationIp.Addr[1], + ModeData.DnsConfigData.StationIp.Addr[2], + ModeData.DnsConfigData.StationIp.Addr[3], + ModeData.DnsConfigData.LocalPort + ); + + if (gDnsControllerNameTable != NULL) { + FreeUnicodeStringTable (gDnsControllerNameTable); + gDnsControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gDnsComponentName.SupportedLanguages, + &gDnsControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gDnsComponentName2.SupportedLanguages, + &gDnsControllerNameTable, + HandleName, + FALSE + ); +} + +/** + Update the component name for the Dns6 child handle. + + @param Dns6 A pointer to the EFI_DNS6_PROTOCOL. + + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + +**/ +EFI_STATUS +UpdateDns6Name ( + EFI_DNS6_PROTOCOL *Dns6 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[128]; + EFI_DNS6_MODE_DATA ModeData; + CHAR16 Address[sizeof"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"]; + + if (Dns6 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer as: + // DNSv6 (StationIp=?, LocalPort=?) + // + Status = Dns6->GetModeData (Dns6, &ModeData); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = NetLibIp6ToStr (&ModeData.DnsConfigData.StationIp, Address, sizeof (Address)); + if (EFI_ERROR (Status)) { + return Status; + } + UnicodeSPrint ( + HandleName, + sizeof (HandleName), + L"DNSv6 (StationIp=%s, LocalPort=%d)", + Address, + ModeData.DnsConfigData.LocalPort + ); + + if (gDnsControllerNameTable != NULL) { + FreeUnicodeStringTable (gDnsControllerNameTable); + gDnsControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gDnsComponentName.SupportedLanguages, + &gDnsControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gDnsComponentName2.SupportedLanguages, + &gDnsControllerNameTable, + HandleName, + FALSE + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DnsComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_DNS4_PROTOCOL *Dns4; + EFI_DNS6_PROTOCOL *Dns6; + + // + // ChildHandle must be NULL for a Device Driver + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiUdp6ProtocolGuid + ); + if (!EFI_ERROR (Status)) { + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiDns6ProtocolGuid, + (VOID **)&Dns6, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateDns6Name (Dns6); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiUdp4ProtocolGuid + ); + if (!EFI_ERROR (Status)) { + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiDns4ProtocolGuid, + (VOID **)&Dns4, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateDns4Name (Dns4); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gDnsControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gDnsComponentName) + ); +} diff --git a/Core/NetworkPkg/DnsDxe/DnsDhcp.c b/Core/NetworkPkg/DnsDxe/DnsDhcp.c new file mode 100644 index 0000000000..6b409ba5e3 --- /dev/null +++ b/Core/NetworkPkg/DnsDxe/DnsDhcp.c @@ -0,0 +1,920 @@ +/** @file +Functions implementation related with DHCPv4/v6 for DNS driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DnsImpl.h" + +/** + The callback function for the timer event used to get map. + + @param[in] Event The event this function is registered to. + @param[in] Context The context registered to the event. +**/ +VOID +EFIAPI +TimeoutToGetMap ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; + return ; +} + +/** + Create an IP child, use it to start the auto configuration, then destroy it. + + @param[in] Controller The controller which has the service installed. + @param[in] Image The image handle used to open service. + + @retval EFI_SUCCESS The configuration is done. + @retval Others Other errors as indicated. +**/ +EFI_STATUS +EFIAPI +DnsStartIp4( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image + ) +{ + EFI_IP4_PROTOCOL *Ip4; + EFI_HANDLE Ip4Handle; + EFI_EVENT TimerToGetMap; + EFI_IP4_CONFIG_DATA Ip4ConfigData; + EFI_IP4_MODE_DATA Ip4Mode; + EFI_STATUS Status; + + BOOLEAN Timeout; + + // + // Get the Ip4ServiceBinding Protocol + // + Ip4Handle = NULL; + Ip4 = NULL; + TimerToGetMap = NULL; + + Timeout = FALSE; + + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiIp4ServiceBindingProtocolGuid, + &Ip4Handle + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Ip4Handle, + &gEfiIp4ProtocolGuid, + (VOID **) &Ip4, + Controller, + Image, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Ip4ConfigData.DefaultProtocol = EFI_IP_PROTO_ICMP; + Ip4ConfigData.AcceptAnyProtocol = FALSE; + Ip4ConfigData.AcceptIcmpErrors = FALSE; + Ip4ConfigData.AcceptBroadcast = FALSE; + Ip4ConfigData.AcceptPromiscuous = FALSE; + Ip4ConfigData.UseDefaultAddress = TRUE; + ZeroMem (&Ip4ConfigData.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Ip4ConfigData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + Ip4ConfigData.TypeOfService = 0; + Ip4ConfigData.TimeToLive = 1; + Ip4ConfigData.DoNotFragment = FALSE; + Ip4ConfigData.RawData = FALSE; + Ip4ConfigData.ReceiveTimeout = 0; + Ip4ConfigData.TransmitTimeout = 0; + + Status = Ip4->Configure (Ip4, &Ip4ConfigData); + + if (Status == EFI_NO_MAPPING) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + TimeoutToGetMap, + &Timeout, + &TimerToGetMap + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + TimerToGetMap, + TimerRelative, + MultU64x32 (10000000, 5) + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + while (!Timeout) { + Ip4->Poll (Ip4); + + if (!EFI_ERROR (Ip4->GetModeData (Ip4, &Ip4Mode, NULL, NULL)) && + Ip4Mode.IsConfigured) { + break; + } + } + + if (Timeout) { + Status = EFI_DEVICE_ERROR; + } + } + +ON_EXIT: + + if (TimerToGetMap != NULL) { + gBS->SetTimer (TimerToGetMap, TimerCancel, 0); + gBS->CloseEvent (TimerToGetMap); + } + + NetLibDestroyServiceChild ( + Controller, + Image, + &gEfiIp4ServiceBindingProtocolGuid, + Ip4Handle + ); + + return Status; +} + +/** + This function initialize the DHCP4 message instance. + + This function will pad each item of dhcp4 message packet. + + @param Seed Pointer to the message instance of the DHCP4 packet. + @param InterfaceInfo Pointer to the EFI_IP4_CONFIG2_INTERFACE_INFO instance. + +**/ +VOID +DnsInitSeedPacket ( + OUT EFI_DHCP4_PACKET *Seed, + IN EFI_IP4_CONFIG2_INTERFACE_INFO *InterfaceInfo + ) +{ + EFI_DHCP4_HEADER *Header; + + // + // Get IfType and HwAddressSize from SNP mode data. + // + Seed->Size = sizeof (EFI_DHCP4_PACKET); + Seed->Length = sizeof (Seed->Dhcp4); + Header = &Seed->Dhcp4.Header; + ZeroMem (Header, sizeof (EFI_DHCP4_HEADER)); + Header->OpCode = DHCP4_OPCODE_REQUEST; + Header->HwType = InterfaceInfo->IfType; + Header->HwAddrLen = (UINT8) InterfaceInfo->HwAddressSize; + CopyMem (Header->ClientHwAddr, &(InterfaceInfo->HwAddress), Header->HwAddrLen); + + Seed->Dhcp4.Magik = DHCP4_MAGIC; + Seed->Dhcp4.Option[0] = DHCP4_TAG_EOP; +} + +/** + The common notify function. + + @param[in] Event The event signaled. + @param[in] Context The context. + +**/ +VOID +EFIAPI +DhcpCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if ((Event == NULL) || (Context == NULL)) { + return ; + } + + *((BOOLEAN *) Context) = TRUE; +} + +/** + Parse the ACK to get required information + + @param Dhcp4 The DHCP4 protocol. + @param Packet Packet waiting for parse. + @param DnsServerInfor The required Dns4 server information. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_NO_MAPPING DHCP failed to acquire address and other information. + @retval EFI_DEVICE_ERROR Other errors as indicated. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +ParseDhcp4Ack ( + IN EFI_DHCP4_PROTOCOL *Dhcp4, + IN EFI_DHCP4_PACKET *Packet, + IN DNS4_SERVER_INFOR *DnsServerInfor + ) +{ + EFI_STATUS Status; + UINT32 OptionCount; + EFI_DHCP4_PACKET_OPTION **OptionList; + UINT32 ServerCount; + EFI_IPv4_ADDRESS *ServerList; + UINT32 Index; + UINT32 Count; + + ServerCount = 0; + ServerList = NULL; + + OptionCount = 0; + OptionList = NULL; + + Status = Dhcp4->Parse (Dhcp4, Packet, &OptionCount, OptionList); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_DEVICE_ERROR; + } + + OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *)); + if (OptionList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Dhcp4->Parse (Dhcp4, Packet, &OptionCount, OptionList); + if (EFI_ERROR (Status)) { + gBS->FreePool (OptionList); + return EFI_DEVICE_ERROR; + } + + Status = EFI_NOT_FOUND; + + for (Index = 0; Index < OptionCount; Index++) { + // + // Get DNS server addresses + // + if (OptionList[Index]->OpCode == DHCP4_TAG_DNS_SERVER) { + + if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) { + Status = EFI_DEVICE_ERROR; + break; + } + + ServerCount = OptionList[Index]->Length/4; + ServerList = AllocatePool (ServerCount * sizeof (EFI_IPv4_ADDRESS)); + if (ServerList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for(Count=0; Count < ServerCount; Count++){ + CopyMem (ServerList + Count, &OptionList[Index]->Data[4 * Count], sizeof (EFI_IPv4_ADDRESS)); + } + + *(DnsServerInfor->ServerCount) = ServerCount; + DnsServerInfor->ServerList = ServerList; + + Status = EFI_SUCCESS; + } + } + + gBS->FreePool (OptionList); + + return Status; +} + +/** + EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol + instance to intercept events that occurs in the DHCPv6 Information Request + exchange process. + + @param This Pointer to the EFI_DHCP6_PROTOCOL instance that + is used to configure this callback function. + @param Context Pointer to the context that is initialized in + the EFI_DHCP6_PROTOCOL.InfoRequest(). + @param Packet Pointer to Reply packet that has been received. + The EFI DHCPv6 Protocol instance is responsible + for freeing the buffer. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_DEVICE_ERROR Other errors as indicated. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ParseDhcp6Ack ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT32 OptionCount; + EFI_DHCP6_PACKET_OPTION **OptionList; + DNS6_SERVER_INFOR *DnsServerInfor; + UINT32 ServerCount; + EFI_IPv6_ADDRESS *ServerList; + UINT32 Index; + UINT32 Count; + + OptionCount = 0; + ServerCount = 0; + ServerList = NULL; + + Status = This->Parse (This, Packet, &OptionCount, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_DEVICE_ERROR; + } + + OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *)); + if (OptionList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = This->Parse (This, Packet, &OptionCount, OptionList); + if (EFI_ERROR (Status)) { + gBS->FreePool (OptionList); + return EFI_DEVICE_ERROR; + } + + DnsServerInfor = (DNS6_SERVER_INFOR *) Context; + + for (Index = 0; Index < OptionCount; Index++) { + OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode); + OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen); + + // + // Get DNS server addresses from this reply packet. + // + if (OptionList[Index]->OpCode == DHCP6_TAG_DNS_SERVER) { + + if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) { + Status = EFI_DEVICE_ERROR; + gBS->FreePool (OptionList); + return Status; + } + + ServerCount = OptionList[Index]->OpLen/16; + ServerList = AllocatePool (ServerCount * sizeof (EFI_IPv6_ADDRESS)); + if (ServerList == NULL) { + gBS->FreePool (OptionList); + return EFI_OUT_OF_RESOURCES; + } + + for(Count=0; Count < ServerCount; Count++){ + CopyMem (ServerList + Count, &OptionList[Index]->Data[16 * Count], sizeof (EFI_IPv6_ADDRESS)); + } + + *(DnsServerInfor->ServerCount) = ServerCount; + DnsServerInfor->ServerList = ServerList; + } + } + + gBS->FreePool (OptionList); + + return Status; + +} + +/** + Parse the DHCP ACK to get Dns4 server information. + + @param Instance The DNS instance. + @param DnsServerCount Retrieved Dns4 server Ip count. + @param DnsServerList Retrieved Dns4 server Ip list. + + @retval EFI_SUCCESS The Dns4 information is got from the DHCP ACK. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NO_MEDIA There was a media error. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +GetDns4ServerFromDhcp4 ( + IN DNS_INSTANCE *Instance, + OUT UINT32 *DnsServerCount, + OUT EFI_IPv4_ADDRESS **DnsServerList + ) +{ + EFI_STATUS Status; + EFI_HANDLE Image; + EFI_HANDLE Controller; + BOOLEAN MediaPresent; + EFI_HANDLE MnpChildHandle; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData; + EFI_HANDLE Dhcp4Handle; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + UINTN DataSize; + VOID *Data; + EFI_IP4_CONFIG2_INTERFACE_INFO *InterfaceInfo; + EFI_DHCP4_PACKET SeedPacket; + EFI_DHCP4_PACKET_OPTION *ParaList[2]; + DNS4_SERVER_INFOR DnsServerInfor; + + EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN Token; + BOOLEAN IsDone; + UINTN Index; + + Image = Instance->Service->ImageHandle; + Controller = Instance->Service->ControllerHandle; + + MnpChildHandle = NULL; + Mnp = NULL; + + Dhcp4Handle = NULL; + Dhcp4 = NULL; + + Ip4Config2 = NULL; + DataSize = 0; + Data = NULL; + InterfaceInfo = NULL; + + ZeroMem ((UINT8 *) ParaList, sizeof (ParaList)); + + ZeroMem (&MnpConfigData, sizeof (EFI_MANAGED_NETWORK_CONFIG_DATA)); + + ZeroMem (&DnsServerInfor, sizeof (DNS4_SERVER_INFOR)); + + ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN)); + + DnsServerInfor.ServerCount = DnsServerCount; + + IsDone = FALSE; + + // + // Check media. + // + MediaPresent = TRUE; + NetLibDetectMedia (Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Start the auto configuration if UseDefaultSetting. + // + if (Instance->Dns4CfgData.UseDefaultSetting) { + Status = DnsStartIp4 (Controller, Image); + if (EFI_ERROR(Status)) { + return Status; + } + } + + // + // Create a Mnp child instance, get the protocol and config for it. + // + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiManagedNetworkServiceBindingProtocolGuid, + &MnpChildHandle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) &Mnp, + Image, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + MnpConfigData.ReceivedQueueTimeoutValue = 0; + MnpConfigData.TransmitQueueTimeoutValue = 0; + MnpConfigData.ProtocolTypeFilter = IP4_ETHER_PROTO; + MnpConfigData.EnableUnicastReceive = TRUE; + MnpConfigData.EnableMulticastReceive = TRUE; + MnpConfigData.EnableBroadcastReceive = TRUE; + MnpConfigData.EnablePromiscuousReceive = FALSE; + MnpConfigData.FlushQueuesOnReset = TRUE; + MnpConfigData.EnableReceiveTimestamps = FALSE; + MnpConfigData.DisableBackgroundPolling = FALSE; + + Status = Mnp->Configure(Mnp, &MnpConfigData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Create a DHCP4 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiDhcp4ServiceBindingProtocolGuid, + &Dhcp4Handle + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->OpenProtocol ( + Dhcp4Handle, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Dhcp4, + Image, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Get Ip4Config2 instance info. + // + Status = gBS->HandleProtocol (Controller, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeInterfaceInfo, &DataSize, Data); + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + goto ON_EXIT; + } + + Data = AllocateZeroPool (DataSize); + if (Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeInterfaceInfo, &DataSize, Data); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + InterfaceInfo = (EFI_IP4_CONFIG2_INTERFACE_INFO *)Data; + + // + // Build required Token. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + DhcpCommonNotify, + &IsDone, + &Token.CompletionEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff); + + Token.RemotePort = 67; + + Token.ListenPointCount = 1; + + Token.ListenPoints = AllocateZeroPool (Token.ListenPointCount * sizeof (EFI_DHCP4_LISTEN_POINT)); + if (Token.ListenPoints == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + if (Instance->Dns4CfgData.UseDefaultSetting) { + CopyMem (&(Token.ListenPoints[0].ListenAddress), &(InterfaceInfo->StationAddress), sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&(Token.ListenPoints[0].SubnetMask), &(InterfaceInfo->SubnetMask), sizeof (EFI_IPv4_ADDRESS)); + } else { + CopyMem (&(Token.ListenPoints[0].ListenAddress), &(Instance->Dns4CfgData.StationIp), sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&(Token.ListenPoints[0].SubnetMask), &(Instance->Dns4CfgData.SubnetMask), sizeof (EFI_IPv4_ADDRESS)); + } + + Token.ListenPoints[0].ListenPort = 68; + + Token.TimeoutValue = DNS_TIME_TO_GETMAP; + + DnsInitSeedPacket (&SeedPacket, InterfaceInfo); + + ParaList[0] = AllocateZeroPool (sizeof (EFI_DHCP4_PACKET_OPTION)); + if (ParaList[0] == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + ParaList[0]->OpCode = DHCP4_TAG_TYPE; + ParaList[0]->Length = 1; + ParaList[0]->Data[0] = DHCP4_MSG_INFORM; + + ParaList[1] = AllocateZeroPool (sizeof (EFI_DHCP4_PACKET_OPTION)); + if (ParaList[1] == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + ParaList[1]->OpCode = DHCP4_TAG_PARA_LIST; + ParaList[1]->Length = 1; + ParaList[1]->Data[0] = DHCP4_TAG_DNS_SERVER; + + Status = Dhcp4->Build (Dhcp4, &SeedPacket, 0, NULL, 2, ParaList, &Token.Packet); + + Token.Packet->Dhcp4.Header.Xid = HTONL(NET_RANDOM (NetRandomInitSeed ())); + + Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16)0x8000); + + if (Instance->Dns4CfgData.UseDefaultSetting) { + CopyMem (&(Token.Packet->Dhcp4.Header.ClientAddr), &(InterfaceInfo->StationAddress), sizeof (EFI_IPv4_ADDRESS)); + } else { + CopyMem (&(Token.Packet->Dhcp4.Header.ClientAddr), &(Instance->Dns4CfgData.StationIp), sizeof (EFI_IPv4_ADDRESS)); + } + + CopyMem (Token.Packet->Dhcp4.Header.ClientHwAddr, &(InterfaceInfo->HwAddress), InterfaceInfo->HwAddressSize); + + Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8)(InterfaceInfo->HwAddressSize); + + // + // TransmitReceive Token + // + Status = Dhcp4->TransmitReceive (Dhcp4, &Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Poll the packet + // + do { + Status = Mnp->Poll (Mnp); + } while (!IsDone); + + // + // Parse the ACK to get required information if received done. + // + if (IsDone && !EFI_ERROR (Token.Status)) { + for (Index = 0; Index < Token.ResponseCount; Index++) { + Status = ParseDhcp4Ack (Dhcp4, &Token.ResponseList[Index], &DnsServerInfor); + if (!EFI_ERROR (Status)) { + break; + } + } + + *DnsServerList = DnsServerInfor.ServerList; + } else { + Status = Token.Status; + } + +ON_EXIT: + + if (Data != NULL) { + FreePool (Data); + } + + for (Index = 0; Index < 2; Index++) { + if (ParaList[Index] != NULL) { + FreePool (ParaList[Index]); + } + } + + if (Token.ListenPoints) { + FreePool (Token.ListenPoints); + } + + if (Token.Packet) { + FreePool (Token.Packet); + } + + if (Token.ResponseList != NULL) { + FreePool (Token.ResponseList); + } + + if (Token.CompletionEvent != NULL) { + gBS->CloseEvent (Token.CompletionEvent); + } + + if (Dhcp4 != NULL) { + Dhcp4->Stop (Dhcp4); + Dhcp4->Configure (Dhcp4, NULL); + + gBS->CloseProtocol ( + Dhcp4Handle, + &gEfiDhcp4ProtocolGuid, + Image, + Controller + ); + } + + if (Dhcp4Handle != NULL) { + NetLibDestroyServiceChild ( + Controller, + Image, + &gEfiDhcp4ServiceBindingProtocolGuid, + Dhcp4Handle + ); + } + + if (Mnp != NULL) { + Mnp->Configure (Mnp, NULL); + + gBS->CloseProtocol ( + MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + Image, + Controller + ); + } + + NetLibDestroyServiceChild ( + Controller, + Image, + &gEfiManagedNetworkServiceBindingProtocolGuid, + MnpChildHandle + ); + + return Status; +} + +/** + Parse the DHCP ACK to get Dns6 server information. + + @param Image The handle of the driver image. + @param Controller The handle of the controller. + @param DnsServerCount Retrieved Dns6 server Ip count. + @param DnsServerList Retrieved Dns6 server Ip list. + + @retval EFI_SUCCESS The Dns6 information is got from the DHCP ACK. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NO_MEDIA There was a media error. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +GetDns6ServerFromDhcp6 ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + OUT UINT32 *DnsServerCount, + OUT EFI_IPv6_ADDRESS **DnsServerList + ) +{ + EFI_HANDLE Dhcp6Handle; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_STATUS Status; + EFI_STATUS TimerStatus; + EFI_DHCP6_PACKET_OPTION *Oro; + EFI_DHCP6_RETRANSMISSION InfoReqReXmit; + EFI_EVENT Timer; + BOOLEAN MediaPresent; + DNS6_SERVER_INFOR DnsServerInfor; + + Dhcp6Handle = NULL; + Dhcp6 = NULL; + Oro = NULL; + Timer = NULL; + + ZeroMem (&DnsServerInfor, sizeof (DNS6_SERVER_INFOR)); + + DnsServerInfor.ServerCount = DnsServerCount; + + // + // Check media status before doing DHCP. + // + MediaPresent = TRUE; + NetLibDetectMedia (Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Create a DHCP6 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Dhcp6Handle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Dhcp6, + Image, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 1); + if (Oro == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Ask the server to reply with DNS options. + // All members in EFI_DHCP6_PACKET_OPTION are in network order. + // + Oro->OpCode = HTONS (DHCP6_TAG_DNS_REQUEST); + Oro->OpLen = HTONS (2); + Oro->Data[1] = DHCP6_TAG_DNS_SERVER; + + InfoReqReXmit.Irt = 4; + InfoReqReXmit.Mrc = 1; + InfoReqReXmit.Mrt = 10; + InfoReqReXmit.Mrd = 30; + + Status = Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + NULL, + ParseDhcp6Ack, + &DnsServerInfor + ); + if (Status == EFI_NO_MAPPING) { + Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + Timer, + TimerRelative, + DNS_TIME_TO_GETMAP * TICKS_PER_SECOND + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + do { + TimerStatus = gBS->CheckEvent (Timer); + if (!EFI_ERROR (TimerStatus)) { + Status = Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + NULL, + ParseDhcp6Ack, + &DnsServerInfor + ); + } + } while (TimerStatus == EFI_NOT_READY); + } + + *DnsServerList = DnsServerInfor.ServerList; + +ON_EXIT: + + if (Oro != NULL) { + FreePool (Oro); + } + + if (Timer != NULL) { + gBS->CloseEvent (Timer); + } + + if (Dhcp6 != NULL) { + gBS->CloseProtocol ( + Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + Image, + Controller + ); + } + + NetLibDestroyServiceChild ( + Controller, + Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + Dhcp6Handle + ); + + return Status; + +} + diff --git a/Core/NetworkPkg/DnsDxe/DnsDhcp.h b/Core/NetworkPkg/DnsDxe/DnsDhcp.h new file mode 100644 index 0000000000..62bf7174e1 --- /dev/null +++ b/Core/NetworkPkg/DnsDxe/DnsDhcp.h @@ -0,0 +1,145 @@ +/** @file +Functions implementation related with DHCPv4/v6 for DNS driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _DNS_DHCP_H_ +#define _DNS_DHCP_H_ + +// +// DHCP DNS related +// +#pragma pack(1) + +#define IP4_ETHER_PROTO 0x0800 + +#define DHCP4_OPCODE_REQUEST 1 +#define DHCP4_MAGIC 0x63538263 /// network byte order +#define DHCP4_TAG_EOP 255 /// End Option + +#define DHCP4_TAG_TYPE 53 +#define DHCP4_MSG_REQUEST 3 +#define DHCP4_MSG_INFORM 8 + +#define DHCP4_TAG_PARA_LIST 55 +#define DHCP4_TAG_DNS_SERVER 6 + + +#define DHCP6_TAG_DNS_REQUEST 6 +#define DHCP6_TAG_DNS_SERVER 23 + +// +// The required Dns4 server information. +// +typedef struct { + UINT32 *ServerCount; + EFI_IPv4_ADDRESS *ServerList; +} DNS4_SERVER_INFOR; + +// +// The required Dns6 server information. +// +typedef struct { + UINT32 *ServerCount; + EFI_IPv6_ADDRESS *ServerList; +} DNS6_SERVER_INFOR; + +#pragma pack() + +/** + Parse the ACK to get required information + + @param Dhcp4 The DHCP4 protocol. + @param Packet Packet waiting for parse. + @param DnsServerInfor The required Dns4 server information. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_NO_MAPPING DHCP failed to acquire address and other information. + @retval EFI_DEVICE_ERROR Other errors as indicated. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +ParseDhcp4Ack ( + IN EFI_DHCP4_PROTOCOL *Dhcp4, + IN EFI_DHCP4_PACKET *Packet, + IN DNS4_SERVER_INFOR *DnsServerInfor + ); + +/** + EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol + instance to intercept events that occurs in the DHCPv6 Information Request + exchange process. + + @param This Pointer to the EFI_DHCP6_PROTOCOL instance that + is used to configure this callback function. + @param Context Pointer to the context that is initialized in + the EFI_DHCP6_PROTOCOL.InfoRequest(). + @param Packet Pointer to Reply packet that has been received. + The EFI DHCPv6 Protocol instance is responsible + for freeing the buffer. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_DEVICE_ERROR Other errors as indicated. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +EFIAPI +ParseDhcp6Ack ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ); + +/** + Parse the DHCP ACK to get Dns4 server information. + + @param Instance The DNS instance. + @param DnsServerCount Retrieved Dns4 server Ip count. + @param DnsServerList Retrieved Dns4 server Ip list. + + @retval EFI_SUCCESS The Dns4 information is got from the DHCP ACK. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NO_MEDIA There was a media error. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +GetDns4ServerFromDhcp4 ( + IN DNS_INSTANCE *Instance, + OUT UINT32 *DnsServerCount, + OUT EFI_IPv4_ADDRESS **DnsServerList + ); + +/** + Parse the DHCP ACK to get Dns6 server information. + + @param Image The handle of the driver image. + @param Controller The handle of the controller. + @param DnsServerCount Retrieved Dns6 server Ip count. + @param DnsServerList Retrieved Dns6 server Ip list. + + @retval EFI_SUCCESS The Dns6 information is got from the DHCP ACK. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NO_MEDIA There was a media error. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +GetDns6ServerFromDhcp6 ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + OUT UINT32 *DnsServerCount, + OUT EFI_IPv6_ADDRESS **DnsServerList + ); + +#endif \ No newline at end of file diff --git a/Core/NetworkPkg/DnsDxe/DnsDriver.c b/Core/NetworkPkg/DnsDxe/DnsDriver.c new file mode 100644 index 0000000000..6ca8aa7bdd --- /dev/null +++ b/Core/NetworkPkg/DnsDxe/DnsDriver.c @@ -0,0 +1,1554 @@ +/** @file +The driver binding and service binding protocol for DnsDxe driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DnsImpl.h" + +EFI_DRIVER_BINDING_PROTOCOL gDns4DriverBinding = { + Dns4DriverBindingSupported, + Dns4DriverBindingStart, + Dns4DriverBindingStop, + DNS_VERSION, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gDns6DriverBinding = { + Dns6DriverBindingSupported, + Dns6DriverBindingStart, + Dns6DriverBindingStop, + DNS_VERSION, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL mDns4ServiceBinding = { + Dns4ServiceBindingCreateChild, + Dns4ServiceBindingDestroyChild +}; + +EFI_SERVICE_BINDING_PROTOCOL mDns6ServiceBinding = { + Dns6ServiceBindingCreateChild, + Dns6ServiceBindingDestroyChild +}; + +DNS_DRIVER_DATA *mDriverData = NULL; + +/** + Destroy the DNS instance and recycle the resources. + + @param[in] Instance The pointer to the DNS instance. + +**/ +VOID +DnsDestroyInstance ( + IN DNS_INSTANCE *Instance + ) +{ + ZeroMem (&Instance->Dns4CfgData, sizeof (EFI_DNS4_CONFIG_DATA)); + + ZeroMem (&Instance->Dns6CfgData, sizeof (EFI_DNS6_CONFIG_DATA)); + + if (!NetMapIsEmpty (&Instance->Dns4TxTokens)) { + Dns4InstanceCancelToken (Instance, NULL); + } + + if (!NetMapIsEmpty (&Instance->Dns6TxTokens)) { + Dns6InstanceCancelToken (Instance, NULL); + } + + if (Instance->UdpIo!= NULL) { + UdpIoFreeIo (Instance->UdpIo); + } + + FreePool (Instance); +} + +/** + Create the DNS instance and initialize it. + + @param[in] Service The pointer to the DNS service. + @param[out] Instance The pointer to the DNS instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The DNS instance is created. + +**/ +EFI_STATUS +DnsCreateInstance ( + IN DNS_SERVICE *Service, + OUT DNS_INSTANCE **Instance + ) +{ + DNS_INSTANCE *DnsIns; + + *Instance = NULL; + + DnsIns = AllocateZeroPool (sizeof (DNS_INSTANCE)); + if (DnsIns == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DnsIns->Signature = DNS_INSTANCE_SIGNATURE; + InitializeListHead (&DnsIns->Link); + DnsIns->State = DNS_STATE_UNCONFIGED; + DnsIns->InDestroy = FALSE; + DnsIns->Service = Service; + + if (Service->IpVersion == IP_VERSION_4) { + CopyMem (&DnsIns->Dns4, &mDns4Protocol, sizeof (DnsIns->Dns4)); + NetMapInit (&DnsIns->Dns4TxTokens); + } else { + CopyMem (&DnsIns->Dns6, &mDns6Protocol, sizeof (DnsIns->Dns6)); + NetMapInit (&DnsIns->Dns6TxTokens); + } + + DnsIns->UdpIo = UdpIoCreateIo ( + Service->ControllerHandle, /// NicHandle + Service->ImageHandle, + DnsConfigNullUdp, + Service->IpVersion, + DnsIns + ); + if (DnsIns->UdpIo == NULL) { + FreePool (DnsIns); + return EFI_OUT_OF_RESOURCES; + } + + *Instance = DnsIns; + + return EFI_SUCCESS; +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +DnsDestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + DNS_INSTANCE *Instance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = NET_LIST_USER_STRUCT_S (Entry, DNS_INSTANCE, Link, DNS_INSTANCE_SIGNATURE); + ServiceBinding = ((DNS_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((DNS_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((DNS_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (Instance->ChildHandle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle); +} + +/** + Config a NULL UDP that is used to keep the connection between UDP and DNS. + + Just leave the Udp child unconfigured. When UDP is unloaded, + DNS will be informed with DriverBinding Stop. + + @param UdpIo The UDP_IO to configure + @param Context The opaque parameter to the callback + + @retval EFI_SUCCESS It always return EFI_SUCCESS directly. + +**/ +EFI_STATUS +EFIAPI +DnsConfigNullUdp ( + IN UDP_IO *UdpIo, + IN VOID *Context + ) +{ + return EFI_SUCCESS; +} + +/** + Release all the resource used the DNS service binding instance. + + @param DnsSb The Dns service binding instance. + +**/ +VOID +DnsDestroyService ( + IN DNS_SERVICE *DnsSb + ) +{ + UdpIoFreeIo (DnsSb->ConnectUdp); + + if (DnsSb->TimerToGetMap != NULL){ + gBS->CloseEvent (DnsSb->TimerToGetMap); + } + + if (DnsSb->Timer != NULL){ + gBS->CloseEvent (DnsSb->Timer); + } + + FreePool (DnsSb); +} + +/** + Create then initialize a Dns service binding instance. + + @param Controller The controller to install the DNS service + binding on + @param Image The driver binding image of the DNS driver + @param IpVersion IpVersion for this service + @param Service The variable to receive the created service + binding instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to create the instance. + @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep + connection with UDP. + @retval EFI_SUCCESS The service instance is created for the + controller. + +**/ +EFI_STATUS +DnsCreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image, + IN UINT8 IpVersion, + OUT DNS_SERVICE **Service + ) +{ + EFI_STATUS Status; + DNS_SERVICE *DnsSb; + + Status = EFI_SUCCESS; + DnsSb = NULL; + + *Service = NULL; + + DnsSb = AllocateZeroPool (sizeof (DNS_SERVICE)); + if (DnsSb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DnsSb->Signature = DNS_SERVICE_SIGNATURE; + + if (IpVersion == IP_VERSION_4) { + DnsSb->ServiceBinding = mDns4ServiceBinding; + } else { + DnsSb->ServiceBinding = mDns6ServiceBinding; + } + + DnsSb->Dns4ChildrenNum = 0; + InitializeListHead (&DnsSb->Dns4ChildrenList); + + DnsSb->Dns6ChildrenNum = 0; + InitializeListHead (&DnsSb->Dns6ChildrenList); + + DnsSb->ControllerHandle = Controller; + DnsSb->ImageHandle = Image; + + DnsSb->TimerToGetMap = NULL; + + DnsSb->Timer = NULL; + + DnsSb->IpVersion = IpVersion; + + // + // Create the timer used to time out the procedure which is used to + // get the default IP address. + // + if (DnsSb->IpVersion == IP_VERSION_4) { + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &DnsSb->TimerToGetMap + ); + if (EFI_ERROR (Status)) { + FreePool (DnsSb); + return Status; + } + } + + // + // Create the timer to retransmit packets. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + DnsOnTimerRetransmit, + DnsSb, + &DnsSb->Timer + ); + if (EFI_ERROR (Status)) { + if (DnsSb->TimerToGetMap != NULL) { + gBS->CloseEvent (DnsSb->TimerToGetMap); + } + FreePool (DnsSb); + return Status; + } + + DnsSb->ConnectUdp = NULL; + DnsSb->ConnectUdp = UdpIoCreateIo ( + Controller, + Image, + DnsConfigNullUdp, + DnsSb->IpVersion, + NULL + ); + if (DnsSb->ConnectUdp == NULL) { + if (DnsSb->TimerToGetMap != NULL) { + gBS->CloseEvent (DnsSb->TimerToGetMap); + } + gBS->CloseEvent (DnsSb->Timer); + FreePool (DnsSb); + return EFI_DEVICE_ERROR; + } + + *Service = DnsSb; + return Status; +} + +/** + Unloads an image. + + @param ImageHandle Handle that identifies the image to be unloaded. + + @retval EFI_SUCCESS The image has been unloaded. + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. + +**/ +EFI_STATUS +EFIAPI +DnsUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + + LIST_ENTRY *Entry; + DNS4_CACHE *ItemCache4; + DNS4_SERVER_IP *ItemServerIp4; + DNS6_CACHE *ItemCache6; + DNS6_SERVER_IP *ItemServerIp6; + + ItemCache4 = NULL; + ItemServerIp4 = NULL; + ItemCache6 = NULL; + ItemServerIp6 = NULL; + + // + // Disconnect the driver specified by ImageHandle + // + Status = NetLibDefaultUnload(ImageHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Free mDriverData. + // + if (mDriverData != NULL) { + if (mDriverData->Timer != NULL) { + gBS->CloseEvent (mDriverData->Timer); + } + + while (!IsListEmpty (&mDriverData->Dns4CacheList)) { + Entry = NetListRemoveHead (&mDriverData->Dns4CacheList); + ItemCache4 = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink); + if (ItemCache4->DnsCache.HostName != NULL) { + FreePool (ItemCache4->DnsCache.HostName); + } + if (ItemCache4->DnsCache.IpAddress != NULL) { + FreePool (ItemCache4->DnsCache.IpAddress); + } + FreePool (ItemCache4); + } + + while (!IsListEmpty (&mDriverData->Dns4ServerList)) { + Entry = NetListRemoveHead (&mDriverData->Dns4ServerList); + ItemServerIp4 = NET_LIST_USER_STRUCT (Entry, DNS4_SERVER_IP, AllServerLink); + FreePool (ItemServerIp4); + } + + while (!IsListEmpty (&mDriverData->Dns6CacheList)) { + Entry = NetListRemoveHead (&mDriverData->Dns6CacheList); + ItemCache6 = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink); + if (ItemCache6->DnsCache.HostName != NULL) { + FreePool (ItemCache6->DnsCache.HostName); + } + if (ItemCache6->DnsCache.IpAddress != NULL) { + FreePool (ItemCache6->DnsCache.IpAddress); + } + FreePool (ItemCache6); + } + + while (!IsListEmpty (&mDriverData->Dns6ServerList)) { + Entry = NetListRemoveHead (&mDriverData->Dns6ServerList); + ItemServerIp6 = NET_LIST_USER_STRUCT (Entry, DNS6_SERVER_IP, AllServerLink); + FreePool (ItemServerIp6); + } + + FreePool (mDriverData); + } + + return Status; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @param ImageHandle The firmware allocated handle for the UEFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. +**/ +EFI_STATUS +EFIAPI +DnsDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + // + // Install the Dns4 Driver Binding Protocol. + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gDns4DriverBinding, + ImageHandle, + &gDnsComponentName, + &gDnsComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Install the Dns6 Driver Binding Protocol. + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gDns6DriverBinding, + NULL, + &gDnsComponentName, + &gDnsComponentName2 + ); + if (EFI_ERROR (Status)) { + goto Error1; + } + + // + // Create the driver data structures. + // + mDriverData = AllocateZeroPool (sizeof (DNS_DRIVER_DATA)); + if (mDriverData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error2; + } + + // + // Create the timer event to update DNS cache list. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + DnsOnTimerUpdate, + NULL, + &mDriverData->Timer + ); + if (EFI_ERROR (Status)) { + goto Error3; + } + + Status = gBS->SetTimer (mDriverData->Timer, TimerPeriodic, TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto Error4; + } + + InitializeListHead (&mDriverData->Dns4CacheList); + InitializeListHead (&mDriverData->Dns4ServerList); + InitializeListHead (&mDriverData->Dns6CacheList); + InitializeListHead (&mDriverData->Dns6ServerList); + + return Status; + + Error4: + gBS->CloseEvent (mDriverData->Timer); + + Error3: + FreePool (mDriverData); + + Error2: + gBS->UninstallMultipleProtocolInterfaces ( + gDns6DriverBinding.DriverBindingHandle, + &gEfiDriverBindingProtocolGuid, + &gDns6DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gDnsComponentName2, + &gEfiComponentNameProtocolGuid, + &gDnsComponentName, + NULL + ); + + Error1: + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gDns4DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gDnsComponentName2, + &gEfiComponentNameProtocolGuid, + &gDnsComponentName, + NULL + ); + + return Status; +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +Dns4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the Dns4ServiceBinding Protocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDns4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Test for the Udp4ServiceBinding Protocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +Dns4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + DNS_SERVICE *DnsSb; + EFI_STATUS Status; + + Status = DnsCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4, &DnsSb); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (DnsSb != NULL); + + Status = gBS->SetTimer (DnsSb->Timer, TimerPeriodic, TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the Dns4ServiceBinding Protocol onto ControllerHandle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiDns4ServiceBindingProtocolGuid, + &DnsSb->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + DnsDestroyService (DnsSb); + + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Dns4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + DNS_SERVICE *DnsSb; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + LIST_ENTRY *List; + DNS_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + + // + // DNS driver opens UDP child, So, Controller is a UDP + // child handle. Locate the Nic handle first. Then get the + // DNS private data back. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid); + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiDns4ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + DnsSb = DNS_SERVICE_FROM_THIS (ServiceBinding); + + if (!IsListEmpty (&DnsSb->Dns4ChildrenList)) { + // + // Destroy the Dns child instance in ChildHandleBuffer. + // + List = &DnsSb->Dns4ChildrenList; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + DnsDestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } + + if (NumberOfChildren == 0 && IsListEmpty (&DnsSb->Dns4ChildrenList)) { + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiDns4ServiceBindingProtocolGuid, + ServiceBinding + ); + + DnsDestroyService (DnsSb); + + if (gDnsControllerNameTable != NULL) { + FreeUnicodeStringTable (gDnsControllerNameTable); + gDnsControllerNameTable = NULL; + } + + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +Dns6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the Dns6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDns6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Test for the Udp6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +Dns6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + DNS_SERVICE *DnsSb; + EFI_STATUS Status; + + Status = DnsCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6, &DnsSb); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (DnsSb != NULL); + + Status = gBS->SetTimer (DnsSb->Timer, TimerPeriodic, TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the Dns6ServiceBinding Protocol onto ControllerHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiDns6ServiceBindingProtocolGuid, + &DnsSb->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + DnsDestroyService (DnsSb); + + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Dns6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + DNS_SERVICE *DnsSb; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + LIST_ENTRY *List; + DNS_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + + // + // DNS driver opens UDP child, So, Controller is a UDP + // child handle. Locate the Nic handle first. Then get the + // DNS private data back. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid); + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiDns6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + DnsSb = DNS_SERVICE_FROM_THIS (ServiceBinding); + + if (!IsListEmpty (&DnsSb->Dns6ChildrenList)) { + // + // Destroy the Dns child instance in ChildHandleBuffer. + // + List = &DnsSb->Dns6ChildrenList; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + DnsDestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } + + if (NumberOfChildren == 0 && IsListEmpty (&DnsSb->Dns6ChildrenList)) { + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiDns6ServiceBindingProtocolGuid, + ServiceBinding + ); + + DnsDestroyService (DnsSb); + + if (gDnsControllerNameTable != NULL) { + FreeUnicodeStringTable (gDnsControllerNameTable); + gDnsControllerNameTable = NULL; + } + + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +Dns4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + DNS_SERVICE *DnsSb; + DNS_INSTANCE *Instance; + EFI_STATUS Status; + EFI_TPL OldTpl; + VOID *Udp4; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + DnsSb = DNS_SERVICE_FROM_THIS (This); + + Status = DnsCreateInstance (DnsSb, &Instance); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Instance != NULL); + + // + // Install the DNS protocol onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiDns4ProtocolGuid, + &Instance->Dns4, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->ChildHandle = *ChildHandle; + + // + // Open the Udp4 protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + DnsSb->ConnectUdp->UdpHandle, + &gEfiUdp4ProtocolGuid, + (VOID **) &Udp4, + gDns4DriverBinding.DriverBindingHandle, + Instance->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Instance->ChildHandle, + &gEfiDns4ProtocolGuid, + &Instance->Dns4, + NULL + ); + + goto ON_ERROR; + } + + // + // Open the Udp4 protocol by child. + // + Status = gBS->OpenProtocol ( + Instance->UdpIo->UdpHandle, + &gEfiUdp4ProtocolGuid, + (VOID **) &Udp4, + gDns4DriverBinding.DriverBindingHandle, + Instance->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + // + // Close the Udp4 protocol. + // + gBS->CloseProtocol ( + DnsSb->ConnectUdp->UdpHandle, + &gEfiUdp4ProtocolGuid, + gDns4DriverBinding.DriverBindingHandle, + ChildHandle + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Instance->ChildHandle, + &gEfiDns4ProtocolGuid, + &Instance->Dns4, + NULL + ); + + goto ON_ERROR; + } + + // + // Add it to the parent's child list. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&DnsSb->Dns4ChildrenList, &Instance->Link); + DnsSb->Dns4ChildrenNum++; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_ERROR: + + DnsDestroyInstance (Instance); + return Status; +} + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Dns4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + DNS_SERVICE *DnsSb; + DNS_INSTANCE *Instance; + + EFI_DNS4_PROTOCOL *Dns4; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiDns4ProtocolGuid, + (VOID **) &Dns4, + gDns4DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL4 (Dns4); + DnsSb = DNS_SERVICE_FROM_THIS (This); + + if (Instance->Service != DnsSb) { + return EFI_INVALID_PARAMETER; + } + + if (Instance->InDestroy) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->InDestroy = TRUE; + + // + // Close the Udp4 protocol. + // + gBS->CloseProtocol ( + DnsSb->ConnectUdp->UdpHandle, + &gEfiUdp4ProtocolGuid, + gDns4DriverBinding.DriverBindingHandle, + ChildHandle + ); + + gBS->CloseProtocol ( + Instance->UdpIo->UdpHandle, + &gEfiUdp4ProtocolGuid, + gDns4DriverBinding.DriverBindingHandle, + ChildHandle + ); + + gBS->RestoreTPL (OldTpl); + + // + // Uninstall the DNS protocol first to enable a top down destruction. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiDns4ProtocolGuid, + Dns4 + ); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (EFI_ERROR (Status)) { + Instance->InDestroy = FALSE; + gBS->RestoreTPL (OldTpl); + return Status; + } + + RemoveEntryList (&Instance->Link); + DnsSb->Dns4ChildrenNum--; + + gBS->RestoreTPL (OldTpl); + + DnsDestroyInstance (Instance); + return EFI_SUCCESS; +} + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +Dns6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + DNS_SERVICE *DnsSb; + DNS_INSTANCE *Instance; + EFI_STATUS Status; + EFI_TPL OldTpl; + VOID *Udp6; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + DnsSb = DNS_SERVICE_FROM_THIS (This); + + Status = DnsCreateInstance (DnsSb, &Instance); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Instance != NULL); + + // + // Install the DNS protocol onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiDns6ProtocolGuid, + &Instance->Dns6, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->ChildHandle = *ChildHandle; + + // + // Open the Udp6 protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + DnsSb->ConnectUdp->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + gDns6DriverBinding.DriverBindingHandle, + Instance->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Instance->ChildHandle, + &gEfiDns6ProtocolGuid, + &Instance->Dns6, + NULL + ); + + goto ON_ERROR; + } + + // + // Open the Udp6 protocol by child. + // + Status = gBS->OpenProtocol ( + Instance->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + gDns6DriverBinding.DriverBindingHandle, + Instance->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + // + // Close the Udp6 protocol. + // + gBS->CloseProtocol ( + DnsSb->ConnectUdp->UdpHandle, + &gEfiUdp6ProtocolGuid, + gDns6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Instance->ChildHandle, + &gEfiDns6ProtocolGuid, + &Instance->Dns6, + NULL + ); + + goto ON_ERROR; + } + + // + // Add it to the parent's child list. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&DnsSb->Dns6ChildrenList, &Instance->Link); + DnsSb->Dns6ChildrenNum++; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_ERROR: + + DnsDestroyInstance (Instance); + return Status; +} + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Dns6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + DNS_SERVICE *DnsSb; + DNS_INSTANCE *Instance; + + EFI_DNS6_PROTOCOL *Dns6; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiDns6ProtocolGuid, + (VOID **) &Dns6, + gDns6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL6 (Dns6); + DnsSb = DNS_SERVICE_FROM_THIS (This); + + if (Instance->Service != DnsSb) { + return EFI_INVALID_PARAMETER; + } + + if (Instance->InDestroy) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->InDestroy = TRUE; + + // + // Close the Udp6 protocol. + // + gBS->CloseProtocol ( + DnsSb->ConnectUdp->UdpHandle, + &gEfiUdp6ProtocolGuid, + gDns6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + gBS->CloseProtocol ( + Instance->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gDns6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + gBS->RestoreTPL (OldTpl); + + // + // Uninstall the DNS protocol first to enable a top down destruction. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiDns6ProtocolGuid, + Dns6 + ); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (EFI_ERROR (Status)) { + Instance->InDestroy = FALSE; + gBS->RestoreTPL (OldTpl); + return Status; + } + + RemoveEntryList (&Instance->Link); + DnsSb->Dns6ChildrenNum--; + + gBS->RestoreTPL (OldTpl); + + DnsDestroyInstance (Instance); + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/DnsDxe/DnsDriver.h b/Core/NetworkPkg/DnsDxe/DnsDriver.h new file mode 100644 index 0000000000..f6b8a7e8fc --- /dev/null +++ b/Core/NetworkPkg/DnsDxe/DnsDriver.h @@ -0,0 +1,606 @@ +/** @file +The header files of the driver binding and service binding protocol for DnsDxe driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _DNS_DRIVER_H_ +#define _DNS_DRIVER_H_ + +#include +#include + +/// +/// Dns service block +/// +typedef struct _DNS_DRIVER_DATA DNS_DRIVER_DATA; + +/// +/// Dns service block +/// +typedef struct _DNS_SERVICE DNS_SERVICE; + +/// +/// Dns instance block +/// +typedef struct _DNS_INSTANCE DNS_INSTANCE; + +#define DNS_SERVICE_SIGNATURE SIGNATURE_32 ('D', 'N', 'S', 'S') + +#define DNS_INSTANCE_SIGNATURE SIGNATURE_32 ('D', 'N', 'S', 'I') + +struct _DNS_DRIVER_DATA { + EFI_EVENT Timer; /// Ticking timer for DNS cache update. + + LIST_ENTRY Dns4CacheList; + LIST_ENTRY Dns4ServerList; + + LIST_ENTRY Dns6CacheList; + LIST_ENTRY Dns6ServerList; +}; + +struct _DNS_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + + UINT16 Dns4ChildrenNum; + LIST_ENTRY Dns4ChildrenList; + + UINT16 Dns6ChildrenNum; + LIST_ENTRY Dns6ChildrenList; + + EFI_HANDLE ControllerHandle; + EFI_HANDLE ImageHandle; + + EFI_EVENT TimerToGetMap; + + EFI_EVENT Timer; /// Ticking timer for packet retransmission. + + UINT8 IpVersion; + UDP_IO *ConnectUdp; +}; + +struct _DNS_INSTANCE { + UINT32 Signature; + LIST_ENTRY Link; + + EFI_DNS4_PROTOCOL Dns4; + EFI_DNS6_PROTOCOL Dns6; + + INTN State; + BOOLEAN InDestroy; + + DNS_SERVICE *Service; + EFI_HANDLE ChildHandle; + + EFI_DNS4_CONFIG_DATA Dns4CfgData; + EFI_DNS6_CONFIG_DATA Dns6CfgData; + + EFI_IP_ADDRESS SessionDnsServer; + + NET_MAP Dns4TxTokens; + NET_MAP Dns6TxTokens; + + UINT32 MaxRetry; + + UDP_IO *UdpIo; +}; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +} DNS_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +extern DNS_DRIVER_DATA *mDriverData; + +#define DNS_SERVICE_FROM_THIS(a) \ + CR (a, DNS_SERVICE, ServiceBinding, DNS_SERVICE_SIGNATURE) + +#define DNS_INSTANCE_FROM_THIS_PROTOCOL4(a) \ + CR (a, DNS_INSTANCE, Dns4, DNS_INSTANCE_SIGNATURE) + +#define DNS_INSTANCE_FROM_THIS_PROTOCOL6(a) \ + CR (a, DNS_INSTANCE, Dns6, DNS_INSTANCE_SIGNATURE) + + +/** + Destroy the DNS instance and recycle the resources. + + @param[in] Instance The pointer to the DNS instance. + +**/ +VOID +DnsDestroyInstance ( + IN DNS_INSTANCE *Instance + ); + +/** + Create the DNS instance and initialize it. + + @param[in] Service The pointer to the DNS service. + @param[out] Instance The pointer to the DNS instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The DNS instance is created. + +**/ +EFI_STATUS +DnsCreateInstance ( + IN DNS_SERVICE *Service, + OUT DNS_INSTANCE **Instance + ); + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +DnsDestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ); + +/** + Config a NULL UDP that is used to keep the connection between UDP and DNS. + + Just leave the Udp child unconfigured. When UDP is unloaded, + DNS will be informed with DriverBinding Stop. + + @param UdpIo The UDP_IO to configure + @param Context The opaque parameter to the callback + + @retval EFI_SUCCESS It always return EFI_SUCCESS directly. + +**/ +EFI_STATUS +EFIAPI +DnsConfigNullUdp ( + IN UDP_IO *UdpIo, + IN VOID *Context + ); + +/** + Release all the resource used the DNS service binding instance. + + @param DnsSb The Dns service binding instance. + +**/ +VOID +DnsDestroyService ( + IN DNS_SERVICE *DnsSb + ); + +/** + Create then initialize a Dns service binding instance. + + @param Controller The controller to install the DNS service + binding on + @param Image The driver binding image of the DNS driver + @param IpVersion IpVersion for this service + @param Service The variable to receive the created service + binding instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to create the instance. + @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep + connection with UDP. + @retval EFI_SUCCESS The service instance is created for the + controller. + +**/ +EFI_STATUS +DnsCreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image, + IN UINT8 IpVersion, + OUT DNS_SERVICE **Service + ); + +/** + Unloads an image. + + @param ImageHandle Handle that identifies the image to be unloaded. + + @retval EFI_SUCCESS The image has been unloaded. + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. + +**/ +EFI_STATUS +EFIAPI +DnsUnload ( + IN EFI_HANDLE ImageHandle + ); + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @param ImageHandle The firmware allocated handle for the UEFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. +**/ +EFI_STATUS +EFIAPI +DnsDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +Dns4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +Dns4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Dns4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +Dns6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +Dns6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Dns6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +Dns4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Dns4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child + @retval other The child handle was not created + +**/ +EFI_STATUS +EFIAPI +Dns6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Dns6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + + +#endif diff --git a/Core/NetworkPkg/DnsDxe/DnsDxe.inf b/Core/NetworkPkg/DnsDxe/DnsDxe.inf new file mode 100644 index 0000000000..0d1efd8e01 --- /dev/null +++ b/Core/NetworkPkg/DnsDxe/DnsDxe.inf @@ -0,0 +1,78 @@ +## @file +# Implementation of EFI_DNS4_PROTOCOL and EFI_DNS6_PROTOCOL interfaces. +# +# Copyright (c) 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DnsDxe + FILE_GUID = b219e140-dffc-11e3-b956-0022681e6906 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DnsDriverEntryPoint + UNLOAD_IMAGE = DnsUnload + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[Sources] + ComponentName.c + DnsDriver.h + DnsDriver.c + DnsImpl.h + DnsImpl.c + DnsProtocol.c + DnsDhcp.h + DnsDhcp.c + + +[LibraryClasses] + BaseLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + BaseMemoryLib + MemoryAllocationLib + NetLib + DebugLib + DpcLib + PrintLib + UdpIoLib + + +[Protocols] + gEfiDns4ServiceBindingProtocolGuid ## BY_START + gEfiDns4ProtocolGuid ## BY_START + gEfiUdp4ServiceBindingProtocolGuid ## TO_START + gEfiUdp4ProtocolGuid ## TO_START + gEfiDhcp4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDhcp4ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp4ProtocolGuid ## SOMETIMES_CONSUMES + gEfiManagedNetworkServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiManagedNetworkProtocolGuid ## SOMETIMES_CONSUMES + + gEfiDns6ServiceBindingProtocolGuid ## BY_START + gEfiDns6ProtocolGuid ## BY_START + gEfiUdp6ServiceBindingProtocolGuid ## TO_START + gEfiUdp6ProtocolGuid ## TO_START + gEfiDhcp6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDhcp6ProtocolGuid ## SOMETIMES_CONSUMES + + +[Guids] + diff --git a/Core/NetworkPkg/DnsDxe/DnsDxe.uni b/Core/NetworkPkg/DnsDxe/DnsDxe.uni new file mode 100644 index 0000000000..981d7561e6 Binary files /dev/null and b/Core/NetworkPkg/DnsDxe/DnsDxe.uni differ diff --git a/Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni b/Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni new file mode 100644 index 0000000000..70d583d7d2 Binary files /dev/null and b/Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni differ diff --git a/Core/NetworkPkg/DnsDxe/DnsImpl.c b/Core/NetworkPkg/DnsDxe/DnsImpl.c new file mode 100644 index 0000000000..71daccea80 --- /dev/null +++ b/Core/NetworkPkg/DnsDxe/DnsImpl.c @@ -0,0 +1,2077 @@ +/** @file +DnsDxe support functions implementation. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DnsImpl.h" + +/** + Remove TokenEntry from TokenMap. + + @param[in] TokenMap All DNSv4 Token entrys. + @param[in] TokenEntry TokenEntry need to be removed. + + @retval EFI_SUCCESS Remove TokenEntry from TokenMap sucessfully. + @retval EFI_NOT_FOUND TokenEntry is not found in TokenMap. + +**/ +EFI_STATUS +Dns4RemoveTokenEntry ( + IN NET_MAP *TokenMap, + IN DNS4_TOKEN_ENTRY *TokenEntry + ) +{ + NET_MAP_ITEM *Item; + + // + // Find the TokenEntry first. + // + Item = NetMapFindKey (TokenMap, (VOID *) TokenEntry); + + if (Item != NULL) { + // + // Remove the TokenEntry if it's found in the map. + // + NetMapRemoveItem (TokenMap, Item, NULL); + + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +/** + Remove TokenEntry from TokenMap. + + @param[in] TokenMap All DNSv6 Token entrys. + @param[in] TokenEntry TokenEntry need to be removed. + + @retval EFI_SUCCESS Remove TokenEntry from TokenMap sucessfully. + @retval EFI_NOT_FOUND TokenEntry is not found in TokenMap. + +**/ +EFI_STATUS +Dns6RemoveTokenEntry ( + IN NET_MAP *TokenMap, + IN DNS6_TOKEN_ENTRY *TokenEntry + ) +{ + NET_MAP_ITEM *Item; + + // + // Find the TokenEntry first. + // + Item = NetMapFindKey (TokenMap, (VOID *) TokenEntry); + + if (Item != NULL) { + // + // Remove the TokenEntry if it's found in the map. + // + NetMapRemoveItem (TokenMap, Item, NULL); + + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +/** + This function cancle the token specified by Arg in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled. If NULL, all + the tokens in this Map will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token + is not the same as that in the Item, if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Dns4CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + DNS4_TOKEN_ENTRY *TokenEntry; + NET_BUF *Packet; + UDP_IO *UdpIo; + + if ((Arg != NULL) && (Item->Key != Arg)) { + return EFI_SUCCESS; + } + + if (Item->Value != NULL) { + // + // If the TokenEntry is a transmit TokenEntry, the corresponding Packet is recorded in + // Item->Value. + // + Packet = (NET_BUF *) (Item->Value); + UdpIo = (UDP_IO *) (*((UINTN *) &Packet->ProtoData[0])); + + UdpIoCancelSentDatagram (UdpIo, Packet); + } + + // + // Remove TokenEntry from Dns4TxTokens. + // + TokenEntry = (DNS4_TOKEN_ENTRY *) Item->Key; + if (Dns4RemoveTokenEntry (Map, TokenEntry) == EFI_SUCCESS) { + TokenEntry->Token->Status = EFI_ABORTED; + gBS->SignalEvent (TokenEntry->Token->Event); + DispatchDpc (); + } + + if (Arg != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + This function cancle the token specified by Arg in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled. If NULL, all + the tokens in this Map will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token + is not the same as that in the Item, if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Dns6CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + DNS6_TOKEN_ENTRY *TokenEntry; + NET_BUF *Packet; + UDP_IO *UdpIo; + + if ((Arg != NULL) && (Item->Key != Arg)) { + return EFI_SUCCESS; + } + + if (Item->Value != NULL) { + // + // If the TokenEntry is a transmit TokenEntry, the corresponding Packet is recorded in + // Item->Value. + // + Packet = (NET_BUF *) (Item->Value); + UdpIo = (UDP_IO *) (*((UINTN *) &Packet->ProtoData[0])); + + UdpIoCancelSentDatagram (UdpIo, Packet); + } + + // + // Remove TokenEntry from Dns6TxTokens. + // + TokenEntry = (DNS6_TOKEN_ENTRY *) Item->Key; + if (Dns6RemoveTokenEntry (Map, TokenEntry) == EFI_SUCCESS) { + TokenEntry->Token->Status = EFI_ABORTED; + gBS->SignalEvent (TokenEntry->Token->Event); + DispatchDpc (); + } + + if (Arg != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Get the TokenEntry from the TokensMap. + + @param[in] TokensMap All DNSv4 Token entrys + @param[in] Token Pointer to the token to be get. + @param[out] TokenEntry Pointer to TokenEntry corresponding Token. + + @retval EFI_SUCCESS Get the TokenEntry from the TokensMap sucessfully. + @retval EFI_NOT_FOUND TokenEntry is not found in TokenMap. + +**/ +EFI_STATUS +EFIAPI +GetDns4TokenEntry ( + IN NET_MAP *TokensMap, + IN EFI_DNS4_COMPLETION_TOKEN *Token, + OUT DNS4_TOKEN_ENTRY **TokenEntry + ) +{ + LIST_ENTRY *Entry; + + NET_MAP_ITEM *Item; + + NET_LIST_FOR_EACH (Entry, &TokensMap->Used) { + Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); + *TokenEntry = (DNS4_TOKEN_ENTRY *) (Item->Key); + if ((*TokenEntry)->Token == Token) { + return EFI_SUCCESS; + } + } + + *TokenEntry = NULL; + + return EFI_NOT_FOUND; +} + +/** + Get the TokenEntry from the TokensMap. + + @param[in] TokensMap All DNSv6 Token entrys + @param[in] Token Pointer to the token to be get. + @param[out] TokenEntry Pointer to TokenEntry corresponding Token. + + @retval EFI_SUCCESS Get the TokenEntry from the TokensMap sucessfully. + @retval EFI_NOT_FOUND TokenEntry is not found in TokenMap. + +**/ +EFI_STATUS +EFIAPI +GetDns6TokenEntry ( + IN NET_MAP *TokensMap, + IN EFI_DNS6_COMPLETION_TOKEN *Token, + OUT DNS6_TOKEN_ENTRY **TokenEntry + ) +{ + LIST_ENTRY *Entry; + + NET_MAP_ITEM *Item; + + NET_LIST_FOR_EACH (Entry, &TokensMap->Used) { + Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); + *TokenEntry = (DNS6_TOKEN_ENTRY *) (Item->Key); + if ((*TokenEntry)->Token == Token) { + return EFI_SUCCESS; + } + } + + *TokenEntry =NULL; + + return EFI_NOT_FOUND; +} + +/** + Cancel DNS4 tokens from the DNS4 instance. + + @param[in] Instance Pointer to the DNS instance context data. + @param[in] Token Pointer to the token to be canceled. If NULL, all + tokens in this instance will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Dns4InstanceCancelToken ( + IN DNS_INSTANCE *Instance, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + DNS4_TOKEN_ENTRY *TokenEntry; + + TokenEntry = NULL; + + if(Token != NULL ) { + Status = GetDns4TokenEntry (&Instance->Dns4TxTokens, Token, &TokenEntry); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + TokenEntry = NULL; + } + + // + // Cancel this TokenEntry from the Dns4TxTokens map. + // + Status = NetMapIterate (&Instance->Dns4TxTokens, Dns4CancelTokens, TokenEntry); + + if ((TokenEntry != NULL) && (Status == EFI_ABORTED)) { + // + // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from + // the Dns4TxTokens and returns success. + // + if (NetMapIsEmpty (&Instance->Dns4TxTokens)) { + Instance->UdpIo->Protocol.Udp4->Cancel (Instance->UdpIo->Protocol.Udp4, &Instance->UdpIo->RecvRequest->Token.Udp4); + } + return EFI_SUCCESS; + } + + ASSERT ((TokenEntry != NULL) || (0 == NetMapGetCount (&Instance->Dns4TxTokens))); + + if (NetMapIsEmpty (&Instance->Dns4TxTokens)) { + Instance->UdpIo->Protocol.Udp4->Cancel (Instance->UdpIo->Protocol.Udp4, &Instance->UdpIo->RecvRequest->Token.Udp4); + } + + return EFI_SUCCESS; +} + +/** + Cancel DNS6 tokens from the DNS6 instance. + + @param[in] Instance Pointer to the DNS instance context data. + @param[in] Token Pointer to the token to be canceled. If NULL, all + tokens in this instance will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Dns6InstanceCancelToken ( + IN DNS_INSTANCE *Instance, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + DNS6_TOKEN_ENTRY *TokenEntry; + + TokenEntry = NULL; + + if(Token != NULL ) { + Status = GetDns6TokenEntry (&Instance->Dns6TxTokens, Token, &TokenEntry); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + TokenEntry = NULL; + } + + // + // Cancel this TokenEntry from the Dns6TxTokens map. + // + Status = NetMapIterate (&Instance->Dns6TxTokens, Dns6CancelTokens, TokenEntry); + + if ((TokenEntry != NULL) && (Status == EFI_ABORTED)) { + // + // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from + // the Dns6TxTokens and returns success. + // + if (NetMapIsEmpty (&Instance->Dns6TxTokens)) { + Instance->UdpIo->Protocol.Udp6->Cancel (Instance->UdpIo->Protocol.Udp6, &Instance->UdpIo->RecvRequest->Token.Udp6); + } + return EFI_SUCCESS; + } + + ASSERT ((TokenEntry != NULL) || (0 == NetMapGetCount (&Instance->Dns6TxTokens))); + + if (NetMapIsEmpty (&Instance->Dns6TxTokens)) { + Instance->UdpIo->Protocol.Udp6->Cancel (Instance->UdpIo->Protocol.Udp6, &Instance->UdpIo->RecvRequest->Token.Udp6); + } + + return EFI_SUCCESS; +} + +/** + Free the resource related to the configure parameters. + + @param Config The DNS configure data + +**/ +VOID +Dns4CleanConfigure ( + IN OUT EFI_DNS4_CONFIG_DATA *Config + ) +{ + if (Config->DnsServerList != NULL) { + FreePool (Config->DnsServerList); + } + + ZeroMem (Config, sizeof (EFI_DNS4_CONFIG_DATA)); +} + +/** + Free the resource related to the configure parameters. + + @param Config The DNS configure data + +**/ +VOID +Dns6CleanConfigure ( + IN OUT EFI_DNS6_CONFIG_DATA *Config + ) +{ + if (Config->DnsServerList != NULL) { + FreePool (Config->DnsServerList); + } + + ZeroMem (Config, sizeof (EFI_DNS6_CONFIG_DATA)); +} + +/** + Allocate memory for configure parameter such as timeout value for Dst, + then copy the configure parameter from Src to Dst. + + @param[out] Dst The destination DHCP configure data. + @param[in] Src The source DHCP configure data. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_SUCCESS The configure is copied. + +**/ +EFI_STATUS +Dns4CopyConfigure ( + OUT EFI_DNS4_CONFIG_DATA *Dst, + IN EFI_DNS4_CONFIG_DATA *Src + ) +{ + UINTN Len; + UINT32 Index; + + CopyMem (Dst, Src, sizeof (*Dst)); + Dst->DnsServerList = NULL; + + // + // Allocate a memory then copy DnsServerList to it + // + if (Src->DnsServerList != NULL) { + Len = Src->DnsServerListCount * sizeof (EFI_IPv4_ADDRESS); + Dst->DnsServerList = AllocatePool (Len); + if (Dst->DnsServerList == NULL) { + Dns4CleanConfigure (Dst); + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < Src->DnsServerListCount; Index++) { + CopyMem (&Dst->DnsServerList[Index], &Src->DnsServerList[Index], sizeof (EFI_IPv4_ADDRESS)); + } + } + + return EFI_SUCCESS; +} + +/** + Allocate memory for configure parameter such as timeout value for Dst, + then copy the configure parameter from Src to Dst. + + @param[out] Dst The destination DHCP configure data. + @param[in] Src The source DHCP configure data. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_SUCCESS The configure is copied. + +**/ +EFI_STATUS +Dns6CopyConfigure ( + OUT EFI_DNS6_CONFIG_DATA *Dst, + IN EFI_DNS6_CONFIG_DATA *Src + ) +{ + UINTN Len; + UINT32 Index; + + CopyMem (Dst, Src, sizeof (*Dst)); + Dst->DnsServerList = NULL; + + // + // Allocate a memory then copy DnsServerList to it + // + if (Src->DnsServerList != NULL) { + Len = Src->DnsServerCount * sizeof (EFI_IPv6_ADDRESS); + Dst->DnsServerList = AllocatePool (Len); + if (Dst->DnsServerList == NULL) { + Dns6CleanConfigure (Dst); + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0; Index < Src->DnsServerCount; Index++) { + CopyMem (&Dst->DnsServerList[Index], &Src->DnsServerList[Index], sizeof (EFI_IPv6_ADDRESS)); + } + } + + return EFI_SUCCESS; +} + +/** + Callback of Dns packet. Does nothing. + + @param Arg The context. + +**/ +VOID +EFIAPI +DnsDummyExtFree ( + IN VOID *Arg + ) +{ +} + +/** + Poll the UDP to get the IP4 default address, which may be retrieved + by DHCP. + + The default time out value is 5 seconds. If IP has retrieved the default address, + the UDP is reconfigured. + + @param Instance The DNS instance + @param UdpIo The UDP_IO to poll + @param UdpCfgData The UDP configure data to reconfigure the UDP_IO + + @retval TRUE The default address is retrieved and UDP is reconfigured. + @retval FALSE Some error occured. + +**/ +BOOLEAN +Dns4GetMapping ( + IN DNS_INSTANCE *Instance, + IN UDP_IO *UdpIo, + IN EFI_UDP4_CONFIG_DATA *UdpCfgData + ) +{ + DNS_SERVICE *Service; + EFI_IP4_MODE_DATA Ip4Mode; + EFI_UDP4_PROTOCOL *Udp; + EFI_STATUS Status; + + ASSERT (Instance->Dns4CfgData.UseDefaultSetting); + + Service = Instance->Service; + Udp = UdpIo->Protocol.Udp4; + + Status = gBS->SetTimer ( + Service->TimerToGetMap, + TimerRelative, + DNS_TIME_TO_GETMAP * TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + while (!EFI_ERROR (gBS->CheckEvent (Service->TimerToGetMap))) { + Udp->Poll (Udp); + + if (!EFI_ERROR (Udp->GetModeData (Udp, NULL, &Ip4Mode, NULL, NULL)) && + Ip4Mode.IsConfigured) { + + Udp->Configure (Udp, NULL); + return (BOOLEAN) (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS); + } + } + + return FALSE; +} + +/** + Configure the opened Udp6 instance until the corresponding Ip6 instance + has been configured. + + @param Instance The DNS instance + @param UdpIo The UDP_IO to poll + @param UdpCfgData The UDP configure data to reconfigure the UDP_IO + + @retval TRUE Configure the Udp6 instance successfully. + @retval FALSE Some error occured. + +**/ +BOOLEAN +Dns6GetMapping ( + IN DNS_INSTANCE *Instance, + IN UDP_IO *UdpIo, + IN EFI_UDP6_CONFIG_DATA *UdpCfgData + ) +{ + DNS_SERVICE *Service; + EFI_IP6_MODE_DATA Ip6Mode; + EFI_UDP6_PROTOCOL *Udp; + EFI_STATUS Status; + + Service = Instance->Service; + Udp = UdpIo->Protocol.Udp6; + + Status = gBS->SetTimer ( + Service->TimerToGetMap, + TimerRelative, + DNS_TIME_TO_GETMAP * TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + while (!EFI_ERROR (gBS->CheckEvent (Service->TimerToGetMap))) { + Udp->Poll (Udp); + + if (!EFI_ERROR (Udp->GetModeData (Udp, NULL, &Ip6Mode, NULL, NULL)) && + Ip6Mode.IsConfigured) { + + Udp->Configure (Udp, NULL); + return (BOOLEAN) (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS); + } + } + + return FALSE; +} + +/** + Configure the UDP. + + @param Instance The DNS session + @param UdpIo The UDP_IO instance + + @retval EFI_SUCCESS The UDP is successfully configured for the + session. + +**/ +EFI_STATUS +Dns4ConfigUdp ( + IN DNS_INSTANCE *Instance, + IN UDP_IO *UdpIo + ) +{ + EFI_DNS4_CONFIG_DATA *Config; + EFI_UDP4_CONFIG_DATA UdpConfig; + EFI_STATUS Status; + + Config = &Instance->Dns4CfgData; + + UdpConfig.AcceptBroadcast = FALSE; + UdpConfig.AcceptPromiscuous = FALSE; + UdpConfig.AcceptAnyPort = FALSE; + UdpConfig.AllowDuplicatePort = FALSE; + UdpConfig.TypeOfService = 0; + UdpConfig.TimeToLive = 128; + UdpConfig.DoNotFragment = FALSE; + UdpConfig.ReceiveTimeout = 0; + UdpConfig.TransmitTimeout = 0; + UdpConfig.UseDefaultAddress = Config->UseDefaultSetting; + UdpConfig.SubnetMask = Config->SubnetMask; + UdpConfig.StationPort = Config->LocalPort; + UdpConfig.RemotePort = DNS_SERVER_PORT; + + CopyMem (&UdpConfig.StationAddress, &Config->StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&UdpConfig.RemoteAddress, &Instance->SessionDnsServer.v4, sizeof (EFI_IPv4_ADDRESS)); + + Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfig); + + if ((Status == EFI_NO_MAPPING) && Dns4GetMapping (Instance, UdpIo, &UdpConfig)) { + return EFI_SUCCESS; + } + + return Status; +} + +/** + Configure the UDP. + + @param Instance The DNS session + @param UdpIo The UDP_IO instance + + @retval EFI_SUCCESS The UDP is successfully configured for the + session. + +**/ +EFI_STATUS +Dns6ConfigUdp ( + IN DNS_INSTANCE *Instance, + IN UDP_IO *UdpIo + ) +{ + EFI_DNS6_CONFIG_DATA *Config; + EFI_UDP6_CONFIG_DATA UdpConfig; + EFI_STATUS Status; + + Config = &Instance->Dns6CfgData; + + UdpConfig.AcceptPromiscuous = FALSE; + UdpConfig.AcceptAnyPort = FALSE; + UdpConfig.AllowDuplicatePort = FALSE; + UdpConfig.TrafficClass = 0; + UdpConfig.HopLimit = 128; + UdpConfig.ReceiveTimeout = 0; + UdpConfig.TransmitTimeout = 0; + UdpConfig.StationPort = Config->LocalPort; + UdpConfig.RemotePort = DNS_SERVER_PORT; + CopyMem (&UdpConfig.StationAddress, &Config->StationIp, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&UdpConfig.RemoteAddress, &Instance->SessionDnsServer.v6, sizeof (EFI_IPv6_ADDRESS)); + + Status = UdpIo->Protocol.Udp6->Configure (UdpIo->Protocol.Udp6, &UdpConfig); + + if ((Status == EFI_NO_MAPPING) && Dns6GetMapping (Instance, UdpIo, &UdpConfig)) { + return EFI_SUCCESS; + } + + return Status; +} + +/** + Update Dns4 cache to shared list of caches of all DNSv4 instances. + + @param Dns4CacheList All Dns4 cache list. + @param DeleteFlag If FALSE, this function is to add one entry to the DNS Cache. + If TRUE, this function will delete matching DNS Cache entry. + @param Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter. + If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists. + @param DnsCacheEntry Entry Pointer to DNS Cache entry. + + @retval EFI_SUCCESS Update Dns4 cache successfully. + @retval Others Failed to update Dns4 cache. + +**/ +EFI_STATUS +EFIAPI +UpdateDns4Cache ( + IN LIST_ENTRY *Dns4CacheList, + IN BOOLEAN DeleteFlag, + IN BOOLEAN Override, + IN EFI_DNS4_CACHE_ENTRY DnsCacheEntry + ) +{ + DNS4_CACHE *NewDnsCache; + DNS4_CACHE *Item; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + + NewDnsCache = NULL; + Item = NULL; + + // + // Search the database for the matching EFI_DNS_CACHE_ENTRY + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, Dns4CacheList) { + Item = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink); + if (StrCmp (DnsCacheEntry.HostName, Item->DnsCache.HostName) == 0 && \ + CompareMem (DnsCacheEntry.IpAddress, Item->DnsCache.IpAddress, sizeof (EFI_IPv4_ADDRESS)) == 0) { + // + // This is the Dns cache entry + // + if (DeleteFlag) { + // + // Delete matching DNS Cache entry + // + RemoveEntryList (&Item->AllCacheLink); + + return EFI_SUCCESS; + } else if (Override) { + // + // Update this one + // + Item->DnsCache.Timeout = DnsCacheEntry.Timeout; + + return EFI_SUCCESS; + }else { + return EFI_ACCESS_DENIED; + } + } + } + + // + // Add new one + // + NewDnsCache = AllocatePool (sizeof (DNS4_CACHE)); + if (NewDnsCache == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (&NewDnsCache->AllCacheLink); + + NewDnsCache->DnsCache.HostName = AllocatePool (StrSize (DnsCacheEntry.HostName)); + if (NewDnsCache->DnsCache.HostName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (NewDnsCache->DnsCache.HostName, DnsCacheEntry.HostName, StrSize (DnsCacheEntry.HostName)); + + NewDnsCache->DnsCache.IpAddress = AllocatePool (sizeof (EFI_IPv4_ADDRESS)); + if (NewDnsCache->DnsCache.IpAddress == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (NewDnsCache->DnsCache.IpAddress, DnsCacheEntry.IpAddress, sizeof (EFI_IPv4_ADDRESS)); + + NewDnsCache->DnsCache.Timeout = DnsCacheEntry.Timeout; + + InsertTailList (Dns4CacheList, &NewDnsCache->AllCacheLink); + + return EFI_SUCCESS; +} + +/** + Update Dns6 cache to shared list of caches of all DNSv6 instances. + + @param Dns6CacheList All Dns6 cache list. + @param DeleteFlag If FALSE, this function is to add one entry to the DNS Cache. + If TRUE, this function will delete matching DNS Cache entry. + @param Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter. + If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists. + @param DnsCacheEntry Entry Pointer to DNS Cache entry. + + @retval EFI_SUCCESS Update Dns6 cache successfully. + @retval Others Failed to update Dns6 cache. +**/ +EFI_STATUS +EFIAPI +UpdateDns6Cache ( + IN LIST_ENTRY *Dns6CacheList, + IN BOOLEAN DeleteFlag, + IN BOOLEAN Override, + IN EFI_DNS6_CACHE_ENTRY DnsCacheEntry + ) +{ + DNS6_CACHE *NewDnsCache; + DNS6_CACHE *Item; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + + NewDnsCache = NULL; + Item = NULL; + + // + // Search the database for the matching EFI_DNS_CACHE_ENTRY + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, Dns6CacheList) { + Item = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink); + if (StrCmp (DnsCacheEntry.HostName, Item->DnsCache.HostName) == 0 && \ + CompareMem (DnsCacheEntry.IpAddress, Item->DnsCache.IpAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) { + // + // This is the Dns cache entry + // + if (DeleteFlag) { + // + // Delete matching DNS Cache entry + // + RemoveEntryList (&Item->AllCacheLink); + + return EFI_SUCCESS; + } else if (Override) { + // + // Update this one + // + Item->DnsCache.Timeout = DnsCacheEntry.Timeout; + + return EFI_SUCCESS; + }else { + return EFI_ACCESS_DENIED; + } + } + } + + // + // Add new one + // + NewDnsCache = AllocatePool (sizeof (DNS6_CACHE)); + if (NewDnsCache == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (&NewDnsCache->AllCacheLink); + + NewDnsCache->DnsCache.HostName = AllocatePool (StrSize (DnsCacheEntry.HostName)); + if (NewDnsCache->DnsCache.HostName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (NewDnsCache->DnsCache.HostName, DnsCacheEntry.HostName, StrSize (DnsCacheEntry.HostName)); + + NewDnsCache->DnsCache.IpAddress = AllocatePool (sizeof (EFI_IPv6_ADDRESS)); + if (NewDnsCache->DnsCache.IpAddress == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (NewDnsCache->DnsCache.IpAddress, DnsCacheEntry.IpAddress, sizeof (EFI_IPv6_ADDRESS)); + + NewDnsCache->DnsCache.Timeout = DnsCacheEntry.Timeout; + + InsertTailList (Dns6CacheList, &NewDnsCache->AllCacheLink); + + return EFI_SUCCESS; +} + +/** + Add Dns4 ServerIp to common list of addresses of all configured DNSv4 server. + + @param Dns4ServerList Common list of addresses of all configured DNSv4 server. + @param ServerIp DNS server Ip. + + @retval EFI_SUCCESS Add Dns4 ServerIp to common list successfully. + @retval Others Failed to add Dns4 ServerIp to common list. + +**/ +EFI_STATUS +EFIAPI +AddDns4ServerIp ( + IN LIST_ENTRY *Dns4ServerList, + IN EFI_IPv4_ADDRESS ServerIp + ) +{ + DNS4_SERVER_IP *NewServerIp; + DNS4_SERVER_IP *Item; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + + NewServerIp = NULL; + Item = NULL; + + // + // Search the database for the matching ServerIp + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, Dns4ServerList) { + Item = NET_LIST_USER_STRUCT (Entry, DNS4_SERVER_IP, AllServerLink); + if (CompareMem (&Item->Dns4ServerIp, &ServerIp, sizeof (EFI_IPv4_ADDRESS)) == 0) { + // + // Already done. + // + return EFI_SUCCESS; + } + } + + // + // Add new one + // + NewServerIp = AllocatePool (sizeof (DNS4_SERVER_IP)); + if (NewServerIp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (&NewServerIp->AllServerLink); + + CopyMem (&NewServerIp->Dns4ServerIp, &ServerIp, sizeof (EFI_IPv4_ADDRESS)); + + InsertTailList (Dns4ServerList, &NewServerIp->AllServerLink); + + return EFI_SUCCESS; +} + +/** + Add Dns6 ServerIp to common list of addresses of all configured DNSv6 server. + + @param Dns6ServerList Common list of addresses of all configured DNSv6 server. + @param ServerIp DNS server Ip. + + @retval EFI_SUCCESS Add Dns6 ServerIp to common list successfully. + @retval Others Failed to add Dns6 ServerIp to common list. + +**/ +EFI_STATUS +EFIAPI +AddDns6ServerIp ( + IN LIST_ENTRY *Dns6ServerList, + IN EFI_IPv6_ADDRESS ServerIp + ) +{ + DNS6_SERVER_IP *NewServerIp; + DNS6_SERVER_IP *Item; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + + NewServerIp = NULL; + Item = NULL; + + // + // Search the database for the matching ServerIp + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, Dns6ServerList) { + Item = NET_LIST_USER_STRUCT (Entry, DNS6_SERVER_IP, AllServerLink); + if (CompareMem (&Item->Dns6ServerIp, &ServerIp, sizeof (EFI_IPv6_ADDRESS)) == 0) { + // + // Already done. + // + return EFI_SUCCESS; + } + } + + // + // Add new one + // + NewServerIp = AllocatePool (sizeof (DNS6_SERVER_IP)); + if (NewServerIp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (&NewServerIp->AllServerLink); + + CopyMem (&NewServerIp->Dns6ServerIp, &ServerIp, sizeof (EFI_IPv6_ADDRESS)); + + InsertTailList (Dns6ServerList, &NewServerIp->AllServerLink); + + return EFI_SUCCESS; +} + +/** + Fill QName for IP querying. QName is a domain name represented as + a sequence of labels, where each label consists of a length octet + followed by that number of octets. The domain name terminates with + the zero length octet for the null label of the root. Caller should + take responsibility to the buffer in QName. + + @param HostName Queried HostName + + @retval NULL Failed to fill QName. + @return QName filled successfully. + +**/ +CHAR8 * +EFIAPI +DnsFillinQNameForQueryIp ( + IN CHAR16 *HostName + ) +{ + CHAR8 *QueryName; + CHAR8 *Header; + CHAR8 *Tail; + UINTN Len; + UINTN Index; + + QueryName = NULL; + Header = NULL; + Tail = NULL; + + QueryName = AllocateZeroPool (DNS_DEFAULT_BLKSIZE); + if (QueryName == NULL) { + return NULL; + } + + Header = QueryName; + Tail = Header + 1; + Len = 0; + for (Index = 0; HostName[Index] != 0; Index++) { + *Tail = (CHAR8) HostName[Index]; + if (*Tail == '.') { + *Header = (CHAR8) Len; + Header = Tail; + Tail ++; + Len = 0; + } else { + Tail++; + Len++; + } + } + *Header = (CHAR8) Len; + *Tail = 0; + + return QueryName; +} + +/** + Find out whether the response is valid or invalid. + + @param TokensMap All DNS transmittal Tokens entry. + @param Identification Identification for queried packet. + @param Type Type for queried packet. + @param Item Return corresponding Token entry. + + @retval TRUE The response is valid. + @retval FALSE The response is invalid. + +**/ +BOOLEAN +IsValidDnsResponse ( + IN NET_MAP *TokensMap, + IN UINT16 Identification, + IN UINT16 Type, + OUT NET_MAP_ITEM **Item + ) +{ + LIST_ENTRY *Entry; + + NET_BUF *Packet; + UINT8 *TxString; + DNS_HEADER *DnsHeader; + CHAR8 *QueryName; + DNS_QUERY_SECTION *QuerySection; + + NET_LIST_FOR_EACH (Entry, &TokensMap->Used) { + *Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); + Packet = (NET_BUF *) ((*Item)->Value); + if (Packet == NULL){ + + continue; + } else { + TxString = NetbufGetByte (Packet, 0, NULL); + ASSERT (TxString != NULL); + DnsHeader = (DNS_HEADER *) TxString; + QueryName = (CHAR8 *) (TxString + sizeof (*DnsHeader)); + QuerySection = (DNS_QUERY_SECTION *) (QueryName + AsciiStrLen (QueryName) + 1); + + DnsHeader->Identification = NTOHS (DnsHeader->Identification); + QuerySection->Type = NTOHS (QuerySection->Type); + + if (DnsHeader->Identification == Identification && QuerySection->Type == Type) { + return TRUE; + } + } + } + + *Item =NULL; + + return FALSE; +} + +/** + Parse Dns Response. + + @param Instance The DNS instance + @param RxString Received buffer. + @param Completed Flag to indicate that Dns response is valid. + + @retval EFI_SUCCESS Parse Dns Response successfully. + @retval Others Failed to parse Dns Response. + +**/ +EFI_STATUS +ParseDnsResponse ( + IN OUT DNS_INSTANCE *Instance, + IN UINT8 *RxString, + OUT BOOLEAN *Completed + ) +{ + DNS_HEADER *DnsHeader; + + CHAR8 *QueryName; + DNS_QUERY_SECTION *QuerySection; + + CHAR8 *AnswerName; + DNS_ANSWER_SECTION *AnswerSection; + UINT8 *AnswerData; + + NET_MAP_ITEM *Item; + DNS4_TOKEN_ENTRY *Dns4TokenEntry; + DNS6_TOKEN_ENTRY *Dns6TokenEntry; + + UINT32 IpCount; + UINT32 RRCount; + UINT32 AnswerSectionNum; + + EFI_IPv4_ADDRESS *HostAddr4; + EFI_IPv6_ADDRESS *HostAddr6; + + EFI_DNS4_CACHE_ENTRY *Dns4CacheEntry; + EFI_DNS6_CACHE_ENTRY *Dns6CacheEntry; + + DNS_RESOURCE_RECORD *Dns4RR; + DNS6_RESOURCE_RECORD *Dns6RR; + + EFI_STATUS Status; + + EFI_TPL OldTpl; + + Item = NULL; + Dns4TokenEntry = NULL; + Dns6TokenEntry = NULL; + + IpCount = 0; + RRCount = 0; + AnswerSectionNum = 0; + + HostAddr4 = NULL; + HostAddr6 = NULL; + + Dns4CacheEntry = NULL; + Dns6CacheEntry = NULL; + + Dns4RR = NULL; + Dns6RR = NULL; + + *Completed = TRUE; + Status = EFI_SUCCESS; + + // + // Get header + // + DnsHeader = (DNS_HEADER *) RxString; + + DnsHeader->Identification = NTOHS (DnsHeader->Identification); + DnsHeader->Flags.Uint16 = NTOHS (DnsHeader->Flags.Uint16); + DnsHeader->QuestionsNum = NTOHS (DnsHeader->QuestionsNum); + DnsHeader->AnswersNum = NTOHS (DnsHeader->AnswersNum); + DnsHeader->AuthorityNum = NTOHS (DnsHeader->AuthorityNum); + DnsHeader->AditionalNum = NTOHS (DnsHeader->AditionalNum); + + // + // Get Query name + // + QueryName = (CHAR8 *) (RxString + sizeof (*DnsHeader)); + + // + // Get query section + // + QuerySection = (DNS_QUERY_SECTION *) (QueryName + AsciiStrLen (QueryName) + 1); + QuerySection->Type = NTOHS (QuerySection->Type); + QuerySection->Class = NTOHS (QuerySection->Class); + + // + // Get Answer name + // + AnswerName = (CHAR8 *) QuerySection + sizeof (*QuerySection); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Check DnsResponse Validity, if so, also get a valid NET_MAP_ITEM. + // + if (Instance->Service->IpVersion == IP_VERSION_4) { + if (!IsValidDnsResponse (&Instance->Dns4TxTokens, DnsHeader->Identification, QuerySection->Type, &Item)) { + *Completed = FALSE; + Status = EFI_ABORTED; + goto ON_EXIT; + } + ASSERT (Item != NULL); + Dns4TokenEntry = (DNS4_TOKEN_ENTRY *) (Item->Key); + } else { + if (!IsValidDnsResponse (&Instance->Dns6TxTokens, DnsHeader->Identification, QuerySection->Type, &Item)) { + *Completed = FALSE; + Status = EFI_ABORTED; + goto ON_EXIT; + } + ASSERT (Item != NULL); + Dns6TokenEntry = (DNS6_TOKEN_ENTRY *) (Item->Key); + } + + // + // Continue Check Some Errors. + // + if (DnsHeader->Flags.Bits.RCode != DNS_FLAGS_RCODE_NO_ERROR || DnsHeader->AnswersNum < 1 || \ + DnsHeader->Flags.Bits.QR != DNS_FLAGS_QR_RESPONSE || QuerySection->Class != DNS_CLASS_INET) { + Status = EFI_ABORTED; + goto ON_EXIT; + } + + // + // Free the sending packet. + // + if (Item->Value != NULL) { + NetbufFree ((NET_BUF *) (Item->Value)); + } + + // + // Do some buffer allocations. + // + if (Instance->Service->IpVersion == IP_VERSION_4) { + ASSERT (Dns4TokenEntry != NULL); + + if (Dns4TokenEntry->GeneralLookUp) { + // + // It's the GeneralLookUp querying. + // + Dns4TokenEntry->Token->RspData.GLookupData = AllocatePool (sizeof (DNS_RESOURCE_RECORD)); + if (Dns4TokenEntry->Token->RspData.GLookupData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + Dns4TokenEntry->Token->RspData.GLookupData->RRList = AllocatePool (DnsHeader->AnswersNum * sizeof (DNS_RESOURCE_RECORD)); + if (Dns4TokenEntry->Token->RspData.GLookupData->RRList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + } else { + // + // It's not the GeneralLookUp querying. Check the Query type. + // + if (QuerySection->Type == DNS_TYPE_A) { + Dns4TokenEntry->Token->RspData.H2AData = AllocatePool (sizeof (DNS_HOST_TO_ADDR_DATA)); + if (Dns4TokenEntry->Token->RspData.H2AData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + Dns4TokenEntry->Token->RspData.H2AData->IpList = AllocatePool (DnsHeader->AnswersNum * sizeof (EFI_IPv4_ADDRESS)); + if (Dns4TokenEntry->Token->RspData.H2AData->IpList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + } else { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + } + } else { + ASSERT (Dns6TokenEntry != NULL); + + if (Dns6TokenEntry->GeneralLookUp) { + // + // It's the GeneralLookUp querying. + // + Dns6TokenEntry->Token->RspData.GLookupData = AllocatePool (sizeof (DNS_RESOURCE_RECORD)); + if (Dns6TokenEntry->Token->RspData.GLookupData == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + Dns6TokenEntry->Token->RspData.GLookupData->RRList = AllocatePool (DnsHeader->AnswersNum * sizeof (DNS_RESOURCE_RECORD)); + if (Dns6TokenEntry->Token->RspData.GLookupData->RRList == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + } else { + // + // It's not the GeneralLookUp querying. Check the Query type. + // + if (QuerySection->Type == DNS_TYPE_AAAA) { + Dns6TokenEntry->Token->RspData.H2AData = AllocatePool (sizeof (DNS6_HOST_TO_ADDR_DATA)); + if (Dns6TokenEntry->Token->RspData.H2AData == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + Dns6TokenEntry->Token->RspData.H2AData->IpList = AllocatePool (DnsHeader->AnswersNum * sizeof (EFI_IPv6_ADDRESS)); + if (Dns6TokenEntry->Token->RspData.H2AData->IpList == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + } else { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + } + } + + // + // Processing AnswerSection. + // + while (AnswerSectionNum < DnsHeader->AnswersNum) { + // + // Answer name should be PTR. + // + ASSERT ((*(UINT8 *) AnswerName & 0xC0) == 0xC0); + + // + // Get Answer section. + // + AnswerSection = (DNS_ANSWER_SECTION *) (AnswerName + sizeof (UINT16)); + AnswerSection->Type = NTOHS (AnswerSection->Type); + AnswerSection->Class = NTOHS (AnswerSection->Class); + AnswerSection->Ttl = NTOHL (AnswerSection->Ttl); + AnswerSection->DataLength = NTOHS (AnswerSection->DataLength); + + // + // Check whether it's the GeneralLookUp querying. + // + if (Instance->Service->IpVersion == IP_VERSION_4 && Dns4TokenEntry->GeneralLookUp) { + Dns4RR = Dns4TokenEntry->Token->RspData.GLookupData->RRList; + AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection); + + // + // Fill the ResourceRecord. + // + Dns4RR[RRCount].QName = AllocateZeroPool (AsciiStrLen (QueryName) + 1); + if (Dns4RR[RRCount].QName == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + CopyMem (Dns4RR[RRCount].QName, QueryName, AsciiStrLen (QueryName)); + Dns4RR[RRCount].QType = AnswerSection->Type; + Dns4RR[RRCount].QClass = AnswerSection->Class; + Dns4RR[RRCount].TTL = AnswerSection->Ttl; + Dns4RR[RRCount].DataLength = AnswerSection->DataLength; + Dns4RR[RRCount].RData = AllocateZeroPool (Dns4RR[RRCount].DataLength); + if (Dns4RR[RRCount].RData == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + CopyMem (Dns4RR[RRCount].RData, AnswerData, Dns4RR[RRCount].DataLength); + + RRCount ++; + } else if (Instance->Service->IpVersion == IP_VERSION_6 && Dns6TokenEntry->GeneralLookUp) { + Dns6RR = Dns6TokenEntry->Token->RspData.GLookupData->RRList; + AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection); + + // + // Fill the ResourceRecord. + // + Dns6RR[RRCount].QName = AllocateZeroPool (AsciiStrLen (QueryName) + 1); + if (Dns6RR[RRCount].QName == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + CopyMem (Dns6RR[RRCount].QName, QueryName, AsciiStrLen (QueryName)); + Dns6RR[RRCount].QType = AnswerSection->Type; + Dns6RR[RRCount].QClass = AnswerSection->Class; + Dns6RR[RRCount].TTL = AnswerSection->Ttl; + Dns6RR[RRCount].DataLength = AnswerSection->DataLength; + Dns6RR[RRCount].RData = AllocateZeroPool (Dns6RR[RRCount].DataLength); + if (Dns6RR[RRCount].RData == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + CopyMem (Dns6RR[RRCount].RData, AnswerData, Dns6RR[RRCount].DataLength); + + RRCount ++; + } else { + // + // It's not the GeneralLookUp querying. + // Check the Query type, parse the response packet. + // + switch (AnswerSection->Type) { + case DNS_TYPE_A: + // + // This is address entry, get Data. + // + ASSERT (Dns4TokenEntry != NULL && AnswerSection->DataLength == 4); + + HostAddr4 = Dns4TokenEntry->Token->RspData.H2AData->IpList; + AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection); + CopyMem (&HostAddr4[IpCount], AnswerData, sizeof (EFI_IPv4_ADDRESS)); + + // + // Update DNS cache dynamically. + // + if (Dns4CacheEntry != NULL) { + if (Dns4CacheEntry->HostName != NULL) { + FreePool (Dns4CacheEntry->HostName); + } + + if (Dns4CacheEntry->IpAddress != NULL) { + FreePool (Dns4CacheEntry->IpAddress); + } + + FreePool (Dns4CacheEntry); + } + + // + // Allocate new CacheEntry pool. + // + Dns4CacheEntry = AllocateZeroPool (sizeof (EFI_DNS4_CACHE_ENTRY)); + if (Dns4CacheEntry == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + Dns4CacheEntry->HostName = AllocateZeroPool (2 * (StrLen(Dns4TokenEntry->QueryHostName) + 1)); + if (Dns4CacheEntry->HostName == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + CopyMem (Dns4CacheEntry->HostName, Dns4TokenEntry->QueryHostName, 2 * (StrLen(Dns4TokenEntry->QueryHostName) + 1)); + Dns4CacheEntry->IpAddress = AllocateZeroPool (sizeof (EFI_IPv4_ADDRESS)); + if (Dns4CacheEntry->IpAddress == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + CopyMem (Dns4CacheEntry->IpAddress, AnswerData, sizeof (EFI_IPv4_ADDRESS)); + Dns4CacheEntry->Timeout = AnswerSection->Ttl; + + UpdateDns4Cache (&mDriverData->Dns4CacheList, FALSE, TRUE, *Dns4CacheEntry); + + IpCount ++; + break; + case DNS_TYPE_AAAA: + // + // This is address entry, get Data. + // + ASSERT (Dns6TokenEntry != NULL && AnswerSection->DataLength == 16); + + HostAddr6 = Dns6TokenEntry->Token->RspData.H2AData->IpList; + AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection); + CopyMem (&HostAddr6[IpCount], AnswerData, sizeof (EFI_IPv6_ADDRESS)); + + // + // Update DNS cache dynamically. + // + if (Dns6CacheEntry != NULL) { + if (Dns6CacheEntry->HostName != NULL) { + FreePool (Dns6CacheEntry->HostName); + } + + if (Dns6CacheEntry->IpAddress != NULL) { + FreePool (Dns6CacheEntry->IpAddress); + } + + FreePool (Dns6CacheEntry); + } + + // + // Allocate new CacheEntry pool. + // + Dns6CacheEntry = AllocateZeroPool (sizeof (EFI_DNS6_CACHE_ENTRY)); + if (Dns6CacheEntry == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + Dns6CacheEntry->HostName = AllocateZeroPool (2 * (StrLen(Dns6TokenEntry->QueryHostName) + 1)); + if (Dns6CacheEntry->HostName == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + CopyMem (Dns6CacheEntry->HostName, Dns6TokenEntry->QueryHostName, 2 * (StrLen(Dns6TokenEntry->QueryHostName) + 1)); + Dns6CacheEntry->IpAddress = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS)); + if (Dns6CacheEntry->IpAddress == NULL) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + CopyMem (Dns6CacheEntry->IpAddress, AnswerData, sizeof (EFI_IPv6_ADDRESS)); + Dns6CacheEntry->Timeout = AnswerSection->Ttl; + + UpdateDns6Cache (&mDriverData->Dns6CacheList, FALSE, TRUE, *Dns6CacheEntry); + + IpCount ++; + break; + default: + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + } + + // + // Find next one + // + AnswerName = (CHAR8 *) AnswerSection + sizeof (*AnswerSection) + AnswerSection->DataLength; + AnswerSectionNum ++; + } + + if (Instance->Service->IpVersion == IP_VERSION_4) { + ASSERT (Dns4TokenEntry != NULL); + + if (Dns4TokenEntry->GeneralLookUp) { + Dns4TokenEntry->Token->RspData.GLookupData->RRCount = RRCount; + } else { + if (QuerySection->Type == DNS_TYPE_A) { + Dns4TokenEntry->Token->RspData.H2AData->IpCount = IpCount; + } else { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + } + } else { + ASSERT (Dns6TokenEntry != NULL); + + if (Dns6TokenEntry->GeneralLookUp) { + Dns6TokenEntry->Token->RspData.GLookupData->RRCount = RRCount; + } else { + if (QuerySection->Type == DNS_TYPE_AAAA) { + Dns6TokenEntry->Token->RspData.H2AData->IpCount = IpCount; + } else { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + } + } + + // + // Parsing is complete, SignalEvent here. + // + if (Instance->Service->IpVersion == IP_VERSION_4) { + ASSERT (Dns4TokenEntry != NULL); + Dns4RemoveTokenEntry (&Instance->Dns4TxTokens, Dns4TokenEntry); + Dns4TokenEntry->Token->Status = EFI_SUCCESS; + if (Dns4TokenEntry->Token->Event != NULL) { + gBS->SignalEvent (Dns4TokenEntry->Token->Event); + DispatchDpc (); + } + } else { + ASSERT (Dns6TokenEntry != NULL); + Dns6RemoveTokenEntry (&Instance->Dns6TxTokens, Dns6TokenEntry); + Dns6TokenEntry->Token->Status = EFI_SUCCESS; + if (Dns6TokenEntry->Token->Event != NULL) { + gBS->SignalEvent (Dns6TokenEntry->Token->Event); + DispatchDpc (); + } + } + + // + // Free allocated CacheEntry pool. + // + if (Dns4CacheEntry != NULL) { + if (Dns4CacheEntry->HostName != NULL) { + FreePool (Dns4CacheEntry->HostName); + } + + if (Dns4CacheEntry->IpAddress != NULL) { + FreePool (Dns4CacheEntry->IpAddress); + } + + FreePool (Dns4CacheEntry); + } + + if (Dns6CacheEntry != NULL) { + if (Dns6CacheEntry->HostName != NULL) { + FreePool (Dns6CacheEntry->HostName); + } + + if (Dns6CacheEntry->IpAddress != NULL) { + FreePool (Dns6CacheEntry->IpAddress); + } + + FreePool (Dns6CacheEntry); + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Parse response packet. + + @param Packet The packets received. + @param EndPoint The local/remote UDP access point + @param IoStatus The status of the UDP receive + @param Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +DnsOnPacketReceived ( + NET_BUF *Packet, + UDP_END_POINT *EndPoint, + EFI_STATUS IoStatus, + VOID *Context + ) +{ + DNS_INSTANCE *Instance; + + UINT8 *RcvString; + + BOOLEAN Completed; + + Instance = (DNS_INSTANCE *) Context; + NET_CHECK_SIGNATURE (Instance, DNS_INSTANCE_SIGNATURE); + + RcvString = NULL; + Completed = FALSE; + + if (EFI_ERROR (IoStatus)) { + goto ON_EXIT; + } + + ASSERT (Packet != NULL); + + RcvString = NetbufGetByte (Packet, 0, NULL); + ASSERT (RcvString != NULL); + + // + // Parse Dns Response + // + ParseDnsResponse (Instance, RcvString, &Completed); + + ON_EXIT: + + if (Packet != NULL) { + NetbufFree (Packet); + } + + if (!Completed) { + UdpIoRecvDatagram (Instance->UdpIo, DnsOnPacketReceived, Instance, 0); + } +} + +/** + Release the net buffer when packet is sent. + + @param Packet The packets received. + @param EndPoint The local/remote UDP access point + @param IoStatus The status of the UDP receive + @param Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +DnsOnPacketSent ( + NET_BUF *Packet, + UDP_END_POINT *EndPoint, + EFI_STATUS IoStatus, + VOID *Context + ) +{ + DNS_INSTANCE *Instance; + LIST_ENTRY *Entry; + NET_MAP_ITEM *Item; + DNS4_TOKEN_ENTRY *Dns4TokenEntry; + DNS6_TOKEN_ENTRY *Dns6TokenEntry; + + Dns4TokenEntry = NULL; + Dns6TokenEntry = NULL; + + Instance = (DNS_INSTANCE *) Context; + NET_CHECK_SIGNATURE (Instance, DNS_INSTANCE_SIGNATURE); + + if (Instance->Service->IpVersion == IP_VERSION_4) { + NET_LIST_FOR_EACH (Entry, &Instance->Dns4TxTokens.Used) { + Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); + if (Packet == (NET_BUF *)(Item->Value)) { + Dns4TokenEntry = ((DNS4_TOKEN_ENTRY *)Item->Key); + Dns4TokenEntry->PacketToLive = Dns4TokenEntry->Token->RetryInterval; + break; + } + } + } else { + NET_LIST_FOR_EACH (Entry, &Instance->Dns6TxTokens.Used) { + Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); + if (Packet == (NET_BUF *)(Item->Value)) { + Dns6TokenEntry = ((DNS6_TOKEN_ENTRY *)Item->Key); + Dns6TokenEntry->PacketToLive = Dns6TokenEntry->Token->RetryInterval; + break; + } + } + } + + NetbufFree (Packet); +} + +/** + Query request information. + + @param Instance The DNS instance + @param Packet The packet for querying request information. + + @retval EFI_SUCCESS Query request information successfully. + @retval Others Failed to query request information. + +**/ +EFI_STATUS +DoDnsQuery ( + IN DNS_INSTANCE *Instance, + IN NET_BUF *Packet + ) +{ + EFI_STATUS Status; + + // + // Ready to receive the DNS response. + // + if (Instance->UdpIo->RecvRequest == NULL) { + Status = UdpIoRecvDatagram (Instance->UdpIo, DnsOnPacketReceived, Instance, 0); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Transmit the DNS packet. + // + NET_GET_REF (Packet); + + Status = UdpIoSendDatagram (Instance->UdpIo, Packet, NULL, NULL, DnsOnPacketSent, Instance); + + return Status; +} + +/** + Construct the Packet according query section. + + @param Instance The DNS instance + @param QueryName Queried Name + @param Type Queried Type + @param Class Queried Class + @param Packet The packet for query + + @retval EFI_SUCCESS The packet is constructed. + @retval Others Failed to construct the Packet. + +**/ +EFI_STATUS +ConstructDNSQuery ( + IN DNS_INSTANCE *Instance, + IN CHAR8 *QueryName, + IN UINT16 Type, + IN UINT16 Class, + OUT NET_BUF **Packet + ) +{ + NET_FRAGMENT Frag; + DNS_HEADER *DnsHeader; + DNS_QUERY_SECTION *DnsQuery; + + Frag.Bulk = AllocatePool (DNS_DEFAULT_BLKSIZE * sizeof (UINT8)); + if (Frag.Bulk == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Fill header + // + DnsHeader = (DNS_HEADER *) Frag.Bulk; + DnsHeader->Identification = (UINT16)NET_RANDOM (NetRandomInitSeed()); + DnsHeader->Flags.Uint16 = 0x0000; + DnsHeader->Flags.Bits.RD = 1; + DnsHeader->Flags.Bits.OpCode = DNS_FLAGS_OPCODE_STANDARD; + DnsHeader->Flags.Bits.QR = DNS_FLAGS_QR_QUERY; + DnsHeader->QuestionsNum = 1; + DnsHeader->AnswersNum = 0; + DnsHeader->AuthorityNum = 0; + DnsHeader->AditionalNum = 0; + + DnsHeader->Identification = HTONS (DnsHeader->Identification); + DnsHeader->Flags.Uint16 = HTONS (DnsHeader->Flags.Uint16); + DnsHeader->QuestionsNum = HTONS (DnsHeader->QuestionsNum); + DnsHeader->AnswersNum = HTONS (DnsHeader->AnswersNum); + DnsHeader->AuthorityNum = HTONS (DnsHeader->AuthorityNum); + DnsHeader->AditionalNum = HTONS (DnsHeader->AditionalNum); + + Frag.Len = sizeof (*DnsHeader); + + // + // Fill Query name + // + CopyMem (Frag.Bulk + Frag.Len, QueryName, AsciiStrLen (QueryName)); + Frag.Len = (UINT32) (Frag.Len + AsciiStrLen (QueryName)); + *(Frag.Bulk + Frag.Len) = 0; + Frag.Len ++; + + // + // Rest query section + // + DnsQuery = (DNS_QUERY_SECTION *) (Frag.Bulk + Frag.Len); + + DnsQuery->Type = HTONS (Type); + DnsQuery->Class = HTONS (Class); + + Frag.Len += sizeof (*DnsQuery); + + // + // Wrap the Frag in a net buffer. + // + *Packet = NetbufFromExt (&Frag, 1, 0, 0, DnsDummyExtFree, NULL); + if (*Packet == NULL) { + FreePool (Frag.Bulk); + return EFI_OUT_OF_RESOURCES; + } + + // + // Store the UdpIo in ProtoData. + // + *((UINTN *) &((*Packet)->ProtoData[0])) = (UINTN) (Instance->UdpIo); + + return EFI_SUCCESS; +} + +/** + Retransmit the packet. + + @param Instance The DNS instance + @param Packet Retransmit the packet + + @retval EFI_SUCCESS The packet is retransmitted. + @retval Others Failed to retransmit. + +**/ +EFI_STATUS +DnsRetransmit ( + IN DNS_INSTANCE *Instance, + IN NET_BUF *Packet + ) +{ + EFI_STATUS Status; + + UINT8 *Buffer; + + ASSERT (Packet != NULL); + + // + // Set the requests to the listening port, other packets to the connected port + // + Buffer = NetbufGetByte (Packet, 0, NULL); + ASSERT (Buffer != NULL); + + NET_GET_REF (Packet); + + Status = UdpIoSendDatagram ( + Instance->UdpIo, + Packet, + NULL, + NULL, + DnsOnPacketSent, + Instance + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (Packet); + } + + return Status; +} + +/** + The timer ticking function for the DNS services. + + @param Event The ticking event + @param Context The DNS service instance + +**/ +VOID +EFIAPI +DnsOnTimerRetransmit ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + DNS_SERVICE *Service; + + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + + DNS_INSTANCE *Instance; + LIST_ENTRY *EntryNetMap; + NET_MAP_ITEM *ItemNetMap; + DNS4_TOKEN_ENTRY *Dns4TokenEntry; + DNS6_TOKEN_ENTRY *Dns6TokenEntry; + + Dns4TokenEntry = NULL; + Dns6TokenEntry = NULL; + + Service = (DNS_SERVICE *) Context; + + + if (Service->IpVersion == IP_VERSION_4) { + // + // Iterate through all the children of the DNS service instance. Time + // out the packet. If maximum retries reached, clean the Token up. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Dns4ChildrenList) { + Instance = NET_LIST_USER_STRUCT (Entry, DNS_INSTANCE, Link); + + EntryNetMap = Instance->Dns4TxTokens.Used.ForwardLink; + while (EntryNetMap != &Instance->Dns4TxTokens.Used) { + ItemNetMap = NET_LIST_USER_STRUCT (EntryNetMap, NET_MAP_ITEM, Link); + Dns4TokenEntry = (DNS4_TOKEN_ENTRY *)(ItemNetMap->Key); + if (Dns4TokenEntry->PacketToLive == 0 || (--Dns4TokenEntry->PacketToLive > 0)) { + EntryNetMap = EntryNetMap->ForwardLink; + continue; + } + + // + // Retransmit the packet if haven't reach the maxmium retry count, + // otherwise exit the transfer. + // + if (++Dns4TokenEntry->Token->RetryCount < Instance->MaxRetry) { + DnsRetransmit (Instance, (NET_BUF *)ItemNetMap->Value); + EntryNetMap = EntryNetMap->ForwardLink; + } else { + // + // Maximum retries reached, clean the Token up. + // + Dns4RemoveTokenEntry (&Instance->Dns4TxTokens, Dns4TokenEntry); + Dns4TokenEntry->Token->Status = EFI_TIMEOUT; + gBS->SignalEvent (Dns4TokenEntry->Token->Event); + DispatchDpc (); + + // + // Free the sending packet. + // + if (ItemNetMap->Value != NULL) { + NetbufFree ((NET_BUF *)(ItemNetMap->Value)); + } + + EntryNetMap = Instance->Dns4TxTokens.Used.ForwardLink; + } + } + } + }else { + // + // Iterate through all the children of the DNS service instance. Time + // out the packet. If maximum retries reached, clean the Token up. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Dns6ChildrenList) { + Instance = NET_LIST_USER_STRUCT (Entry, DNS_INSTANCE, Link); + + EntryNetMap = Instance->Dns6TxTokens.Used.ForwardLink; + while (EntryNetMap != &Instance->Dns6TxTokens.Used) { + ItemNetMap = NET_LIST_USER_STRUCT (EntryNetMap, NET_MAP_ITEM, Link); + Dns6TokenEntry = (DNS6_TOKEN_ENTRY *) (ItemNetMap->Key); + if (Dns6TokenEntry->PacketToLive == 0 || (--Dns6TokenEntry->PacketToLive > 0)) { + EntryNetMap = EntryNetMap->ForwardLink; + continue; + } + + // + // Retransmit the packet if haven't reach the maxmium retry count, + // otherwise exit the transfer. + // + if (++Dns6TokenEntry->Token->RetryCount < Instance->MaxRetry) { + DnsRetransmit (Instance, (NET_BUF *) ItemNetMap->Value); + EntryNetMap = EntryNetMap->ForwardLink; + } else { + // + // Maximum retries reached, clean the Token up. + // + Dns6RemoveTokenEntry (&Instance->Dns6TxTokens, Dns6TokenEntry); + Dns6TokenEntry->Token->Status = EFI_TIMEOUT; + gBS->SignalEvent (Dns6TokenEntry->Token->Event); + DispatchDpc (); + + // + // Free the sending packet. + // + if (ItemNetMap->Value != NULL) { + NetbufFree ((NET_BUF *) (ItemNetMap->Value)); + } + + EntryNetMap = Instance->Dns6TxTokens.Used.ForwardLink; + } + } + } + } +} + +/** + The timer ticking function for the DNS driver. + + @param Event The ticking event + @param Context NULL + +**/ +VOID +EFIAPI +DnsOnTimerUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + DNS4_CACHE *Item4; + DNS6_CACHE *Item6; + + Item4 = NULL; + Item6 = NULL; + + // + // Iterate through all the DNS4 cache list. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns4CacheList) { + Item4 = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink); + Item4->DnsCache.Timeout--; + } + + Entry = mDriverData->Dns4CacheList.ForwardLink; + while (Entry != &mDriverData->Dns4CacheList) { + Item4 = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink); + if (Item4->DnsCache.Timeout<=0) { + RemoveEntryList (&Item4->AllCacheLink); + Entry = mDriverData->Dns4CacheList.ForwardLink; + } else { + Entry = Entry->ForwardLink; + } + } + + // + // Iterate through all the DNS6 cache list. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns6CacheList) { + Item6 = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink); + Item6->DnsCache.Timeout--; + } + + Entry = mDriverData->Dns6CacheList.ForwardLink; + while (Entry != &mDriverData->Dns6CacheList) { + Item6 = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink); + if (Item6->DnsCache.Timeout<=0) { + RemoveEntryList (&Item6->AllCacheLink); + Entry = mDriverData->Dns6CacheList.ForwardLink; + } else { + Entry = Entry->ForwardLink; + } + } +} + diff --git a/Core/NetworkPkg/DnsDxe/DnsImpl.h b/Core/NetworkPkg/DnsDxe/DnsImpl.h new file mode 100644 index 0000000000..847cd15973 --- /dev/null +++ b/Core/NetworkPkg/DnsDxe/DnsImpl.h @@ -0,0 +1,1161 @@ +/** @file +DnsDxe support functions implementation. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_DNS_IMPL_H_ +#define __EFI_DNS_IMPL_H_ + +#include + +// +// Libraries classes +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// UEFI Driver Model Protocols +// +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "DnsDriver.h" +#include "DnsDhcp.h" + +// +// Driver Version +// +#define DNS_VERSION 0x00000000 + +// +// Protocol instances +// +extern EFI_COMPONENT_NAME_PROTOCOL gDnsComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gDnsComponentName2; +extern EFI_UNICODE_STRING_TABLE *gDnsControllerNameTable; + +extern EFI_DRIVER_BINDING_PROTOCOL gDns4DriverBinding; +extern EFI_SERVICE_BINDING_PROTOCOL mDns4ServiceBinding; +extern EFI_DNS4_PROTOCOL mDns4Protocol; + +extern EFI_DRIVER_BINDING_PROTOCOL gDns6DriverBinding; +extern EFI_SERVICE_BINDING_PROTOCOL mDns6ServiceBinding; +extern EFI_DNS6_PROTOCOL mDns6Protocol; + +// +// DNS related +// +#define DNS_SERVER_PORT 53 + +#define DNS_PROTOCOL_UDP EFI_IP_PROTO_UDP +#define DNS_PROTOCOL_TCP EFI_IP_PROTO_TCP + +#define DNS_STATE_UNCONFIGED 0 +#define DNS_STATE_CONFIGED 1 +#define DNS_STATE_DESTROY 2 + +#define DNS_DEFAULT_TIMEOUT 2 +#define DNS_DEFAULT_RETRY 3 +#define DNS_DEFAULT_BLKSIZE 512 + +#define DNS_TIME_TO_GETMAP 5 + +#pragma pack(1) + +typedef union _DNS_FLAGS DNS_FLAGS; + +typedef struct { + LIST_ENTRY AllCacheLink; + EFI_DNS4_CACHE_ENTRY DnsCache; +} DNS4_CACHE; + +typedef struct { + LIST_ENTRY AllCacheLink; + EFI_DNS6_CACHE_ENTRY DnsCache; +} DNS6_CACHE; + +typedef struct { + LIST_ENTRY AllServerLink; + EFI_IPv4_ADDRESS Dns4ServerIp; +} DNS4_SERVER_IP; + +typedef struct { + LIST_ENTRY AllServerLink; + EFI_IPv6_ADDRESS Dns6ServerIp; +} DNS6_SERVER_IP; + +typedef struct { + UINT32 PacketToLive; + CHAR16 *QueryHostName; + EFI_IPv4_ADDRESS QueryIpAddress; + BOOLEAN GeneralLookUp; + EFI_DNS4_COMPLETION_TOKEN *Token; +} DNS4_TOKEN_ENTRY; + +typedef struct { + UINT32 PacketToLive; + CHAR16 *QueryHostName; + EFI_IPv6_ADDRESS QueryIpAddress; + BOOLEAN GeneralLookUp; + EFI_DNS6_COMPLETION_TOKEN *Token; +} DNS6_TOKEN_ENTRY; + +union _DNS_FLAGS{ + struct { + UINT16 RCode:4; + UINT16 Zero:3; + UINT16 RA:1; + UINT16 RD:1; + UINT16 TC:1; + UINT16 AA:1; + UINT16 OpCode:4; + UINT16 QR:1; + } Bits; + UINT16 Uint16; +}; + +#define DNS_FLAGS_QR_QUERY 0 +#define DNS_FLAGS_QR_RESPONSE 1 + +#define DNS_FLAGS_OPCODE_STANDARD 0 +#define DNS_FLAGS_OPCODE_INVERSE 1 +#define DNS_FLAGS_OPCODE_SERVER_STATE 2 + +#define DNS_FLAGS_RCODE_NO_ERROR 0 +#define DNS_FLAGS_RCODE_NAME_ERROR 3 + +typedef struct { + UINT16 Identification; + DNS_FLAGS Flags; + UINT16 QuestionsNum; + UINT16 AnswersNum; + UINT16 AuthorityNum; + UINT16 AditionalNum; +} DNS_HEADER; + +typedef struct { + UINT16 Type; + UINT16 Class; +} DNS_QUERY_SECTION; + +typedef struct { + UINT16 Type; + UINT16 Class; + UINT32 Ttl; + UINT16 DataLength; +} DNS_ANSWER_SECTION; + +#define DNS_TYPE_A 1 +#define DNS_TYPE_NS 2 +#define DNS_TYPE_CNAME 5 +#define DNS_TYPE_PTR 12 +#define DNS_TYPE_HINFO 13 +#define DNS_TYPE_MX 15 +#define DNS_TYPE_AAAA 28 +#define DNS_TYPE_SRV_RR 33 +#define DNS_TYPE_AXFR 252 +#define DNS_TYPE_ANY 255 + +#define DNS_CLASS_INET 1 + +#define DNS4_DOMAIN L"in-addr.arpa" +#define DNS6_DOMAIN L"IP6.ARPA" + + +#pragma pack() + +/** + Remove TokenEntry from TokenMap. + + @param[in] TokenMap All DNSv4 Token entrys. + @param[in] TokenEntry TokenEntry need to be removed. + + @retval EFI_SUCCESS Remove TokenEntry from TokenMap sucessfully. + @retval EFI_NOT_FOUND TokenEntry is not found in TokenMap. + +**/ +EFI_STATUS +Dns4RemoveTokenEntry ( + IN NET_MAP *TokenMap, + IN DNS4_TOKEN_ENTRY *TokenEntry + ); + +/** + Remove TokenEntry from TokenMap. + + @param[in] TokenMap All DNSv6 Token entrys. + @param[in] TokenEntry TokenEntry need to be removed. + + @retval EFI_SUCCESS Remove TokenEntry from TokenMap sucessfully. + @retval EFI_NOT_FOUND TokenEntry is not found in TokenMap. + +**/ +EFI_STATUS +Dns6RemoveTokenEntry ( + IN NET_MAP *TokenMap, + IN DNS6_TOKEN_ENTRY *TokenEntry + ); + +/** + This function cancle the token specified by Arg in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled. If NULL, all + the tokens in this Map will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token + is not the same as that in the Item, if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Dns4CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ); + +/** + This function cancle the token specified by Arg in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled. If NULL, all + the tokens in this Map will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token + is not the same as that in the Item, if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Dns6CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ); + +/** + Get the TokenEntry from the TokensMap. + + @param[in] TokensMap All DNSv4 Token entrys + @param[in] Token Pointer to the token to be get. + @param[out] TokenEntry Pointer to TokenEntry corresponding Token. + + @retval EFI_SUCCESS Get the TokenEntry from the TokensMap sucessfully. + @retval EFI_NOT_FOUND TokenEntry is not found in TokenMap. + +**/ +EFI_STATUS +EFIAPI +GetDns4TokenEntry ( + IN NET_MAP *TokensMap, + IN EFI_DNS4_COMPLETION_TOKEN *Token, + OUT DNS4_TOKEN_ENTRY **TokenEntry + ); + +/** + Get the TokenEntry from the TokensMap. + + @param[in] TokensMap All DNSv6 Token entrys + @param[in] Token Pointer to the token to be get. + @param[out] TokenEntry Pointer to TokenEntry corresponding Token. + + @retval EFI_SUCCESS Get the TokenEntry from the TokensMap sucessfully. + @retval EFI_NOT_FOUND TokenEntry is not found in TokenMap. + +**/ +EFI_STATUS +EFIAPI +GetDns6TokenEntry ( + IN NET_MAP *TokensMap, + IN EFI_DNS6_COMPLETION_TOKEN *Token, + OUT DNS6_TOKEN_ENTRY **TokenEntry + ); + +/** + Cancel DNS4 tokens from the DNS4 instance. + + @param[in] Instance Pointer to the DNS instance context data. + @param[in] Token Pointer to the token to be canceled. If NULL, all + tokens in this instance will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Dns4InstanceCancelToken ( + IN DNS_INSTANCE *Instance, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ); + +/** + Cancel DNS6 tokens from the DNS6 instance. + + @param[in] Instance Pointer to the DNS instance context data. + @param[in] Token Pointer to the token to be canceled. If NULL, all + tokens in this instance will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Dns6InstanceCancelToken ( + IN DNS_INSTANCE *Instance, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ); + +/** + Free the resource related to the configure parameters. + + @param Config The DNS configure data + +**/ +VOID +Dns4CleanConfigure ( + IN OUT EFI_DNS4_CONFIG_DATA *Config + ); + +/** + Free the resource related to the configure parameters. + + @param Config The DNS configure data + +**/ +VOID +Dns6CleanConfigure ( + IN OUT EFI_DNS6_CONFIG_DATA *Config + ); + +/** + Allocate memory for configure parameter such as timeout value for Dst, + then copy the configure parameter from Src to Dst. + + @param[out] Dst The destination DHCP configure data. + @param[in] Src The source DHCP configure data. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_SUCCESS The configure is copied. + +**/ +EFI_STATUS +Dns4CopyConfigure ( + OUT EFI_DNS4_CONFIG_DATA *Dst, + IN EFI_DNS4_CONFIG_DATA *Src + ); + +/** + Allocate memory for configure parameter such as timeout value for Dst, + then copy the configure parameter from Src to Dst. + + @param[out] Dst The destination DHCP configure data. + @param[in] Src The source DHCP configure data. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_SUCCESS The configure is copied. + +**/ +EFI_STATUS +Dns6CopyConfigure ( + OUT EFI_DNS6_CONFIG_DATA *Dst, + IN EFI_DNS6_CONFIG_DATA *Src + ); + +/** + Callback of Dns packet. Does nothing. + + @param Arg The context. + +**/ +VOID +EFIAPI +DnsDummyExtFree ( + IN VOID *Arg + ); + +/** + Poll the UDP to get the IP4 default address, which may be retrieved + by DHCP. + + The default time out value is 5 seconds. If IP has retrieved the default address, + the UDP is reconfigured. + + @param Instance The DNS instance + @param UdpIo The UDP_IO to poll + @param UdpCfgData The UDP configure data to reconfigure the UDP_IO + + @retval TRUE The default address is retrieved and UDP is reconfigured. + @retval FALSE Some error occured. + +**/ +BOOLEAN +Dns4GetMapping ( + IN DNS_INSTANCE *Instance, + IN UDP_IO *UdpIo, + IN EFI_UDP4_CONFIG_DATA *UdpCfgData + ); + +/** + Configure the opened Udp6 instance until the corresponding Ip6 instance + has been configured. + + @param Instance The DNS instance + @param UdpIo The UDP_IO to poll + @param UdpCfgData The UDP configure data to reconfigure the UDP_IO + + @retval TRUE Configure the Udp6 instance successfully. + @retval FALSE Some error occured. + +**/ +BOOLEAN +Dns6GetMapping ( + IN DNS_INSTANCE *Instance, + IN UDP_IO *UdpIo, + IN EFI_UDP6_CONFIG_DATA *UdpCfgData + ); + +/** + Configure the UDP. + + @param Instance The DNS session + @param UdpIo The UDP_IO instance + + @retval EFI_SUCCESS The UDP is successfully configured for the + session. + +**/ +EFI_STATUS +Dns4ConfigUdp ( + IN DNS_INSTANCE *Instance, + IN UDP_IO *UdpIo + ); + +/** + Configure the UDP. + + @param Instance The DNS session + @param UdpIo The UDP_IO instance + + @retval EFI_SUCCESS The UDP is successfully configured for the + session. + +**/ +EFI_STATUS +Dns6ConfigUdp ( + IN DNS_INSTANCE *Instance, + IN UDP_IO *UdpIo + ); + +/** + Update Dns4 cache to shared list of caches of all DNSv4 instances. + + @param Dns4CacheList All Dns4 cache list. + @param DeleteFlag If FALSE, this function is to add one entry to the DNS Cache. + If TRUE, this function will delete matching DNS Cache entry. + @param Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter. + If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists. + @param DnsCacheEntry Entry Pointer to DNS Cache entry. + + @retval EFI_SUCCESS Update Dns4 cache successfully. + @retval Others Failed to update Dns4 cache. + +**/ +EFI_STATUS +EFIAPI +UpdateDns4Cache ( + IN LIST_ENTRY *Dns4CacheList, + IN BOOLEAN DeleteFlag, + IN BOOLEAN Override, + IN EFI_DNS4_CACHE_ENTRY DnsCacheEntry + ); + +/** + Update Dns6 cache to shared list of caches of all DNSv6 instances. + + @param Dns6CacheList All Dns6 cache list. + @param DeleteFlag If FALSE, this function is to add one entry to the DNS Cache. + If TRUE, this function will delete matching DNS Cache entry. + @param Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter. + If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists. + @param DnsCacheEntry Entry Pointer to DNS Cache entry. + + @retval EFI_SUCCESS Update Dns6 cache successfully. + @retval Others Failed to update Dns6 cache. +**/ +EFI_STATUS +EFIAPI +UpdateDns6Cache ( + IN LIST_ENTRY *Dns6CacheList, + IN BOOLEAN DeleteFlag, + IN BOOLEAN Override, + IN EFI_DNS6_CACHE_ENTRY DnsCacheEntry + ); + +/** + Add Dns4 ServerIp to common list of addresses of all configured DNSv4 server. + + @param Dns4ServerList Common list of addresses of all configured DNSv4 server. + @param ServerIp DNS server Ip. + + @retval EFI_SUCCESS Add Dns4 ServerIp to common list successfully. + @retval Others Failed to add Dns4 ServerIp to common list. + +**/ +EFI_STATUS +EFIAPI +AddDns4ServerIp ( + IN LIST_ENTRY *Dns4ServerList, + IN EFI_IPv4_ADDRESS ServerIp + ); + +/** + Add Dns6 ServerIp to common list of addresses of all configured DNSv6 server. + + @param Dns6ServerList Common list of addresses of all configured DNSv6 server. + @param ServerIp DNS server Ip. + + @retval EFI_SUCCESS Add Dns6 ServerIp to common list successfully. + @retval Others Failed to add Dns6 ServerIp to common list. + +**/ +EFI_STATUS +EFIAPI +AddDns6ServerIp ( + IN LIST_ENTRY *Dns6ServerList, + IN EFI_IPv6_ADDRESS ServerIp + ); + +/** + Fill QName for IP querying. QName is a domain name represented as + a sequence of labels, where each label consists of a length octet + followed by that number of octets. The domain name terminates with + the zero length octet for the null label of the root. + + @param HostName Queried HostName + + @retval NULL Failed to fill QName. + @return QName filled successfully. + +**/ +CHAR8 * +EFIAPI +DnsFillinQNameForQueryIp ( + IN CHAR16 *HostName + ); + +/** + Find out whether the response is valid or invalid. + + @param TokensMap All DNS transmittal Tokens entry. + @param Identification Identification for queried packet. + @param Type Type for queried packet. + @param Item Return corresponding Token entry. + + @retval TRUE The response is valid. + @retval FALSE The response is invalid. + +**/ +BOOLEAN +IsValidDnsResponse ( + IN NET_MAP *TokensMap, + IN UINT16 Identification, + IN UINT16 Type, + OUT NET_MAP_ITEM **Item + ); + +/** + Parse Dns Response. + + @param Instance The DNS instance + @param RxString Received buffer. + @param Completed Flag to indicate that Dns response is valid. + + @retval EFI_SUCCESS Parse Dns Response successfully. + @retval Others Failed to parse Dns Response. + +**/ +EFI_STATUS +ParseDnsResponse ( + IN OUT DNS_INSTANCE *Instance, + IN UINT8 *RxString, + OUT BOOLEAN *Completed + ); + +/** + Parse response packet. + + @param Packet The packets received. + @param EndPoint The local/remote UDP access point + @param IoStatus The status of the UDP receive + @param Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +DnsOnPacketReceived ( + NET_BUF *Packet, + UDP_END_POINT *EndPoint, + EFI_STATUS IoStatus, + VOID *Context + ); + +/** + Release the net buffer when packet is sent. + + @param Packet The packets received. + @param EndPoint The local/remote UDP access point + @param IoStatus The status of the UDP receive + @param Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +DnsOnPacketSent ( + NET_BUF *Packet, + UDP_END_POINT *EndPoint, + EFI_STATUS IoStatus, + VOID *Context + ); + +/** + Query request information. + + @param Instance The DNS instance + @param Packet The packet for querying request information. + + @retval EFI_SUCCESS Query request information successfully. + @retval Others Failed to query request information. + +**/ +EFI_STATUS +DoDnsQuery ( + IN DNS_INSTANCE *Instance, + IN NET_BUF *Packet + ); + +/** + Construct the Packet according query section. + + @param Instance The DNS instance + @param QueryName Queried Name + @param Type Queried Type + @param Class Queried Class + @param Packet The packet for query + + @retval EFI_SUCCESS The packet is constructed. + @retval Others Failed to construct the Packet. + +**/ +EFI_STATUS +ConstructDNSQuery ( + IN DNS_INSTANCE *Instance, + IN CHAR8 *QueryName, + IN UINT16 Type, + IN UINT16 Class, + OUT NET_BUF **Packet + ); + +/** + Retransmit the packet. + + @param Instance The DNS instance + @param Packet Retransmit the packet + + @retval EFI_SUCCESS The packet is retransmitted. + @retval Others Failed to retransmit. + +**/ +EFI_STATUS +DnsRetransmit ( + IN DNS_INSTANCE *Instance, + IN NET_BUF *Packet + ); + +/** + The timer ticking function for the DNS service. + + @param Event The ticking event + @param Context The DNS service instance + +**/ +VOID +EFIAPI +DnsOnTimerRetransmit ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + The timer ticking function for the DNS driver. + + @param Event The ticking event + @param Context NULL + +**/ +VOID +EFIAPI +DnsOnTimerUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + This function is used to retrieve DNS mode data for this DNS instance. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[out] DnsModeData Pointer to the caller-allocated storage for the EFI_DNS4_MODE_DATA structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED When DnsConfigData is queried, no configuration data is + available because this instance has not been configured. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL or DnsModeData is NULL. + +**/ +EFI_STATUS +EFIAPI +Dns4GetModeData ( + IN EFI_DNS4_PROTOCOL *This, + OUT EFI_DNS4_MODE_DATA *DnsModeData + ); + +/** + This function is used to configure DNS configuration data for this DNS instance. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] DnsConfigData Pointer to caller-allocated buffer containing EFI_DNS4_CONFIG_DATA structure. + If NULL, the driver will reinitialize the protocol instance to the unconfigured state. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_UNSUPPORTED The designated protocol is not supported. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + The StationIp address provided in DnsConfigData is not a valid unicast. + DnsServerList is NULL while DnsServerListCount is not equal to Zero. + DnsServerListCount is Zero while DnsServerListCount is not equal to NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI DNSv4 Protocol instance is not configured. + +**/ +EFI_STATUS +EFIAPI +Dns4Configure ( + IN EFI_DNS4_PROTOCOL *This, + IN EFI_DNS4_CONFIG_DATA *DnsConfigData + ); + +/** + The function is used to translate the host name to host IP address. + A type A query is used to get the one or more IP addresses for this host. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] HostName Pointer to caller-supplied buffer containing Host name to be translated. + This buffer contains 16 bit characters but these are translated to ASCII for use with + DNSv4 server and there is no requirement for driver to support non-ASCII Unicode characters. + @param[in] Token Pointer to the caller-allocated completion token to return at the completion of the process to translate host name to host address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is.NULL + HostName is NULL + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + +**/ +EFI_STATUS +EFIAPI +Dns4HostNameToIp ( + IN EFI_DNS4_PROTOCOL *This, + IN CHAR16 *HostName, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ); + +/** + The function is used to translate the host address to host name. + A type PTR query is used to get the primary name of the host. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] IpAddress IP address. + @param[in] Token Pointer to the caller-allocated completion used token to translate host address to host name. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is NULL. + IpAddress is not valid IP address. + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +Dns4IpToHostName ( + IN EFI_DNS4_PROTOCOL *This, + IN EFI_IPv4_ADDRESS IpAddress, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ); + +/** + This function retrieves arbitrary information from the DNS. + The caller supplies a QNAME, QTYPE, and QCLASS, and all of the matching RRs are returned. + All RR content (e.g., Ttl) was returned. + The caller need parse the returned RR to get required information. This function is optional. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] QName Pointer to Query Name. + @param[in] QType Query Type. + @param[in] QClass Query Name. + @param[in] Token Point to the caller-allocated completion token to retrieve arbitrary information. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is NULL. + QName is NULL. + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_ALREADY_STARTED This Token is being used in another DNS session. + @retval EFI_UNSUPPORTED This function is not supported. Or the requested QType is not supported + +**/ +EFI_STATUS +EFIAPI +Dns4GeneralLookUp ( + IN EFI_DNS4_PROTOCOL *This, + IN CHAR8 *QName, + IN UINT16 QType, + IN UINT16 QClass, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ); + +/** + This function is used to add/delete/modify DNS cache entry. + DNS cache can be normally dynamically updated after the DNS resolve succeeds. + This function provided capability to manually add/delete/modify the DNS cache. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] DeleteFlag If FALSE, this function is to add one entry to the DNS Cache. + If TRUE, this function will delete matching DNS Cache entry. + @param[in] Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter. + If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists. + @param[in] DnsCacheEntry Pointer to DNS Cache entry. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + DnsCacheEntry.HostName is NULL. + DnsCacheEntry.IpAddress is NULL. + DnsCacheEntry.Timeout is zero. + @retval EFI_ACCESS_DENIED The DNS cache entry already exists and Override is not TRUE. + +**/ +EFI_STATUS +EFIAPI +Dns4UpdateDnsCache ( + IN EFI_DNS4_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN BOOLEAN Override, + IN EFI_DNS4_CACHE_ENTRY DnsCacheEntry + ); + +/** + This function can be used by network drivers and applications to increase the rate that data packets are moved between + the communications device and the transmit and receive queues. In some systems, the periodic timer event in the managed + network driver may not poll the underlying communications device fast enough to transmit and/or receive all data packets + without missing incoming packets or dropping outgoing packets. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Dns4Poll ( + IN EFI_DNS4_PROTOCOL *This + ); + +/** + This function is used to abort a pending resolution request. + After calling this function, Token.Status will be set to EFI_ABORTED and then Token. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by EFI_DNS4_PROTOCOL.HostNameToIp(), + EFI_DNS4_PROTOCOL.IpToHostName() or EFI_DNS4_PROTOCOL.GeneralLookup(). + If NULL, all pending tokens are aborted. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started. + @retval EFI_NOT_FOUND When Token is not NULL, and the asynchronous DNS operation was not found in the transmit queue. + It was either completed or was not issued by HostNameToIp(), IpToHostName() or GeneralLookup(). + +**/ +EFI_STATUS +EFIAPI +Dns4Cancel ( + IN EFI_DNS4_PROTOCOL *This, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ); + + +/** + This function is used to retrieve DNS mode data for this DNS instance. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[out] DnsModeData Pointer to the caller-allocated storage for the EFI_DNS6_MODE_DATA structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED When DnsConfigData is queried, no configuration data is + available because this instance has not been configured. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL or DnsModeData is NULL. + +**/ +EFI_STATUS +EFIAPI +Dns6GetModeData ( + IN EFI_DNS6_PROTOCOL *This, + OUT EFI_DNS6_MODE_DATA *DnsModeData + ); + +/** + The function is used to set and change the configuration data for this EFI DNSv6 Protocol driver instance. + Reset the DNS instance if DnsConfigData is NULL. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] DnsConfigData Pointer to the configuration data structure. + All associated storage to be allocated and released by caller. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_UNSUPPORTED The designated protocol is not supported. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + The StationIp address provided in DnsConfigData is not a valid unicast. + DnsServerList is NULL while DnsServerListCount is not equal to Zero. + DnsServerListCount is Zero while DnsServerList is not equal to NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI DNSv6 Protocol instance is not configured. + +**/ +EFI_STATUS +EFIAPI +Dns6Configure ( + IN EFI_DNS6_PROTOCOL *This, + IN EFI_DNS6_CONFIG_DATA *DnsConfigData + ); + +/** + The function is used to translate the host name to host IP address. + A type AAAA query is used to get the one or more IPv6 addresses for this host. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] HostName Pointer to caller-supplied buffer containing Host name to be translated. + This buffer contains 16 bit characters but these are translated to ASCII for use with + DNSv4 server and there is no requirement for driver to support non-ASCII Unicode characters. + @param[in] Token Pointer to the caller-allocated completion token to return at the completion of the process to translate host name to host address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is.NULL + HostName is NULL + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + +**/ +EFI_STATUS +EFIAPI +Dns6HostNameToIp ( + IN EFI_DNS6_PROTOCOL *This, + IN CHAR16 *HostName, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ); + +/** + The function is used to translate the host address to host name. + A type PTR query is used to get the primary name of the host. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] IpAddress IP address. + @param[in] Token Pointer to the caller-allocated completion used token to translate host address to host name. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is NULL. + IpAddress is not valid IP address. + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +Dns6IpToHostName ( + IN EFI_DNS6_PROTOCOL *This, + IN EFI_IPv6_ADDRESS IpAddress, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ); + +/** + This function retrieves arbitrary information from the DNS. + The caller supplies a QNAME, QTYPE, and QCLASS, and all of the matching RRs are returned. + All RR content (e.g., Ttl) was returned. + The caller need parse the returned RR to get required information. This function is optional. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] QName Pointer to Query Name. + @param[in] QType Query Type. + @param[in] QClass Query Name. + @param[in] Token Point to the caller-allocated completion token to retrieve arbitrary information. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is NULL. + QName is NULL. + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_UNSUPPORTED This function is not supported. Or the requested QType is not supported + +**/ +EFI_STATUS +EFIAPI +Dns6GeneralLookUp ( + IN EFI_DNS6_PROTOCOL *This, + IN CHAR8 *QName, + IN UINT16 QType, + IN UINT16 QClass, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ); + +/** + This function is used to add/delete/modify DNS cache entry. + DNS cache can be normally dynamically updated after the DNS resolve succeeds. + This function provided capability to manually add/delete/modify the DNS cache. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] DeleteFlag If FALSE, this function is to add one entry to the DNS Cache. + If TRUE, this function will delete matching DNS Cache entry. + @param[in] Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter. + If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists. + @param[in] DnsCacheEntry Pointer to DNS Cache entry. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + DnsCacheEntry.HostName is NULL. + DnsCacheEntry.IpAddress is NULL. + DnsCacheEntry.Timeout is zero. + @retval EFI_ACCESS_DENIED The DNS cache entry already exists and Override is not TRUE. + +**/ +EFI_STATUS +EFIAPI +Dns6UpdateDnsCache ( + IN EFI_DNS6_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN BOOLEAN Override, + IN EFI_DNS6_CACHE_ENTRY DnsCacheEntry + ); + +/** + This function can be used by network drivers and applications to increase the rate that data packets are moved between + the communications device and the transmit and receive queues. In some systems, the periodic timer event in the managed + network driver may not poll the underlying communications device fast enough to transmit and/or receive all data packets + without missing incoming packets or dropping outgoing packets. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Dns6Poll ( + IN EFI_DNS6_PROTOCOL *This + ); + +/** + This function is used to abort a pending resolution request. + After calling this function, Token.Status will be set to EFI_ABORTED and then Token. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by EFI_DNS6_PROTOCOL.HostNameToIp(), + EFI_DNS6_PROTOCOL.IpToHostName() or EFI_DNS6_PROTOCOL.GeneralLookup(). + If NULL, all pending tokens are aborted. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started. + @retval EFI_NOT_FOUND When Token is not NULL, and the asynchronous DNS operation was not found in the transmit queue. + It was either completed or was not issued by HostNameToIp(), IpToHostName() or GeneralLookup(). + +**/ +EFI_STATUS +EFIAPI +Dns6Cancel ( + IN EFI_DNS6_PROTOCOL *This, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ); + +#endif diff --git a/Core/NetworkPkg/DnsDxe/DnsProtocol.c b/Core/NetworkPkg/DnsDxe/DnsProtocol.c new file mode 100644 index 0000000000..a3f3de9766 --- /dev/null +++ b/Core/NetworkPkg/DnsDxe/DnsProtocol.c @@ -0,0 +1,1591 @@ +/** @file +Implementation of EFI_DNS4_PROTOCOL and EFI_DNS6_PROTOCOL interfaces. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DnsImpl.h" + +EFI_DNS4_PROTOCOL mDns4Protocol = { + Dns4GetModeData, + Dns4Configure, + Dns4HostNameToIp, + Dns4IpToHostName, + Dns4GeneralLookUp, + Dns4UpdateDnsCache, + Dns4Poll, + Dns4Cancel +}; + +EFI_DNS6_PROTOCOL mDns6Protocol = { + Dns6GetModeData, + Dns6Configure, + Dns6HostNameToIp, + Dns6IpToHostName, + Dns6GeneralLookUp, + Dns6UpdateDnsCache, + Dns6Poll, + Dns6Cancel +}; + +/** + This function is used to retrieve DNS mode data for this DNS instance. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[out] DnsModeData Pointer to the caller-allocated storage for the EFI_DNS4_MODE_DATA structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED When DnsConfigData is queried, no configuration data is + available because this instance has not been configured. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL or DnsModeData is NULL. + +**/ +EFI_STATUS +EFIAPI +Dns4GetModeData ( + IN EFI_DNS4_PROTOCOL *This, + OUT EFI_DNS4_MODE_DATA *DnsModeData + ) +{ + DNS_INSTANCE *Instance; + + EFI_TPL OldTpl; + + UINTN Index; + + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + + DNS4_SERVER_IP *ServerItem; + EFI_IPv4_ADDRESS *ServerList; + DNS4_CACHE *CacheItem; + EFI_DNS4_CACHE_ENTRY *CacheList; + EFI_STATUS Status; + + ServerItem = NULL; + ServerList = NULL; + CacheItem = NULL; + CacheList = NULL; + Status = EFI_SUCCESS; + + + if ((This == NULL) || (DnsModeData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL4 (This); + if (Instance->State == DNS_STATE_UNCONFIGED) { + gBS->RestoreTPL (OldTpl); + return EFI_NOT_STARTED; + } + + ZeroMem (DnsModeData, sizeof (EFI_DNS4_MODE_DATA)); + + // + // Get the current configuration data of this instance. + // + Status = Dns4CopyConfigure (&DnsModeData->DnsConfigData, &Instance->Dns4CfgData); + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // Get the DnsServerCount and DnsServerList + // + Index = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns4ServerList) { + Index++; + } + DnsModeData->DnsServerCount = (UINT32) Index; + ServerList = AllocatePool (sizeof (EFI_IPv4_ADDRESS) * DnsModeData->DnsServerCount); + ASSERT (ServerList != NULL); + Index = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns4ServerList) { + ServerItem = NET_LIST_USER_STRUCT (Entry, DNS4_SERVER_IP, AllServerLink); + CopyMem (ServerList + Index, &ServerItem->Dns4ServerIp, sizeof (EFI_IPv4_ADDRESS)); + Index++; + } + DnsModeData->DnsServerList = ServerList; + + // + // Get the DnsCacheCount and DnsCacheList + // + Index =0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns4CacheList) { + Index++; + } + DnsModeData->DnsCacheCount = (UINT32) Index; + CacheList = AllocatePool (sizeof (EFI_DNS4_CACHE_ENTRY) * DnsModeData->DnsCacheCount); + ASSERT (CacheList != NULL); + Index =0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns4CacheList) { + CacheItem = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink); + CopyMem (CacheList + Index, &CacheItem->DnsCache, sizeof (EFI_DNS4_CACHE_ENTRY)); + Index++; + } + DnsModeData->DnsCacheList = CacheList; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +/** + This function is used to configure DNS configuration data for this DNS instance. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] DnsConfigData Pointer to caller-allocated buffer containing EFI_DNS4_CONFIG_DATA structure. + If NULL, the driver will reinitialize the protocol instance to the unconfigured state. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_UNSUPPORTED The designated protocol is not supported. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + The StationIp address provided in DnsConfigData is not a valid unicast. + DnsServerList is NULL while DnsServerListCount is not equal to Zero. + DnsServerListCount is Zero while DnsServerListCount is not equal to NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI DNSv4 Protocol instance is not configured. + +**/ +EFI_STATUS +EFIAPI +Dns4Configure ( + IN EFI_DNS4_PROTOCOL *This, + IN EFI_DNS4_CONFIG_DATA *DnsConfigData + ) +{ + EFI_STATUS Status; + DNS_INSTANCE *Instance; + + EFI_TPL OldTpl; + IP4_ADDR Ip; + IP4_ADDR Netmask; + + UINT32 ServerListCount; + EFI_IPv4_ADDRESS *ServerList; + + Status = EFI_SUCCESS; + ServerList = NULL; + + if (This == NULL || + (DnsConfigData != NULL && ((DnsConfigData->DnsServerListCount != 0 && DnsConfigData->DnsServerList == NULL) || + (DnsConfigData->DnsServerListCount == 0 && DnsConfigData->DnsServerList != NULL)))) { + return EFI_INVALID_PARAMETER; + } + + if (DnsConfigData != NULL && DnsConfigData->Protocol != DNS_PROTOCOL_UDP) { + return EFI_UNSUPPORTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL4 (This); + + if (DnsConfigData == NULL) { + ZeroMem (&Instance->SessionDnsServer, sizeof (EFI_IP_ADDRESS)); + + // + // Reset the Instance if ConfigData is NULL + // + if (!NetMapIsEmpty(&Instance->Dns4TxTokens)) { + Dns4InstanceCancelToken(Instance, NULL); + } + + Instance->MaxRetry = 0; + + if (Instance->UdpIo != NULL){ + UdpIoCleanIo (Instance->UdpIo); + } + + if (Instance->Dns4CfgData.DnsServerList != NULL) { + FreePool (Instance->Dns4CfgData.DnsServerList); + } + ZeroMem (&Instance->Dns4CfgData, sizeof (EFI_DNS4_CONFIG_DATA)); + + Instance->State = DNS_STATE_UNCONFIGED; + } else { + // + // Configure the parameters for new operation. + // + CopyMem (&Ip, &DnsConfigData->StationIp, sizeof (IP4_ADDR)); + CopyMem (&Netmask, &DnsConfigData->SubnetMask, sizeof (IP4_ADDR)); + + Ip = NTOHL (Ip); + Netmask = NTOHL (Netmask); + + if (!DnsConfigData->UseDefaultSetting && + ((!IP4_IS_VALID_NETMASK (Netmask) || !NetIp4IsUnicast (Ip, Netmask)))) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Status = Dns4CopyConfigure (&Instance->Dns4CfgData, DnsConfigData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (DnsConfigData->DnsServerListCount == 0 || DnsConfigData->DnsServerList == NULL) { + gBS->RestoreTPL (OldTpl); + + // + // The DNS instance will retrieve DNS server from DHCP Server + // + Status = GetDns4ServerFromDhcp4 ( + Instance, + &ServerListCount, + &ServerList + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT(ServerList != NULL); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + CopyMem (&Instance->SessionDnsServer.v4, &ServerList[0], sizeof (EFI_IPv4_ADDRESS)); + } else { + CopyMem (&Instance->SessionDnsServer.v4, &DnsConfigData->DnsServerList[0], sizeof (EFI_IPv4_ADDRESS)); + } + + // + // Config UDP + // + Status = Dns4ConfigUdp (Instance, Instance->UdpIo); + if (EFI_ERROR (Status)) { + if (Instance->Dns4CfgData.DnsServerList != NULL) { + FreePool (Instance->Dns4CfgData.DnsServerList); + } + goto ON_EXIT; + } + + // + // Add configured DNS server used by this instance to ServerList. + // + Status = AddDns4ServerIp (&mDriverData->Dns4ServerList, Instance->SessionDnsServer.v4); + if (EFI_ERROR (Status)) { + if (Instance->Dns4CfgData.DnsServerList != NULL) { + FreePool (Instance->Dns4CfgData.DnsServerList); + } + goto ON_EXIT; + } + + Instance->State = DNS_STATE_CONFIGED; + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + The function is used to translate the host name to host IP address. + A type A query is used to get the one or more IP addresses for this host. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] HostName Pointer to caller-supplied buffer containing Host name to be translated. + This buffer contains 16 bit characters but these are translated to ASCII for use with + DNSv4 server and there is no requirement for driver to support non-ASCII Unicode characters. + @param[in] Token Pointer to the caller-allocated completion token to return at the completion of the process to translate host name to host address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is.NULL + HostName is NULL + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + +**/ +EFI_STATUS +EFIAPI +Dns4HostNameToIp ( + IN EFI_DNS4_PROTOCOL *This, + IN CHAR16 *HostName, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + + DNS_INSTANCE *Instance; + + EFI_DNS4_CONFIG_DATA *ConfigData; + + UINTN Index; + DNS4_CACHE *Item; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + + CHAR8 *QueryName; + + DNS4_TOKEN_ENTRY *TokenEntry; + NET_BUF *Packet; + + EFI_TPL OldTpl; + + Status = EFI_SUCCESS; + Item = NULL; + QueryName = NULL; + TokenEntry = NULL; + Packet = NULL; + + // + // Validate the parameters + // + if ((This == NULL) || (HostName == NULL) || Token == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL4 (This); + + ConfigData = &(Instance->Dns4CfgData); + + Instance->MaxRetry = ConfigData->RetryCount; + + Token->Status = EFI_NOT_READY; + Token->RetryCount = 0; + Token->RetryInterval = ConfigData->RetryInterval; + + if (Instance->State != DNS_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + // + // Check the MaxRetry and RetryInterval values. + // + if (Instance->MaxRetry == 0) { + Instance->MaxRetry = DNS_DEFAULT_RETRY; + } + + if (Token->RetryInterval < DNS_DEFAULT_TIMEOUT) { + Token->RetryInterval = DNS_DEFAULT_TIMEOUT; + } + + // + // Check cache + // + if (ConfigData->EnableDnsCache) { + Index = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns4CacheList) { + Item = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink); + if (StrCmp (HostName, Item->DnsCache.HostName) == 0) { + Index++; + } + } + + if (Index != 0) { + Token->RspData.H2AData = AllocatePool (sizeof (DNS_HOST_TO_ADDR_DATA)); + if (Token->RspData.H2AData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Token->RspData.H2AData->IpCount = (UINT32)Index; + Token->RspData.H2AData->IpList = AllocatePool (sizeof (EFI_IPv4_ADDRESS) * Index); + if (Token->RspData.H2AData->IpList == NULL) { + if (Token->RspData.H2AData != NULL) { + FreePool (Token->RspData.H2AData); + } + + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Index = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns4CacheList) { + Item = NET_LIST_USER_STRUCT (Entry, DNS4_CACHE, AllCacheLink); + if ((UINT32)Index < Token->RspData.H2AData->IpCount && StrCmp (HostName, Item->DnsCache.HostName) == 0) { + CopyMem ((Token->RspData.H2AData->IpList) + Index, Item->DnsCache.IpAddress, sizeof (EFI_IPv4_ADDRESS)); + Index++; + } + } + + Token->Status = EFI_SUCCESS; + + if (Token->Event != NULL) { + gBS->SignalEvent (Token->Event); + DispatchDpc (); + } + + Status = Token->Status; + goto ON_EXIT; + } + } + + // + // Construct DNS TokenEntry. + // + TokenEntry = AllocateZeroPool (sizeof(DNS4_TOKEN_ENTRY)); + if (TokenEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + TokenEntry->PacketToLive = Token->RetryInterval; + TokenEntry->QueryHostName = HostName; + TokenEntry->Token = Token; + + // + // Construct QName. + // + QueryName = DnsFillinQNameForQueryIp (TokenEntry->QueryHostName); + if (QueryName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Construct DNS Query Packet. + // + Status = ConstructDNSQuery (Instance, QueryName, DNS_TYPE_A, DNS_CLASS_INET, &Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + goto ON_EXIT; + } + + ASSERT (Packet != NULL); + + // + // Save the token into the Dns4TxTokens map. + // + Status = NetMapInsertTail (&Instance->Dns4TxTokens, TokenEntry, Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + NetbufFree (Packet); + + goto ON_EXIT; + } + + // + // Dns Query Ip + // + Status = DoDnsQuery (Instance, Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + NetbufFree (Packet); + } + +ON_EXIT: + if (QueryName != NULL) { + FreePool (QueryName); + } + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + The function is used to translate the host address to host name. + A type PTR query is used to get the primary name of the host. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] IpAddress IP address. + @param[in] Token Pointer to the caller-allocated completion used token to translate host address to host name. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is NULL. + IpAddress is not valid IP address. + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +Dns4IpToHostName ( + IN EFI_DNS4_PROTOCOL *This, + IN EFI_IPv4_ADDRESS IpAddress, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ) +{ + return EFI_UNSUPPORTED; +} + +/** + This function retrieves arbitrary information from the DNS. + The caller supplies a QNAME, QTYPE, and QCLASS, and all of the matching RRs are returned. + All RR content (e.g., Ttl) was returned. + The caller need parse the returned RR to get required information. This function is optional. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] QName Pointer to Query Name. + @param[in] QType Query Type. + @param[in] QClass Query Name. + @param[in] Token Point to the caller-allocated completion token to retrieve arbitrary information. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is NULL. + QName is NULL. + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_ALREADY_STARTED This Token is being used in another DNS session. + @retval EFI_UNSUPPORTED This function is not supported. Or the requested QType is not supported + +**/ +EFI_STATUS +EFIAPI +Dns4GeneralLookUp ( + IN EFI_DNS4_PROTOCOL *This, + IN CHAR8 *QName, + IN UINT16 QType, + IN UINT16 QClass, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + + DNS_INSTANCE *Instance; + + EFI_DNS4_CONFIG_DATA *ConfigData; + + DNS4_TOKEN_ENTRY *TokenEntry; + NET_BUF *Packet; + + EFI_TPL OldTpl; + + Status = EFI_SUCCESS; + TokenEntry = NULL; + Packet = NULL; + + // + // Validate the parameters + // + if ((This == NULL) || (QName == NULL) || Token == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL4 (This); + + ConfigData = &(Instance->Dns4CfgData); + + Instance->MaxRetry = ConfigData->RetryCount; + + Token->Status = EFI_NOT_READY; + Token->RetryCount = 0; + Token->RetryInterval = ConfigData->RetryInterval; + + if (Instance->State != DNS_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + // + // Check the MaxRetry and RetryInterval values. + // + if (Instance->MaxRetry == 0) { + Instance->MaxRetry = DNS_DEFAULT_RETRY; + } + + if (Token->RetryInterval < DNS_DEFAULT_TIMEOUT) { + Token->RetryInterval = DNS_DEFAULT_TIMEOUT; + } + + // + // Construct DNS TokenEntry. + // + TokenEntry = AllocateZeroPool (sizeof(DNS4_TOKEN_ENTRY)); + if (TokenEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + TokenEntry->PacketToLive = Token->RetryInterval; + TokenEntry->GeneralLookUp = TRUE; + TokenEntry->Token = Token; + + // + // Construct DNS Query Packet. + // + Status = ConstructDNSQuery (Instance, QName, QType, QClass, &Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + goto ON_EXIT; + } + + ASSERT (Packet != NULL); + + // + // Save the token into the Dns4TxTokens map. + // + Status = NetMapInsertTail (&Instance->Dns4TxTokens, TokenEntry, Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + NetbufFree (Packet); + + goto ON_EXIT; + } + + // + // Dns Query Ip + // + Status = DoDnsQuery (Instance, Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + NetbufFree (Packet); + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + This function is used to add/delete/modify DNS cache entry. + DNS cache can be normally dynamically updated after the DNS resolve succeeds. + This function provided capability to manually add/delete/modify the DNS cache. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] DeleteFlag If FALSE, this function is to add one entry to the DNS Cache. + If TRUE, this function will delete matching DNS Cache entry. + @param[in] Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter. + If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists. + @param[in] DnsCacheEntry Pointer to DNS Cache entry. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + DnsCacheEntry.HostName is NULL. + DnsCacheEntry.IpAddress is NULL. + DnsCacheEntry.Timeout is zero. + @retval EFI_ACCESS_DENIED The DNS cache entry already exists and Override is not TRUE. + +**/ +EFI_STATUS +EFIAPI +Dns4UpdateDnsCache ( + IN EFI_DNS4_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN BOOLEAN Override, + IN EFI_DNS4_CACHE_ENTRY DnsCacheEntry + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + + Status = EFI_SUCCESS; + + if (DnsCacheEntry.HostName == NULL || DnsCacheEntry.IpAddress == NULL || DnsCacheEntry.Timeout == 0) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Update Dns4Cache here. + // + Status = UpdateDns4Cache (&mDriverData->Dns4CacheList, DeleteFlag, Override, DnsCacheEntry); + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + This function can be used by network drivers and applications to increase the rate that data packets are moved between + the communications device and the transmit and receive queues. In some systems, the periodic timer event in the managed + network driver may not poll the underlying communications device fast enough to transmit and/or receive all data packets + without missing incoming packets or dropping outgoing packets. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Dns4Poll ( + IN EFI_DNS4_PROTOCOL *This + ) +{ + DNS_INSTANCE *Instance; + EFI_UDP4_PROTOCOL *Udp; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL4 (This); + + if (Instance->State == DNS_STATE_UNCONFIGED) { + return EFI_NOT_STARTED; + } else if (Instance->State == DNS_STATE_DESTROY) { + return EFI_DEVICE_ERROR; + } + + Udp = Instance->UdpIo->Protocol.Udp4; + + return Udp->Poll (Udp); +} + +/** + This function is used to abort a pending resolution request. + After calling this function, Token.Status will be set to EFI_ABORTED and then Token. + + @param[in] This Pointer to EFI_DNS4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by EFI_DNS4_PROTOCOL.HostNameToIp(), + EFI_DNS4_PROTOCOL.IpToHostName() or EFI_DNS4_PROTOCOL.GeneralLookup(). + If NULL, all pending tokens are aborted. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started. + @retval EFI_NOT_FOUND When Token is not NULL, and the asynchronous DNS operation was not found in the transmit queue. + It was either completed or was not issued by HostNameToIp(), IpToHostName() or GeneralLookup(). + +**/ +EFI_STATUS +EFIAPI +Dns4Cancel ( + IN EFI_DNS4_PROTOCOL *This, + IN EFI_DNS4_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + DNS_INSTANCE *Instance; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL4 (This); + + if (Instance->State == DNS_STATE_UNCONFIGED) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Cancle the tokens specified by Token for this instance. + // + Status = Dns4InstanceCancelToken (Instance, Token); + + // + // Dispatch the DPC queued by the NotifyFunction of the canceled token's events. + // + DispatchDpc (); + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + This function is used to retrieve DNS mode data for this DNS instance. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[out] DnsModeData Pointer to the caller-allocated storage for the EFI_DNS6_MODE_DATA structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED When DnsConfigData is queried, no configuration data is + available because this instance has not been configured. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL or DnsModeData is NULL. + +**/ +EFI_STATUS +EFIAPI +Dns6GetModeData ( + IN EFI_DNS6_PROTOCOL *This, + OUT EFI_DNS6_MODE_DATA *DnsModeData + ) +{ + DNS_INSTANCE *Instance; + + EFI_TPL OldTpl; + + UINTN Index; + + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + + DNS6_SERVER_IP *ServerItem; + EFI_IPv6_ADDRESS *ServerList; + DNS6_CACHE *CacheItem; + EFI_DNS6_CACHE_ENTRY *CacheList; + EFI_STATUS Status; + + ServerItem = NULL; + ServerList = NULL; + CacheItem = NULL; + CacheList = NULL; + Status = EFI_SUCCESS; + + if ((This == NULL) || (DnsModeData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL6 (This); + if (Instance->State == DNS_STATE_UNCONFIGED) { + gBS->RestoreTPL (OldTpl); + return EFI_NOT_STARTED; + } + + ZeroMem (DnsModeData, sizeof (EFI_DNS6_MODE_DATA)); + + // + // Get the current configuration data of this instance. + // + Status = Dns6CopyConfigure(&DnsModeData->DnsConfigData, &Instance->Dns6CfgData); + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // Get the DnsServerCount and DnsServerList + // + Index = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns6ServerList) { + Index++; + } + DnsModeData->DnsServerCount = (UINT32) Index; + ServerList = AllocatePool (sizeof(EFI_IPv6_ADDRESS) * DnsModeData->DnsServerCount); + ASSERT (ServerList != NULL); + Index = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns6ServerList) { + ServerItem = NET_LIST_USER_STRUCT (Entry, DNS6_SERVER_IP, AllServerLink); + CopyMem (ServerList + Index, &ServerItem->Dns6ServerIp, sizeof (EFI_IPv6_ADDRESS)); + Index++; + } + DnsModeData->DnsServerList = ServerList; + + // + // Get the DnsCacheCount and DnsCacheList + // + Index =0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns6CacheList) { + Index++; + } + DnsModeData->DnsCacheCount = (UINT32) Index; + CacheList = AllocatePool (sizeof(EFI_DNS6_CACHE_ENTRY) * DnsModeData->DnsCacheCount); + ASSERT (CacheList != NULL); + Index =0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns6CacheList) { + CacheItem = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink); + CopyMem (CacheList + Index, &CacheItem->DnsCache, sizeof (EFI_DNS6_CACHE_ENTRY)); + Index++; + } + DnsModeData->DnsCacheList = CacheList; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +/** + The function is used to set and change the configuration data for this EFI DNSv6 Protocol driver instance. + Reset the DNS instance if DnsConfigData is NULL. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] DnsConfigData Pointer to the configuration data structure. + All associated storage to be allocated and released by caller. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_UNSUPPORTED The designated protocol is not supported. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + The StationIp address provided in DnsConfigData is not a valid unicast. + DnsServerList is NULL while DnsServerListCount is not equal to Zero. + DnsServerListCount is Zero while DnsServerList is not equal to NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI DNSv6 Protocol instance is not configured. + +**/ +EFI_STATUS +EFIAPI +Dns6Configure ( + IN EFI_DNS6_PROTOCOL *This, + IN EFI_DNS6_CONFIG_DATA *DnsConfigData + ) +{ + EFI_STATUS Status; + DNS_INSTANCE *Instance; + + EFI_TPL OldTpl; + + UINT32 ServerListCount; + EFI_IPv6_ADDRESS *ServerList; + + Status = EFI_SUCCESS; + ServerList = NULL; + + if (This == NULL || + (DnsConfigData != NULL && ((DnsConfigData->DnsServerCount != 0 && DnsConfigData->DnsServerList == NULL) || + (DnsConfigData->DnsServerCount == 0 && DnsConfigData->DnsServerList != NULL)))) { + return EFI_INVALID_PARAMETER; + } + + if (DnsConfigData != NULL && DnsConfigData->Protocol != DNS_PROTOCOL_UDP) { + return EFI_UNSUPPORTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL6 (This); + + if (DnsConfigData == NULL) { + ZeroMem (&Instance->SessionDnsServer, sizeof (EFI_IP_ADDRESS)); + + // + // Reset the Instance if ConfigData is NULL + // + if (!NetMapIsEmpty(&Instance->Dns6TxTokens)) { + Dns6InstanceCancelToken(Instance, NULL); + } + + Instance->MaxRetry = 0; + + if (Instance->UdpIo != NULL){ + UdpIoCleanIo (Instance->UdpIo); + } + + if (Instance->Dns6CfgData.DnsServerList != NULL) { + FreePool (Instance->Dns6CfgData.DnsServerList); + } + ZeroMem (&Instance->Dns6CfgData, sizeof (EFI_DNS6_CONFIG_DATA)); + + Instance->State = DNS_STATE_UNCONFIGED; + } else { + // + // Configure the parameters for new operation. + // + if (!NetIp6IsUnspecifiedAddr (&DnsConfigData->StationIp) && !NetIp6IsValidUnicast (&DnsConfigData->StationIp)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Status = Dns6CopyConfigure (&Instance->Dns6CfgData, DnsConfigData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (DnsConfigData->DnsServerCount == 0 || DnsConfigData->DnsServerList == NULL) { + gBS->RestoreTPL (OldTpl); + + // + //The DNS instance will retrieve DNS server from DHCP Server. + // + Status = GetDns6ServerFromDhcp6 ( + Instance->Service->ImageHandle, + Instance->Service->ControllerHandle, + &ServerListCount, + &ServerList + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + ASSERT(ServerList != NULL); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + CopyMem (&Instance->SessionDnsServer.v6, &ServerList[0], sizeof (EFI_IPv6_ADDRESS)); + } else { + CopyMem (&Instance->SessionDnsServer.v6, &DnsConfigData->DnsServerList[0], sizeof (EFI_IPv6_ADDRESS)); + } + + // + // Config UDP + // + Status = Dns6ConfigUdp (Instance, Instance->UdpIo); + if (EFI_ERROR (Status)) { + if (Instance->Dns6CfgData.DnsServerList != NULL) { + FreePool (Instance->Dns6CfgData.DnsServerList); + } + goto ON_EXIT; + } + + // + // Add configured DNS server used by this instance to ServerList. + // + Status = AddDns6ServerIp (&mDriverData->Dns6ServerList, Instance->SessionDnsServer.v6); + if (EFI_ERROR (Status)) { + if (Instance->Dns6CfgData.DnsServerList != NULL) { + FreePool (Instance->Dns6CfgData.DnsServerList); + } + goto ON_EXIT; + } + + Instance->State = DNS_STATE_CONFIGED; + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + The function is used to translate the host name to host IP address. + A type AAAA query is used to get the one or more IPv6 addresses for this host. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] HostName Pointer to caller-supplied buffer containing Host name to be translated. + This buffer contains 16 bit characters but these are translated to ASCII for use with + DNSv4 server and there is no requirement for driver to support non-ASCII Unicode characters. + @param[in] Token Pointer to the caller-allocated completion token to return at the completion of the process to translate host name to host address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is.NULL + HostName is NULL + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + +**/ +EFI_STATUS +EFIAPI +Dns6HostNameToIp ( + IN EFI_DNS6_PROTOCOL *This, + IN CHAR16 *HostName, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + + DNS_INSTANCE *Instance; + + EFI_DNS6_CONFIG_DATA *ConfigData; + + UINTN Index; + DNS6_CACHE *Item; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + + CHAR8 *QueryName; + + DNS6_TOKEN_ENTRY *TokenEntry; + NET_BUF *Packet; + + EFI_TPL OldTpl; + + Status = EFI_SUCCESS; + Item = NULL; + QueryName = NULL; + TokenEntry = NULL; + Packet = NULL; + + // + // Validate the parameters + // + if ((This == NULL) || (HostName == NULL) || Token == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL6 (This); + + ConfigData = &(Instance->Dns6CfgData); + + Instance->MaxRetry = ConfigData->RetryCount; + + Token->Status = EFI_NOT_READY; + Token->RetryCount = 0; + Token->RetryInterval = ConfigData->RetryInterval; + + if (Instance->State != DNS_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + // + // Check the MaxRetry and RetryInterval values. + // + if (Instance->MaxRetry == 0) { + Instance->MaxRetry = DNS_DEFAULT_RETRY; + } + + if (Token->RetryInterval < DNS_DEFAULT_TIMEOUT) { + Token->RetryInterval = DNS_DEFAULT_TIMEOUT; + } + + // + // Check cache + // + if (ConfigData->EnableDnsCache) { + Index = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns6CacheList) { + Item = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink); + if (StrCmp (HostName, Item->DnsCache.HostName) == 0) { + Index++; + } + } + + if (Index != 0) { + Token->RspData.H2AData = AllocatePool (sizeof (DNS6_HOST_TO_ADDR_DATA)); + if (Token->RspData.H2AData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Token->RspData.H2AData->IpCount = (UINT32)Index; + Token->RspData.H2AData->IpList = AllocatePool (sizeof (EFI_IPv6_ADDRESS) * Index); + if (Token->RspData.H2AData->IpList == NULL) { + if (Token->RspData.H2AData != NULL) { + FreePool (Token->RspData.H2AData); + } + + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Index = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &mDriverData->Dns6CacheList) { + Item = NET_LIST_USER_STRUCT (Entry, DNS6_CACHE, AllCacheLink); + if ((UINT32)Index < Token->RspData.H2AData->IpCount && StrCmp (HostName, Item->DnsCache.HostName) == 0) { + CopyMem ((Token->RspData.H2AData->IpList) + Index, Item->DnsCache.IpAddress, sizeof (EFI_IPv6_ADDRESS)); + Index++; + } + } + + Token->Status = EFI_SUCCESS; + + if (Token->Event != NULL) { + gBS->SignalEvent (Token->Event); + DispatchDpc (); + } + + Status = Token->Status; + goto ON_EXIT; + } + } + + // + // Construct DNS TokenEntry. + // + TokenEntry = AllocateZeroPool (sizeof (DNS6_TOKEN_ENTRY)); + if (TokenEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + TokenEntry->PacketToLive = Token->RetryInterval; + TokenEntry->QueryHostName = HostName; + TokenEntry->Token = Token; + + + // + // Construct QName. + // + QueryName = DnsFillinQNameForQueryIp (TokenEntry->QueryHostName); + if (QueryName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Construct DNS Query Packet. + // + Status = ConstructDNSQuery (Instance, QueryName, DNS_TYPE_AAAA, DNS_CLASS_INET, &Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + goto ON_EXIT; + } + + ASSERT (Packet != NULL); + + // + // Save the token into the Dns6TxTokens map. + // + Status = NetMapInsertTail (&Instance->Dns6TxTokens, TokenEntry, Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + NetbufFree (Packet); + + goto ON_EXIT; + } + + // + // Dns Query Ip + // + Status = DoDnsQuery (Instance, Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + NetbufFree (Packet); + } + +ON_EXIT: + if (QueryName != NULL) { + FreePool (QueryName); + } + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + The function is used to translate the host address to host name. + A type PTR query is used to get the primary name of the host. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] IpAddress IP address. + @param[in] Token Pointer to the caller-allocated completion used token to translate host address to host name. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is NULL. + IpAddress is not valid IP address. + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +EFIAPI +Dns6IpToHostName ( + IN EFI_DNS6_PROTOCOL *This, + IN EFI_IPv6_ADDRESS IpAddress, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ) +{ + return EFI_UNSUPPORTED; +} + +/** + This function retrieves arbitrary information from the DNS. + The caller supplies a QNAME, QTYPE, and QCLASS, and all of the matching RRs are returned. + All RR content (e.g., Ttl) was returned. + The caller need parse the returned RR to get required information. This function is optional. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] QName Pointer to Query Name. + @param[in] QType Query Type. + @param[in] QClass Query Name. + @param[in] Token Point to the caller-allocated completion token to retrieve arbitrary information. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_INVALID_PARAMETER This is NULL. + Token is NULL. + Token.Event is NULL. + QName is NULL. + @retval EFI_NO_MAPPING There's no source address is available for use. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_UNSUPPORTED This function is not supported. Or the requested QType is not supported + +**/ +EFI_STATUS +EFIAPI +Dns6GeneralLookUp ( + IN EFI_DNS6_PROTOCOL *This, + IN CHAR8 *QName, + IN UINT16 QType, + IN UINT16 QClass, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + + DNS_INSTANCE *Instance; + + EFI_DNS6_CONFIG_DATA *ConfigData; + + DNS6_TOKEN_ENTRY *TokenEntry; + NET_BUF *Packet; + + EFI_TPL OldTpl; + + Status = EFI_SUCCESS; + TokenEntry = NULL; + Packet = NULL; + + // + // Validate the parameters + // + if ((This == NULL) || (QName == NULL) || Token == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL6 (This); + + ConfigData = &(Instance->Dns6CfgData); + + Instance->MaxRetry = ConfigData->RetryCount; + + Token->Status = EFI_NOT_READY; + Token->RetryCount = 0; + Token->RetryInterval = ConfigData->RetryInterval; + + if (Instance->State != DNS_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + // + // Check the MaxRetry and RetryInterval values. + // + if (Instance->MaxRetry == 0) { + Instance->MaxRetry = DNS_DEFAULT_RETRY; + } + + if (Token->RetryInterval < DNS_DEFAULT_TIMEOUT) { + Token->RetryInterval = DNS_DEFAULT_TIMEOUT; + } + + // + // Construct DNS TokenEntry. + // + TokenEntry = AllocateZeroPool (sizeof(DNS6_TOKEN_ENTRY)); + if (TokenEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + TokenEntry->PacketToLive = Token->RetryInterval; + TokenEntry->GeneralLookUp = TRUE; + TokenEntry->Token = Token; + + // + // Construct DNS Query Packet. + // + Status = ConstructDNSQuery (Instance, QName, QType, QClass, &Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + goto ON_EXIT; + } + + ASSERT (Packet != NULL); + + // + // Save the token into the Dns6TxTokens map. + // + Status = NetMapInsertTail (&Instance->Dns6TxTokens, TokenEntry, Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + NetbufFree (Packet); + + goto ON_EXIT; + } + + // + // Dns Query Ip + // + Status = DoDnsQuery (Instance, Packet); + if (EFI_ERROR (Status)) { + if (TokenEntry != NULL) { + FreePool (TokenEntry); + } + + NetbufFree (Packet); + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + This function is used to add/delete/modify DNS cache entry. + DNS cache can be normally dynamically updated after the DNS resolve succeeds. + This function provided capability to manually add/delete/modify the DNS cache. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] DeleteFlag If FALSE, this function is to add one entry to the DNS Cache. + If TRUE, this function will delete matching DNS Cache entry. + @param[in] Override If TRUE, the matching DNS cache entry will be overwritten with the supplied parameter. + If FALSE, EFI_ACCESS_DENIED will be returned if the entry to be added is already exists. + @param[in] DnsCacheEntry Pointer to DNS Cache entry. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + DnsCacheEntry.HostName is NULL. + DnsCacheEntry.IpAddress is NULL. + DnsCacheEntry.Timeout is zero. + @retval EFI_ACCESS_DENIED The DNS cache entry already exists and Override is not TRUE. + +**/ +EFI_STATUS +EFIAPI +Dns6UpdateDnsCache ( + IN EFI_DNS6_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN BOOLEAN Override, + IN EFI_DNS6_CACHE_ENTRY DnsCacheEntry + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + + Status = EFI_SUCCESS; + + if (DnsCacheEntry.HostName == NULL || DnsCacheEntry.IpAddress == NULL || DnsCacheEntry.Timeout == 0) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Update Dns6Cache here. + // + Status = UpdateDns6Cache (&mDriverData->Dns6CacheList, DeleteFlag, Override, DnsCacheEntry); + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + This function can be used by network drivers and applications to increase the rate that data packets are moved between + the communications device and the transmit and receive queues. In some systems, the periodic timer event in the managed + network driver may not poll the underlying communications device fast enough to transmit and/or receive all data packets + without missing incoming packets or dropping outgoing packets. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Dns6Poll ( + IN EFI_DNS6_PROTOCOL *This + ) +{ + DNS_INSTANCE *Instance; + EFI_UDP6_PROTOCOL *Udp; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL6 (This); + + if (Instance->State == DNS_STATE_UNCONFIGED) { + return EFI_NOT_STARTED; + } else if (Instance->State == DNS_STATE_DESTROY) { + return EFI_DEVICE_ERROR; + } + + Udp = Instance->UdpIo->Protocol.Udp6; + + return Udp->Poll (Udp); +} + +/** + This function is used to abort a pending resolution request. + After calling this function, Token.Status will be set to EFI_ABORTED and then Token. + + @param[in] This Pointer to EFI_DNS6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by EFI_DNS6_PROTOCOL.HostNameToIp(), + EFI_DNS6_PROTOCOL.IpToHostName() or EFI_DNS6_PROTOCOL.GeneralLookup(). + If NULL, all pending tokens are aborted. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started. + @retval EFI_NOT_FOUND When Token is not NULL, and the asynchronous DNS operation was not found in the transmit queue. + It was either completed or was not issued by HostNameToIp(), IpToHostName() or GeneralLookup(). + +**/ +EFI_STATUS +EFIAPI +Dns6Cancel ( + IN EFI_DNS6_PROTOCOL *This, + IN EFI_DNS6_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + DNS_INSTANCE *Instance; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = DNS_INSTANCE_FROM_THIS_PROTOCOL6 (This); + + if (Instance->State == DNS_STATE_UNCONFIGED) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Cancle the tokens specified by Token for this instance. + // + Status = Dns6InstanceCancelToken (Instance, Token); + + // + // Dispatch the DPC queued by the NotifyFunction of the canceled token's events. + // + DispatchDpc (); + + gBS->RestoreTPL (OldTpl); + + return Status; +} + diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootClient.c b/Core/NetworkPkg/HttpBootDxe/HttpBootClient.c new file mode 100644 index 0000000000..319fefbbbd --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootClient.c @@ -0,0 +1,1077 @@ +/** @file + Implementation of the boot file download function. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/** + Update the IP and URL device path node to include the boot resource information. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Device patch successfully updated. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootUpdateDevicePath ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_DEV_PATH *Node; + EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + UINTN Length; + EFI_STATUS Status; + + TmpDevicePath = NULL; + + // + // Update the IP node with DHCP assigned information. + // + if (!Private->UsingIpv6) { + Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH)); + if (Node == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv4.Header.SubType = MSG_IPv4_DP; + SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH)); + CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); + Node->Ipv4.RemotePort = Private->Port; + Node->Ipv4.Protocol = EFI_IP_PROTO_TCP; + Node->Ipv4.StaticIpAddress = FALSE; + CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + } else { + Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH)); + if (Node == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv6.Header.SubType = MSG_IPv6_DP; + SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH)); + Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH; + Node->Ipv6.RemotePort = Private->Port; + Node->Ipv6.Protocol = EFI_IP_PROTO_TCP; + Node->Ipv6.IpAddressOrigin = 0; + CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS)); + } + + TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + if (TmpDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Update the URI node with the boot file URI. + // + Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri); + Node = AllocatePool (Length); + if (Node == NULL) { + FreePool (TmpDevicePath); + return EFI_OUT_OF_RESOURCES; + } + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_URI_DP; + SetDevicePathNodeLength (Node, Length); + CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri)); + + NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (TmpDevicePath); + if (NewDevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (!Private->UsingIpv6) { + // + // Reinstall the device path protocol of the child handle. + // + Status = gBS->ReinstallProtocolInterface ( + Private->Ip4Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + NewDevicePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + + FreePool (Private->Ip4Nic->DevicePath); + Private->Ip4Nic->DevicePath = NewDevicePath; + } else { + // + // Reinstall the device path protocol of the child handle. + // + Status = gBS->ReinstallProtocolInterface ( + Private->Ip6Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + NewDevicePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + FreePool (Private->Ip6Nic->DevicePath); + Private->Ip6Nic->DevicePath = NewDevicePath; + } + + return EFI_SUCCESS; +} + +/** + Parse the boot file URI information from the selected Dhcp4 offer packet. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully parsed out all the boot information. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +HttpBootDhcp4ExtractUriInfo ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + HTTP_BOOT_DHCP4_PACKET_CACHE *SelectOffer; + HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer; + UINT32 SelectIndex; + UINT32 ProxyIndex; + EFI_DHCP4_PACKET_OPTION *Option; + EFI_STATUS Status; + + ASSERT (Private != NULL); + ASSERT (Private->SelectIndex != 0); + SelectIndex = Private->SelectIndex - 1; + ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM); + + Status = EFI_SUCCESS; + + // + // SelectOffer contains the IP address configuration and name server configuration. + // HttpOffer contains the boot file URL. + // + SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4; + if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { + HttpOffer = SelectOffer; + } else { + ASSERT (Private->SelectProxyType != HttpOfferTypeMax); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4; + } + + // + // Configure the default DNS server if server assigned. + // + if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) { + Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER]; + ASSERT (Option != NULL); + Status = HttpBootRegisterIp4Dns ( + Private, + Option->Length, + Option->Data + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Extract the port from URL, and use default HTTP port 80 if not provided. + // + Status = HttpUrlGetPort ( + (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, + HttpOffer->UriParser, + &Private->Port + ); + if (EFI_ERROR (Status) || Private->Port == 0) { + Private->Port = 80; + } + + // + // Record the URI of boot file from the selected HTTP offer. + // + Private->BootFileUriParser = HttpOffer->UriParser; + Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data; + + + // + // All boot informations are valid here. + // + AsciiPrint ("\n URI: %a", Private->BootFileUri); + + // + // Update the device path to include the IP and boot URI information. + // + Status = HttpBootUpdateDevicePath (Private); + + return Status; +} + +/** + Parse the boot file URI information from the selected Dhcp6 offer packet. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully parsed out all the boot information. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +HttpBootDhcp6ExtractUriInfo ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + HTTP_BOOT_DHCP6_PACKET_CACHE *SelectOffer; + HTTP_BOOT_DHCP6_PACKET_CACHE *HttpOffer; + UINT32 SelectIndex; + UINT32 ProxyIndex; + EFI_DHCP6_PACKET_OPTION *Option; + EFI_IPv6_ADDRESS IpAddr; + CHAR8 *HostName; + UINTN HostNameSize; + CHAR16 *HostNameStr; + EFI_STATUS Status; + + ASSERT (Private != NULL); + ASSERT (Private->SelectIndex != 0); + SelectIndex = Private->SelectIndex - 1; + ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM); + + Status = EFI_SUCCESS; + HostName = NULL; + // + // SelectOffer contains the IP address configuration and name server configuration. + // HttpOffer contains the boot file URL. + // + SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6; + if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) { + HttpOffer = SelectOffer; + } else { + ASSERT (Private->SelectProxyType != HttpOfferTypeMax); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6; + } + + // + // Set the Local station address to IP layer. + // + Status = HttpBootSetIp6Address (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Configure the default DNS server if server assigned. + // + if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) { + Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER]; + ASSERT (Option != NULL); + Status = HttpBootSetIp6Dns ( + Private, + HTONS (Option->OpLen), + Option->Data + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Extract the HTTP server Ip frome URL. This is used to Check route table + // whether can send message to HTTP Server Ip through the GateWay. + // + Status = HttpUrlGetIp6 ( + (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + HttpOffer->UriParser, + &IpAddr + ); + + if (EFI_ERROR (Status)) { + // + // The Http server address is expressed by Name Ip, so perform DNS resolution + // + Status = HttpUrlGetHostName ( + (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + HttpOffer->UriParser, + &HostName + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HostNameSize = AsciiStrSize (HostName); + HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16)); + if (HostNameStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize); + Status = HttpBootDns (Private, HostNameStr, &IpAddr); + FreePool (HostNameStr); + if (EFI_ERROR (Status)) { + goto Error; + } + } + + CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS)); + + // + // register the IPv6 gateway address to the network device. + // + Status = HttpBootSetIp6Gateway (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Extract the port from URL, and use default HTTP port 80 if not provided. + // + Status = HttpUrlGetPort ( + (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + HttpOffer->UriParser, + &Private->Port + ); + if (EFI_ERROR (Status) || Private->Port == 0) { + Private->Port = 80; + } + + // + // Record the URI of boot file from the selected HTTP offer. + // + Private->BootFileUriParser = HttpOffer->UriParser; + Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data; + + + // + // All boot informations are valid here. + // + AsciiPrint ("\n URI: %a", Private->BootFileUri); + // + // Update the device path to include the IP and boot URI information. + // + Status = HttpBootUpdateDevicePath (Private); + +Error: + + if (HostName != NULL) { + FreePool (HostName); + } + + return Status; +} + + +/** + Discover all the boot information for boot file. + + @param[in, out] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully obtained all the boot information . + @retval Others Failed to retrieve the boot information. + +**/ +EFI_STATUS +HttpBootDiscoverBootInfo ( + IN OUT HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + // + // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and + // other Http boot information. + // + Status = HttpBootDhcp (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!Private->UsingIpv6) { + Status = HttpBootDhcp4ExtractUriInfo (Private); + } else { + Status = HttpBootDhcp6ExtractUriInfo (Private); + } + + return Status; +} + +/** + Create a HttpIo instance for the file download. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully created. + @retval Others Failed to create HttpIo. + +**/ +EFI_STATUS +HttpBootCreateHttpIo ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + HTTP_IO_CONFIG_DATA ConfigData; + EFI_STATUS Status; + + ASSERT (Private != NULL); + + ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA)); + if (!Private->UsingIpv6) { + ConfigData.Config4.HttpVersion = HttpVersion11; + ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT; + IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4); + IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4); + } else { + ConfigData.Config6.HttpVersion = HttpVersion11; + ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT; + IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6); + } + + Status = HttpIoCreateIo ( + Private->Image, + Private->Controller, + Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4, + &ConfigData, + &Private->HttpIo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private->HttpCreated = TRUE; + return EFI_SUCCESS; +} + +/** + Release all the resource of a cache item. + + @param[in] Cache The pointer to the cache item. + +**/ +VOID +HttpBootFreeCache ( + IN HTTP_BOOT_CACHE_CONTENT *Cache + ) +{ + UINTN Index; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + HTTP_BOOT_ENTITY_DATA *EntityData; + + if (Cache != NULL) { + // + // Free the request data + // + if (Cache->RequestData != NULL) { + if (Cache->RequestData->Url != NULL) { + FreePool (Cache->RequestData->Url); + } + FreePool (Cache->RequestData); + } + + // + // Free the response header + // + if (Cache->ResponseData != NULL) { + if (Cache->ResponseData->Headers != NULL) { + for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) { + FreePool (Cache->ResponseData->Headers[Index].FieldName); + FreePool (Cache->ResponseData->Headers[Index].FieldValue); + } + FreePool (Cache->ResponseData->Headers); + } + } + + // + // Free the response body + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) { + EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link); + if (EntityData->Block != NULL) { + FreePool (EntityData->Block); + } + RemoveEntryList (&EntityData->Link); + FreePool (EntityData); + } + + FreePool (Cache); + } +} + +/** + Clean up all cached data. + + @param[in] Private The pointer to the driver's private data. + +**/ +VOID +HttpBootFreeCacheList ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + HTTP_BOOT_CACHE_CONTENT *Cache; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) { + Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link); + RemoveEntryList (&Cache->Link); + HttpBootFreeCache (Cache); + } +} + +/** + Get the file content from cached data. + + @param[in] Private The pointer to the driver's private data. + @param[in] Uri Uri of the file to be retrieved from cache. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS Successfully created. + @retval Others Failed to create HttpIo. + +**/ +EFI_STATUS +HttpBootGetFileFromCache ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN CHAR16 *Uri, + IN OUT UINTN *BufferSize, + OUT UINT8 *Buffer + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + HTTP_BOOT_CACHE_CONTENT *Cache; + HTTP_BOOT_ENTITY_DATA *EntityData; + UINTN CopyedSize; + + if (Uri == NULL || BufferSize == 0 || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Search file in the cache list, the cache entry will be released upon a successful + // match. + // + NET_LIST_FOR_EACH (Entry, &Private->CacheList) { + Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link); + // + // Compare the URI to see whether we already have a cache for this file. + // + if ((Cache->RequestData != NULL) && + (Cache->RequestData->Url != NULL) && + (StrCmp (Uri, Cache->RequestData->Url) == 0)) + { + // + // Hit cache, check buffer size. + // + if (*BufferSize < Cache->EntityLength) { + *BufferSize = Cache->EntityLength; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fill data to buffer. + // + CopyedSize = 0; + NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) { + EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link); + if (*BufferSize > CopyedSize) { + CopyMem ( + Buffer + CopyedSize, + EntityData->DataStart, + MIN (EntityData->DataLength, *BufferSize - CopyedSize) + ); + CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize); + } + } + *BufferSize = CopyedSize; + + // + // On success, free the cached data to release the memory resource. + // + RemoveEntryList (&Cache->Link); + HttpBootFreeCache (Cache); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + A callback function to intercept events during message parser. + + This function will be invoked during HttpParseMessageBody() with various events type. An error + return status of the callback function will cause the HttpParseMessageBody() aborted. + + @param[in] EventType Event type of this callback call. + @param[in] Data A pointer to data buffer. + @param[in] Length Length in bytes of the Data. + @param[in] Context Callback context set by HttpInitMsgParser(). + + @retval EFI_SUCCESS Continue to parser the message body. + @retval Others Abort the parse. + +**/ +EFI_STATUS +EFIAPI +HttpBootGetBootFileCallback ( + IN HTTP_BODY_PARSE_EVENT EventType, + IN CHAR8 *Data, + IN UINTN Length, + IN VOID *Context + ) +{ + HTTP_BOOT_CALLBACK_DATA *CallbackData; + HTTP_BOOT_ENTITY_DATA *NewEntityData; + + // + // We only care about the entity data. + // + if (EventType != BodyParseEventOnData) { + return EFI_SUCCESS; + } + + CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context; + // + // Copy data if caller has provided a buffer. + // + if (CallbackData->BufferSize > CallbackData->CopyedSize) { + CopyMem ( + CallbackData->Buffer + CallbackData->CopyedSize, + Data, + MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize) + ); + CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize); + } + + // + // The caller doesn't provide a buffer, save the block into cache list. + // + if (CallbackData->Cache != NULL) { + NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA)); + if (NewEntityData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + if (CallbackData->NewBlock) { + NewEntityData->Block = CallbackData->Block; + CallbackData->Block = NULL; + } + NewEntityData->DataLength = Length; + NewEntityData->DataStart = (UINT8*) Data; + InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link); + } + return EFI_SUCCESS; +} + +/** + This function download the boot file by using UEFI HTTP protocol. + + @param[in] Private The pointer to the driver's private data. + @param[in] HeaderOnly Only request the response header, it could save a lot of time if + the caller only want to know the size of the requested file. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete + the request. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootGetBootFile ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN BOOLEAN HeaderOnly, + IN OUT UINTN *BufferSize, + OUT UINT8 *Buffer + ) +{ + EFI_STATUS Status; + CHAR8 *HostName; + EFI_HTTP_REQUEST_DATA *RequestData; + HTTP_IO_RESPONSE_DATA *ResponseData; + HTTP_IO_RESPONSE_DATA ResponseBody; + HTTP_IO *HttpIo; + HTTP_IO_HEADER *HttpIoHeader; + VOID *Parser; + HTTP_BOOT_CALLBACK_DATA Context; + UINTN ContentLength; + HTTP_BOOT_CACHE_CONTENT *Cache; + UINT8 *Block; + UINTN UrlSize; + CHAR16 *Url; + BOOLEAN IdentityMode; + UINTN ReceivedSize; + + ASSERT (Private != NULL); + ASSERT (Private->HttpCreated); + + if (BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*BufferSize != 0 && Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // First, check whether we already cached the requested Uri. + // + UrlSize = AsciiStrSize (Private->BootFileUri); + Url = AllocatePool (UrlSize * sizeof (CHAR16)); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize); + if (!HeaderOnly) { + Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer); + if (Status != EFI_NOT_FOUND) { + FreePool (Url); + return Status; + } + } + + // + // Not found in cache, try to download it through HTTP. + // + + // + // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer. + // + Cache = NULL; + if ((!HeaderOnly) && (*BufferSize == 0)) { + Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT)); + if (Cache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_1; + } + InitializeListHead (&Cache->EntityDataList); + } + + // + // 2. Send HTTP request message. + // + + // + // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file: + // Host + // Accept + // User-Agent + // + HttpIoHeader = HttpBootCreateHeader (3); + if (HttpIoHeader == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_2; + } + + // + // Add HTTP header field 1: Host + // + HostName = NULL; + Status = HttpUrlGetHostName ( + Private->BootFileUri, + Private->BootFileUriParser, + &HostName + ); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + Status = HttpBootSetHeader ( + HttpIoHeader, + HTTP_FIELD_NAME_HOST, + HostName + ); + FreePool (HostName); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + + // + // Add HTTP header field 2: Accept + // + Status = HttpBootSetHeader ( + HttpIoHeader, + HTTP_FIELD_NAME_ACCEPT, + "*/*" + ); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + + // + // Add HTTP header field 3: User-Agent + // + Status = HttpBootSetHeader ( + HttpIoHeader, + HTTP_FIELD_NAME_USER_AGENT, + HTTP_USER_AGENT_EFI_HTTP_BOOT + ); + if (EFI_ERROR (Status)) { + goto ERROR_3; + } + + // + // 2.2 Build the rest of HTTP request info. + // + RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA)); + if (RequestData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_3; + } + RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet; + RequestData->Url = Url; + + // + // 2.3 Record the request info in a temp cache item. + // + if (Cache != NULL) { + Cache->RequestData = RequestData; + } + + // + // 2.4 Send out the request to HTTP server. + // + HttpIo = &Private->HttpIo; + Status = HttpIoSendRequest ( + HttpIo, + RequestData, + HttpIoHeader->HeaderCount, + HttpIoHeader->Headers, + 0, + NULL + ); + if (EFI_ERROR (Status)) { + goto ERROR_4; + } + + // + // 3. Receive HTTP response message. + // + + // + // 3.1 First step, use zero BodyLength to only receive the response headers. + // + ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA)); + if (ResponseData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_4; + } + Status = HttpIoRecvResponse ( + &Private->HttpIo, + TRUE, + ResponseData + ); + if (EFI_ERROR (Status)) { + goto ERROR_5; + } + + // + // 3.2 Cache the response header. + // + if (Cache != NULL) { + Cache->ResponseData = ResponseData; + } + + // + // 3.3 Init a message-body parser from the header information. + // + Parser = NULL; + Context.NewBlock = FALSE; + Context.Block = NULL; + Context.CopyedSize = 0; + Context.Buffer = Buffer; + Context.BufferSize = *BufferSize; + Context.Cache = Cache; + Status = HttpInitMsgParser ( + HeaderOnly? HttpMethodHead : HttpMethodGet, + ResponseData->Response.StatusCode, + ResponseData->HeaderCount, + ResponseData->Headers, + HttpBootGetBootFileCallback, + (VOID*) &Context, + &Parser + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + + // + // 3.4 Continue to receive and parse message-body if needed. + // + Block = NULL; + if (!HeaderOnly) { + // + // 3.4.1, check whether we are in identity transfer-coding. + // + ContentLength = 0; + Status = HttpGetEntityLength (Parser, &ContentLength); + if (!EFI_ERROR (Status)) { + IdentityMode = TRUE; + } else { + IdentityMode = FALSE; + } + + // + // 3.4.2, start the message-body download, the identity and chunked transfer-coding + // is handled in different path here. + // + ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA)); + if (IdentityMode) { + // + // In identity transfer-coding there is no need to parse the message body, + // just download the message body to the user provided buffer directly. + // + ReceivedSize = 0; + while (ReceivedSize < ContentLength) { + ResponseBody.Body = (CHAR8*) Buffer + ReceivedSize; + ResponseBody.BodyLength = *BufferSize - ReceivedSize; + Status = HttpIoRecvResponse ( + &Private->HttpIo, + FALSE, + &ResponseBody + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + ReceivedSize += ResponseBody.BodyLength; + } + } else { + // + // In "chunked" transfer-coding mode, so we need to parse the received + // data to get the real entity content. + // + Block = NULL; + while (!HttpIsMessageComplete (Parser)) { + // + // Allocate a buffer in Block to hold the message-body. + // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse(). + // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before + // every HttpIoRecvResponse(). + // + if (Block == NULL || Context.BufferSize == 0) { + Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE); + if (Block == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ERROR_6; + } + Context.NewBlock = TRUE; + Context.Block = Block; + } else { + Context.NewBlock = FALSE; + } + + ResponseBody.Body = (CHAR8*) Block; + ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE; + Status = HttpIoRecvResponse ( + &Private->HttpIo, + FALSE, + &ResponseBody + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + + // + // Parse the new received block of the message-body, the block will be saved in cache. + // + Status = HttpParseMessageBody ( + Parser, + ResponseBody.BodyLength, + ResponseBody.Body + ); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + } + } + } + + // + // 3.5 Message-body receive & parse is completed, we should be able to get the file size now. + // + Status = HttpGetEntityLength (Parser, &ContentLength); + if (EFI_ERROR (Status)) { + goto ERROR_6; + } + + if (*BufferSize < ContentLength) { + Status = EFI_BUFFER_TOO_SMALL; + } + *BufferSize = ContentLength; + + // + // 4. Save the cache item to driver's cache list and return. + // + if (Cache != NULL) { + Cache->EntityLength = ContentLength; + InsertTailList (&Private->CacheList, &Cache->Link); + } + + if (Parser != NULL) { + HttpFreeMsgParser (Parser); + } + + return EFI_SUCCESS; + +ERROR_6: + if (Parser != NULL) { + HttpFreeMsgParser (Parser); + } + if (Context.Block != NULL) { + FreePool (Context.Block); + } + HttpBootFreeCache (Cache); + +ERROR_5: + if (ResponseData != NULL) { + FreePool (ResponseData); + } +ERROR_4: + if (RequestData != NULL) { + FreePool (RequestData); + } +ERROR_3: + HttpBootFreeHeader (HttpIoHeader); +ERROR_2: + if (Cache != NULL) { + FreePool (Cache); + } +ERROR_1: + if (Url != NULL) { + FreePool (Url); + } + + return Status; +} diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootClient.h b/Core/NetworkPkg/HttpBootDxe/HttpBootClient.h new file mode 100644 index 0000000000..e618316f10 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootClient.h @@ -0,0 +1,139 @@ +/** @file + Declaration of the boot file download function. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_BOOT_HTTP_H__ +#define __EFI_HTTP_BOOT_HTTP_H__ + +#define HTTP_BOOT_REQUEST_TIMEOUT 5000 // 5 seconds in uints of millisecond. +#define HTTP_BOOT_BLOCK_SIZE 1500 + +#define HTTP_FIELD_NAME_USER_AGENT "User-Agent" +#define HTTP_FIELD_NAME_HOST "Host" +#define HTTP_FIELD_NAME_ACCEPT "Accept" + + +#define HTTP_USER_AGENT_EFI_HTTP_BOOT "UefiHttpBoot/1.0" + +// +// Record the data length and start address of a data block. +// +typedef struct { + LIST_ENTRY Link; // Link to the EntityDataList in HTTP_BOOT_CACHE_CONTENT + UINT8 *Block; // If NULL, the data is in previous data block. + UINT8 *DataStart; // Point to somewhere in the Block + UINTN DataLength; +} HTTP_BOOT_ENTITY_DATA; + +// +// Structure for a cache item +// +typedef struct { + LIST_ENTRY Link; // Link to the CacheList in driver's private data. + EFI_HTTP_REQUEST_DATA *RequestData; + HTTP_IO_RESPONSE_DATA *ResponseData; // Not include any message-body data. + UINTN EntityLength; + LIST_ENTRY EntityDataList; // Entity data (message-body) +} HTTP_BOOT_CACHE_CONTENT; + +// +// Callback data for HTTP_BODY_PARSER_CALLBACK() +// +typedef struct { + EFI_STATUS Status; + // + // Cache info. + // + HTTP_BOOT_CACHE_CONTENT *Cache; + BOOLEAN NewBlock; + UINT8 *Block; + + // + // Caller provided buffer to load the file in. + // + UINTN CopyedSize; + UINTN BufferSize; + UINT8 *Buffer; +} HTTP_BOOT_CALLBACK_DATA; + +/** + Discover all the boot information for boot file. + + @param[in, out] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully obtained all the boot information . + @retval Others Failed to retrieve the boot information. + +**/ +EFI_STATUS +HttpBootDiscoverBootInfo ( + IN OUT HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + Create a HttpIo instance for the file download. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Successfully created. + @retval Others Failed to create HttpIo. + +**/ +EFI_STATUS +HttpBootCreateHttpIo ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + This function download the boot file by using UEFI HTTP protocol. + + @param[in] Private The pointer to the driver's private data. + @param[in] HeaderOnly Only request the response header, it could save a lot of time if + the caller only want to know the size of the requested file. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete + the request. + @retval Others Unexpected error happened. + +**/ +EFI_STATUS +HttpBootGetBootFile ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN BOOLEAN HeaderOnly, + IN OUT UINTN *BufferSize, + OUT UINT8 *Buffer + ); + +/** + Clean up all cached data. + + @param[in] Private The pointer to the driver's private data. + +**/ +VOID +HttpBootFreeCacheList ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +#endif diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.c b/Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.c new file mode 100644 index 0000000000..2c39089da3 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.c @@ -0,0 +1,183 @@ +/** @file + Implementation of EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL protocol. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/// +/// Component Name Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME_PROTOCOL gHttpBootDxeComponentName = { + (EFI_COMPONENT_NAME_GET_DRIVER_NAME) HttpBootDxeComponentNameGetDriverName, + (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME)HttpBootDxeComponentNameGetControllerName, + "eng" +}; + +/// +/// Component Name 2 Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME2_PROTOCOL gHttpBootDxeComponentName2 = { + HttpBootDxeComponentNameGetDriverName, + HttpBootDxeComponentNameGetControllerName, + "en" +}; + +/// +/// Table of driver names +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_UNICODE_STRING_TABLE mHttpBootDxeDriverNameTable[] = { + { "eng;en", (CHAR16 *)L"UEFI HTTP Boot Driver" }, + { NULL, NULL } +}; + +/// +/// Table of controller names +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_UNICODE_STRING_TABLE mHttpBootDxeControllerNameTable[] = { + { "eng;en", (CHAR16 *)L"UEFI Http Boot Controller" }, + { NULL, NULL } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mHttpBootDxeDriverNameTable, + DriverName, + (BOOLEAN) (This != &gHttpBootDxeComponentName2) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + UINT32 *Id; + + if (ControllerHandle == NULL || ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + NicHandle = HttpBootGetNicByIp4Children (ControllerHandle); + if (NicHandle == NULL) { + NicHandle = HttpBootGetNicByIp6Children(ControllerHandle); + if (NicHandle == NULL) { + return EFI_UNSUPPORTED; + } + } + + // + // Try to retrieve the private data by caller ID GUID. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mHttpBootDxeControllerNameTable, + ControllerName, + (BOOLEAN)(This != &gHttpBootDxeComponentName2) + ); + +} diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.h b/Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.h new file mode 100644 index 0000000000..3fce9b75ab --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.h @@ -0,0 +1,99 @@ +/** @file + Declaration of HTTP boot driver's EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL function. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_UEFI_HTTP_BOOT_COM_NAME_H__ +#define __EFI_UEFI_HTTP_BOOT_COM_NAME_H__ + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c new file mode 100644 index 0000000000..9a947a6ea6 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c @@ -0,0 +1,829 @@ +/** @file + Functions implementation related with DHCPv4 for HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +// +// This is a map from the interested DHCP4 option tags' index to the tag value. +// +UINT8 mInterestedDhcp4Tags[HTTP_BOOT_DHCP4_TAG_INDEX_MAX] = { + HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN, + HTTP_BOOT_DHCP4_TAG_OVERLOAD, + HTTP_BOOT_DHCP4_TAG_MSG_TYPE, + HTTP_BOOT_DHCP4_TAG_SERVER_ID, + HTTP_BOOT_DHCP4_TAG_CLASS_ID, + HTTP_BOOT_DHCP4_TAG_BOOTFILE, + HTTP_BOOT_DHCP4_TAG_DNS_SERVER +}; + +// +// There are 4 times retries with the value of 4, 8, 16 and 32, refers to UEFI 2.5 spec. +// +UINT32 mHttpDhcpTimeout[4] = {4, 8, 16, 32}; + +/** + Build the options buffer for the DHCPv4 request packet. + + @param[in] Private Pointer to HTTP boot driver private data. + @param[out] OptList Pointer to the option pointer array. + @param[in] Buffer Pointer to the buffer to contain the option list. + + @return Index The count of the built-in options. + +**/ +UINT32 +HttpBootBuildDhcp4Options ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + OUT EFI_DHCP4_PACKET_OPTION **OptList, + IN UINT8 *Buffer + ) +{ + HTTP_BOOT_DHCP4_OPTION_ENTRY OptEnt; + UINT16 Value; + UINT32 Index; + + Index = 0; + OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer; + + // + // Append parameter request list option. + // + OptList[Index]->OpCode = HTTP_BOOT_DHCP4_TAG_PARA_LIST; + OptList[Index]->Length = 27; + OptEnt.Para = (HTTP_BOOT_DHCP4_OPTION_PARA *) OptList[Index]->Data; + OptEnt.Para->ParaList[0] = HTTP_BOOT_DHCP4_TAG_NETMASK; + OptEnt.Para->ParaList[1] = HTTP_BOOT_DHCP4_TAG_TIME_OFFSET; + OptEnt.Para->ParaList[2] = HTTP_BOOT_DHCP4_TAG_ROUTER; + OptEnt.Para->ParaList[3] = HTTP_BOOT_DHCP4_TAG_TIME_SERVER; + OptEnt.Para->ParaList[4] = HTTP_BOOT_DHCP4_TAG_NAME_SERVER; + OptEnt.Para->ParaList[5] = HTTP_BOOT_DHCP4_TAG_DNS_SERVER; + OptEnt.Para->ParaList[6] = HTTP_BOOT_DHCP4_TAG_HOSTNAME; + OptEnt.Para->ParaList[7] = HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN; + OptEnt.Para->ParaList[8] = HTTP_BOOT_DHCP4_TAG_DOMAINNAME; + OptEnt.Para->ParaList[9] = HTTP_BOOT_DHCP4_TAG_ROOTPATH; + OptEnt.Para->ParaList[10] = HTTP_BOOT_DHCP4_TAG_EXTEND_PATH; + OptEnt.Para->ParaList[11] = HTTP_BOOT_DHCP4_TAG_EMTU; + OptEnt.Para->ParaList[12] = HTTP_BOOT_DHCP4_TAG_TTL; + OptEnt.Para->ParaList[13] = HTTP_BOOT_DHCP4_TAG_BROADCAST; + OptEnt.Para->ParaList[14] = HTTP_BOOT_DHCP4_TAG_NIS_DOMAIN; + OptEnt.Para->ParaList[15] = HTTP_BOOT_DHCP4_TAG_NIS_SERVER; + OptEnt.Para->ParaList[16] = HTTP_BOOT_DHCP4_TAG_NTP_SERVER; + OptEnt.Para->ParaList[17] = HTTP_BOOT_DHCP4_TAG_VENDOR; + OptEnt.Para->ParaList[18] = HTTP_BOOT_DHCP4_TAG_REQUEST_IP; + OptEnt.Para->ParaList[19] = HTTP_BOOT_DHCP4_TAG_LEASE; + OptEnt.Para->ParaList[20] = HTTP_BOOT_DHCP4_TAG_SERVER_ID; + OptEnt.Para->ParaList[21] = HTTP_BOOT_DHCP4_TAG_T1; + OptEnt.Para->ParaList[22] = HTTP_BOOT_DHCP4_TAG_T2; + OptEnt.Para->ParaList[23] = HTTP_BOOT_DHCP4_TAG_CLASS_ID; + OptEnt.Para->ParaList[25] = HTTP_BOOT_DHCP4_TAG_BOOTFILE; + OptEnt.Para->ParaList[26] = HTTP_BOOT_DHCP4_TAG_UUID; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append UUID/Guid-based client identifier option + // + OptList[Index]->OpCode = HTTP_BOOT_DHCP4_TAG_UUID; + OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UUID); + OptEnt.Uuid = (HTTP_BOOT_DHCP4_OPTION_UUID *) OptList[Index]->Data; + OptEnt.Uuid->Type = 0; + if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID)); + } + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append client network device interface option + // + OptList[Index]->OpCode = HTTP_BOOT_DHCP4_TAG_UNDI; + OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UNDI); + OptEnt.Undi = (HTTP_BOOT_DHCP4_OPTION_UNDI *) OptList[Index]->Data; + + if (Private->Nii != NULL) { + OptEnt.Undi->Type = Private->Nii->Type; + OptEnt.Undi->MajorVer = Private->Nii->MajorVer; + OptEnt.Undi->MinorVer = Private->Nii->MinorVer; + } else { + OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; + OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; + OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; + } + + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = HTTP_BOOT_DHCP4_TAG_ARCH; + OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_ARCH); + OptEnt.Arch = (HTTP_BOOT_DHCP4_OPTION_ARCH *) OptList[Index]->Data; + Value = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE); + CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append vendor class identify option + // + OptList[Index]->OpCode = HTTP_BOOT_DHCP4_TAG_CLASS_ID; + OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_CLID); + OptEnt.Clid = (HTTP_BOOT_DHCP4_OPTION_CLID *) OptList[Index]->Data; + CopyMem ( + OptEnt.Clid, + DEFAULT_CLASS_ID_DATA, + sizeof (HTTP_BOOT_DHCP4_OPTION_CLID) + ); + HttpBootUintnToAscDecWithFormat ( + EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.Clid->ArchitectureType, + sizeof (OptEnt.Clid->ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName)); + HttpBootUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor)); + HttpBootUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor)); + } + + Index++; + + return Index; +} + +/** + Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer. + + @param[in] Buffer Pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag Tag of the required option. + + @retval NULL Failed to find the required option. + @retval Others The position of the required option. + +**/ +EFI_DHCP4_PACKET_OPTION * +HttpBootParseDhcp4Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT8 OptTag + ) +{ + EFI_DHCP4_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP4_PACKET_OPTION *) Buffer; + Offset = 0; + + while (Offset < Length && Option->OpCode != HTTP_BOOT_DHCP4_TAG_EOP) { + + if (Option->OpCode == OptTag) { + // + // Found the required option. + // + return Option; + } + + // + // Skip the current option to the next. + // + if (Option->OpCode == HTTP_BOOT_DHCP4_TAG_PAD) { + Offset++; + } else { + Offset += Option->Length + 2; + } + + Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; +} + +/** + Cache the DHCPv4 packet. + + @param[in] Dst Pointer to the cache buffer for DHCPv4 packet. + @param[in] Src Pointer to the DHCPv4 packet to be cached. + +**/ +VOID +HttpBootCacheDhcp4Packet ( + IN EFI_DHCP4_PACKET *Dst, + IN EFI_DHCP4_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); + Dst->Length = Src->Length; +} + +/** + Parse the cached DHCPv4 packet, including all the options. + + @param[in] Cache4 Pointer to cached DHCPv4 packet. + + @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse an invalid packet. + +**/ +EFI_STATUS +HttpBootParseDhcp4Packet ( + IN HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4 + ) +{ + EFI_DHCP4_PACKET *Offer; + EFI_DHCP4_PACKET_OPTION **Options; + UINTN Index; + EFI_DHCP4_PACKET_OPTION *Option; + BOOLEAN IsProxyOffer; + BOOLEAN IsHttpOffer; + BOOLEAN IsDnsOffer; + BOOLEAN IpExpressedUri; + UINT8 *Ptr8; + EFI_STATUS Status; + HTTP_BOOT_OFFER_TYPE OfferType; + EFI_IPv4_ADDRESS IpAddr; + + IsDnsOffer = FALSE; + IpExpressedUri = FALSE; + IsProxyOffer = FALSE; + IsHttpOffer = FALSE; + + ZeroMem (Cache4->OptList, sizeof (Cache4->OptList)); + + Offer = &Cache4->Packet.Offer; + Options = Cache4->OptList; + + // + // Parse DHCPv4 options in this offer, and store the pointers. + // First, try to parse DHCPv4 options from the DHCP optional parameters field. + // + for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { + Options[Index] = HttpBootParseDhcp4Options ( + Offer->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Offer), + mInterestedDhcp4Tags[Index] + ); + } + // + // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132. + // If yes, try to parse options from the BootFileName field, then ServerName field. + // + Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD]; + if (Option != NULL) { + if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_FILE) != 0) { + for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { + if (Options[Index] == NULL) { + Options[Index] = HttpBootParseDhcp4Options ( + (UINT8 *) Offer->Dhcp4.Header.BootFileName, + sizeof (Offer->Dhcp4.Header.BootFileName), + mInterestedDhcp4Tags[Index] + ); + } + } + } + if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME) != 0) { + for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) { + if (Options[Index] == NULL) { + Options[Index] = HttpBootParseDhcp4Options ( + (UINT8 *) Offer->Dhcp4.Header.ServerName, + sizeof (Offer->Dhcp4.Header.ServerName), + mInterestedDhcp4Tags[Index] + ); + } + } + } + } + + // + // The offer with "yiaddr" is a proxy offer. + // + if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) { + IsProxyOffer = TRUE; + } + + // + // The offer with "HTTPClient" is a Http offer. + // + Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID]; + if ((Option != NULL) && (Option->Length >= 9) && + (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) { + IsHttpOffer = TRUE; + } + + // + // The offer with Domain Server is a DNS offer. + // + Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER]; + if (Option != NULL) { + IsDnsOffer = TRUE; + } + + // + // Parse boot file name: + // Boot URI information is provided thru 'file' field in DHCP Header or option 67. + // According to RFC 2132, boot file name should be read from DHCP option 67 (bootfile name) if present. + // Otherwise, read from boot file field in DHCP header. + // + if (Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + // + // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null + // terminated string. So force to append null terminated character at the end of string. + // + Ptr8 = (UINT8*)&Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data[0]; + Ptr8 += Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Length; + if (*(Ptr8 - 1) != '\0') { + *Ptr8 = '\0'; + } + } else if (Offer->Dhcp4.Header.BootFileName[0] != 0) { + // + // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it. + // Do not count dhcp option header here, or else will destroy the serverhostname. + // + Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) + (&Offer->Dhcp4.Header.BootFileName[0] - + OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0])); + } + + // + // Http offer must have a boot URI. + // + if (IsHttpOffer && Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // Try to retrieve the IP of HTTP server from URI. + // + if (IsHttpOffer) { + Status = HttpParseUrl ( + (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, + (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data), + FALSE, + &Cache4->UriParser + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Status = HttpUrlGetIp4 ( + (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data, + Cache4->UriParser, + &IpAddr + ); + IpExpressedUri = !EFI_ERROR (Status); + } + + // + // Determine offer type of the DHCPv4 packet. + // + if (IsHttpOffer) { + if (IpExpressedUri) { + OfferType = IsProxyOffer ? HttpOfferTypeProxyIpUri : HttpOfferTypeDhcpIpUri; + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri; + } else { + OfferType = HttpOfferTypeProxyNameUri; + } + } + + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly; + } else { + return EFI_DEVICE_ERROR; + } + } + + Cache4->OfferType = OfferType; + return EFI_SUCCESS; +} + +/** + Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount. + + @param[in] Private Pointer to HTTP boot driver private data. + @param[in] RcvdOffer Pointer to the received offer packet. + +**/ +VOID +HttpBootCacheDhcp4Offer ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN EFI_DHCP4_PACKET *RcvdOffer + ) +{ + HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET *Offer; + HTTP_BOOT_OFFER_TYPE OfferType; + + ASSERT (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM); + Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4; + Offer = &Cache4->Packet.Offer; + + // + // Cache the content of DHCPv4 packet firstly. + // + HttpBootCacheDhcp4Packet (Offer, RcvdOffer); + + // + // Validate the DHCPv4 packet, and parse the options and offer type. + // + if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) { + return; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache4->OfferType; + ASSERT (OfferType < HttpOfferTypeMax); + ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM); + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + Private->OfferNum++; +} + +/** + Select an DHCPv4 or DHCP6 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private Pointer to HTTP boot driver private data. + +**/ +VOID +HttpBootSelectDhcpOffer ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + Private->SelectIndex = 0; + Private->SelectProxyType = HttpOfferTypeMax; + + // + // Priority1: HttpOfferTypeDhcpIpUri + // Priority2: HttpOfferTypeDhcpNameUriDns + // Priority3: HttpOfferTypeDhcpOnly + HttpOfferTypeProxyIpUri + // Priority4: HttpOfferTypeDhcpDns + HttpOfferTypeProxyIpUri + // Priority5: HttpOfferTypeDhcpDns + HttpOfferTypeProxyNameUri + // Priority6: HttpOfferTypeDhcpDns + HttpOfferTypeDhcpNameUri + // + if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1; + + } else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1; + + } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0 && + Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = HttpOfferTypeProxyIpUri; + + } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && + Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; + Private->SelectProxyType = HttpOfferTypeProxyIpUri; + + } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && + Private->OfferCount[HttpOfferTypeProxyNameUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; + Private->SelectProxyType = HttpOfferTypeProxyNameUri; + + } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 && + Private->OfferCount[HttpOfferTypeDhcpNameUri] > 0) { + + Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1; + Private->SelectProxyType = HttpOfferTypeDhcpNameUri; + } +} + + +/** + EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This Pointer to the EFI DHCPv4 Protocol. + @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver. + @param[in] Dhcp4Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv4 packet that is going to be sent or already received. + @param[out] NewPacket The packet that is used to replace the above Packet. + + @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol + driver will continue to wait for more DHCPOFFER packets until the + retry timeout expires. + @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process + and return to the Dhcp4Init or Dhcp4InitReboot state. + +**/ +EFI_STATUS +EFIAPI +HttpBootDhcp4CallBack ( + IN EFI_DHCP4_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP4_STATE CurrentState, + IN EFI_DHCP4_EVENT Dhcp4Event, + IN EFI_DHCP4_PACKET *Packet OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL + ) +{ + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_DHCP4_PACKET_OPTION *MaxMsgSize; + UINT16 Value; + EFI_STATUS Status; + + if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) { + return EFI_SUCCESS; + } + + Private = (HTTP_BOOT_PRIVATE_DATA *) Context; + + // + // Override the Maximum DHCP Message Size. + // + MaxMsgSize = HttpBootParseDhcp4Options ( + Packet->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Packet), + HTTP_BOOT_DHCP4_TAG_MAXMSG + ); + if (MaxMsgSize != NULL) { + Value = HTONS (HTTP_BOOT_DHCP4_PACKET_MAX_SIZE); + CopyMem (MaxMsgSize->Data, &Value, sizeof (Value)); + } + + Status = EFI_SUCCESS; + switch (Dhcp4Event) { + case Dhcp4RcvdOffer: + Status = EFI_NOT_READY; + if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { + // + // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + HttpBootCacheDhcp4Offer (Private, Packet); + } + break; + + case Dhcp4SelectOffer: + // + // Select offer according to the priority in UEFI spec, and record the SelectIndex + // and SelectProxyType. + // + HttpBootSelectDhcpOffer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer; + } + break; + + default: + break; + } + + return Status; +} + +/** + This function will register the IPv4 gateway address to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootRegisterIp4Gateway ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + + ASSERT (!Private->UsingIpv6); + + Ip4Config2 = Private->Ip4Config2; + + // + // Configure the gateway if valid. + // + if (!EFI_IP4_EQUAL (&Private->GatewayIp, &mZeroIp4Addr)) { + Status = Ip4Config2->SetData ( + Ip4Config2, + Ip4Config2DataTypeGateway, + sizeof (EFI_IPv4_ADDRESS), + &Private->GatewayIp + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + This function will register the default DNS addresses to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes. + @param[in] DnsServerData Point a list of DNS server address in an array + of EFI_IPv4_ADDRESS instances. + + @retval EFI_SUCCESS The DNS configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootRegisterIp4Dns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN DataLength, + IN VOID *DnsServerData + ) +{ + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + + ASSERT (!Private->UsingIpv6); + + Ip4Config2 = Private->Ip4Config2; + + return Ip4Config2->SetData ( + Ip4Config2, + Ip4Config2DataTypeDnsServer, + DataLength, + DnsServerData + ); +} + + +/** + This function will switch the IP4 configuration policy to Static. + + @param[in] Private Pointer to HTTP boot driver private data. + + @retval EFI_SUCCESS The policy is already configured to static. + @retval Others Other error as indicated.. + +**/ +EFI_STATUS +HttpBootSetIp4Policy ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_IP4_CONFIG2_POLICY Policy; + EFI_STATUS Status; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + UINTN DataSize; + + Ip4Config2 = Private->Ip4Config2; + + DataSize = sizeof (EFI_IP4_CONFIG2_POLICY); + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + &DataSize, + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Policy != Ip4Config2PolicyStatic) { + Policy = Ip4Config2PolicyStatic; + Status= Ip4Config2->SetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + sizeof (EFI_IP4_CONFIG2_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information. + + @param[in] Private Pointer to HTTP boot driver private data. + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +HttpBootDhcp4Dora ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_DHCP4_PROTOCOL *Dhcp4; + UINT32 OptCount; + EFI_DHCP4_PACKET_OPTION *OptList[HTTP_BOOT_DHCP4_OPTION_MAX_NUM]; + UINT8 Buffer[HTTP_BOOT_DHCP4_OPTION_MAX_SIZE]; + EFI_DHCP4_CONFIG_DATA Config; + EFI_STATUS Status; + EFI_DHCP4_MODE_DATA Mode; + + Dhcp4 = Private->Dhcp4; + ASSERT (Dhcp4 != NULL); + + Status = HttpBootSetIp4Policy (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Build option list for the request packet. + // + OptCount = HttpBootBuildDhcp4Options (Private, OptList, Buffer); + ASSERT (OptCount > 0); + + ZeroMem (&Config, sizeof(Config)); + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp4Callback = HttpBootDhcp4CallBack; + Config.CallbackContext = Private; + Config.DiscoverTryCount = HTTP_BOOT_DHCP_RETRIES; + Config.DiscoverTimeout = mHttpDhcpTimeout; + + // + // Configure the DHCPv4 instance for HTTP boot. + // + Status = Dhcp4->Configure (Dhcp4, &Config); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Initialize the record fields for DHCPv4 offer in private data. + // + Private->OfferNum = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + // + // Start DHCPv4 D.O.R.A. process to acquire IPv4 address. + // + Status = Dhcp4->Start (Dhcp4, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Get the acquired IPv4 address and store them. + // + Status = Dhcp4->GetModeData (Dhcp4, &Mode); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + ASSERT (Mode.State == Dhcp4Bound); + CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); + + Status = HttpBootRegisterIp4Gateway (Private); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + AsciiPrint ("\n Station IP address is "); + HttpBootShowIp4Addr (&Private->StationIp.v4); + AsciiPrint ("\n"); + +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp4->Stop (Dhcp4); + Dhcp4->Configure (Dhcp4, NULL); + } else { + ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); + Dhcp4->Configure (Dhcp4, &Config); + } + + return Status; +} diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h new file mode 100644 index 0000000000..2bc46deafd --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h @@ -0,0 +1,292 @@ +/** @file + Functions declaration related with DHCPv4 for HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_UEFI_HTTP_BOOT_DHCP4_H__ +#define __EFI_UEFI_HTTP_BOOT_DHCP4_H__ + +#define HTTP_BOOT_DHCP4_OPTION_MAX_NUM 16 +#define HTTP_BOOT_DHCP4_OPTION_MAX_SIZE 312 +#define HTTP_BOOT_DHCP4_PACKET_MAX_SIZE 1472 + +#define HTTP_BOOT_DHCP4_OPCODE_REQUEST 1 +#define HTTP_BOOT_DHCP4_OPCODE_REPLY 2 +#define HTTP_BOOT_DHCP4_MSG_TYPE_REQUEST 3 +#define HTTP_BOOT_DHCP4_MAGIC 0x63538263 // network byte order + +// +// Dhcp Options +// +#define HTTP_BOOT_DHCP4_TAG_PAD 0 // Pad Option +#define HTTP_BOOT_DHCP4_TAG_EOP 255 // End Option +#define HTTP_BOOT_DHCP4_TAG_NETMASK 1 // Subnet Mask +#define HTTP_BOOT_DHCP4_TAG_TIME_OFFSET 2 // Time Offset from UTC +#define HTTP_BOOT_DHCP4_TAG_ROUTER 3 // Router option, +#define HTTP_BOOT_DHCP4_TAG_TIME_SERVER 4 // Time Server +#define HTTP_BOOT_DHCP4_TAG_NAME_SERVER 5 // Name Server +#define HTTP_BOOT_DHCP4_TAG_DNS_SERVER 6 // Domain Name Server +#define HTTP_BOOT_DHCP4_TAG_HOSTNAME 12 // Host Name +#define HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN 13 // Boot File Size +#define HTTP_BOOT_DHCP4_TAG_DUMP 14 // Merit Dump File +#define HTTP_BOOT_DHCP4_TAG_DOMAINNAME 15 // Domain Name +#define HTTP_BOOT_DHCP4_TAG_ROOTPATH 17 // Root path +#define HTTP_BOOT_DHCP4_TAG_EXTEND_PATH 18 // Extensions Path +#define HTTP_BOOT_DHCP4_TAG_EMTU 22 // Maximum Datagram Reassembly Size +#define HTTP_BOOT_DHCP4_TAG_TTL 23 // Default IP Time-to-live +#define HTTP_BOOT_DHCP4_TAG_BROADCAST 28 // Broadcast Address +#define HTTP_BOOT_DHCP4_TAG_NIS_DOMAIN 40 // Network Information Service Domain +#define HTTP_BOOT_DHCP4_TAG_NIS_SERVER 41 // Network Information Servers +#define HTTP_BOOT_DHCP4_TAG_NTP_SERVER 42 // Network Time Protocol Servers +#define HTTP_BOOT_DHCP4_TAG_VENDOR 43 // Vendor Specific Information +#define HTTP_BOOT_DHCP4_TAG_REQUEST_IP 50 // Requested IP Address +#define HTTP_BOOT_DHCP4_TAG_LEASE 51 // IP Address Lease Time +#define HTTP_BOOT_DHCP4_TAG_OVERLOAD 52 // Option Overload +#define HTTP_BOOT_DHCP4_TAG_MSG_TYPE 53 // DHCP Message Type +#define HTTP_BOOT_DHCP4_TAG_SERVER_ID 54 // Server Identifier +#define HTTP_BOOT_DHCP4_TAG_PARA_LIST 55 // Parameter Request List +#define HTTP_BOOT_DHCP4_TAG_MAXMSG 57 // Maximum DHCP Message Size +#define HTTP_BOOT_DHCP4_TAG_T1 58 // Renewal (T1) Time Value +#define HTTP_BOOT_DHCP4_TAG_T2 59 // Rebinding (T2) Time Value +#define HTTP_BOOT_DHCP4_TAG_CLASS_ID 60 // Vendor class identifier +#define HTTP_BOOT_DHCP4_TAG_CLIENT_ID 61 // Client-identifier +#define HTTP_BOOT_DHCP4_TAG_TFTP 66 // TFTP server name +#define HTTP_BOOT_DHCP4_TAG_BOOTFILE 67 // Bootfile name +#define HTTP_BOOT_DHCP4_TAG_ARCH 93 +#define HTTP_BOOT_DHCP4_TAG_UNDI 94 +#define HTTP_BOOT_DHCP4_TAG_UUID 97 + +#define HTTP_BOOT_DHCP4_OVERLOAD_FILE 1 +#define HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME 2 + +/// +/// HTTP Tag definition that identifies the processor +/// and programming environment of the client system. +/// These identifiers are defined by IETF: +/// http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml +/// +#if defined (MDE_CPU_IA32) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE 0x000F +#elif defined (MDE_CPU_X64) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE 0x0010 +#elif defined (MDE_CPU_ARM) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE 0x0012 +#elif defined (MDE_CPU_AARCH64) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE 0x0013 +#elif defined (MDE_CPU_EBC) +#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE 0x0011 +#endif + +/// DHCP offer types among HTTP boot. +/// Dhcp4 and Dhcp6 share this definition, and corresponding +/// relatioinship is as follows: +/// Dhcp4Discover <> Dhcp6Solicit +/// Dhcp4Offer <> Dhcp6Advertise +/// Dhcp4Request <> Dhcp6Request +/// Dhcp4Ack <> DHcp6Reply +/// +typedef enum { + // + // or + // + // + HttpOfferTypeDhcpIpUri, + // + // + // + HttpOfferTypeDhcpNameUriDns, + // + // + // + HttpOfferTypeDhcpDns, + // + // + // + HttpOfferTypeDhcpOnly, + // + // or + // + // + HttpOfferTypeProxyNameUri, + // + // or + // + // + HttpOfferTypeProxyIpUri, + // + // + // + HttpOfferTypeDhcpNameUri, + HttpOfferTypeMax +} HTTP_BOOT_OFFER_TYPE; + +#define HTTP_BOOT_DHCP_RETRIES 4 +#define HTTP_BOOT_OFFER_MAX_NUM 16 + +// The array index of the DHCP4 option tag interested +// +#define HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE_LEN 0 +#define HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD 1 +#define HTTP_BOOT_DHCP4_TAG_INDEX_MSG_TYPE 2 +#define HTTP_BOOT_DHCP4_TAG_INDEX_SERVER_ID 3 +#define HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID 4 +#define HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE 5 +#define HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER 6 +#define HTTP_BOOT_DHCP4_TAG_INDEX_MAX 7 + +#pragma pack(1) + +typedef struct { + UINT8 ParaList[135]; +} HTTP_BOOT_DHCP4_OPTION_PARA; + +typedef struct { + UINT16 Size; +} HTTP_BOOT_DHCP4_OPTION_MAX_MESG_SIZE; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; +} HTTP_BOOT_DHCP4_OPTION_UNDI; + +typedef struct { + UINT8 Type; +} HTTP_BOOT_DHCP4_OPTION_MESG; + +typedef struct { + UINT16 Type; +} HTTP_BOOT_DHCP4_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[11]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} HTTP_BOOT_DHCP4_OPTION_CLID; + +typedef struct { + UINT8 Type; + UINT8 Guid[16]; +} HTTP_BOOT_DHCP4_OPTION_UUID; + +typedef struct { + UINT16 Type; + UINT16 Layer; +} HTTP_BOOT_OPTION_BOOT_ITEM; + +#pragma pack() + +typedef union { + HTTP_BOOT_DHCP4_OPTION_PARA *Para; + HTTP_BOOT_DHCP4_OPTION_UNDI *Undi; + HTTP_BOOT_DHCP4_OPTION_ARCH *Arch; + HTTP_BOOT_DHCP4_OPTION_CLID *Clid; + HTTP_BOOT_DHCP4_OPTION_UUID *Uuid; + HTTP_BOOT_DHCP4_OPTION_MESG *Mesg; + HTTP_BOOT_DHCP4_OPTION_MAX_MESG_SIZE *MaxMesgSize; +} HTTP_BOOT_DHCP4_OPTION_ENTRY; + +#define GET_NEXT_DHCP_OPTION(Opt) \ + (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1) + +#define GET_OPTION_BUFFER_LEN(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4) + +#define DEFAULT_CLASS_ID_DATA "HTTPClient:Arch:xxxxx:UNDI:003000" +#define DEFAULT_UNDI_TYPE 1 +#define DEFAULT_UNDI_MAJOR 3 +#define DEFAULT_UNDI_MINOR 0 + +typedef struct { + UINT32 Reserved; +} HTTP_BOOT_VENDOR_OPTION; + +typedef union { + EFI_DHCP4_PACKET Offer; + EFI_DHCP4_PACKET Ack; + UINT8 Buffer[HTTP_BOOT_DHCP4_PACKET_MAX_SIZE]; +} HTTP_BOOT_DHCP4_PACKET; + +typedef struct { + // + // URI component + // + CHAR8 *Scheme; + CHAR8 *Authority; + CHAR8 *Path; + CHAR8 *Query; + CHAR8 *Fragment; /// TODO: may not required in HTTP URL + + CHAR8 *RegName; /// Point to somewhere in Authority + BOOLEAN AddrIsOk; + EFI_IP_ADDRESS Address; + UINT16 Port; +} HTTP_BOOT_URI_CONTENT; + +typedef struct { + HTTP_BOOT_DHCP4_PACKET Packet; + HTTP_BOOT_OFFER_TYPE OfferType; + VOID *UriParser; + EFI_DHCP4_PACKET_OPTION *OptList[HTTP_BOOT_DHCP4_TAG_INDEX_MAX]; +} HTTP_BOOT_DHCP4_PACKET_CACHE; + +/** + Select an DHCPv4 or DHCP6 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private Pointer to HTTP boot driver private data. + +**/ +VOID +HttpBootSelectDhcpOffer ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information. + + @param[in] Private Pointer to HTTP_BOOT private data. + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +HttpBootDhcp4Dora ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + This function will register the default DNS addresses to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes. + @param[in] DnsServerData Point a list of DNS server address in an array + of EFI_IPv4_ADDRESS instances. + + @retval EFI_SUCCESS The DNS configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootRegisterIp4Dns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN DataLength, + IN VOID *DnsServerData + ); + +#endif diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c new file mode 100644 index 0000000000..e5cf894714 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c @@ -0,0 +1,984 @@ +/** @file + Functions implementation related with DHCPv6 for HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/** + Build the options buffer for the DHCPv6 request packet. + + @param[in] Private The pointer to HTTP BOOT driver private data. + @param[out] OptList The pointer to the option pointer array. + @param[in] Buffer The pointer to the buffer to contain the option list. + + @return Index The count of the built-in options. + +**/ +UINT32 +HttpBootBuildDhcp6Options ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + OUT EFI_DHCP6_PACKET_OPTION **OptList, + IN UINT8 *Buffer + ) +{ + HTTP_BOOT_DHCP6_OPTION_ENTRY OptEnt; + UINT16 Value; + UINT32 Index; + + Index = 0; + OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer; + + // + // Append client option request option + // + OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_ORO); + OptList[Index]->OpLen = HTONS (8); + OptEnt.Oro = (HTTP_BOOT_DHCP6_OPTION_ORO *) OptList[Index]->Data; + OptEnt.Oro->OpCode[0] = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL); + OptEnt.Oro->OpCode[1] = HTONS(HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM); + OptEnt.Oro->OpCode[2] = HTONS(HTTP_BOOT_DHCP6_OPT_DNS_SERVERS); + OptEnt.Oro->OpCode[3] = HTONS(HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client network device interface option + // + OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_UNDI); + OptList[Index]->OpLen = HTONS ((UINT16)3); + OptEnt.Undi = (HTTP_BOOT_DHCP6_OPTION_UNDI *) OptList[Index]->Data; + + if (Private->Nii != NULL) { + OptEnt.Undi->Type = Private->Nii->Type; + OptEnt.Undi->MajorVer = Private->Nii->MajorVer; + OptEnt.Undi->MinorVer = Private->Nii->MinorVer; + } else { + OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; + OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; + OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; + } + + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_ARCH); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_ARCH)); + OptEnt.Arch = (HTTP_BOOT_DHCP6_OPTION_ARCH *) OptList[Index]->Data; + Value = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE); + CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append vendor class identify option. + // + OptList[Index]->OpCode = HTONS (HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS)); + OptEnt.VendorClass = (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data; + OptEnt.VendorClass->Vendor = HTONL (HTTP_BOOT_DHCP6_ENTERPRISE_NUM); + OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (HTTP_BOOT_CLASS_ID)); + CopyMem ( + &OptEnt.VendorClass->ClassId, + DEFAULT_CLASS_ID_DATA, + sizeof (HTTP_BOOT_CLASS_ID) + ); + HttpBootUintnToAscDecWithFormat ( + EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.VendorClass->ClassId.ArchitectureType, + sizeof (OptEnt.VendorClass->ClassId.ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem ( + OptEnt.VendorClass->ClassId.InterfaceName, + Private->Nii->StringId, + sizeof (OptEnt.VendorClass->ClassId.InterfaceName) + ); + HttpBootUintnToAscDecWithFormat ( + Private->Nii->MajorVer, + OptEnt.VendorClass->ClassId.UndiMajor, + sizeof (OptEnt.VendorClass->ClassId.UndiMajor) + ); + HttpBootUintnToAscDecWithFormat ( + Private->Nii->MinorVer, + OptEnt.VendorClass->ClassId.UndiMinor, + sizeof (OptEnt.VendorClass->ClassId.UndiMinor) + ); + } + + Index++; + + return Index; +} + +/** + Parse out a DHCPv6 option by OptTag, and find the position in buffer. + + @param[in] Buffer The pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag The required option tag. + + @retval NULL Failed to parse the required option. + @retval Others The postion of the required option in buffer. + +**/ +EFI_DHCP6_PACKET_OPTION * +HttpBootParseDhcp6Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT16 OptTag + ) +{ + EFI_DHCP6_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP6_PACKET_OPTION *) Buffer; + Offset = 0; + + // + // OpLen and OpCode here are both stored in network order. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == OptTag) { + + return Option; + } + + Offset += (NTOHS(Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; + +} + +/** + Parse the cached DHCPv6 packet, including all the options. + + @param[in] Cache6 The pointer to a cached DHCPv6 packet. + + @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet. + +**/ +EFI_STATUS +HttpBootParseDhcp6Packet ( + IN HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6 + ) +{ + EFI_DHCP6_PACKET *Offer; + EFI_DHCP6_PACKET_OPTION **Options; + EFI_DHCP6_PACKET_OPTION *Option; + HTTP_BOOT_OFFER_TYPE OfferType; + EFI_IPv6_ADDRESS IpAddr; + BOOLEAN IsProxyOffer; + BOOLEAN IsHttpOffer; + BOOLEAN IsDnsOffer; + BOOLEAN IpExpressedUri; + EFI_STATUS Status; + UINT32 Offset; + UINT32 Length; + + IsDnsOffer = FALSE; + IpExpressedUri = FALSE; + IsProxyOffer = TRUE; + IsHttpOffer = FALSE; + Offer = &Cache6->Packet.Offer; + Options = Cache6->OptList; + + ZeroMem (Cache6->OptList, sizeof (Cache6->OptList)); + + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option); + Offset = 0; + Length = GET_DHCP6_OPTION_SIZE (Offer); + + // + // OpLen and OpCode here are both stored in network order, since they are from original packet. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_IA_NA) { + Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option; + } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL) { + // + // The server sends this option to inform the client about an URL to a boot file. + // + Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] = Option; + } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM) { + Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option; + } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS) { + Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option; + } else if (NTOHS (Option->OpCode) == HTTP_BOOT_DHCP6_OPT_DNS_SERVERS) { + Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER] = Option; + } + + Offset += (NTOHS (Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset); + } + // + // The offer with assigned client address is NOT a proxy offer. + // An ia_na option, embeded with valid ia_addr option and a status_code of success. + // + Option = Options[HTTP_BOOT_DHCP6_IDX_IA_NA]; + if (Option != NULL) { + Option = HttpBootParseDhcp6Options ( + Option->Data + 12, + NTOHS (Option->OpLen), + HTTP_BOOT_DHCP6_OPT_STATUS_CODE + ); + if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) { + IsProxyOffer = FALSE; + } + } + + // + // The offer with "HTTPClient" is a Http offer. + // + Option = Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS]; + + if (Option != NULL && + NTOHS(Option->OpLen) >= 10 && + CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 10) == 0) { + IsHttpOffer = TRUE; + } + + // + // The offer with Domain Server is a DNS offer. + // + Option = Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER]; + if (Option != NULL) { + IsDnsOffer = TRUE; + } + + // + // Http offer must have a boot URI. + // + if (IsHttpOffer && Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // Try to retrieve the IP of HTTP server from URI. + // + if (IsHttpOffer) { + Status = HttpParseUrl ( + (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data), + FALSE, + &Cache6->UriParser + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Status = HttpUrlGetIp6 ( + (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data, + Cache6->UriParser, + &IpAddr + ); + IpExpressedUri = !EFI_ERROR (Status); + } + + // + // Determine offer type of the DHCPv6 packet. + // + if (IsHttpOffer) { + if (IpExpressedUri) { + OfferType = IsProxyOffer ? HttpOfferTypeProxyIpUri : HttpOfferTypeDhcpIpUri; + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri; + } else { + OfferType = HttpOfferTypeProxyNameUri; + } + } + + } else { + if (!IsProxyOffer) { + OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly; + } else { + return EFI_DEVICE_ERROR; + } + } + + Cache6->OfferType = OfferType; + return EFI_SUCCESS; +} + +/** + Cache the DHCPv6 packet. + + @param[in] Dst The pointer to the cache buffer for DHCPv6 packet. + @param[in] Src The pointer to the DHCPv6 packet to be cached. + +**/ +VOID +HttpBootCacheDhcp6Packet ( + IN EFI_DHCP6_PACKET *Dst, + IN EFI_DHCP6_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length); + Dst->Length = Src->Length; +} + +/** + Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] RcvdOffer The pointer to the received offer packet. + +**/ +VOID +HttpBootCacheDhcp6Offer ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN EFI_DHCP6_PACKET *RcvdOffer + ) +{ + HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6; + EFI_DHCP6_PACKET *Offer; + HTTP_BOOT_OFFER_TYPE OfferType; + + Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6; + Offer = &Cache6->Packet.Offer; + + // + // Cache the content of DHCPv6 packet firstly. + // + HttpBootCacheDhcp6Packet(Offer, RcvdOffer); + + // + // Validate the DHCPv6 packet, and parse the options and offer type. + // + if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) { + return ; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache6->OfferType; + ASSERT (OfferType < HttpOfferTypeMax); + ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM); + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + Private->OfferNum++; +} + +/** + EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This The pointer to the EFI DHCPv6 Protocol. + @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver. + @param[in] Dhcp6Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv6 packet that is going to be sent or was already received. + @param[out] NewPacket The packet that is used to replace the Packet above. + + @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol + driver will continue to wait for more packets. + @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process. + +**/ +EFI_STATUS +EFIAPI +HttpBootDhcp6CallBack ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_STATE CurrentState, + IN EFI_DHCP6_EVENT Dhcp6Event, + IN EFI_DHCP6_PACKET *Packet, + OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL + ) +{ + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_DHCP6_PACKET *SelectAd; + EFI_STATUS Status; + if ((Dhcp6Event != Dhcp6RcvdAdvertise) && (Dhcp6Event != Dhcp6SelectAdvertise)) { + return EFI_SUCCESS; + } + + ASSERT (Packet != NULL); + + Private = (HTTP_BOOT_PRIVATE_DATA *) Context; + Status = EFI_SUCCESS; + switch (Dhcp6Event) { + + case Dhcp6RcvdAdvertise: + Status = EFI_NOT_READY; + if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) { + // + // Cache the dhcp offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + HttpBootCacheDhcp6Offer (Private, Packet); + } + break; + + case Dhcp6SelectAdvertise: + // + // Select offer by the default policy or by order, and record the SelectIndex + // and SelectProxyType. + // + HttpBootSelectDhcpOffer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + ASSERT (NewPacket != NULL); + SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer; + *NewPacket = AllocateZeroPool (SelectAd->Size); + ASSERT (*NewPacket != NULL); + CopyMem (*NewPacket, SelectAd, SelectAd->Size); + } + break; + + default: + break; + } + + return Status; +} + +/** + Check whether IP driver could route the message which will be sent to ServerIp address. + + This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid + route is found in IP6 route table, the address will be filed in GatewayAddr and return. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] TimeOutInSecond Timeout value in seconds. + @param[out] GatewayAddr Pointer to store the gateway IP address. + + @retval EFI_SUCCESS Found a valid gateway address successfully. + @retval EFI_TIMEOUT The operation is time out. + @retval Other Unexpect error happened. + +**/ +EFI_STATUS +HttpBootCheckRouteTable ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN TimeOutInSecond, + OUT EFI_IPv6_ADDRESS *GatewayAddr + ) +{ + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_MODE_DATA Ip6ModeData; + UINTN Index; + EFI_EVENT TimeOutEvt; + UINTN RetryCount; + BOOLEAN GatewayIsFound; + + ASSERT (GatewayAddr != NULL); + ASSERT (Private != NULL); + + Ip6 = Private->Ip6; + GatewayIsFound = FALSE; + RetryCount = 0; + TimeOutEvt = NULL; + Status = EFI_SUCCESS; + ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS)); + + while (TRUE) { + Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Find out the gateway address which can route the message which send to ServerIp. + // + for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) { + if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) { + IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway); + GatewayIsFound = TRUE; + break; + } + } + + if (Ip6ModeData.AddressList != NULL) { + FreePool (Ip6ModeData.AddressList); + } + if (Ip6ModeData.GroupTable != NULL) { + FreePool (Ip6ModeData.GroupTable); + } + if (Ip6ModeData.RouteTable != NULL) { + FreePool (Ip6ModeData.RouteTable); + } + if (Ip6ModeData.NeighborCache != NULL) { + FreePool (Ip6ModeData.NeighborCache); + } + if (Ip6ModeData.PrefixTable != NULL) { + FreePool (Ip6ModeData.PrefixTable); + } + if (Ip6ModeData.IcmpTypeList != NULL) { + FreePool (Ip6ModeData.IcmpTypeList); + } + + if (GatewayIsFound || RetryCount == TimeOutInSecond) { + break; + } + + RetryCount++; + + // + // Delay 1 second then recheck it again. + // + if (TimeOutEvt == NULL) { + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeOutEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { + Ip6->Poll (Ip6); + } + } + +ON_EXIT: + if (TimeOutEvt != NULL) { + gBS->CloseEvent (TimeOutEvt); + } + + if (GatewayIsFound) { + Status = EFI_SUCCESS; + } else if (RetryCount == TimeOutInSecond) { + Status = EFI_TIMEOUT; + } + + return Status; +} + +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +HttpBootSetIp6Policy ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_STATUS Status; + UINTN DataSize; + + Ip6Config = Private->Ip6Config; + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + + // + // Get and store the current policy of IP6 driver. + // + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + &DataSize, + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Policy == Ip6ConfigPolicyManual) { + Policy = Ip6ConfigPolicyAutomatic; + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + return EFI_SUCCESS; +} + +/** + This function will register the default DNS addresses to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes. + @param[in] DnsServerData Point a list of DNS server address in an array + of EFI_IPv6_ADDRESS instances. + + @retval EFI_SUCCESS The DNS configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Dns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN DataLength, + IN VOID *DnsServerData + ) +{ + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + + ASSERT (Private->UsingIpv6); + + Ip6Config = Private->Ip6Config; + + return Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeDnsServer, + DataLength, + DnsServerData + ); +} + +/** + This function will register the IPv6 gateway address to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Gateway ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_STATUS Status; + + ASSERT (Private->UsingIpv6); + Ip6Config = Private->Ip6Config; + + // + // Set the default gateway address. + // + if (!Private->NoGateway && !NetIp6IsUnspecifiedAddr (&Private->GatewayIp.v6)) { + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeGateway, + sizeof (EFI_IPv6_ADDRESS), + &Private->GatewayIp.v6 + ); + if (EFI_ERROR(Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + This function will register the station IP address. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Address ( + IN HTTP_BOOT_PRIVATE_DATA *Private +) +{ + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr; + EFI_IPv6_ADDRESS *Ip6Addr; + EFI_IPv6_ADDRESS GatewayAddr; + EFI_IP6_CONFIG_DATA Ip6CfgData; + EFI_EVENT MappedEvt; + UINTN DataSize; + BOOLEAN IsAddressOk; + UINTN Index; + + ASSERT (Private->UsingIpv6); + + MappedEvt = NULL; + IsAddressOk = FALSE; + Ip6Addr = NULL; + Ip6Cfg = Private->Ip6Config; + Ip6 = Private->Ip6; + + ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + CopyMem (&CfgAddr, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&Ip6CfgData, sizeof (EFI_IP6_CONFIG_DATA)); + + Ip6CfgData.AcceptIcmpErrors = TRUE; + Ip6CfgData.DefaultProtocol = IP6_ICMP; + Ip6CfgData.HopLimit = HTTP_BOOT_DEFAULT_HOPLIMIT; + Ip6CfgData.ReceiveTimeout = HTTP_BOOT_DEFAULT_LIFETIME; + Ip6CfgData.TransmitTimeout = HTTP_BOOT_DEFAULT_LIFETIME; + + Status = Ip6->Configure (Ip6, &Ip6CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Retrieve the gateway address from IP6 route table. + // + Status = HttpBootCheckRouteTable (Private, HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr); + if (EFI_ERROR (Status)) { + Private->NoGateway = TRUE; + } else { + IP6_COPY_ADDRESS (&Private->GatewayIp.v6, &GatewayAddr); + } + + // + // Set the new address by Ip6ConfigProtocol manually. + // + Policy = Ip6ConfigPolicyManual; + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Create a notify event to set address flag when DAD if IP6 driver succeeded. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpBootCommonNotify, + &IsAddressOk, + &MappedEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Set static host ip6 address. This is a asynchronous process. + // + Status = Ip6Cfg->RegisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS), + &CfgAddr + ); + if (EFI_ERROR (Status) && Status != EFI_NOT_READY) { + goto ON_EXIT; + } else if (Status == EFI_NOT_READY) { + // + // Poll the network until the asynchronous process is finished. + // + while (!IsAddressOk) { + Ip6->Poll (Ip6); + } + // + // Check whether the Ip6 Address setting is successed. + // + DataSize = 0; + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + &DataSize, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Ip6Addr = AllocatePool (DataSize); + if (Ip6Addr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + &DataSize, + (VOID *) Ip6Addr + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index ++) { + if (CompareMem (Ip6Addr + Index, &CfgAddr, sizeof (EFI_IPv6_ADDRESS)) == 0) { + break; + } + } + if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) { + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + +ON_EXIT: + if (MappedEvt != NULL) { + Ip6Cfg->UnregisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + gBS->CloseEvent (MappedEvt); + } + + if (Ip6Addr != NULL) { + FreePool (Ip6Addr); + } + + return Status; +} + +/** + Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information. + + @param[in] Private Pointer to HTTP_BOOT private data. + + @retval EFI_SUCCESS The S.A.R.R process successfully finished. + @retval Others Failed to finish the S.A.R.R process. + +**/ +EFI_STATUS +HttpBootDhcp6Sarr ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_DHCP6_CONFIG_DATA Config; + EFI_DHCP6_MODE_DATA Mode; + EFI_DHCP6_RETRANSMISSION *Retransmit; + EFI_DHCP6_PACKET_OPTION *OptList[HTTP_BOOT_DHCP6_OPTION_MAX_NUM]; + UINT32 OptCount; + UINT8 Buffer[HTTP_BOOT_DHCP6_OPTION_MAX_SIZE]; + EFI_STATUS Status; + + Dhcp6 = Private->Dhcp6; + ASSERT (Dhcp6 != NULL); + + // + // Build options list for the request packet. + // + OptCount = HttpBootBuildDhcp6Options (Private, OptList, Buffer); + ASSERT (OptCount >0); + + Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION)); + if (Retransmit == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA)); + ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA)); + + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp6Callback = HttpBootDhcp6CallBack; + Config.CallbackContext = Private; + Config.IaInfoEvent = NULL; + Config.RapidCommit = FALSE; + Config.ReconfigureAccept = FALSE; + Config.IaDescriptor.IaId = NET_RANDOM (NetRandomInitSeed ()); + Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; + Config.SolicitRetransmission = Retransmit; + Retransmit->Irt = 4; + Retransmit->Mrc = 4; + Retransmit->Mrt = 32; + Retransmit->Mrd = 60; + + // + // Configure the DHCPv6 instance for HTTP boot. + // + Status = Dhcp6->Configure (Dhcp6, &Config); + FreePool (Retransmit); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Initialize the record fields for DHCPv6 offer in private data. + // + Private->OfferNum = 0; + Private->SelectIndex = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + // + // Start DHCPv6 S.A.R.R. process to acquire IPv6 address. + // + Status = Dhcp6->Start (Dhcp6); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Get the acquired IPv6 address and store them. + // + Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + ASSERT (Mode.Ia->State == Dhcp6Bound); + CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS)); + + AsciiPrint ("\n Station IPv6 address is "); + HttpBootShowIp6Addr (&Private->StationIp.v6); + AsciiPrint ("\n"); + +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + Dhcp6->Configure (Dhcp6, NULL); + } else { + ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA)); + ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA)); + Dhcp6->Configure (Dhcp6, &Config); + } + + return Status; + +} + diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h new file mode 100644 index 0000000000..59ca19e464 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h @@ -0,0 +1,198 @@ +/** @file + Functions declaration related with DHCPv6 for HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + + +#ifndef __EFI_HTTP_BOOT_DHCP6_H__ +#define __EFI_HTTP_BOOT_DHCP6_H__ + +#define HTTP_BOOT_OFFER_MAX_NUM 16 +#define HTTP_BOOT_DHCP6_OPTION_MAX_NUM 16 +#define HTTP_BOOT_DHCP6_OPTION_MAX_SIZE 312 +#define HTTP_BOOT_DHCP6_PACKET_MAX_SIZE 1472 +#define HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT 10 +#define HTTP_BOOT_DEFAULT_HOPLIMIT 64 +#define HTTP_BOOT_DEFAULT_LIFETIME 50000 + + +#define HTTP_BOOT_DHCP6_OPT_CLIENT_ID 1 +#define HTTP_BOOT_DHCP6_OPT_SERVER_ID 2 +#define HTTP_BOOT_DHCP6_OPT_IA_NA 3 +#define HTTP_BOOT_DHCP6_OPT_IA_TA 4 +#define HTTP_BOOT_DHCP6_OPT_IAADDR 5 +#define HTTP_BOOT_DHCP6_OPT_ORO 6 +#define HTTP_BOOT_DHCP6_OPT_PREFERENCE 7 +#define HTTP_BOOT_DHCP6_OPT_ELAPSED_TIME 8 +#define HTTP_BOOT_DHCP6_OPT_REPLAY_MSG 9 +#define HTTP_BOOT_DHCP6_OPT_AUTH 11 +#define HTTP_BOOT_DHCP6_OPT_UNICAST 12 +#define HTTP_BOOT_DHCP6_OPT_STATUS_CODE 13 +#define HTTP_BOOT_DHCP6_OPT_RAPID_COMMIT 14 +#define HTTP_BOOT_DHCP6_OPT_USER_CLASS 15 +#define HTTP_BOOT_DHCP6_OPT_VENDOR_CLASS 16 +#define HTTP_BOOT_DHCP6_OPT_VENDOR_OPTS 17 +#define HTTP_BOOT_DHCP6_OPT_INTERFACE_ID 18 +#define HTTP_BOOT_DHCP6_OPT_RECONFIG_MSG 19 +#define HTTP_BOOT_DHCP6_OPT_RECONFIG_ACCEPT 20 +#define HTTP_BOOT_DHCP6_OPT_DNS_SERVERS 23 +#define HTTP_BOOT_DHCP6_OPT_BOOT_FILE_URL 59 // Assigned by IANA, RFC 5970 +#define HTTP_BOOT_DHCP6_OPT_BOOT_FILE_PARAM 60 // Assigned by IANA, RFC 5970 +#define HTTP_BOOT_DHCP6_OPT_ARCH 61 // Assigned by IANA, RFC 5970 +#define HTTP_BOOT_DHCP6_OPT_UNDI 62 // Assigned by IANA, RFC 5970 +#define HTTP_BOOT_DHCP6_ENTERPRISE_NUM 343 // TODO: IANA TBD: temporarily using Intel's +#define HTTP_BOOT_DHCP6_MAX_BOOT_FILE_SIZE 65535 // It's a limitation of bit length, 65535*512 bytes. + +#define HTTP_BOOT_DHCP6_IDX_IA_NA 0 +#define HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL 1 +#define HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM 2 +#define HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS 3 +#define HTTP_BOOT_DHCP6_IDX_DNS_SERVER 4 +#define HTTP_BOOT_DHCP6_IDX_MAX 5 + +#pragma pack(1) +typedef struct { + UINT16 OpCode[256]; +} HTTP_BOOT_DHCP6_OPTION_ORO; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; +} HTTP_BOOT_DHCP6_OPTION_UNDI; + +typedef struct { + UINT16 Type; +} HTTP_BOOT_DHCP6_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[10]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} HTTP_BOOT_CLASS_ID; + +typedef struct { + UINT32 Vendor; + UINT16 ClassLen; + HTTP_BOOT_CLASS_ID ClassId; +} HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS; + +#pragma pack() + +typedef union { + HTTP_BOOT_DHCP6_OPTION_ORO *Oro; + HTTP_BOOT_DHCP6_OPTION_UNDI *Undi; + HTTP_BOOT_DHCP6_OPTION_ARCH *Arch; + HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS *VendorClass; +} HTTP_BOOT_DHCP6_OPTION_ENTRY; + +typedef union { + EFI_DHCP6_PACKET Offer; + EFI_DHCP6_PACKET Ack; + UINT8 Buffer[HTTP_BOOT_DHCP6_PACKET_MAX_SIZE]; +} HTTP_BOOT_DHCP6_PACKET; + +typedef struct { + HTTP_BOOT_DHCP6_PACKET Packet; + HTTP_BOOT_OFFER_TYPE OfferType; + EFI_DHCP6_PACKET_OPTION *OptList[HTTP_BOOT_DHCP6_IDX_MAX]; + VOID *UriParser; +} HTTP_BOOT_DHCP6_PACKET_CACHE; + +#define GET_NEXT_DHCP6_OPTION(Opt) \ + (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1) + +#define GET_DHCP6_OPTION_SIZE(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER)) + +/** + Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information. + + @param[in] Private Pointer to HTTP_BOOT private data. + + @retval EFI_SUCCESS The S.A.R.R process successfully finished. + @retval Others Failed to finish the S.A.R.R process. + +**/ +EFI_STATUS +HttpBootDhcp6Sarr ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +HttpBootSetIp6Policy ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + This function will register the default DNS addresses to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes. + @param[in] DnsServerData Point a list of DNS server address in an array + of EFI_IPv6_ADDRESS instances. + + @retval EFI_SUCCESS The DNS configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Dns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN UINTN DataLength, + IN VOID *DnsServerData + ); + +/** + This function will register the IPv6 gateway address to the network device. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP configuration has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Gateway ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + This function will register the station IP address. + + @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +HttpBootSetIp6Address ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +#endif diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.c b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.c new file mode 100644 index 0000000000..9fb33bbb53 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.c @@ -0,0 +1,1168 @@ +/** @file + Driver Binding functions implementation for UEFI HTTP boot. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/// +/// Driver Binding Protocol instance +/// +EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp4DxeDriverBinding = { + HttpBootIp4DxeDriverBindingSupported, + HttpBootIp4DxeDriverBindingStart, + HttpBootIp4DxeDriverBindingStop, + HTTP_BOOT_DXE_VERSION, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp6DxeDriverBinding = { + HttpBootIp6DxeDriverBindingSupported, + HttpBootIp6DxeDriverBindingStart, + HttpBootIp6DxeDriverBindingStop, + HTTP_BOOT_DXE_VERSION, + NULL, + NULL +}; + +/** + Destroy the HTTP child based on IPv4 stack. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to HTTP_BOOT_PRIVATE_DATA. + +**/ +VOID +HttpBootDestroyIp4Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + ASSERT (This != NULL); + ASSERT (Private != NULL); + + if (Private->Dhcp4Child != NULL) { + gBS->CloseProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + Private->Dhcp4Child + ); + } + + if (Private->HttpCreated) { + HttpIoDestroyIo (&Private->HttpIo); + Private->HttpCreated = FALSE; + } + + if (Private->Ip4Nic != NULL) { + + gBS->CloseProtocol ( + Private->Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Private->Ip4Nic->Controller + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip4Nic->Controller, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + NULL + ); + FreePool (Private->Ip4Nic); + Private->Ip4Nic = NULL; + } + +} + +/** + Destroy the HTTP child based on IPv6 stack. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to HTTP_BOOT_PRIVATE_DATA. + +**/ +VOID +HttpBootDestroyIp6Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + ASSERT (This != NULL); + ASSERT (Private != NULL); + + if (Private->Ip6Child != NULL) { + gBS->CloseProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + Private->Ip6Child + ); + } + + if (Private->Dhcp6Child != NULL) { + gBS->CloseProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + Private->Dhcp6Child + ); + } + + if (Private->HttpCreated) { + HttpIoDestroyIo(&Private->HttpIo); + Private->HttpCreated = FALSE; + } + + if (Private->Ip6Nic != NULL) { + + gBS->CloseProtocol ( + Private->Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Private->Ip6Nic->Controller + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip6Nic->Controller, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + NULL + ); + FreePool (Private->Ip6Nic); + Private->Ip6Nic = NULL; + } +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpBootIp4DxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Try to open the DHCP4, HTTP4 and Device Path protocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp4DxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_DEV_PATH *Node; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINT32 *Id; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID(Id); + } else { + // + // Initialize the private data structure. + // + Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE; + Private->Controller = ControllerHandle; + Private->Image = This->ImageHandle; + InitializeListHead (&Private->CacheList); + // + // Get the NII interface if it exists, it's not required. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->Nii, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Private->Nii = NULL; + } + + // + // Open Device Path Protocol to prepare for appending IP and URI node. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &Private->ParentDevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between + // NIC handle and the private data. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + &Private->Id + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } + + if (Private->Ip4Nic != NULL) { + // + // Already created before + // + return EFI_SUCCESS; + } + + Private->Ip4Nic = AllocateZeroPool (sizeof (HTTP_BOOT_VIRTUAL_NIC)); + if (Private->Ip4Nic == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Private->Ip4Nic->Private = Private; + Private->Ip4Nic->Signature = HTTP_BOOT_VIRTUAL_NIC_SIGNATURE; + + // + // Create DHCP4 child instance. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + &Private->Dhcp4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Private->Dhcp4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get the Ip4Config2 protocol, it's required to configure the default gateway address. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4Config2ProtocolGuid, + (VOID **) &Private->Ip4Config2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Append IPv4 device path node. + // + Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv4.Header.SubType = MSG_IPv4_DP; + SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH)); + Node->Ipv4.StaticIpAddress = FALSE; + DevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + if (DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Append URI device path node. + // + Node = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_URI_DP; + SetDevicePathNodeLength (Node, sizeof (EFI_DEVICE_PATH_PROTOCOL)); + Private->Ip4Nic->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (DevicePath); + if (Private->Ip4Nic->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Create a child handle for the HTTP boot and install DevPath and Load file protocol on it. + // + CopyMem (&Private->Ip4Nic->LoadFile, &gHttpBootDxeLoadFile, sizeof (EFI_LOAD_FILE_PROTOCOL)); + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip4Nic->Controller, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open the Caller Id child to setup a parent-child relationship between + // real NIC handle and the HTTP boot Ipv4 NIC handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + Private->Ip4Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + + +ON_ERROR: + + HttpBootDestroyIp4Children (This, Private); + FreePool (Private); + + return Status; +} + + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp4DxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_LOAD_FILE_PROTOCOL *LoadFile; + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_HANDLE NicHandle; + UINT32 *Id; + + // + // Try to get the Load File Protocol from the controller handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiLoadFileProtocolGuid, + (VOID **) &LoadFile, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // If failed, try to find the NIC handle for this controller. + // + NicHandle = HttpBootGetNicByIp4Children (ControllerHandle); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Try to retrieve the private data by the Caller Id Guid. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID (Id); + } else { + Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (LoadFile); + NicHandle = Private->Controller; + } + + // + // Disable the HTTP boot function. + // + Status = HttpBootStop (Private); + if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) { + return Status; + } + + // + // Destory all child instance and uninstall protocol interface. + // + HttpBootDestroyIp4Children (This, Private); + + if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) { + // + // Release the cached data. + // + HttpBootFreeCacheList (Private); + + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + FreePool (Private); + + } + + return EFI_SUCCESS; +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Try to open the DHCP6, HTTP and Device Path protocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; + +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_DEV_PATH *Node; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINT32 *Id; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID(Id); + } else { + // + // Initialize the private data structure. + // + Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE; + Private->Controller = ControllerHandle; + Private->Image = This->ImageHandle; + InitializeListHead (&Private->CacheList); + // + // Get the NII interface if it exists, it's not required. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->Nii, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Private->Nii = NULL; + } + + // + // Open Device Path Protocol to prepare for appending IP and URI node. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &Private->ParentDevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between + // NIC handle and the private data. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + &Private->Id + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } + + if (Private->Ip6Nic != NULL) { + // + // Already created before + // + return EFI_SUCCESS; + } + + Private->Ip6Nic = AllocateZeroPool (sizeof (HTTP_BOOT_VIRTUAL_NIC)); + if (Private->Ip6Nic == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Private->Ip6Nic->Private = Private; + Private->Ip6Nic->Signature = HTTP_BOOT_VIRTUAL_NIC_SIGNATURE; + + // + // Create Dhcp6 child and open Dhcp6 protocol + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Private->Dhcp6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Private->Dhcp6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Ip6 child and open Ip6 protocol for background ICMP packets. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + &Private->Ip6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + (VOID **) &Private->Ip6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Locate Ip6Config protocol, it's required to configure the default gateway address. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Private->Ip6Config, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Append IPv6 device path node. + // + Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv6.Header.SubType = MSG_IPv6_DP; + Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH; + SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH)); + DevicePath = AppendDevicePathNode(Private->ParentDevicePath, (EFI_DEVICE_PATH*) Node); + FreePool(Node); + if (DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Append URI device path node. + // + Node = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_URI_DP; + SetDevicePathNodeLength (Node, sizeof (EFI_DEVICE_PATH_PROTOCOL)); + Private->Ip6Nic->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (DevicePath); + if (Private->Ip6Nic->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Create a child handle for the HTTP boot and install DevPath and Load file protocol on it. + // + CopyMem (&Private->Ip6Nic->LoadFile, &gHttpBootDxeLoadFile, sizeof (Private->LoadFile)); + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip6Nic->Controller, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open the Caller Id child to setup a parent-child relationship between + // real NIC handle and the HTTP boot child handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + Private->Ip6Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + HttpBootDestroyIp6Children(This, Private); + FreePool (Private); + + return Status; + +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_LOAD_FILE_PROTOCOL *LoadFile; + HTTP_BOOT_PRIVATE_DATA *Private; + EFI_HANDLE NicHandle; + UINT32 *Id; + + // + // Try to get the Load File Protocol from the controller handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiLoadFileProtocolGuid, + (VOID **) &LoadFile, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // If failed, try to find the NIC handle for this controller. + // + NicHandle = HttpBootGetNicByIp6Children (ControllerHandle); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Try to retrieve the private data by the Caller Id Guid. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID (Id); + } else { + Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (LoadFile); + NicHandle = Private->Controller; + } + + // + // Disable the HTTP boot function. + // + Status = HttpBootStop (Private); + if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) { + return Status; + } + + // + // Destory all child instance and uninstall protocol interface. + // + HttpBootDestroyIp6Children (This, Private); + + if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) { + // + // Release the cached data. + // + HttpBootFreeCacheList (Private); + + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + FreePool (Private); + + } + + return EFI_SUCCESS; +} +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + // + // Install UEFI Driver Model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpBootIp4DxeDriverBinding, + ImageHandle, + &gHttpBootDxeComponentName, + &gHttpBootDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpBootIp6DxeDriverBinding, + NULL, + &gHttpBootDxeComponentName, + &gHttpBootDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gHttpBootIp4DxeDriverBinding, + &gEfiComponentName2ProtocolGuid, + &gHttpBootDxeComponentName2, + &gEfiComponentNameProtocolGuid, + &gHttpBootDxeComponentName, + NULL + ); + } + return Status; +} + diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.h b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.h new file mode 100644 index 0000000000..452c8f4906 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.h @@ -0,0 +1,450 @@ +/** @file + UEFI HTTP boot driver's private data structure and interfaces declaration. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_BOOT_DXE_H__ +#define __EFI_HTTP_BOOT_DXE_H__ + +#include + +// +// Libraries +// +#include +#include +#include +#include +#include +#include +#include +#include + +// +// UEFI Driver Model Protocols +// +#include +#include +#include + +// +// Consumed Protocols +// +#include +#include +#include +#include +#include +#include +#include +// +// Produced Protocols +// +#include + +// +// Driver Version +// +#define HTTP_BOOT_DXE_VERSION 0xa + +// +// Protocol instances +// +extern EFI_DRIVER_BINDING_PROTOCOL gHttpBootDxeDriverBinding; +extern EFI_COMPONENT_NAME2_PROTOCOL gHttpBootDxeComponentName2; +extern EFI_COMPONENT_NAME_PROTOCOL gHttpBootDxeComponentName; + +// +// Private data structure +// +typedef struct _HTTP_BOOT_PRIVATE_DATA HTTP_BOOT_PRIVATE_DATA; +typedef struct _HTTP_BOOT_VIRTUAL_NIC HTTP_BOOT_VIRTUAL_NIC; + +// +// Include files with internal function prototypes +// +#include "HttpBootComponentName.h" +#include "HttpBootDhcp4.h" +#include "HttpBootDhcp6.h" +#include "HttpBootImpl.h" +#include "HttpBootSupport.h" +#include "HttpBootClient.h" + +typedef union { + HTTP_BOOT_DHCP4_PACKET_CACHE Dhcp4; + HTTP_BOOT_DHCP6_PACKET_CACHE Dhcp6; +} HTTP_BOOT_DHCP_PACKET_CACHE; + +struct _HTTP_BOOT_VIRTUAL_NIC { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + HTTP_BOOT_PRIVATE_DATA *Private; +}; + +struct _HTTP_BOOT_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_HANDLE Image; + + HTTP_BOOT_VIRTUAL_NIC *Ip4Nic; + HTTP_BOOT_VIRTUAL_NIC *Ip6Nic; + + // + // Cousumed children + // + EFI_HANDLE Ip6Child; + EFI_HANDLE Dhcp4Child; + EFI_HANDLE Dhcp6Child; + HTTP_IO HttpIo; + BOOLEAN HttpCreated; + + // + // Consumed protocol + // + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii; + EFI_IP6_PROTOCOL *Ip6; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + + // + // Produced protocol + // + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINT32 Id; + + // + // Mode data + // + BOOLEAN UsingIpv6; + BOOLEAN Started; + EFI_IP_ADDRESS StationIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS GatewayIp; + EFI_IP_ADDRESS ServerIp; + UINT16 Port; + CHAR8 *BootFileUri; + VOID *BootFileUriParser; + UINTN BootFileSize; + BOOLEAN NoGateway; + + // + // Cached HTTP data + // + LIST_ENTRY CacheList; + + // + // Cached DHCP offer + // + // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer. + // + // It supposed that + // + // OfferNum: 8 + // OfferBuffer: [ProxyNameUri, DhcpNameUri, DhcpIpUri, ProxyNameUri, ProxyIpUri, DhcpOnly, DhcpIpUri, DhcpNameUriDns] + // (OfferBuffer is 0-based.) + // + // And assume that (DhcpIpUri is the first priority actually.) + // + // SelectIndex: 5 + // SelectProxyType: HttpOfferTypeProxyIpUri + // (SelectIndex is 1-based, and 0 means no one is selected.) + // + // So it should be + // + // DhcpIpUri DhcpNameUriDns DhcpDns DhcpOnly ProxyNameUri ProxyIpUri DhcpNameUri + // OfferCount: [ 2, 1, 0, 1, 2, 1, 1] + // + // OfferIndex: {[ 2, 7, 0, 5, 0, *4, 1] + // [ 6, 0, 0, 0, 3, 0, 0] + // [ 0, 0, 0, 0, 0, 0, 0] + // ... ]} + // (OfferIndex is 0-based.) + // + // + UINT32 SelectIndex; + UINT32 SelectProxyType; + HTTP_BOOT_DHCP_PACKET_CACHE OfferBuffer[HTTP_BOOT_OFFER_MAX_NUM]; + UINT32 OfferNum; + UINT32 OfferCount[HttpOfferTypeMax]; + UINT32 OfferIndex[HttpOfferTypeMax][HTTP_BOOT_OFFER_MAX_NUM]; +}; + +#define HTTP_BOOT_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('H', 'B', 'P', 'D') +#define HTTP_BOOT_VIRTUAL_NIC_SIGNATURE SIGNATURE_32 ('H', 'B', 'V', 'N') +#define HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE(a) CR (a, HTTP_BOOT_PRIVATE_DATA, LoadFile, HTTP_BOOT_PRIVATE_DATA_SIGNATURE) +#define HTTP_BOOT_PRIVATE_DATA_FROM_ID(a) CR (a, HTTP_BOOT_PRIVATE_DATA, Id, HTTP_BOOT_PRIVATE_DATA_SIGNATURE) +#define HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE(a) CR (a, HTTP_BOOT_VIRTUAL_NIC, LoadFile, HTTP_BOOT_VIRTUAL_NIC_SIGNATURE) +extern EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile; + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpBootIp4DxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp4DxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp4DxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpBootIp6DxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); +#endif diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.inf b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.inf new file mode 100644 index 0000000000..e24b568ddc --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.inf @@ -0,0 +1,77 @@ +## @file +# This modules produce the Load File Protocol for UEFI HTTP boot. +# +# Copyright (c) 2015, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HttpBootDxe + FILE_GUID = ecebcb00-d9c8-11e4-af3d-8cdcd426c973 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = HttpBootDxeDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = HttpBootDxe.uni + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[Sources] + HttpBootDxe.h + HttpBootDxe.c + HttpBootComponentName.h + HttpBootComponentName.c + HttpBootImpl.h + HttpBootImpl.c + HttpBootDhcp4.h + HttpBootDhcp4.c + HttpBootDhcp6.h + HttpBootDhcp6.c + HttpBootSupport.h + HttpBootSupport.c + HttpBootClient.h + HttpBootClient.c + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + MemoryAllocationLib + BaseLib + UefiLib + DevicePathLib + DebugLib + NetLib + HttpLib + +[Protocols] + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + + gEfiLoadFileProtocolGuid ## BY_START + gEfiHttpServiceBindingProtocolGuid ## CONSUMES + gEfiHttpProtocolGuid ## CONSUMES + gEfiDhcp4ServiceBindingProtocolGuid ## TO_START + gEfiDhcp4ProtocolGuid ## TO_START + gEfiIp4Config2ProtocolGuid ## TO_START + gEfiDhcp6ServiceBindingProtocolGuid ## TO_START + gEfiDhcp6ProtocolGuid ## TO_START + gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp6ServiceBindingProtocolGuid ## TO_START + gEfiIp6ProtocolGuid ## TO_START + gEfiIp6ConfigProtocolGuid ## TO_START + gEfiNetworkInterfaceIdentifierProtocolGuid_31 ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + HttpBootDxeExtra.uni diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.uni b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.uni new file mode 100644 index 0000000000..fe743df852 Binary files /dev/null and b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.uni differ diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni b/Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni new file mode 100644 index 0000000000..2d45c4cda5 Binary files /dev/null and b/Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni differ diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c new file mode 100644 index 0000000000..9ea0d7f95f --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c @@ -0,0 +1,435 @@ +/** @file + The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + +/** + Enable the use of UEFI HTTP boot function. + + @param[in] Private The pointer to the driver's private data. + @param[in] UsingIpv6 Specifies the type of IP addresses that are to be + used during the session that is being started. + Set to TRUE for IPv6, and FALSE for IPv4. + + @retval EFI_SUCCESS HTTP boot was successfully enabled. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval EFI_ALREADY_STARTED The driver is already in started state. + +**/ +EFI_STATUS +HttpBootStart ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN BOOLEAN UsingIpv6 + ) +{ + UINTN Index; + EFI_STATUS Status; + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->Started) { + return EFI_ALREADY_STARTED; + } + + // + // Detect whether using ipv6 or not, and set it to the private data. + // + if (UsingIpv6 && Private->Ip6Nic != NULL) { + Private->UsingIpv6 = TRUE; + } else if (!UsingIpv6 && Private->Ip4Nic != NULL) { + Private->UsingIpv6 = FALSE; + } else { + return EFI_UNSUPPORTED; + } + + // + // Init the content of cached DHCP offer list. + // + ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer)); + if (!Private->UsingIpv6) { + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_BOOT_DHCP4_PACKET_MAX_SIZE; + } + } else { + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = HTTP_BOOT_DHCP6_PACKET_MAX_SIZE; + } + } + + if (Private->UsingIpv6) { + // + // Set Ip6 policy to Automatic to start the Ip6 router discovery. + // + Status = HttpBootSetIp6Policy (Private); + if (EFI_ERROR (Status)) { + return Status; + } + } + Private->Started = TRUE; + + return EFI_SUCCESS; +} + +/** + Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Boot info was successfully retrieved. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval EFI_NOT_STARTED The driver is in stopped state. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpBootDhcp ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Started) { + return EFI_NOT_STARTED; + } + + Status = EFI_DEVICE_ERROR; + + if (!Private->UsingIpv6) { + // + // Start D.O.R.A process to get a IPv4 address and other boot information. + // + Status = HttpBootDhcp4Dora (Private); + } else { + // + // Start S.A.R.R process to get a IPv6 address and other boot information. + // + Status = HttpBootDhcp6Sarr (Private); + } + + return Status; +} + +/** + Attempt to download the boot file through HTTP message exchange. + + @param[in] Private The pointer to the driver's private data. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[in] Buffer The memory buffer to transfer the file to. If Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS Boot file was loaded successfully. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval EFI_NOT_STARTED The driver is in stopped state. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the boot file. BufferSize has + been updated with the size needed to complete the request. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpBootLoadFile ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + EFI_STATUS Status; + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Started) { + return EFI_NOT_STARTED; + } + + Status = EFI_DEVICE_ERROR; + + if (Private->BootFileUri == NULL) { + // + // Parse the cached offer to get the boot file URL first. + // + Status = HttpBootDiscoverBootInfo (Private); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (!Private->HttpCreated) { + // + // Create HTTP child. + // + Status = HttpBootCreateHttpIo (Private); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (Private->BootFileSize == 0) { + // + // Discover the information about the bootfile if we haven't. + // + + // + // Try to use HTTP HEAD method. + // + Status = HttpBootGetBootFile ( + Private, + TRUE, + &Private->BootFileSize, + NULL + ); + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + // + // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method. + // + ASSERT (Private->BootFileSize == 0); + Status = HttpBootGetBootFile ( + Private, + FALSE, + &Private->BootFileSize, + NULL + ); + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + } + } + + if (*BufferSize < Private->BootFileSize) { + *BufferSize = Private->BootFileSize; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Load the boot file into Buffer + // + return HttpBootGetBootFile ( + Private, + FALSE, + BufferSize, + Buffer + ); +} + +/** + Disable the use of UEFI HTTP boot function. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS HTTP boot was successfully disabled. + @retval EFI_NOT_STARTED The driver is already in stopped state. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval Others Unexpected error when stop the function. + +**/ +EFI_STATUS +HttpBootStop ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ) +{ + UINTN Index; + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Started) { + return EFI_NOT_STARTED; + } + + if (Private->HttpCreated) { + HttpIoDestroyIo (&Private->HttpIo); + Private->HttpCreated = FALSE; + } + + Private->Started = FALSE; + ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS)); + Private->Port = 0; + Private->BootFileUri = NULL; + Private->BootFileUriParser = NULL; + Private->BootFileSize = 0; + Private->SelectIndex = 0; + Private->SelectProxyType = HttpOfferTypeMax; + + if (!Private->UsingIpv6) { + // + // Stop and release the DHCP4 child. + // + Private->Dhcp4->Stop (Private->Dhcp4); + Private->Dhcp4->Configure (Private->Dhcp4, NULL); + + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + if (Private->OfferBuffer[Index].Dhcp4.UriParser) { + HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser); + } + } + } else { + // + // Stop and release the DHCP6 child. + // + Private->Dhcp6->Stop (Private->Dhcp6); + Private->Dhcp6->Configure (Private->Dhcp6, NULL); + + for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) { + if (Private->OfferBuffer[Index].Dhcp6.UriParser) { + HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp6.UriParser); + } + } + } + + ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer)); + Private->OfferNum = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + return EFI_SUCCESS; +} + +/** + Causes the driver to load a specified file. + + @param This Protocol instance pointer. + @param FilePath The device specific path of the file to load. + @param BootPolicy If TRUE, indicates that the request originates from the + boot manager is attempting to load FilePath as a boot + selection. If FALSE, then FilePath must match as exact file + to be loaded. + @param BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NO_MEDIA No medium was present to load the file. + @retval EFI_DEVICE_ERROR The file was not loaded due to a device error. + @retval EFI_NO_RESPONSE The remote system did not respond. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_ABORTED The file load process was manually cancelled. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete + the request. + +**/ +EFI_STATUS +EFIAPI +HttpBootDxeLoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + HTTP_BOOT_PRIVATE_DATA *Private; + HTTP_BOOT_VIRTUAL_NIC *VirtualNic; + BOOLEAN MediaPresent; + BOOLEAN UsingIpv6; + EFI_STATUS Status; + + if (This == NULL || BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Only support BootPolicy + // + if (!BootPolicy) { + return EFI_UNSUPPORTED; + } + + VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This); + Private = VirtualNic->Private; + UsingIpv6 = FALSE; + + // + // Check media status before HTTP boot start + // + MediaPresent = TRUE; + NetLibDetectMedia (Private->Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Check whether the virtual nic is using IPv6 or not. + // + if (VirtualNic == Private->Ip6Nic) { + UsingIpv6 = TRUE; + } + + // + // Initialize HTTP boot and load the boot file. + // + Status = HttpBootStart (Private, UsingIpv6); + if (Status == EFI_ALREADY_STARTED && UsingIpv6 != Private->UsingIpv6) { + // + // Http boot Driver has already been started but not on the required IP version, restart it. + // + Status = HttpBootStop (Private); + if (!EFI_ERROR (Status)) { + Status = HttpBootStart (Private, UsingIpv6); + } + } + if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) { + Status = HttpBootLoadFile (Private, BufferSize, Buffer); + } + + if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) { + HttpBootStop (Private); + } else { + if (!Private->UsingIpv6) { + // + // Stop and release the DHCP4 child. + // + Private->Dhcp4->Stop (Private->Dhcp4); + Private->Dhcp4->Configure (Private->Dhcp4, NULL); + } else { + // + // Stop and release the DHCP6 child. + // + Private->Dhcp6->Stop (Private->Dhcp6); + Private->Dhcp6->Configure (Private->Dhcp6, NULL); + } + } + + return Status; +} + +/// +/// Load File Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = { + HttpBootDxeLoadFile +}; diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootImpl.h b/Core/NetworkPkg/HttpBootDxe/HttpBootImpl.h new file mode 100644 index 0000000000..7066338175 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootImpl.h @@ -0,0 +1,50 @@ +/** @file + The declaration of UEFI HTTP boot function. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef __EFI_HTTP_BOOT_IMPL_H__ +#define __EFI_HTTP_BOOT_IMPL_H__ + +/** + Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS Boot info was successfully retrieved. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval EFI_NOT_STARTED The driver is in stopped state. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpBootDhcp ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +/** + Disable the use of UEFI HTTP boot function. + + @param[in] Private The pointer to the driver's private data. + + @retval EFI_SUCCESS HTTP boot was successfully disabled. + @retval EFI_NOT_STARTED The driver is already in stopped state. + @retval EFI_INVALID_PARAMETER Private is NULL. + @retval Others Unexpected error when stop the function. + +**/ +EFI_STATUS +HttpBootStop ( + IN HTTP_BOOT_PRIVATE_DATA *Private + ); + +#endif diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.c b/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.c new file mode 100644 index 0000000000..f9bbe4b1a0 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.c @@ -0,0 +1,987 @@ +/** @file + Support functions implementation for UEFI HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpBootDxe.h" + + +/** + Get the Nic handle using any child handle in the IPv4 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv4. + + @return NicHandle The pointer to the Nic handle. + @return NULL Can't find the Nic handle. + +**/ +EFI_HANDLE +HttpBootGetNicByIp4Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + + return NicHandle; +} + +/** + Get the Nic handle using any child handle in the IPv6 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv6. + + @return NicHandle The pointer to the Nic handle. + @return NULL Can't find the Nic handle. + +**/ +EFI_HANDLE +HttpBootGetNicByIp6Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + + return NicHandle; +} + +/** + This function is to convert UINTN to ASCII string with the required formatting. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + @param[in] Length The length of the required format. + +**/ +VOID +HttpBootUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ) +{ + UINTN Remainder; + + while (Length > 0) { + Length--; + Remainder = Number % 10; + Number /= 10; + Buffer[Length] = (UINT8) ('0' + Remainder); + } +} + +/** + This function is to display the IPv4 address. + + @param[in] Ip The pointer to the IPv4 address. + +**/ +VOID +HttpBootShowIp4Addr ( + IN EFI_IPv4_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 4; Index++) { + AsciiPrint ("%d", Ip->Addr[Index]); + if (Index < 3) { + AsciiPrint ("."); + } + } +} + +/** + This function is to display the IPv6 address. + + @param[in] Ip The pointer to the IPv6 address. + +**/ +VOID +HttpBootShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 16; Index++) { + + if (Ip->Addr[Index] != 0) { + AsciiPrint ("%x", Ip->Addr[Index]); + } + Index++; + if (Index > 15) { + return; + } + if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) { + AsciiPrint ("0"); + } + AsciiPrint ("%x", Ip->Addr[Index]); + if (Index < 15) { + AsciiPrint (":"); + } + } +} + +/** + This function is to display the HTTP error status. + + @param[in] StatusCode The status code value in HTTP message. + +**/ +VOID +HttpBootPrintErrorMessage ( + EFI_HTTP_STATUS_CODE StatusCode + ) +{ + AsciiPrint ("\n"); + + switch (StatusCode) { + case HTTP_STATUS_300_MULTIPLE_CHIOCES: + AsciiPrint ("\n Redirection: 300 Multiple Choices"); + break; + + case HTTP_STATUS_301_MOVED_PERMANENTLY: + AsciiPrint ("\n Redirection: 301 Moved Permanently"); + break; + + case HTTP_STATUS_302_FOUND: + AsciiPrint ("\n Redirection: 302 Found"); + break; + + case HTTP_STATUS_303_SEE_OTHER: + AsciiPrint ("\n Redirection: 303 See Other"); + break; + + case HTTP_STATUS_304_NOT_MODIFIED: + AsciiPrint ("\n Redirection: 304 Not Modified"); + break; + + case HTTP_STATUS_305_USE_PROXY: + AsciiPrint ("\n Redirection: 305 Use Proxy"); + break; + + case HTTP_STATUS_307_TEMPORARY_REDIRECT: + AsciiPrint ("\n Redirection: 307 Temporary Redirect"); + break; + + case HTTP_STATUS_400_BAD_REQUEST: + AsciiPrint ("\n Client Error: 400 Bad Request"); + break; + + case HTTP_STATUS_401_UNAUTHORIZED: + AsciiPrint ("\n Client Error: 401 Unauthorized"); + break; + + case HTTP_STATUS_402_PAYMENT_REQUIRED: + AsciiPrint ("\n Client Error: 402 Payment Required"); + break; + + case HTTP_STATUS_403_FORBIDDEN: + AsciiPrint ("\n Client Error: 403 Forbidden"); + break; + + case HTTP_STATUS_404_NOT_FOUND: + AsciiPrint ("\n Client Error: 404 Not Found"); + break; + + case HTTP_STATUS_405_METHOD_NOT_ALLOWED: + AsciiPrint ("\n Client Error: 405 Method Not Allowed"); + break; + + case HTTP_STATUS_406_NOT_ACCEPTABLE: + AsciiPrint ("\n Client Error: 406 Not Acceptable"); + break; + + case HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED: + AsciiPrint ("\n Client Error: 407 Proxy Authentication Required"); + break; + + case HTTP_STATUS_408_REQUEST_TIME_OUT: + AsciiPrint ("\n Client Error: 408 Request Timeout"); + break; + + case HTTP_STATUS_409_CONFLICT: + AsciiPrint ("\n Client Error: 409 Conflict"); + break; + + case HTTP_STATUS_410_GONE: + AsciiPrint ("\n Client Error: 410 Gone"); + break; + + case HTTP_STATUS_411_LENGTH_REQUIRED: + AsciiPrint ("\n Client Error: 411 Length Required"); + break; + + case HTTP_STATUS_412_PRECONDITION_FAILED: + AsciiPrint ("\n Client Error: 412 Precondition Failed"); + break; + + case HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE: + AsciiPrint ("\n Client Error: 413 Request Entity Too Large"); + break; + + case HTTP_STATUS_414_REQUEST_URI_TOO_LARGE: + AsciiPrint ("\n Client Error: 414 Request URI Too Long"); + break; + + case HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE: + AsciiPrint ("\n Client Error: 415 Unsupported Media Type"); + break; + + case HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED: + AsciiPrint ("\n Client Error: 416 Requested Range Not Satisfiable"); + break; + + case HTTP_STATUS_417_EXPECTATION_FAILED: + AsciiPrint ("\n Client Error: 417 Expectation Failed"); + break; + + case HTTP_STATUS_500_INTERNAL_SERVER_ERROR: + AsciiPrint ("\n Server Error: 500 Internal Server Error"); + break; + + case HTTP_STATUS_501_NOT_IMPLEMENTED: + AsciiPrint ("\n Server Error: 501 Not Implemented"); + break; + + case HTTP_STATUS_502_BAD_GATEWAY: + AsciiPrint ("\n Server Error: 502 Bad Gateway"); + break; + + case HTTP_STATUS_503_SERVICE_UNAVAILABLE: + AsciiPrint ("\n Server Error: 503 Service Unavailable"); + break; + + case HTTP_STATUS_504_GATEWAY_TIME_OUT: + AsciiPrint ("\n Server Error: 504 Gateway Timeout"); + break; + + case HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED: + AsciiPrint ("\n Server Error: 505 HTTP Version Not Supported"); + break; + + default: ; + + } +} + +/** + Notify the callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +HttpBootCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] Private The pointer to the driver's private data. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. +**/ +EFI_STATUS +HttpBootDns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ) +{ + EFI_STATUS Status; + EFI_DNS6_PROTOCOL *Dns6; + EFI_DNS6_CONFIG_DATA Dns6ConfigData; + EFI_DNS6_COMPLETION_TOKEN Token; + EFI_HANDLE Dns6Handle; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_IPv6_ADDRESS *DnsServerList; + UINTN DnsServerListCount; + UINTN DataSize; + BOOLEAN IsDone; + + DnsServerList = NULL; + DnsServerListCount = 0; + Dns6 = NULL; + Dns6Handle = NULL; + ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN)); + + // + // Get DNS server list from EFI IPv6 Configuration protocol. + // + Status = gBS->HandleProtocol (Private->Controller, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config); + if (!EFI_ERROR (Status)) { + // + // Get the required size. + // + DataSize = 0; + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + DnsServerList = AllocatePool (DataSize); + if (DnsServerList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList); + if (EFI_ERROR (Status)) { + FreePool (DnsServerList); + DnsServerList = NULL; + } else { + DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + } + } + } + // + // Create a DNSv6 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Private->Controller, + Private->Image, + &gEfiDns6ServiceBindingProtocolGuid, + &Dns6Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + (VOID **) &Dns6, + Private->Image, + Private->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Configure DNS6 instance for the DNS server address and protocol. + // + ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA)); + Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount; + Dns6ConfigData.DnsServerList = DnsServerList; + Dns6ConfigData.EnableDnsCache = TRUE; + Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP; + IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp,&Private->StationIp.v6); + Status = Dns6->Configure ( + Dns6, + &Dns6ConfigData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + // + // Create event to set the IsDone flag when name resolution is finished. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpBootCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Start asynchronous name resolution. + // + Status = Dns6->HostNameToIp (Dns6, HostName, &Token); + if (EFI_ERROR (Status)) { + goto Exit; + } + + while (!IsDone) { + Dns6->Poll (Dns6); + } + + // + // Name resolution is done, check result. + // + Status = Token.Status; + if (!EFI_ERROR (Status)) { + if (Token.RspData.H2AData == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // We just return the first IPv6 address from DNS protocol. + // + IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); + Status = EFI_SUCCESS; + } +Exit: + + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + if (Token.RspData.H2AData != NULL) { + if (Token.RspData.H2AData->IpList != NULL) { + FreePool (Token.RspData.H2AData->IpList); + } + FreePool (Token.RspData.H2AData); + } + + if (Dns6 != NULL) { + Dns6->Configure (Dns6, NULL); + + gBS->CloseProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + Private->Image, + Private->Controller + ); + } + + if (Dns6Handle != NULL) { + NetLibDestroyServiceChild ( + Private->Controller, + Private->Image, + &gEfiDns6ServiceBindingProtocolGuid, + Dns6Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} +/** + Create a HTTP_IO_HEADER to hold the HTTP header items. + + @param[in] MaxHeaderCount The maximun number of HTTP header in this holder. + + @return A pointer of the HTTP header holder or NULL if failed. + +**/ +HTTP_IO_HEADER * +HttpBootCreateHeader ( + UINTN MaxHeaderCount + ) +{ + HTTP_IO_HEADER *HttpIoHeader; + + if (MaxHeaderCount == 0) { + return NULL; + } + + HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER)); + if (HttpIoHeader == NULL) { + return NULL; + } + + HttpIoHeader->MaxHeaderCount = MaxHeaderCount; + HttpIoHeader->Headers = (EFI_HTTP_HEADER *) (HttpIoHeader + 1); + + return HttpIoHeader; +} + +/** + Destroy the HTTP_IO_HEADER and release the resouces. + + @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed. + +**/ +VOID +HttpBootFreeHeader ( + IN HTTP_IO_HEADER *HttpIoHeader + ) +{ + UINTN Index; + + if (HttpIoHeader != NULL) { + if (HttpIoHeader->HeaderCount != 0) { + for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) { + FreePool (HttpIoHeader->Headers[Index].FieldName); + FreePool (HttpIoHeader->Headers[Index].FieldValue); + } + } + FreePool (HttpIoHeader); + } +} + +/** + Find a specified header field according to the field name. + + @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] Headers Array containing list of HTTP headers. + @param[in] FieldName Null terminated string which describes a field name. + + @return Pointer to the found header or NULL. + +**/ +EFI_HTTP_HEADER * +HttpBootFindHeader ( + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, + IN CHAR8 *FieldName + ) +{ + UINTN Index; + + if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) { + return NULL; + } + + for (Index = 0; Index < HeaderCount; Index++){ + // + // Field names are case-insensitive (RFC 2616). + // + if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) { + return &Headers[Index]; + } + } + return NULL; +} + +/** + Set or update a HTTP header with the field name and corresponding value. + + @param[in] HttpIoHeader Point to the HTTP header holder. + @param[in] FieldName Null terminated string which describes a field name. + @param[in] FieldValue Null terminated string which describes the corresponding field value. + + @retval EFI_SUCCESS The HTTP header has been set or updated. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation. + @retval Other Unexpected error happened. + +**/ +EFI_STATUS +HttpBootSetHeader ( + IN HTTP_IO_HEADER *HttpIoHeader, + IN CHAR8 *FieldName, + IN CHAR8 *FieldValue + ) +{ + EFI_HTTP_HEADER *Header; + UINTN StrSize; + CHAR8 *NewFieldValue; + + if (HttpIoHeader == NULL || FieldName == NULL || FieldValue == NULL) { + return EFI_INVALID_PARAMETER; + } + + Header = HttpBootFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName); + if (Header == NULL) { + // + // Add a new header. + // + if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) { + return EFI_OUT_OF_RESOURCES; + } + Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount]; + + StrSize = AsciiStrSize (FieldName); + Header->FieldName = AllocatePool (StrSize); + if (Header->FieldName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Header->FieldName, FieldName, StrSize); + Header->FieldName[StrSize -1] = '\0'; + + StrSize = AsciiStrSize (FieldValue); + Header->FieldValue = AllocatePool (StrSize); + if (Header->FieldValue == NULL) { + FreePool (Header->FieldName); + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Header->FieldValue, FieldValue, StrSize); + Header->FieldValue[StrSize -1] = '\0'; + + HttpIoHeader->HeaderCount++; + } else { + // + // Update an existing one. + // + StrSize = AsciiStrSize (FieldValue); + NewFieldValue = AllocatePool (StrSize); + if (NewFieldValue == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (NewFieldValue, FieldValue, StrSize); + NewFieldValue[StrSize -1] = '\0'; + + if (Header->FieldValue != NULL) { + FreePool (Header->FieldValue); + } + Header->FieldValue = NewFieldValue; + } + + return EFI_SUCCESS; +} + +/** + Create a HTTP_IO to access the HTTP service. It will create and configure + a HTTP child handle. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + @param[in] ConfigData The HTTP_IO configuration data. + @param[out] HttpIo The HTTP_IO. + + @retval EFI_SUCCESS The HTTP_IO is created and configured. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Failed to create the HTTP_IO or configure it. + +**/ +EFI_STATUS +HttpIoCreateIo ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN UINT8 IpVersion, + IN HTTP_IO_CONFIG_DATA *ConfigData, + OUT HTTP_IO *HttpIo + ) +{ + EFI_STATUS Status; + EFI_HTTP_CONFIG_DATA HttpConfigData; + EFI_HTTPv4_ACCESS_POINT Http4AccessPoint; + EFI_HTTPv6_ACCESS_POINT Http6AccessPoint; + EFI_HTTP_PROTOCOL *Http; + EFI_EVENT Event; + + if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) { + return EFI_UNSUPPORTED; + } + + ZeroMem (HttpIo, sizeof (HTTP_IO)); + + // + // Create the HTTP child instance and get the HTTP protocol. + // + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiHttpServiceBindingProtocolGuid, + &HttpIo->Handle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + HttpIo->Handle, + &gEfiHttpProtocolGuid, + (VOID **) &Http, + Image, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) || (Http == NULL)) { + goto ON_ERROR; + } + + // + // Init the configuration data and configure the HTTP child. + // + HttpIo->Image = Image; + HttpIo->Controller = Controller; + HttpIo->IpVersion = IpVersion; + HttpIo->Http = Http; + + ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA)); + HttpConfigData.HttpVersion = HttpVersion11; + HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut; + if (HttpIo->IpVersion == IP_VERSION_4) { + HttpConfigData.LocalAddressIsIPv6 = FALSE; + + Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress; + Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort; + IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp); + IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask); + HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint; + } else { + HttpConfigData.LocalAddressIsIPv6 = TRUE; + Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort; + IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp); + HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint; + } + + Status = Http->Configure (Http, &HttpConfigData); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create events for variuos asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpBootCommonNotify, + &HttpIo->IsTxDone, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + HttpIo->ReqToken.Event = Event; + HttpIo->ReqToken.Message = &HttpIo->ReqMessage; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpBootCommonNotify, + &HttpIo->IsRxDone, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + HttpIo->RspToken.Event = Event; + HttpIo->RspToken.Message = &HttpIo->RspMessage; + + return EFI_SUCCESS; + +ON_ERROR: + HttpIoDestroyIo (HttpIo); + + return Status; +} + +/** + Destroy the HTTP_IO and release the resouces. + + @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed. + +**/ +VOID +HttpIoDestroyIo ( + IN HTTP_IO *HttpIo + ) +{ + EFI_HTTP_PROTOCOL *Http; + EFI_EVENT Event; + + if (HttpIo == NULL) { + return; + } + + Event = HttpIo->ReqToken.Event; + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + Event = HttpIo->RspToken.Event; + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + Http = HttpIo->Http; + if (Http != NULL) { + Http->Configure (Http, NULL); + gBS->CloseProtocol ( + HttpIo->Handle, + &gEfiHttpProtocolGuid, + HttpIo->Image, + HttpIo->Controller + ); + } + + NetLibDestroyServiceChild ( + HttpIo->Controller, + HttpIo->Image, + &gEfiHttpServiceBindingProtocolGuid, + HttpIo->Handle + ); +} + +/** + Synchronously send a HTTP REQUEST message to the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] Request A pointer to storage such data as URL and HTTP method. + @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] Headers Array containing list of HTTP headers. + @param[in] BodyLength Length in bytes of the HTTP body. + @param[in] Body Body associated with the HTTP request. + + @retval EFI_SUCCESS The HTTP request is trasmitted. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoSendRequest ( + IN HTTP_IO *HttpIo, + IN EFI_HTTP_REQUEST_DATA *Request, + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, + IN UINTN BodyLength, + IN VOID *Body + ) +{ + EFI_STATUS Status; + EFI_HTTP_PROTOCOL *Http; + + if (HttpIo == NULL || HttpIo->Http == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpIo->ReqToken.Status = EFI_NOT_READY; + HttpIo->ReqToken.Message->Data.Request = Request; + HttpIo->ReqToken.Message->HeaderCount = HeaderCount; + HttpIo->ReqToken.Message->Headers = Headers; + HttpIo->ReqToken.Message->BodyLength = BodyLength; + HttpIo->ReqToken.Message->Body = Body; + + // + // Queue the request token to HTTP instances. + // + Http = HttpIo->Http; + HttpIo->IsTxDone = FALSE; + Status = Http->Request ( + Http, + &HttpIo->ReqToken + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the network until transmit finish. + // + while (!HttpIo->IsTxDone) { + Http->Poll (Http); + } + + return HttpIo->ReqToken.Status; +} + +/** + Synchronously receive a HTTP RESPONSE message from the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header). + FALSE to continue receive the previous response message. + @param[out] ResponseData Point to a wrapper of the received response data. + + @retval EFI_SUCCESS The HTTP response is received. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoRecvResponse ( + IN HTTP_IO *HttpIo, + IN BOOLEAN RecvMsgHeader, + OUT HTTP_IO_RESPONSE_DATA *ResponseData + ) +{ + EFI_STATUS Status; + EFI_HTTP_PROTOCOL *Http; + EFI_HTTP_STATUS_CODE StatusCode; + + if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Queue the response token to HTTP instances. + // + HttpIo->RspToken.Status = EFI_NOT_READY; + if (RecvMsgHeader) { + HttpIo->RspToken.Message->Data.Response = &ResponseData->Response; + } else { + HttpIo->RspToken.Message->Data.Response = NULL; + } + HttpIo->RspToken.Message->HeaderCount = 0; + HttpIo->RspToken.Message->Headers = NULL; + HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength; + HttpIo->RspToken.Message->Body = ResponseData->Body; + + Http = HttpIo->Http; + HttpIo->IsRxDone = FALSE; + Status = Http->Response ( + Http, + &HttpIo->RspToken + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the network until receive finish. + // + while (!HttpIo->IsRxDone) { + Http->Poll (Http); + } + + // + // Store the received data into the wrapper. + // + Status = HttpIo->RspToken.Status; + if (!EFI_ERROR (Status)) { + ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount; + ResponseData->Headers = HttpIo->RspToken.Message->Headers; + ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength; + } + + if (RecvMsgHeader) { + StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode; + HttpBootPrintErrorMessage (StatusCode); + } + + return Status; +} diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h b/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h new file mode 100644 index 0000000000..2eba14e026 --- /dev/null +++ b/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h @@ -0,0 +1,320 @@ +/** @file + Support functions declaration for UEFI HTTP boot driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_BOOT_SUPPORT_H__ +#define __EFI_HTTP_BOOT_SUPPORT_H__ + +/** + Get the Nic handle using any child handle in the IPv4 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv4. + + @return NicHandle The pointer to the Nic handle. + @return NULL Can't find the Nic handle. + +**/ +EFI_HANDLE +HttpBootGetNicByIp4Children ( + IN EFI_HANDLE ControllerHandle + ); + +/** + Get the Nic handle using any child handle in the IPv6 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv6. + + @return NicHandle The pointer to the Nic handle. + @return NULL Can't find the Nic handle. + +**/ +EFI_HANDLE +HttpBootGetNicByIp6Children ( + IN EFI_HANDLE ControllerHandle + ); + +/** + This function is to convert UINTN to ASCII string with the required formatting. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + @param[in] Length The length of the required format. + +**/ +VOID +HttpBootUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ); + + +/** + This function is to display the IPv4 address. + + @param[in] Ip The pointer to the IPv4 address. + +**/ +VOID +HttpBootShowIp4Addr ( + IN EFI_IPv4_ADDRESS *Ip + ); + +/** + This function is to display the IPv6 address. + + @param[in] Ip The pointer to the IPv6 address. + +**/ +VOID +HttpBootShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ); + +// +// A wrapper structure to hold the HTTP headers. +// +typedef struct { + UINTN MaxHeaderCount; + UINTN HeaderCount; + EFI_HTTP_HEADER *Headers; +} HTTP_IO_HEADER; + +/** + Create a HTTP_IO_HEADER to hold the HTTP header items. + + @param[in] MaxHeaderCount The maximun number of HTTP header in this holder. + + @return A pointer of the HTTP header holder or NULL if failed. + +**/ +HTTP_IO_HEADER * +HttpBootCreateHeader ( + IN UINTN MaxHeaderCount + ); + +/** + Destroy the HTTP_IO_HEADER and release the resouces. + + @param[in] HttpIoHeader Point to the HTTP header holder to be destroyed. + +**/ +VOID +HttpBootFreeHeader ( + IN HTTP_IO_HEADER *HttpIoHeader + ); + +/** + Set or update a HTTP header with the field name and corresponding value. + + @param[in] HttpIoHeader Point to the HTTP header holder. + @param[in] FieldName Null terminated string which describes a field name. + @param[in] FieldValue Null terminated string which describes the corresponding field value. + + @retval EFI_SUCCESS The HTTP header has been set or updated. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resource to complete the operation. + @retval Other Unexpected error happened. + +**/ +EFI_STATUS +HttpBootSetHeader ( + IN HTTP_IO_HEADER *HttpIoHeader, + IN CHAR8 *FieldName, + IN CHAR8 *FieldValue + ); + +// +// HTTP_IO configuration data for IPv4 +// +typedef struct { + EFI_HTTP_VERSION HttpVersion; + UINT32 RequestTimeOut; // In milliseconds. + UINT32 ResponseTimeOut; // In milliseconds. + BOOLEAN UseDefaultAddress; + EFI_IPv4_ADDRESS LocalIp; + EFI_IPv4_ADDRESS SubnetMask; + UINT16 LocalPort; +} HTTP4_IO_CONFIG_DATA; + +// +// HTTP_IO configuration data for IPv6 +// +typedef struct { + EFI_HTTP_VERSION HttpVersion; + UINT32 RequestTimeOut; // In milliseconds. + BOOLEAN UseDefaultAddress; + EFI_IPv6_ADDRESS LocalIp; + UINT16 LocalPort; +} HTTP6_IO_CONFIG_DATA; + + +// +// HTTP_IO configuration +// +typedef union { + HTTP4_IO_CONFIG_DATA Config4; + HTTP6_IO_CONFIG_DATA Config6; +} HTTP_IO_CONFIG_DATA; + +// +// HTTP_IO wrapper of the EFI HTTP service. +// +typedef struct { + UINT8 IpVersion; + EFI_HANDLE Image; + EFI_HANDLE Controller; + EFI_HANDLE Handle; + + EFI_HTTP_PROTOCOL *Http; + + EFI_HTTP_TOKEN ReqToken; + EFI_HTTP_MESSAGE ReqMessage; + EFI_HTTP_TOKEN RspToken; + EFI_HTTP_MESSAGE RspMessage; + + BOOLEAN IsTxDone; + BOOLEAN IsRxDone; +} HTTP_IO; + +// +// A wrapper structure to hold the received HTTP response data. +// +typedef struct { + EFI_HTTP_RESPONSE_DATA Response; + UINTN HeaderCount; + EFI_HTTP_HEADER *Headers; + UINTN BodyLength; + CHAR8 *Body; +} HTTP_IO_RESPONSE_DATA; + +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] Private The pointer to the driver's private data. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. +**/ +EFI_STATUS +HttpBootDns ( + IN HTTP_BOOT_PRIVATE_DATA *Private, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ); + +/** + Notify the callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +HttpBootCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Create a HTTP_IO to access the HTTP service. It will create and configure + a HTTP child handle. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + @param[in] ConfigData The HTTP_IO configuration data. + @param[out] HttpIo The HTTP_IO. + + @retval EFI_SUCCESS The HTTP_IO is created and configured. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Failed to create the HTTP_IO or configure it. + +**/ +EFI_STATUS +HttpIoCreateIo ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN UINT8 IpVersion, + IN HTTP_IO_CONFIG_DATA *ConfigData, + OUT HTTP_IO *HttpIo + ); + +/** + Destroy the HTTP_IO and release the resouces. + + @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed. + +**/ +VOID +HttpIoDestroyIo ( + IN HTTP_IO *HttpIo + ); + +/** + Synchronously send a HTTP REQUEST message to the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] Request A pointer to storage such data as URL and HTTP method. + @param[in] HeaderCount Number of HTTP header structures in Headers list. + @param[in] Headers Array containing list of HTTP headers. + @param[in] BodyLength Length in bytes of the HTTP body. + @param[in] Body Body associated with the HTTP request. + + @retval EFI_SUCCESS The HTTP request is trasmitted. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoSendRequest ( + IN HTTP_IO *HttpIo, + IN EFI_HTTP_REQUEST_DATA *Request, OPTIONAL + IN UINTN HeaderCount, + IN EFI_HTTP_HEADER *Headers, OPTIONAL + IN UINTN BodyLength, + IN VOID *Body OPTIONAL + ); + +/** + Synchronously receive a HTTP RESPONSE message from the server. + + @param[in] HttpIo The HttpIo wrapping the HTTP service. + @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header). + FALSE to continue receive the previous response message. + @param[out] ResponseData Point to a wrapper of the received response data. + + @retval EFI_SUCCESS The HTTP response is received. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpIoRecvResponse ( + IN HTTP_IO *HttpIo, + IN BOOLEAN RecvMsgHeader, + OUT HTTP_IO_RESPONSE_DATA *ResponseData + ); + +#endif diff --git a/Core/NetworkPkg/HttpDxe/ComponentName.c b/Core/NetworkPkg/HttpDxe/ComponentName.c new file mode 100644 index 0000000000..fdd2e7f344 --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/ComponentName.c @@ -0,0 +1,138 @@ +/** @file + Implementation of EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL protocol. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpDriver.h" + +/// +/// Component Name Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME_PROTOCOL gHttpDxeComponentName = { + (EFI_COMPONENT_NAME_GET_DRIVER_NAME) HttpDxeComponentNameGetDriverName, + (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) HttpDxeComponentNameGetControllerName, + "eng" +}; + +/// +/// Component Name 2 Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME2_PROTOCOL gHttpDxeComponentName2 = { + HttpDxeComponentNameGetDriverName, + HttpDxeComponentNameGetControllerName, + "en" +}; + +/// +/// Table of driver names +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_UNICODE_STRING_TABLE mHttpDxeDriverNameTable[] = { + { "eng;en", (CHAR16 *) L"HttpDxe" }, + { NULL, NULL } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mHttpDxeDriverNameTable, + DriverName, + (BOOLEAN)(This != &gHttpDxeComponentName2) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/Core/NetworkPkg/HttpDxe/ComponentName.h b/Core/NetworkPkg/HttpDxe/ComponentName.h new file mode 100644 index 0000000000..29a91f2ec1 --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/ComponentName.h @@ -0,0 +1,98 @@ +/** @file + Header file for implementation of UEFI Component Name(2) protocol. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_COMPONENT_NAME_H__ +#define __EFI_HTTP_COMPONENT_NAME_H__ + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif diff --git a/Core/NetworkPkg/HttpDxe/HttpDns.c b/Core/NetworkPkg/HttpDxe/HttpDns.c new file mode 100644 index 0000000000..0f5fe18072 --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpDns.c @@ -0,0 +1,415 @@ +/** @file + Routines for HttpDxe driver to perform DNS resolution based on UEFI DNS protocols. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpDriver.h" + +/** + Retrieve the host address using the EFI_DNS4_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv4 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv4_ADDRESS *IpAddress + ) +{ + EFI_STATUS Status; + EFI_DNS4_PROTOCOL *Dns4; + EFI_DNS4_CONFIG_DATA Dns4CfgData; + EFI_DNS4_COMPLETION_TOKEN Token; + BOOLEAN IsDone; + HTTP_SERVICE *Service; + EFI_HANDLE Dns4Handle; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + UINTN DnsServerListCount; + EFI_IPv4_ADDRESS *DnsServerList; + UINTN DataSize; + + + Service = HttpInstance->Service; + ASSERT (Service != NULL); + + DnsServerList = NULL; + DnsServerListCount = 0; + ZeroMem (&Token, sizeof (EFI_DNS4_COMPLETION_TOKEN)); + + // + // Get DNS server list from EFI IPv4 Configuration II protocol. + // + Status = gBS->HandleProtocol (Service->ControllerHandle, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2); + if (!EFI_ERROR (Status)) { + // + // Get the required size. + // + DataSize = 0; + Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeDnsServer, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + DnsServerList = AllocatePool (DataSize); + if (DnsServerList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeDnsServer, &DataSize, DnsServerList); + if (EFI_ERROR (Status)) { + FreePool (DnsServerList); + DnsServerList = NULL; + } else { + DnsServerListCount = DataSize / sizeof (EFI_IPv4_ADDRESS); + } + } + } + + Dns4Handle = NULL; + Dns4 = NULL; + + // + // Create a DNS child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Service->ControllerHandle, + Service->ImageHandle, + &gEfiDns4ServiceBindingProtocolGuid, + &Dns4Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns4Handle, + &gEfiDns4ProtocolGuid, + (VOID **) &Dns4, + Service->ImageHandle, + Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Configure DNS4 instance for the DNS server address and protocol. + // + ZeroMem (&Dns4CfgData, sizeof (Dns4CfgData)); + Dns4CfgData.DnsServerListCount = DnsServerListCount; + Dns4CfgData.DnsServerList = DnsServerList; + Dns4CfgData.UseDefaultSetting = HttpInstance->IPv4Node.UseDefaultAddress; + if (!Dns4CfgData.UseDefaultSetting) { + IP4_COPY_ADDRESS (&Dns4CfgData.StationIp, &HttpInstance->IPv4Node.LocalAddress); + IP4_COPY_ADDRESS (&Dns4CfgData.SubnetMask, &HttpInstance->IPv4Node.LocalSubnet); + } + Dns4CfgData.EnableDnsCache = TRUE; + Dns4CfgData.Protocol = EFI_IP_PROTO_UDP; + Status = Dns4->Configure ( + Dns4, + &Dns4CfgData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Create event to set the is done flag when name resolution is finished. + // + ZeroMem (&Token, sizeof (Token)); + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Start asynchronous name resolution. + // + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + Status = Dns4->HostNameToIp (Dns4, HostName, &Token); + if (EFI_ERROR (Status)) { + goto Exit; + } + + while (!IsDone) { + Dns4->Poll (Dns4); + } + + // + // Name resolution is done, check result. + // + Status = Token.Status; + if (!EFI_ERROR (Status)) { + if (Token.RspData.H2AData == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // We just return the first IP address from DNS protocol. + // + IP4_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); + Status = EFI_SUCCESS; + } + +Exit: + + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + if (Token.RspData.H2AData != NULL) { + if (Token.RspData.H2AData->IpList != NULL) { + FreePool (Token.RspData.H2AData->IpList); + } + FreePool (Token.RspData.H2AData); + } + + if (Dns4 != NULL) { + Dns4->Configure (Dns4, NULL); + + gBS->CloseProtocol ( + Dns4Handle, + &gEfiDns4ProtocolGuid, + Service->ImageHandle, + Service->ControllerHandle + ); + } + + if (Dns4Handle != NULL) { + NetLibDestroyServiceChild ( + Service->ControllerHandle, + Service->ImageHandle, + &gEfiDns4ServiceBindingProtocolGuid, + Dns4Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} + +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ) +{ + EFI_STATUS Status; + HTTP_SERVICE *Service; + EFI_DNS6_PROTOCOL *Dns6; + EFI_DNS6_CONFIG_DATA Dns6ConfigData; + EFI_DNS6_COMPLETION_TOKEN Token; + EFI_HANDLE Dns6Handle; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_IPv6_ADDRESS *DnsServerList; + UINTN DnsServerListCount; + UINTN DataSize; + BOOLEAN IsDone; + + + Service = HttpInstance->Service; + ASSERT (Service != NULL); + + DnsServerList = NULL; + DnsServerListCount = 0; + Dns6 = NULL; + Dns6Handle = NULL; + ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN)); + + // + // Get DNS server list from EFI IPv6 Configuration protocol. + // + Status = gBS->HandleProtocol (Service->ControllerHandle, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config); + if (!EFI_ERROR (Status)) { + // + // Get the required size. + // + DataSize = 0; + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + DnsServerList = AllocatePool (DataSize); + if (DnsServerList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList); + if (EFI_ERROR (Status)) { + FreePool (DnsServerList); + DnsServerList = NULL; + } else { + DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + } + } + } + + // + // Create a DNSv6 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Service->ControllerHandle, + Service->ImageHandle, + &gEfiDns6ServiceBindingProtocolGuid, + &Dns6Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + (VOID **) &Dns6, + Service->ImageHandle, + Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Configure DNS6 instance for the DNS server address and protocol. + // + ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA)); + Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount; + Dns6ConfigData.DnsServerList = DnsServerList; + Dns6ConfigData.EnableDnsCache = TRUE; + Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP; + IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &HttpInstance->Ipv6Node.LocalAddress); + Status = Dns6->Configure ( + Dns6, + &Dns6ConfigData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + // + // Create event to set the IsDone flag when name resolution is finished. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Start asynchronous name resolution. + // + Status = Dns6->HostNameToIp (Dns6, HostName, &Token); + if (EFI_ERROR (Status)) { + goto Exit; + } + + while (!IsDone) { + Dns6->Poll (Dns6); + } + + // + // Name resolution is done, check result. + // + Status = Token.Status; + if (!EFI_ERROR (Status)) { + if (Token.RspData.H2AData == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // We just return the first IPv6 address from DNS protocol. + // + IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); + Status = EFI_SUCCESS; + } + +Exit: + + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + if (Token.RspData.H2AData != NULL) { + if (Token.RspData.H2AData->IpList != NULL) { + FreePool (Token.RspData.H2AData->IpList); + } + FreePool (Token.RspData.H2AData); + } + + if (Dns6 != NULL) { + Dns6->Configure (Dns6, NULL); + + gBS->CloseProtocol ( + Dns6Handle, + &gEfiDns6ProtocolGuid, + Service->ImageHandle, + Service->ControllerHandle + ); + } + + if (Dns6Handle != NULL) { + NetLibDestroyServiceChild ( + Service->ControllerHandle, + Service->ImageHandle, + &gEfiDns6ServiceBindingProtocolGuid, + Dns6Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} diff --git a/Core/NetworkPkg/HttpDxe/HttpDns.h b/Core/NetworkPkg/HttpDxe/HttpDns.h new file mode 100644 index 0000000000..fa0c8f4a99 --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpDns.h @@ -0,0 +1,58 @@ +/** @file + The header file of routines for HttpDxe driver to perform DNS resolution based on UEFI DNS protocols. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_DNS_H__ +#define __EFI_HTTP_DNS_H__ + +/** + Retrieve the host address using the EFI_DNS4_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv4 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv4_ADDRESS *IpAddress + ); + +/** + Retrieve the host address using the EFI_DNS6_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv6 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv6_ADDRESS *IpAddress + ); + +#endif \ No newline at end of file diff --git a/Core/NetworkPkg/HttpDxe/HttpDriver.c b/Core/NetworkPkg/HttpDxe/HttpDriver.c new file mode 100644 index 0000000000..2518f4e707 --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpDriver.c @@ -0,0 +1,1065 @@ +/** @file + The driver binding and service binding protocol for HttpDxe driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpDriver.h" + +EFI_HTTP_UTILITIES_PROTOCOL *mHttpUtilities = NULL; + +/// +/// Driver Binding Protocol instance +/// +EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp4DriverBinding = { + HttpDxeIp4DriverBindingSupported, + HttpDxeIp4DriverBindingStart, + HttpDxeIp4DriverBindingStop, + HTTP_DRIVER_VERSION, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp6DriverBinding = { + HttpDxeIp6DriverBindingSupported, + HttpDxeIp6DriverBindingStart, + HttpDxeIp6DriverBindingStop, + HTTP_DRIVER_VERSION, + NULL, + NULL +}; + + +/** + Create a HTTP driver service binding private instance. + + @param[in] Controller The controller that has TCP4 service binding + installed. + @param[in] ImageHandle The HTTP driver's image handle. + @param[out] ServiceData Point to HTTP driver private instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_SUCCESS A new HTTP driver private instance is created. + +**/ +EFI_STATUS +HttpCreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT HTTP_SERVICE **ServiceData + ) +{ + HTTP_SERVICE *HttpService; + + ASSERT (ServiceData != NULL); + *ServiceData = NULL; + + HttpService = AllocateZeroPool (sizeof (HTTP_SERVICE)); + if (HttpService == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + HttpService->Signature = HTTP_SERVICE_SIGNATURE; + HttpService->ServiceBinding.CreateChild = HttpServiceBindingCreateChild; + HttpService->ServiceBinding.DestroyChild = HttpServiceBindingDestroyChild; + HttpService->ImageHandle = ImageHandle; + HttpService->ControllerHandle = Controller; + HttpService->ChildrenNumber = 0; + InitializeListHead (&HttpService->ChildrenList); + + *ServiceData = HttpService; + return EFI_SUCCESS; +} + +/** + Release all the resource used the HTTP service binding instance. + + @param[in] HttpService The HTTP private instance. + @param[in] UsingIpv6 Indicate use TCP4 protocol or TCP6 protocol. + if TRUE, use Tcp6 protocol. + if FALSE, use Tcp4 protocl. +**/ +VOID +HttpCleanService ( + IN HTTP_SERVICE *HttpService, + IN BOOLEAN UsingIpv6 + ) +{ + + if (HttpService == NULL) { + return ; + } + if (!UsingIpv6) { + if (HttpService->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpService->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpService->ImageHandle, + HttpService->ControllerHandle + ); + + NetLibDestroyServiceChild ( + HttpService->ControllerHandle, + HttpService->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpService->Tcp4ChildHandle + ); + + HttpService->Tcp4ChildHandle = NULL; + } + } else { + if (HttpService->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpService->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpService->ImageHandle, + HttpService->ControllerHandle + ); + + NetLibDestroyServiceChild ( + HttpService->ControllerHandle, + HttpService->ImageHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpService->Tcp6ChildHandle + ); + + HttpService->Tcp6ChildHandle = NULL; + } + } + +} + +/** + The event process routine when the http utilities protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context The pointer to the IP4 config2 instance data or IP6 Config instance data. + +**/ +VOID +EFIAPI +HttpUtilitiesInstalledCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + gBS->LocateProtocol ( + &gEfiHttpUtilitiesProtocolGuid, + NULL, + (VOID **) &mHttpUtilities + ); + + // + // Close the event if Http utilities protocol is loacted. + // + if (mHttpUtilities != NULL && Event != NULL) { + gBS->CloseEvent (Event); + } +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @param ImageHandle The firmware allocated handle for the UEFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +EFIAPI +HttpDxeDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + VOID *Registration; + + gBS->LocateProtocol ( + &gEfiHttpUtilitiesProtocolGuid, + NULL, + (VOID **) &mHttpUtilities + ); + + if (mHttpUtilities == NULL) { + // + // No Http utilities protocol, register a notify. + // + EfiCreateProtocolNotifyEvent ( + &gEfiHttpUtilitiesProtocolGuid, + TPL_CALLBACK, + HttpUtilitiesInstalledCallback, + NULL, + &Registration + ); + } + + // + // Install UEFI Driver Model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpDxeIp4DriverBinding, + ImageHandle, + &gHttpDxeComponentName, + &gHttpDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpDxeIp6DriverBinding, + NULL, + &gHttpDxeComponentName, + &gHttpDxeComponentName2 + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gHttpDxeIp4DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gHttpDxeComponentName2, + &gEfiComponentNameProtocolGuid, + &gHttpDxeComponentName, + NULL + ); + } + return Status; +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_INVALID_PARAMETER Any input parameter is NULL. + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +HttpDestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = NET_LIST_USER_STRUCT_S (Entry, HTTP_PROTOCOL, Link, HTTP_PROTOCOL_SIGNATURE); + ServiceBinding = ((HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (HttpInstance->Handle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, HttpInstance->Handle); +} + +/** + Test to see if this driver supports ControllerHandle. This is the worker function for + HttpDxeIp4(6)DriverBindingSupported. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *TcpServiceBindingProtocolGuid; + + if (IpVersion == IP_VERSION_4) { + TcpServiceBindingProtocolGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + TcpServiceBindingProtocolGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + TcpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Start this driver on ControllerHandle. This is the worker function for + HttpDxeIp4(6)DriverBindingStart. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + HTTP_SERVICE *HttpService; + VOID *Interface; + BOOLEAN UsingIpv6; + + UsingIpv6 = FALSE; + + // + // Test for the Http service binding protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + HttpService = HTTP_SERVICE_FROM_PROTOCOL (ServiceBinding); + } else { + Status = HttpCreateService (ControllerHandle, This->DriverBindingHandle, &HttpService); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (HttpService != NULL); + + // + // Install the HttpServiceBinding Protocol onto Controller + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + &HttpService->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + if (IpVersion == IP_VERSION_4) { + + if (HttpService->Tcp4ChildHandle == NULL) { + // + // Create a TCP4 child instance, but do not configure it. This will establish the parent-child relationship. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &HttpService->Tcp4ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpService->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + &Interface, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } else { + return EFI_ALREADY_STARTED; + } + + } else { + UsingIpv6 = TRUE; + + if (HttpService->Tcp6ChildHandle == NULL) { + // + // Create a TCP6 child instance, but do not configure it. This will establish the parent-child relationship. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + &HttpService->Tcp6ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpService->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + &Interface, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + } else { + return EFI_ALREADY_STARTED; + } + + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (HttpService != NULL) { + HttpCleanService (HttpService, UsingIpv6); + if (HttpService->Tcp4ChildHandle == NULL && HttpService->Tcp6ChildHandle == NULL) { + FreePool (HttpService); + } + } + + return Status; + + +} + +/** + Stop this driver on ControllerHandle. This is the worker function for + HttpDxeIp4(6)DriverBindingStop. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver was removed ControllerHandle. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +HttpDxeStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer, + IN UINT8 IpVersion + ) +{ + EFI_HANDLE NicHandle; + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + HTTP_SERVICE *HttpService; + LIST_ENTRY *List; + HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + BOOLEAN UsingIpv6; + + // + // HTTP driver opens TCP4(6) child, So, Controller is a TCP4(6) + // child handle. Locate the Nic handle first. Then get the + // HTTP private data back. + // + if (IpVersion == IP_VERSION_4) { + UsingIpv6 = FALSE; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid); + } else { + UsingIpv6 = TRUE; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiTcp6ProtocolGuid); + } + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiHttpServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (ServiceBinding); + + if (NumberOfChildren != 0) { + // + // Destroy the HTTP child instance in ChildHandleBuffer. + // + List = &HttpService->ChildrenList; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + HttpDestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } else { + + HttpCleanService (HttpService, UsingIpv6); + + if (HttpService->Tcp4ChildHandle == NULL && HttpService->Tcp6ChildHandle == NULL) { + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiHttpServiceBindingProtocolGuid, + ServiceBinding + ); + FreePool (HttpService); + } + Status = EFI_SUCCESS; + } + } + + return Status; + +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_ALREADY_STARTED This device is already running on ControllerHandle. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + return HttpDxeStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_4 + ); +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); + +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_ALREADY_STARTED This device is already running on ControllerHandle. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return HttpDxeStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + return HttpDxeStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_6 + ); +} +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL, or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + HTTP_SERVICE *HttpService; + HTTP_PROTOCOL *HttpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (This); + HttpInstance = AllocateZeroPool (sizeof (HTTP_PROTOCOL)); + if (HttpInstance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + HttpInstance->Signature = HTTP_PROTOCOL_SIGNATURE; + HttpInstance->Service = HttpService; + CopyMem (&HttpInstance->Http, &mEfiHttpTemplate, sizeof (HttpInstance->Http)); + NetMapInit (&HttpInstance->TxTokens); + NetMapInit (&HttpInstance->RxTokens); + + // + // Install HTTP protocol onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiHttpProtocolGuid, + &HttpInstance->Http, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + HttpInstance->Handle = *ChildHandle; + + // + // Add it to the HTTP service's child list. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&HttpService->ChildrenList, &HttpInstance->Link); + HttpService->ChildrenNumber++; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_ERROR: + + NetMapClean (&HttpInstance->TxTokens); + NetMapClean (&HttpInstance->RxTokens); + FreePool (HttpInstance); + + return Status; +} + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + HTTP_SERVICE *HttpService; + HTTP_PROTOCOL *HttpInstance; + EFI_HTTP_PROTOCOL *Http; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (This); + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiHttpProtocolGuid, + (VOID **) &Http, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (Http); + if (HttpInstance->Service != HttpService) { + return EFI_INVALID_PARAMETER; + } + + if (HttpInstance->InDestroy) { + return EFI_SUCCESS; + } + + HttpInstance->InDestroy = TRUE; + + // + // Uninstall the HTTP protocol. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiHttpProtocolGuid, + Http + ); + + if (EFI_ERROR (Status)) { + HttpInstance->InDestroy = FALSE; + return Status; + } + + HttpCleanProtocol (HttpInstance); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + RemoveEntryList (&HttpInstance->Link); + HttpService->ChildrenNumber--; + + gBS->RestoreTPL (OldTpl); + + FreePool (HttpInstance); + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/HttpDxe/HttpDriver.h b/Core/NetworkPkg/HttpDxe/HttpDriver.h new file mode 100644 index 0000000000..8fda6b2be4 --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpDriver.h @@ -0,0 +1,397 @@ +/** @file + The header files of the driver binding and service binding protocol for HttpDxe driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_DRIVER_H__ +#define __EFI_HTTP_DRIVER_H__ + +#include + +// +// Libraries +// +#include +#include +#include +#include +#include +#include +#include +#include + +// +// UEFI Driver Model Protocols +// +#include +#include +#include +#include + +// +// Consumed Protocols +// +#include +#include +#include +#include +#include +#include +#include + + +// +// Produced Protocols +// +#include + +// +// Driver Version +// +#define HTTP_DRIVER_VERSION 0xa + +// +// Protocol instances +// +extern EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp4DriverBinding; +extern EFI_DRIVER_BINDING_PROTOCOL gHttpDxeIp6DriverBinding; + +extern EFI_COMPONENT_NAME2_PROTOCOL gHttpDxeComponentName2; +extern EFI_COMPONENT_NAME_PROTOCOL gHttpDxeComponentName; + +extern EFI_HTTP_UTILITIES_PROTOCOL *mHttpUtilities; + +// +// Include files with function prototypes +// +#include "ComponentName.h" +#include "HttpImpl.h" +#include "HttpProto.h" +#include "HttpDns.h" + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +} HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_ALREADY_STARTED This device is already running on ControllerHandle. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL, or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + + +extern EFI_HTTP_PROTOCOL mEfiHttpProtocolTemplete; + +#endif diff --git a/Core/NetworkPkg/HttpDxe/HttpDxe.inf b/Core/NetworkPkg/HttpDxe/HttpDxe.inf new file mode 100644 index 0000000000..bf2cbee5f7 --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpDxe.inf @@ -0,0 +1,69 @@ +## @file +# Implementation of EFI HTTP protocol interfaces. +# +# Copyright (c) 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HttpDxe + FILE_GUID = 2366c20f-e15a-11e3-8bf1-e4115b28bc50 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = HttpDxeDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = HttpDxe.uni + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[Sources] + ComponentName.h + ComponentName.c + HttpDns.h + HttpDns.c + HttpDriver.h + HttpDriver.c + HttpImpl.h + HttpImpl.c + HttpProto.h + HttpProto.c + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + MemoryAllocationLib + BaseLib + UefiLib + DebugLib + NetLib + HttpLib + DpcLib + +[Protocols] + gEfiHttpServiceBindingProtocolGuid ## BY_START + gEfiHttpProtocolGuid ## BY_START + gEfiHttpUtilitiesProtocolGuid ## CONSUMES + gEfiTcp4ServiceBindingProtocolGuid ## TO_START + gEfiTcp4ProtocolGuid ## TO_START + gEfiTcp6ServiceBindingProtocolGuid ## TO_START + gEfiTcp6ProtocolGuid ## TO_START + gEfiDns4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns4ProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp6ConfigProtocolGuid ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + HttpDxeExtra.uni \ No newline at end of file diff --git a/Core/NetworkPkg/HttpDxe/HttpDxe.uni b/Core/NetworkPkg/HttpDxe/HttpDxe.uni new file mode 100644 index 0000000000..d6792dd41e Binary files /dev/null and b/Core/NetworkPkg/HttpDxe/HttpDxe.uni differ diff --git a/Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni b/Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni new file mode 100644 index 0000000000..1a06619eee Binary files /dev/null and b/Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni differ diff --git a/Core/NetworkPkg/HttpDxe/HttpImpl.c b/Core/NetworkPkg/HttpDxe/HttpImpl.c new file mode 100644 index 0000000000..db50b5b1cc --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpImpl.c @@ -0,0 +1,1329 @@ +/** @file + Implementation of EFI_HTTP_PROTOCOL protocol interfaces. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ (C) Copyright 2015 Hewlett Packard Enterprise Development LP
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpDriver.h" + +EFI_HTTP_PROTOCOL mEfiHttpTemplate = { + EfiHttpGetModeData, + EfiHttpConfigure, + EfiHttpRequest, + EfiHttpCancel, + EfiHttpResponse, + EfiHttpPoll +}; + +/** + Returns the operational parameters for the current HTTP child instance. + + The GetModeData() function is used to read the current mode data (operational + parameters) for this HTTP protocol instance. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[out] HttpConfigData Point to buffer for operational parameters of this + HTTP instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData is NULL. + HttpConfigData->AccessPoint is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_NOT_STARTED The HTTP instance is not configured. + +**/ +EFI_STATUS +EFIAPI +EfiHttpGetModeData ( + IN EFI_HTTP_PROTOCOL *This, + OUT EFI_HTTP_CONFIG_DATA *HttpConfigData + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_HTTPv4_ACCESS_POINT *Http4AccessPoint; + EFI_HTTPv6_ACCESS_POINT *Http6AccessPoint; + + if ((This == NULL) || (HttpConfigData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) { + return EFI_NOT_STARTED; + } + + HttpConfigData->HttpVersion = HttpInstance->HttpVersion; + HttpConfigData->TimeOutMillisec = HttpInstance->TimeOutMillisec; + HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6; + + if (HttpInstance->LocalAddressIsIPv6) { + Http6AccessPoint = AllocateZeroPool (sizeof (EFI_HTTPv6_ACCESS_POINT)); + if (Http6AccessPoint == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem ( + Http6AccessPoint, + &HttpInstance->Ipv6Node, + sizeof (HttpInstance->Ipv6Node) + ); + HttpConfigData->AccessPoint.IPv6Node = Http6AccessPoint; + } else { + Http4AccessPoint = AllocateZeroPool (sizeof (EFI_HTTPv4_ACCESS_POINT)); + if (Http4AccessPoint == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem ( + Http4AccessPoint, + &HttpInstance->IPv4Node, + sizeof (HttpInstance->IPv4Node) + ); + HttpConfigData->AccessPoint.IPv4Node = Http4AccessPoint; + } + + return EFI_SUCCESS; +} + +/** + Initialize or brutally reset the operational parameters for this EFI HTTP instance. + + The Configure() function does the following: + When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring + timeout, local address, port, etc. + When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active + connections with remote hosts, canceling all asynchronous tokens, and flush request + and response buffers without informing the appropriate hosts. + + Except for GetModeData() and Configure(), No other EFI HTTP function can be executed + by this instance until the Configure() function is executed and returns successfully. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] HttpConfigData Pointer to the configure data to configure the instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData->LocalAddressIsIPv6 is FALSE and + HttpConfigData->IPv4Node is NULL. + HttpConfigData->LocalAddressIsIPv6 is TRUE and + HttpConfigData->IPv6Node is NULL. + @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling + Configure() with NULL to reset it. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_UNSUPPORTED One or more options in HttpConfigData are not supported + in the implementation. +**/ +EFI_STATUS +EFIAPI +EfiHttpConfigure ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_CONFIG_DATA *HttpConfigData + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_STATUS Status; + + // + // Check input parameters. + // + if (This == NULL || + (HttpConfigData != NULL && ((HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) || + (!HttpConfigData->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)))) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL); + + if (HttpConfigData != NULL) { + + // + // Now configure this HTTP instance. + // + if (HttpInstance->State != HTTP_STATE_UNCONFIGED) { + return EFI_ALREADY_STARTED; + } + + HttpInstance->HttpVersion = HttpConfigData->HttpVersion; + HttpInstance->TimeOutMillisec = HttpConfigData->TimeOutMillisec; + HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6; + + if (HttpConfigData->LocalAddressIsIPv6) { + CopyMem ( + &HttpInstance->Ipv6Node, + HttpConfigData->AccessPoint.IPv6Node, + sizeof (HttpInstance->Ipv6Node) + ); + } else { + CopyMem ( + &HttpInstance->IPv4Node, + HttpConfigData->AccessPoint.IPv4Node, + sizeof (HttpInstance->IPv4Node) + ); + } + // + // Creat Tcp child + // + Status = HttpInitProtocol (HttpInstance, HttpInstance->LocalAddressIsIPv6); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_HTTP_CONFIGED; + return EFI_SUCCESS; + + } else { + // + // Reset all the resources related to HttpInsance. + // + HttpCleanProtocol (HttpInstance); + HttpInstance->State = HTTP_STATE_UNCONFIGED; + return EFI_SUCCESS; + } +} + + +/** + The Request() function queues an HTTP request to this HTTP instance. + + Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent + successfully, or if there is an error, Status in token will be updated and Event will + be signaled. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP request token. + + @retval EFI_SUCCESS Outgoing data was processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_UNSUPPORTED The HTTP method is not supported in current + implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Request()has not been completed successfully. +**/ +EFI_STATUS +EFIAPI +EfiHttpRequest ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ) +{ + EFI_HTTP_MESSAGE *HttpMsg; + EFI_HTTP_REQUEST_DATA *Request; + VOID *UrlParser; + EFI_STATUS Status; + CHAR8 *HostName; + UINTN HostNameSize; + UINT16 RemotePort; + HTTP_PROTOCOL *HttpInstance; + BOOLEAN Configure; + BOOLEAN ReConfigure; + CHAR8 *RequestStr; + CHAR8 *Url; + UINTN UrlLen; + CHAR16 *HostNameStr; + HTTP_TOKEN_WRAP *Wrap; + CHAR8 *FileUrl; + + if ((This == NULL) || (Token == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpMsg = Token->Message; + if ((HttpMsg == NULL) || (HttpMsg->Headers == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Current implementation does not support POST/PUT method. + // If future version supports these two methods, Request could be NULL for a special case that to send large amounts + // of data. For this case, the implementation need check whether previous call to Request() has been completed or not. + // + // + Request = HttpMsg->Data.Request; + if ((Request == NULL) || (Request->Url == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Only support GET and HEAD method in current implementation. + // + if ((Request->Method != HttpMethodGet) && (Request->Method != HttpMethodHead)) { + return EFI_UNSUPPORTED; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) { + return EFI_NOT_STARTED; + } + + // + // Check whether the token already existed. + // + if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) { + return EFI_ACCESS_DENIED; + } + + HostName = NULL; + Wrap = NULL; + HostNameStr = NULL; + + // + // Parse the URI of the remote host. + // + Url = HttpInstance->Url; + UrlLen = StrLen (Request->Url) + 1; + if (UrlLen > HTTP_URL_BUFFER_LEN) { + Url = AllocateZeroPool (UrlLen); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + FreePool (HttpInstance->Url); + HttpInstance->Url = Url; + } + + + UnicodeStrToAsciiStrS (Request->Url, Url, UrlLen); + UrlParser = NULL; + Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser); + if (EFI_ERROR (Status)) { + goto Error1; + } + + RequestStr = NULL; + HostName = NULL; + Status = HttpUrlGetHostName (Url, UrlParser, &HostName); + if (EFI_ERROR (Status)) { + goto Error1; + } + + Status = HttpUrlGetPort (Url, UrlParser, &RemotePort); + if (EFI_ERROR (Status)) { + RemotePort = HTTP_DEFAULT_PORT; + } + // + // If Configure is TRUE, it indicates the first time to call Request(); + // If ReConfigure is TRUE, it indicates the request URL is not same + // with the previous call to Request(); + // + Configure = TRUE; + ReConfigure = TRUE; + + if (HttpInstance->RemoteHost == NULL) { + // + // Request() is called the first time. + // + ReConfigure = FALSE; + } else { + if ((HttpInstance->RemotePort == RemotePort) && + (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) { + // + // Host Name and port number of the request URL are the same with previous call to Request(). + // Check whether previous TCP packet sent out. + // + if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) { + // + // Wrap the HTTP token in HTTP_TOKEN_WRAP + // + Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); + if (Wrap == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error1; + } + + Wrap->HttpToken = Token; + Wrap->HttpInstance = HttpInstance; + + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + + Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + + Wrap->TcpWrap.Method = Request->Method; + + FreePool (HostName); + + // + // Queue the HTTP token and return. + // + return EFI_SUCCESS; + } else { + // + // Use existing TCP instance to transmit the packet. + // + Configure = FALSE; + ReConfigure = FALSE; + } + } else { + // + // Need close existing TCP instance and create a new TCP instance for data transmit. + // + if (HttpInstance->RemoteHost != NULL) { + FreePool (HttpInstance->RemoteHost); + HttpInstance->RemoteHost = NULL; + HttpInstance->RemotePort = 0; + } + } + } + + if (Configure) { + // + // Parse Url for IPv4 or IPv6 address, if failed, perform DNS resolution. + // + if (!HttpInstance->LocalAddressIsIPv6) { + Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr); + } else { + Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr); + } + + if (EFI_ERROR (Status)) { + HostNameSize = AsciiStrSize (HostName); + HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16)); + if (HostNameStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error1; + } + + AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize); + if (!HttpInstance->LocalAddressIsIPv6) { + Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr); + } else { + Status = HttpDns6 (HttpInstance, HostNameStr, &HttpInstance->RemoteIpv6Addr); + } + + FreePool (HostNameStr); + if (EFI_ERROR (Status)) { + goto Error1; + } + } + + + // + // Save the RemotePort and RemoteHost. + // + ASSERT (HttpInstance->RemoteHost == NULL); + HttpInstance->RemotePort = RemotePort; + HttpInstance->RemoteHost = HostName; + HostName = NULL; + } + + if (ReConfigure) { + // + // The request URL is different from previous calls to Request(), close existing TCP instance. + // + if (!HttpInstance->LocalAddressIsIPv6) { + ASSERT (HttpInstance->Tcp4 != NULL); + } else { + ASSERT (HttpInstance->Tcp6 != NULL); + } + HttpCloseConnection (HttpInstance); + EfiHttpCancel (This, NULL); + } + + // + // Wrap the HTTP token in HTTP_TOKEN_WRAP + // + Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); + if (Wrap == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error1; + } + + Wrap->HttpToken = Token; + Wrap->HttpInstance = HttpInstance; + Wrap->TcpWrap.Method = Request->Method; + + Status = HttpInitTcp (HttpInstance, Wrap, Configure); + if (EFI_ERROR (Status)) { + goto Error2; + } + + if (!Configure) { + // + // For the new HTTP token, create TX TCP token events. + // + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + } + + // + // Create request message. + // + FileUrl = Url; + if (*FileUrl != '/') { + // + // Convert the absolute-URI to the absolute-path + // + while (*FileUrl != ':') { + FileUrl++; + } + if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) { + FileUrl += 3; + while (*FileUrl != '/') { + FileUrl++; + } + } else { + Status = EFI_INVALID_PARAMETER; + goto Error3; + } + } + RequestStr = HttpGenRequestString (HttpInstance, HttpMsg, FileUrl); + if (RequestStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error3; + } + + Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + goto Error4; + } + + // + // Transmit the request message. + // + Status = HttpTransmitTcp ( + HttpInstance, + Wrap, + (UINT8*) RequestStr, + AsciiStrLen (RequestStr) + ); + if (EFI_ERROR (Status)) { + goto Error5; + } + + DispatchDpc (); + + if (HostName != NULL) { + FreePool (HostName); + } + + return EFI_SUCCESS; + +Error5: + NetMapRemoveTail (&HttpInstance->TxTokens, NULL); + +Error4: + if (RequestStr != NULL) { + FreePool (RequestStr); + } + +Error3: + HttpCloseConnection (HttpInstance); + +Error2: + HttpCloseTcpConnCloseEvent (HttpInstance); + if (NULL != Wrap->TcpWrap.Tx4Token.CompletionToken.Event) { + gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); + Wrap->TcpWrap.Tx4Token.CompletionToken.Event = NULL; + } + if (NULL != Wrap->TcpWrap.Tx6Token.CompletionToken.Event) { + gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + Wrap->TcpWrap.Tx6Token.CompletionToken.Event = NULL; + } + +Error1: + + if (HostName != NULL) { + FreePool (HostName); + } + if (Wrap != NULL) { + FreePool (Wrap); + } + if (UrlParser!= NULL) { + HttpUrlFreeParser (UrlParser); + } + + return Status; + +} + +/** + Cancel a user's Token. + + @param[in] Map The HTTP instance's token queue. + @param[in] Item Object container for one HTTP token and token's wrap. + @param[in] Context The user's token to cancel. + + @retval EFI_SUCCESS Continue to check the next Item. + @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled. + +**/ +EFI_STATUS +EFIAPI +HttpCancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + + EFI_HTTP_TOKEN *Token; + HTTP_TOKEN_WRAP *Wrap; + HTTP_PROTOCOL *HttpInstance; + + Token = (EFI_HTTP_TOKEN *) Context; + + // + // Return EFI_SUCCESS to check the next item in the map if + // this one doesn't match. + // + if ((Token != NULL) && (Token != Item->Key)) { + return EFI_SUCCESS; + } + + Wrap = (HTTP_TOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + HttpInstance = Wrap->HttpInstance; + + // + // Free resources. + // + NetMapRemoveItem (Map, Item, NULL); + + if (!HttpInstance->LocalAddressIsIPv6) { + if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Rx4Token.Packet.RxData->FragmentTable[0].FragmentBuffer); + } + + } else { + if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Rx6Token.Packet.RxData->FragmentTable[0].FragmentBuffer); + } + } + + + FreePool (Wrap); + + // + // If only one item is to be cancel, return EFI_ABORTED to stop + // iterating the map any more. + // + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Cancel the user's receive/transmit request. It is the worker function of + EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the + token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Token The token to cancel. If NULL, all token will be + cancelled. + + @retval EFI_SUCCESS The token is cancelled. + @retval EFI_NOT_FOUND The asynchronous request or response token is not found. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCancel ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_HTTP_TOKEN *Token + ) +{ + EFI_STATUS Status; + + // + // First check the tokens queued by EfiHttpRequest(). + // + Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token); + if (EFI_ERROR (Status)) { + if (Token != NULL) { + if (Status == EFI_ABORTED) { + return EFI_SUCCESS; + } + } else { + return Status; + } + } + + // + // Then check the tokens queued by EfiHttpResponse(). + // + Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token); + if (EFI_ERROR (Status)) { + if (Token != NULL) { + if (Status == EFI_ABORTED) { + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } + } else { + return Status; + } + } + + return EFI_SUCCESS; +} + + +/** + Abort an asynchronous HTTP request or response token. + + The Cancel() function aborts a pending HTTP request or response transaction. If + Token is not NULL and the token is in transmit or receive queues when it is being + cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL, + all asynchronous tokens issued by Request() or Response() will be aborted. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Point to storage containing HTTP request or response + token. + + @retval EFI_SUCCESS Request and Response queues are successfully flushed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance hasn't been configured. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, + BOOTP, RARP, etc.) hasn't finished yet. + @retval EFI_NOT_FOUND The asynchronous request or response token is not + found. + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +EFI_STATUS +EFIAPI +EfiHttpCancel ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ) +{ + HTTP_PROTOCOL *HttpInstance; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + return EFI_NOT_STARTED; + } + + return HttpCancel (HttpInstance, Token); + +} + +/** + A callback function to intercept events during message parser. + + This function will be invoked during HttpParseMessageBody() with various events type. An error + return status of the callback function will cause the HttpParseMessageBody() aborted. + + @param[in] EventType Event type of this callback call. + @param[in] Data A pointer to data buffer. + @param[in] Length Length in bytes of the Data. + @param[in] Context Callback context set by HttpInitMsgParser(). + + @retval EFI_SUCCESS Continue to parser the message body. + +**/ +EFI_STATUS +EFIAPI +HttpBodyParserCallback ( + IN HTTP_BODY_PARSE_EVENT EventType, + IN CHAR8 *Data, + IN UINTN Length, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + UINTN BodyLength; + CHAR8 *Body; + + if (EventType != BodyParseEventOnComplete) { + return EFI_SUCCESS; + } + + if (Data == NULL || Length != 0 || Context == NULL) { + return EFI_SUCCESS; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + Body = Wrap->HttpToken->Message->Body; + BodyLength = Wrap->HttpToken->Message->BodyLength; + if (Data < Body + BodyLength) { + Wrap->HttpInstance->NextMsg = Data; + } else { + Wrap->HttpInstance->NextMsg = NULL; + } + + + // + // Free Tx4Token or Tx6Token since already received corrsponding HTTP response. + // + FreePool (Wrap); + + return EFI_SUCCESS; +} + +/** + The work function of EfiHttpResponse(). + + @param[in] Wrap Pointer to HTTP token's wrap data. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources. + @retval EFI_NOT_READY Can't find a corresponding Tx4Token/Tx6Token or + the EFI_HTTP_UTILITIES_PROTOCOL is not available. + +**/ +EFI_STATUS +HttpResponseWorker ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_HTTP_MESSAGE *HttpMsg; + CHAR8 *EndofHeader; + CHAR8 *HttpHeaders; + UINTN SizeofHeaders; + UINTN BufferSize; + UINTN StatusCode; + CHAR8 *Tmp; + CHAR8 *HeaderTmp; + CHAR8 *StatusCodeStr; + UINTN BodyLen; + HTTP_PROTOCOL *HttpInstance; + EFI_HTTP_TOKEN *Token; + NET_MAP_ITEM *Item; + HTTP_TOKEN_WRAP *ValueInItem; + UINTN HdrLen; + + if (Wrap == NULL || Wrap->HttpInstance == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = Wrap->HttpInstance; + Token = Wrap->HttpToken; + HttpMsg = Token->Message; + + HttpInstance->EndofHeader = NULL; + HttpInstance->HttpHeaders = NULL; + HttpMsg->Headers = NULL; + HttpHeaders = NULL; + SizeofHeaders = 0; + BufferSize = 0; + EndofHeader = NULL; + + if (HttpMsg->Data.Response != NULL) { + // + // Need receive the HTTP headers, prepare buffer. + // + Status = HttpCreateTcpRxEventForHeader (HttpInstance); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Check whether we have cached header from previous call. + // + if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) { + // + // The data is stored at [NextMsg, CacheBody + CacheLen]. + // + HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg; + HttpHeaders = AllocateZeroPool (HdrLen); + if (HttpHeaders == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen); + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + HttpInstance->NextMsg = NULL; + HttpInstance->CacheOffset = 0; + SizeofHeaders = HdrLen; + BufferSize = HttpInstance->CacheLen; + + // + // Check whether we cached the whole HTTP headers. + // + EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); + } + + HttpInstance->EndofHeader = &EndofHeader; + HttpInstance->HttpHeaders = &HttpHeaders; + + Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize); + if (EFI_ERROR (Status)) { + goto Error; + } + + ASSERT (HttpHeaders != NULL); + + // + // Cache the part of body. + // + BodyLen = BufferSize - (EndofHeader - HttpHeaders); + if (BodyLen > 0) { + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + } + + HttpInstance->CacheBody = AllocateZeroPool (BodyLen); + if (HttpInstance->CacheBody == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen); + HttpInstance->CacheLen = BodyLen; + } + + // + // Search for Status Code. + // + StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1; + if (StatusCodeStr == NULL) { + goto Error; + } + + StatusCode = AsciiStrDecimalToUintn (StatusCodeStr); + + // + // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n". + // + Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR); + if (Tmp == NULL) { + goto Error; + } + + Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR); + SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders); + HeaderTmp = AllocateZeroPool (SizeofHeaders); + if (HeaderTmp == NULL) { + goto Error; + } + + CopyMem (HeaderTmp, Tmp, SizeofHeaders); + FreePool (HttpHeaders); + HttpHeaders = HeaderTmp; + + // + // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available. + // + if (mHttpUtilities == NULL) { + Status = EFI_NOT_READY; + goto Error; + } + + // + // Parse the HTTP header into array of key/value pairs. + // + Status = mHttpUtilities->Parse ( + mHttpUtilities, + HttpHeaders, + SizeofHeaders, + &HttpMsg->Headers, + &HttpMsg->HeaderCount + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + FreePool (HttpHeaders); + HttpHeaders = NULL; + + HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode); + + // + // Init message-body parser by header information. + // + Status = EFI_NOT_READY; + ValueInItem = NULL; + NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem); + if (ValueInItem == NULL) { + goto Error; + } + + // + // The first Tx Token not transmitted yet, insert back and return error. + // + if (!ValueInItem->TcpWrap.IsTxDone) { + goto Error2; + } + + Status = HttpInitMsgParser ( + ValueInItem->TcpWrap.Method, + HttpMsg->Data.Response->StatusCode, + HttpMsg->HeaderCount, + HttpMsg->Headers, + HttpBodyParserCallback, + (VOID *) ValueInItem, + &HttpInstance->MsgParser + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + + // + // Check whether we received a complete HTTP message. + // + if (HttpInstance->CacheBody != NULL) { + Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody); + if (EFI_ERROR (Status)) { + goto Error2; + } + + if (HttpIsMessageComplete (HttpInstance->MsgParser)) { + // + // Free the MsgParse since we already have a full HTTP message. + // + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + } + + if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { + Status = EFI_SUCCESS; + goto Exit; + } + } + + // + // Receive the response body. + // + BodyLen = 0; + + // + // First check whether we cached some data. + // + if (HttpInstance->CacheBody != NULL) { + // + // Calculate the length of the cached data. + // + if (HttpInstance->NextMsg != NULL) { + // + // We have a cached HTTP message which includes a part of HTTP header of next message. + // + BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); + } else { + BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset; + } + + if (BodyLen > 0) { + // + // We have some cached data. Just copy the data and return. + // + if (HttpMsg->BodyLength < BodyLen) { + CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength); + HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength; + } else { + // + // Copy all cached data out. + // + CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen); + HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset; + HttpMsg->BodyLength = BodyLen; + + if (HttpInstance->NextMsg == NULL) { + // + // There is no HTTP header of next message. Just free the cache buffer. + // + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + HttpInstance->NextMsg = NULL; + HttpInstance->CacheOffset = 0; + } + } + // + // Return since we aready received required data. + // + Status = EFI_SUCCESS; + goto Exit; + } + + if (BodyLen == 0 && HttpInstance->MsgParser == NULL) { + // + // We received a complete HTTP message, and we don't have more data to return to caller. + // + HttpMsg->BodyLength = 0; + Status = EFI_SUCCESS; + goto Exit; + } + } + + ASSERT (HttpInstance->MsgParser != NULL); + + // + // We still need receive more data when there is no cache data and MsgParser is not NULL; + // + Status = HttpTcpReceiveBody (Wrap, HttpMsg); + if (EFI_ERROR (Status)) { + goto Error; + } + + return Status; + +Exit: + Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); + } + Token->Status = Status; + gBS->SignalEvent (Token->Event); + HttpCloseTcpRxEvent (Wrap); + FreePool (Wrap); + return Status; + +Error2: + NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); + +Error: + HttpTcpTokenCleanup (Wrap); + + if (HttpHeaders != NULL) { + FreePool (HttpHeaders); + } + + if (HttpMsg->Headers != NULL) { + FreePool (HttpMsg->Headers); + } + + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + } + + Token->Status = Status; + gBS->SignalEvent (Token->Event); + + return Status; + +} + + +/** + The Response() function queues an HTTP response to this HTTP instance, similar to + Receive() function in the EFI TCP driver. When the HTTP request is sent successfully, + or if there is an error, Status in token will be updated and Event will be signaled. + + The HTTP driver will queue a receive token to the underlying TCP instance. When data + is received in the underlying TCP instance, the data will be parsed and Token will + be populated with the response data. If the data received from the remote host + contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting + (asynchronously) for more data to be sent from the remote host before signaling + Event in Token. + + It is the responsibility of the caller to allocate a buffer for Body and specify the + size in BodyLength. If the remote host provides a response that contains a content + body, up to BodyLength bytes will be copied from the receive buffer into Body and + BodyLength will be updated with the amount of bytes received and copied to Body. This + allows the client to download a large file in chunks instead of into one contiguous + block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is + non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive + token to underlying TCP instance. If data arrives in the receive buffer, up to + BodyLength bytes of data will be copied to Body. The HTTP driver will then update + BodyLength with the amount of bytes received and copied to Body. + + If the HTTP driver does not have an open underlying TCP connection with the host + specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is + consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain + an open TCP connection between client and host. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP response token. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been + initialized. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message->Headers is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Response() has not been completed successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host + specified by response URL. +**/ +EFI_STATUS +EFIAPI +EfiHttpResponse ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ) +{ + EFI_STATUS Status; + EFI_HTTP_MESSAGE *HttpMsg; + HTTP_PROTOCOL *HttpInstance; + HTTP_TOKEN_WRAP *Wrap; + + if ((This == NULL) || (Token == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpMsg = Token->Message; + if (HttpMsg == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + return EFI_NOT_STARTED; + } + + // + // Check whether the token already existed. + // + if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) { + return EFI_ACCESS_DENIED; + } + + Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Wrap->HttpInstance = HttpInstance; + Wrap->HttpToken = Token; + + Status = HttpCreateTcpRxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error; + } + + Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // If already have pending RxTokens, return directly. + // + if (NetMapGetCount (&HttpInstance->RxTokens) > 1) { + return EFI_SUCCESS; + } + + return HttpResponseWorker (Wrap); + +Error: + if (Wrap != NULL) { + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } + FreePool (Wrap); + } + + return Status; +} + +/** + The Poll() function can be used by network drivers and applications to increase the + rate that data packets are moved between the communication devices and the transmit + and receive queues. + + In some systems, the periodic timer event in the managed network driver may not poll + the underlying communications device fast enough to transmit and/or receive all data + packets without missing incoming packets or dropping outgoing packets. Drivers and + applications that are experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + +**/ +EFI_STATUS +EFIAPI +EfiHttpPoll ( + IN EFI_HTTP_PROTOCOL *This + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + return EFI_NOT_STARTED; + } + + if (HttpInstance->LocalAddressIsIPv6) { + if (HttpInstance->Tcp6 == NULL) { + return EFI_NOT_STARTED; + } + Status = HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } else { + if (HttpInstance->Tcp4 == NULL) { + return EFI_NOT_STARTED; + } + Status = HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + DispatchDpc (); + + return Status; +} diff --git a/Core/NetworkPkg/HttpDxe/HttpImpl.h b/Core/NetworkPkg/HttpDxe/HttpImpl.h new file mode 100644 index 0000000000..afbe982283 --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpImpl.h @@ -0,0 +1,240 @@ +/** @file + The header files of implementation of EFI_HTTP_PROTOCOL protocol interfaces. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_IMPL_H__ +#define __EFI_HTTP_IMPL_H__ + +#define HTTP_DEFAULT_PORT 80 +#define HTTP_END_OF_HDR_STR "\r\n\r\n" +#define HTTP_CRLF_STR "\r\n" +#define HTTP_VERSION_STR "HTTP/1.1" +#define HTTP_VERSION_CRLF_STR " HTTP/1.1\r\n" +#define HTTP_GET_STR "GET " +#define HTTP_HEAD_STR "HEAD " +// +// Connect method has maximum length according to EFI_HTTP_METHOD defined in +// UEFI2.5 spec so use this. +// +#define HTTP_MAXIMUM_METHOD_LEN sizeof ("CONNECT") + +/** + Returns the operational parameters for the current HTTP child instance. + + The GetModeData() function is used to read the current mode data (operational + parameters) for this HTTP protocol instance. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[out] HttpConfigData Point to buffer for operational parameters of this + HTTP instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData is NULL. + HttpConfigData->AccessPoint is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_NOT_STARTED The HTTP instance is not configured. + +**/ +EFI_STATUS +EFIAPI +EfiHttpGetModeData ( + IN EFI_HTTP_PROTOCOL *This, + OUT EFI_HTTP_CONFIG_DATA *HttpConfigData + ); + +/** + Initialize or brutally reset the operational parameters for this EFI HTTP instance. + + The Configure() function does the following: + When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring + timeout, local address, port, etc. + When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active + connections with remote hosts, canceling all asynchronous tokens, and flush request + and response buffers without informing the appropriate hosts. + + Except for GetModeData() and Configure(), No other EFI HTTP function can be executed + by this instance until the Configure() function is executed and returns successfully. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] HttpConfigData Pointer to the configure data to configure the instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData->LocalAddressIsIPv6 is FALSE and + HttpConfigData->IPv4Node is NULL. + HttpConfigData->LocalAddressIsIPv6 is TRUE and + HttpConfigData->IPv6Node is NULL. + @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling + Configure() with NULL to reset it. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_UNSUPPORTED One or more options in ConfigData are not supported + in the implementation. +**/ +EFI_STATUS +EFIAPI +EfiHttpConfigure ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_CONFIG_DATA *HttpConfigData + ); + +/** + The Request() function queues an HTTP request to this HTTP instance. + + Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent + successfully, or if there is an error, Status in token will be updated and Event will + be signaled. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP request token. + + @retval EFI_SUCCESS Outgoing data was processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_UNSUPPORTED The HTTP method is not supported in current + implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Request()has not been completed successfully. +**/ +EFI_STATUS +EFIAPI +EfiHttpRequest ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + Abort an asynchronous HTTP request or response token. + + The Cancel() function aborts a pending HTTP request or response transaction. If + Token is not NULL and the token is in transmit or receive queues when it is being + cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL, + all asynchronous tokens issued by Request() or Response() will be aborted. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Point to storage containing HTTP request or response + token. + + @retval EFI_SUCCESS Request and Response queues are successfully flushed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance hasn't been configured. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, + BOOTP, RARP, etc.) hasn't finished yet. + @retval EFI_NOT_FOUND The asynchronous request or response token is not + found. + @retval EFI_UNSUPPORTED The implementation does not support this function. +**/ +EFI_STATUS +EFIAPI +EfiHttpCancel ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + The Response() function queues an HTTP response to this HTTP instance, similar to + Receive() function in the EFI TCP driver. When the HTTP request is sent successfully, + or if there is an error, Status in token will be updated and Event will be signaled. + + The HTTP driver will queue a receive token to the underlying TCP instance. When data + is received in the underlying TCP instance, the data will be parsed and Token will + be populated with the response data. If the data received from the remote host + contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting + (asynchronously) for more data to be sent from the remote host before signaling + Event in Token. + + It is the responsibility of the caller to allocate a buffer for Body and specify the + size in BodyLength. If the remote host provides a response that contains a content + body, up to BodyLength bytes will be copied from the receive buffer into Body and + BodyLength will be updated with the amount of bytes received and copied to Body. This + allows the client to download a large file in chunks instead of into one contiguous + block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is + non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive + token to underlying TCP instance. If data arrives in the receive buffer, up to + BodyLength bytes of data will be copied to Body. The HTTP driver will then update + BodyLength with the amount of bytes received and copied to Body. + + If the HTTP driver does not have an open underlying TCP connection with the host + specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is + consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain + an open TCP connection between client and host. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP response token. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been + initialized. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message->Headers is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Response() has not been completed successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host + specified by response URL. +**/ +EFI_STATUS +EFIAPI +EfiHttpResponse ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + The Poll() function can be used by network drivers and applications to increase the + rate that data packets are moved between the communication devices and the transmit + and receive queues. + + In some systems, the periodic timer event in the managed network driver may not poll + the underlying communications device fast enough to transmit and/or receive all data + packets without missing incoming packets or dropping outgoing packets. Drivers and + applications that are experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + +**/ +EFI_STATUS +EFIAPI +EfiHttpPoll ( + IN EFI_HTTP_PROTOCOL *This + ); + +extern EFI_HTTP_PROTOCOL mEfiHttpTemplate; + +#endif diff --git a/Core/NetworkPkg/HttpDxe/HttpProto.c b/Core/NetworkPkg/HttpDxe/HttpProto.c new file mode 100644 index 0000000000..4315ed494f --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpProto.c @@ -0,0 +1,2098 @@ +/** @file + Miscellaneous routines for HttpDxe driver. + +Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpDriver.h" + +/** + The common notify function used in HTTP driver. + + @param[in] Event The event signaled. + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if ((Event == NULL) || (Context == NULL)) { + return ; + } + + *((BOOLEAN *) Context) = TRUE; +} + +/** + The notify function associated with Tx4Token for Tcp4->Transmit() or Tx6Token for Tcp6->Transmit(). + + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpTcpTransmitNotifyDpc ( + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + HTTP_PROTOCOL *HttpInstance; + + if (Context == NULL) { + return ; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + HttpInstance = Wrap->HttpInstance; + + if (!HttpInstance->LocalAddressIsIPv6) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Tx4Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Free resources. + // + if (Wrap->TcpWrap.Tx4Token.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Tx4Token.Packet.TxData->FragmentTable[0].FragmentBuffer); + } + + if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); + } + + } else { + Wrap->HttpToken->Status = Wrap->TcpWrap.Tx6Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Free resources. + // + if (Wrap->TcpWrap.Tx6Token.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Tx6Token.Packet.TxData->FragmentTable[0].FragmentBuffer); + } + + if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + } + } + + + Wrap->TcpWrap.IsTxDone = TRUE; + + // + // Check pending TxTokens and sent out. + // + NetMapIterate (&Wrap->HttpInstance->TxTokens, HttpTcpTransmit, NULL); + +} + +/** + Request HttpTcpTransmitNotifyDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to TCP for transmit. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +HttpTcpTransmitNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request HttpTcpTransmitNotifyDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, HttpTcpTransmitNotifyDpc, Context); +} + +/** + The notify function associated with Rx4Token for Tcp4->Receive () or Rx6Token for Tcp6->Receive(). + + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpTcpReceiveNotifyDpc ( + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + NET_MAP_ITEM *Item; + UINTN Length; + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + BOOLEAN UsingIpv6; + + if (Context == NULL) { + return ; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + HttpInstance = Wrap->HttpInstance; + UsingIpv6 = HttpInstance->LocalAddressIsIPv6; + + if (UsingIpv6) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + + if (EFI_ERROR (Wrap->TcpWrap.Rx6Token.CompletionToken.Status)) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + FreePool (Wrap); + return ; + } + + } else { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + + if (EFI_ERROR (Wrap->TcpWrap.Rx4Token.CompletionToken.Status)) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + FreePool (Wrap); + return ; + } + } + + // + // Check whether we receive a complete HTTP message. + // + ASSERT (HttpInstance->MsgParser != NULL); + if (UsingIpv6) { + Length = (UINTN) Wrap->TcpWrap.Rx6Data.FragmentTable[0].FragmentLength; + } else { + Length = (UINTN) Wrap->TcpWrap.Rx4Data.FragmentTable[0].FragmentLength; + } + + Status = HttpParseMessageBody ( + HttpInstance->MsgParser, + Length, + Wrap->HttpToken->Message->Body + ); + if (EFI_ERROR (Status)) { + return ; + } + + if (HttpIsMessageComplete (HttpInstance->MsgParser)) { + // + // Free the MsgParse since we already have a full HTTP message. + // + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + + Wrap->HttpToken->Message->BodyLength = Length; + ASSERT (HttpInstance->CacheBody == NULL); + // + // We receive part of header of next HTTP msg. + // + if (HttpInstance->NextMsg != NULL) { + Wrap->HttpToken->Message->BodyLength = HttpInstance->NextMsg - + (CHAR8 *) Wrap->HttpToken->Message->Body; + HttpInstance->CacheLen = Length - Wrap->HttpToken->Message->BodyLength; + if (HttpInstance->CacheLen != 0) { + HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen); + if (HttpInstance->CacheBody == NULL) { + return ; + } + CopyMem (HttpInstance->CacheBody, HttpInstance->NextMsg, HttpInstance->CacheLen); + HttpInstance->NextMsg = HttpInstance->CacheBody; + HttpInstance->CacheOffset = 0; + } + } + + Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); + } + + + Wrap->TcpWrap.IsRxDone = TRUE; + if (UsingIpv6) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status; + } else { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status; + } + + + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Check pending RxTokens and receive the HTTP message. + // + NetMapIterate (&Wrap->HttpInstance->RxTokens, HttpTcpReceive, NULL); + + FreePool (Wrap); +} + +/** + Request HttpTcpReceiveNotifyDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to TCP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +HttpTcpReceiveNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request HttpTcpTransmitNotifyDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, HttpTcpReceiveNotifyDpc, Context); +} + +/** + Create events for the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events are created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (!HttpInstance->LocalAddressIsIPv6) { + // + // Create events for variuos asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp4ConnDone, + &HttpInstance->Tcp4ConnToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Initialize Tcp4CloseToken + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp4CloseDone, + &HttpInstance->Tcp4CloseToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + } else { + // + // Create events for variuos asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp6ConnDone, + &HttpInstance->Tcp6ConnToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Initialize Tcp6CloseToken + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp6CloseDone, + &HttpInstance->Tcp6CloseToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + } + + return EFI_SUCCESS; + +ERROR: + // + // Error handling + // + HttpCloseTcpConnCloseEvent (HttpInstance); + + return Status; +} + + +/** + Close events in the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +HttpCloseTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + ASSERT (HttpInstance != NULL); + + if (HttpInstance->LocalAddressIsIPv6) { + if (NULL != HttpInstance->Tcp6ConnToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp6ConnToken.CompletionToken.Event); + HttpInstance->Tcp6ConnToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp6CloseToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp6CloseToken.CompletionToken.Event); + HttpInstance->Tcp6CloseToken.CompletionToken.Event = NULL; + } + + } else { + if (NULL != HttpInstance->Tcp4ConnToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp4ConnToken.CompletionToken.Event); + HttpInstance->Tcp4ConnToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp4CloseToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp4CloseToken.CompletionToken.Event); + HttpInstance->Tcp4CloseToken.CompletionToken.Event = NULL; + } + } + +} + +/** + Create event for the TCP transmit token. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpTxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + HttpInstance = Wrap->HttpInstance; + TcpWrap = &Wrap->TcpWrap; + + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpTransmitNotify, + Wrap, + &TcpWrap->Tx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Tx4Data.Push = TRUE; + TcpWrap->Tx4Data.Urgent = FALSE; + TcpWrap->Tx4Data.FragmentCount = 1; + TcpWrap->Tx4Token.Packet.TxData = &Wrap->TcpWrap.Tx4Data; + TcpWrap->Tx4Token.CompletionToken.Status = EFI_NOT_READY; + + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpTransmitNotify, + Wrap, + &TcpWrap->Tx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Tx6Data.Push = TRUE; + TcpWrap->Tx6Data.Urgent = FALSE; + TcpWrap->Tx6Data.FragmentCount = 1; + TcpWrap->Tx6Token.Packet.TxData = &Wrap->TcpWrap.Tx6Data; + TcpWrap->Tx6Token.CompletionToken.Status =EFI_NOT_READY; + + + } + + return EFI_SUCCESS; +} + +/** + Create event for the TCP receive token which is used to receive HTTP header. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEventForHeader ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsRxDone, + &HttpInstance->Rx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->Rx4Data.FragmentCount = 1; + HttpInstance->Rx4Token.Packet.RxData = &HttpInstance->Rx4Data; + HttpInstance->Rx4Token.CompletionToken.Status = EFI_NOT_READY; + + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsRxDone, + &HttpInstance->Rx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->Rx6Data.FragmentCount =1; + HttpInstance->Rx6Token.Packet.RxData = &HttpInstance->Rx6Data; + HttpInstance->Rx6Token.CompletionToken.Status = EFI_NOT_READY; + + } + + + return EFI_SUCCESS; +} + +/** + Create event for the TCP receive token which is used to receive HTTP body. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + HttpInstance = Wrap->HttpInstance; + TcpWrap = &Wrap->TcpWrap; + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpReceiveNotify, + Wrap, + &TcpWrap->Rx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Rx4Data.FragmentCount = 1; + TcpWrap->Rx4Token.Packet.RxData = &Wrap->TcpWrap.Rx4Data; + TcpWrap->Rx4Token.CompletionToken.Status = EFI_NOT_READY; + + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpReceiveNotify, + Wrap, + &TcpWrap->Rx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Rx6Data.FragmentCount = 1; + TcpWrap->Rx6Token.Packet.RxData = &Wrap->TcpWrap.Rx6Data; + TcpWrap->Rx6Token.CompletionToken.Status = EFI_NOT_READY; + } + + return EFI_SUCCESS; +} + +/** + Close Events for Tcp Receive Tokens for HTTP body and HTTP header. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpCloseTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + HTTP_PROTOCOL *HttpInstance; + + ASSERT (Wrap != NULL); + HttpInstance = Wrap->HttpInstance; + + if (HttpInstance->LocalAddressIsIPv6) { + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } + + if (HttpInstance->Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx6Token.CompletionToken.Event); + HttpInstance->Rx6Token.CompletionToken.Event = NULL; + } + } else { + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + + if (HttpInstance->Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx4Token.CompletionToken.Event); + HttpInstance->Rx4Token.CompletionToken.Event = NULL; + } + } +} + +/** + Intiialize the HTTP_PROTOCOL structure to the unconfigured state. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] IpVersion Indicate us TCP4 protocol or TCP6 protocol. + + @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitProtocol ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN BOOLEAN IpVersion + ) +{ + EFI_STATUS Status; + VOID *Interface; + BOOLEAN UsingIpv6; + + ASSERT (HttpInstance != NULL); + UsingIpv6 = IpVersion; + + if (!UsingIpv6) { + // + // Create TCP4 child. + // + Status = NetLibCreateServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &HttpInstance->Tcp4ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &HttpInstance->Tcp4, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + } else { + // + // Create TCP6 Child. + // + Status = NetLibCreateServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + &HttpInstance->Tcp6ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &HttpInstance->Tcp6, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + } + + HttpInstance->Url = AllocateZeroPool (HTTP_URL_BUFFER_LEN); + if (HttpInstance->Url == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (HttpInstance->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpInstance->Tcp4ChildHandle + ); + } + + if (HttpInstance->Service->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + } + + if (HttpInstance->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpInstance->Tcp6ChildHandle + ); + } + + if (HttpInstance->Service->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + } + + return EFI_UNSUPPORTED; + +} + +/** + Clean up the HTTP child, release all the resources used by it. + + @param[in] HttpInstance The HTTP child to clean up. + +**/ +VOID +HttpCleanProtocol ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + HttpCloseConnection (HttpInstance); + + HttpCloseTcpConnCloseEvent (HttpInstance); + + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + HttpInstance->NextMsg = NULL; + } + + if (HttpInstance->RemoteHost != NULL) { + FreePool (HttpInstance->RemoteHost); + HttpInstance->RemoteHost = NULL; + } + + if (HttpInstance->MsgParser != NULL) { + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + + if (HttpInstance->Url != NULL) { + FreePool (HttpInstance->Url); + HttpInstance->Url = NULL; + } + + NetMapClean (&HttpInstance->TxTokens); + NetMapClean (&HttpInstance->RxTokens); + + if (HttpInstance->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpInstance->Tcp4ChildHandle + ); + } + + if (HttpInstance->Service->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + } + + if (HttpInstance->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpInstance->Tcp6ChildHandle + ); + } + + if (HttpInstance->Service->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + } + +} + +/** + Establish TCP connection with HTTP server. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateConnection ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + // + // Connect to Http server + // + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->IsTcp4ConnDone = FALSE; + HttpInstance->Tcp4ConnToken.CompletionToken.Status = EFI_NOT_READY; + Status = HttpInstance->Tcp4->Connect (HttpInstance->Tcp4, &HttpInstance->Tcp4ConnToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp4->Connect() = %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsTcp4ConnDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + Status = HttpInstance->Tcp4ConnToken.CompletionToken.Status; + + } else { + HttpInstance->IsTcp6ConnDone = FALSE; + HttpInstance->Tcp6ConnToken.CompletionToken.Status = EFI_NOT_READY; + Status = HttpInstance->Tcp6->Connect (HttpInstance->Tcp6, &HttpInstance->Tcp6ConnToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp6->Connect() = %r\n", Status)); + return Status; + } + + while(!HttpInstance->IsTcp6ConnDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + + Status = HttpInstance->Tcp6ConnToken.CompletionToken.Status; + } + + if (!EFI_ERROR (Status)) { + HttpInstance->State = HTTP_STATE_TCP_CONNECTED; + } + + return Status; +} + +/** + Close existing TCP connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is closed. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCloseConnection ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (HttpInstance->State == HTTP_STATE_TCP_CONNECTED) { + + if (HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp6CloseToken.AbortOnClose = TRUE; + HttpInstance->IsTcp6CloseDone = FALSE; + Status = HttpInstance->Tcp6->Close (HttpInstance->Tcp6, &HttpInstance->Tcp6CloseToken); + if (EFI_ERROR (Status)) { + return Status; + } + + while (!HttpInstance->IsTcp6CloseDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + + } else { + HttpInstance->Tcp4CloseToken.AbortOnClose = TRUE; + HttpInstance->IsTcp4CloseDone = FALSE; + Status = HttpInstance->Tcp4->Close (HttpInstance->Tcp4, &HttpInstance->Tcp4CloseToken); + if (EFI_ERROR (Status)) { + return Status; + } + + while (!HttpInstance->IsTcp4CloseDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + } + + } + + HttpInstance->State = HTTP_STATE_TCP_CLOSED; + return EFI_SUCCESS; +} + +/** + Configure TCP4 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP4 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_TCP4_CONFIG_DATA *Tcp4CfgData; + EFI_TCP4_ACCESS_POINT *Tcp4AP; + EFI_TCP4_OPTION *Tcp4Option; + + ASSERT (HttpInstance != NULL); + + + Tcp4CfgData = &HttpInstance->Tcp4CfgData; + ZeroMem (Tcp4CfgData, sizeof (EFI_TCP4_CONFIG_DATA)); + + Tcp4CfgData->TypeOfService = HTTP_TOS_DEAULT; + Tcp4CfgData->TimeToLive = HTTP_TTL_DEAULT; + Tcp4CfgData->ControlOption = &HttpInstance->Tcp4Option; + + Tcp4AP = &Tcp4CfgData->AccessPoint; + Tcp4AP->UseDefaultAddress = HttpInstance->IPv4Node.UseDefaultAddress; + if (!Tcp4AP->UseDefaultAddress) { + IP4_COPY_ADDRESS (&Tcp4AP->StationAddress, &HttpInstance->IPv4Node.LocalAddress); + IP4_COPY_ADDRESS (&Tcp4AP->SubnetMask, &HttpInstance->IPv4Node.LocalSubnet); + } + + Tcp4AP->StationPort = HttpInstance->IPv4Node.LocalPort; + Tcp4AP->RemotePort = HttpInstance->RemotePort; + Tcp4AP->ActiveFlag = TRUE; + IP4_COPY_ADDRESS (&Tcp4AP->RemoteAddress, &HttpInstance->RemoteAddr); + + Tcp4Option = Tcp4CfgData->ControlOption; + Tcp4Option->ReceiveBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp4Option->SendBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp4Option->MaxSynBackLog = HTTP_MAX_SYN_BACK_LOG; + Tcp4Option->ConnectionTimeout = HTTP_CONNECTION_TIMEOUT; + Tcp4Option->DataRetries = HTTP_DATA_RETRIES; + Tcp4Option->FinTimeout = HTTP_FIN_TIMEOUT; + Tcp4Option->KeepAliveProbes = HTTP_KEEP_ALIVE_PROBES; + Tcp4Option->KeepAliveTime = HTTP_KEEP_ALIVE_TIME; + Tcp4Option->KeepAliveInterval = HTTP_KEEP_ALIVE_INTERVAL; + Tcp4Option->EnableNagle = TRUE; + Tcp4CfgData->ControlOption = Tcp4Option; + + Status = HttpInstance->Tcp4->Configure (HttpInstance->Tcp4, Tcp4CfgData); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpConfigureTcp4 - %r\n", Status)); + return Status; + } + + Status = HttpCreateTcpConnCloseEvent (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_TCP_CONFIGED; + + return EFI_SUCCESS; +} + +/** + Configure TCP6 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP6 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_TCP6_CONFIG_DATA *Tcp6CfgData; + EFI_TCP6_ACCESS_POINT *Tcp6Ap; + EFI_TCP6_OPTION *Tcp6Option; + + ASSERT (HttpInstance != NULL); + + Tcp6CfgData = &HttpInstance->Tcp6CfgData; + ZeroMem (Tcp6CfgData, sizeof (EFI_TCP6_CONFIG_DATA)); + + Tcp6CfgData->TrafficClass = 0; + Tcp6CfgData->HopLimit = 255; + Tcp6CfgData->ControlOption = &HttpInstance->Tcp6Option; + + Tcp6Ap = &Tcp6CfgData->AccessPoint; + Tcp6Ap->ActiveFlag = TRUE; + Tcp6Ap->StationPort = HttpInstance->Ipv6Node.LocalPort; + Tcp6Ap->RemotePort = HttpInstance->RemotePort; + IP6_COPY_ADDRESS (&Tcp6Ap->StationAddress, &HttpInstance->Ipv6Node.LocalAddress); + IP6_COPY_ADDRESS (&Tcp6Ap->RemoteAddress , &HttpInstance->RemoteIpv6Addr); + + Tcp6Option = Tcp6CfgData->ControlOption; + Tcp6Option->ReceiveBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp6Option->SendBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp6Option->MaxSynBackLog = HTTP_MAX_SYN_BACK_LOG; + Tcp6Option->ConnectionTimeout = HTTP_CONNECTION_TIMEOUT; + Tcp6Option->DataRetries = HTTP_DATA_RETRIES; + Tcp6Option->FinTimeout = HTTP_FIN_TIMEOUT; + Tcp6Option->KeepAliveProbes = HTTP_KEEP_ALIVE_PROBES; + Tcp6Option->KeepAliveTime = HTTP_KEEP_ALIVE_TIME; + Tcp6Option->KeepAliveInterval = HTTP_KEEP_ALIVE_INTERVAL; + Tcp6Option->EnableNagle = TRUE; + + Status = HttpInstance->Tcp6->Configure (HttpInstance->Tcp6, Tcp6CfgData); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpConfigureTcp6 - %r\n", Status)); + return Status; + } + + Status = HttpCreateTcpConnCloseEvent (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_TCP_CONFIGED; + + return EFI_SUCCESS; + +} + +/** + Check existing TCP connection, if in error state, recover TCP4 connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP4 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp4 ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + EFI_TCP4_CONNECTION_STATE Tcp4State; + + + if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED || HttpInstance->Tcp4 == NULL) { + return EFI_NOT_READY; + } + + Status = HttpInstance->Tcp4->GetModeData( + HttpInstance->Tcp4, + &Tcp4State, + NULL, + NULL, + NULL, + NULL + ); + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp4 GetModeData fail - %x\n", Status)); + return Status; + } + + if (Tcp4State == Tcp4StateEstablished) { + return EFI_SUCCESS; + } else if (Tcp4State > Tcp4StateEstablished ) { + HttpCloseConnection(HttpInstance); + } + + return HttpCreateConnection (HttpInstance); +} + +/** + Check existing TCP connection, if in error state, recover TCP6 connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP6 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp6 ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + EFI_TCP6_CONNECTION_STATE Tcp6State; + + if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED || HttpInstance->Tcp6 == NULL) { + return EFI_NOT_READY; + } + + Status = HttpInstance->Tcp6->GetModeData ( + HttpInstance->Tcp6, + &Tcp6State, + NULL, + NULL, + NULL, + NULL + ); + + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp6 GetModeData fail - %x\n", Status)); + return Status; + } + + if (Tcp6State == Tcp6StateEstablished) { + return EFI_SUCCESS; + } else if (Tcp6State > Tcp6StateEstablished ) { + HttpCloseConnection(HttpInstance); + } + + return HttpCreateConnection (HttpInstance); +} + +/** + Initialize TCP related data. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] Configure The Flag indicates whether the first time to initialize Tcp. + + @retval EFI_SUCCESS The initialization of TCP instance is done. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitTcp ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN BOOLEAN Configure + ) +{ + EFI_STATUS Status; + ASSERT (HttpInstance != NULL); + + if (!HttpInstance->LocalAddressIsIPv6) { + // + // Configure TCP instance. + // + if (Configure) { + Status = HttpConfigureTcp4 (HttpInstance, Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Connect TCP. + // + Status = HttpConnectTcp4 (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // Configure TCP instance. + // + if (Configure) { + Status = HttpConfigureTcp6 (HttpInstance, Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Connect TCP. + // + Status = HttpConnectTcp6 (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; + +} + +/** + Send the HTTP message through TCP4 or TCP6. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] TxString Buffer containing the HTTP message string. + @param[in] TxStringLen Length of the HTTP message string in bytes. + + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTransmitTcp ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN UINT8 *TxString, + IN UINTN TxStringLen + ) +{ + EFI_STATUS Status; + EFI_TCP4_IO_TOKEN *Tx4Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP6_IO_TOKEN *Tx6Token; + EFI_TCP6_PROTOCOL *Tcp6; + + if (!HttpInstance->LocalAddressIsIPv6) { + Tcp4 = HttpInstance->Tcp4; + Tx4Token = &Wrap->TcpWrap.Tx4Token; + + Tx4Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + Tx4Token->CompletionToken.Status = EFI_NOT_READY; + + Wrap->TcpWrap.IsTxDone = FALSE; + Status = Tcp4->Transmit (Tcp4, Tx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); + return Status; + } + + } else { + Tcp6 = HttpInstance->Tcp6; + Tx6Token = &Wrap->TcpWrap.Tx6Token; + + Tx6Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + Tx6Token->CompletionToken.Status = EFI_NOT_READY; + + Wrap->TcpWrap.IsTxDone = FALSE; + Status = Tcp6->Transmit (Tcp6, Tx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); + return Status; + } + } + + + return Status; +} + +/** + Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined + in UEFI 2.5 specification. + + @param[in] StatusCode The status code value in HTTP message. + + @return Value defined in EFI_HTTP_STATUS_CODE . + +**/ +EFI_HTTP_STATUS_CODE +HttpMappingToStatusCode ( + IN UINTN StatusCode + ) +{ + switch (StatusCode) { + case 100: + return HTTP_STATUS_100_CONTINUE; + case 101: + return HTTP_STATUS_101_SWITCHING_PROTOCOLS; + case 200: + return HTTP_STATUS_200_OK; + case 201: + return HTTP_STATUS_201_CREATED; + case 202: + return HTTP_STATUS_202_ACCEPTED; + case 203: + return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION; + case 204: + return HTTP_STATUS_204_NO_CONTENT; + case 205: + return HTTP_STATUS_205_RESET_CONTENT; + case 206: + return HTTP_STATUS_206_PARTIAL_CONTENT; + case 300: + return HTTP_STATUS_300_MULTIPLE_CHIOCES; + case 301: + return HTTP_STATUS_301_MOVED_PERMANENTLY; + case 302: + return HTTP_STATUS_302_FOUND; + case 303: + return HTTP_STATUS_303_SEE_OTHER; + case 304: + return HTTP_STATUS_304_NOT_MODIFIED; + case 305: + return HTTP_STATUS_305_USE_PROXY; + case 307: + return HTTP_STATUS_307_TEMPORARY_REDIRECT; + case 400: + return HTTP_STATUS_400_BAD_REQUEST; + case 401: + return HTTP_STATUS_401_UNAUTHORIZED; + case 402: + return HTTP_STATUS_402_PAYMENT_REQUIRED; + case 403: + return HTTP_STATUS_403_FORBIDDEN; + case 404: + return HTTP_STATUS_404_NOT_FOUND; + case 405: + return HTTP_STATUS_405_METHOD_NOT_ALLOWED; + case 406: + return HTTP_STATUS_406_NOT_ACCEPTABLE; + case 407: + return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED; + case 408: + return HTTP_STATUS_408_REQUEST_TIME_OUT; + case 409: + return HTTP_STATUS_409_CONFLICT; + case 410: + return HTTP_STATUS_410_GONE; + case 411: + return HTTP_STATUS_411_LENGTH_REQUIRED; + case 412: + return HTTP_STATUS_412_PRECONDITION_FAILED; + case 413: + return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE; + case 414: + return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE; + case 415: + return HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE; + case 416: + return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED; + case 417: + return HTTP_STATUS_417_EXPECTATION_FAILED; + case 500: + return HTTP_STATUS_500_INTERNAL_SERVER_ERROR; + case 501: + return HTTP_STATUS_501_NOT_IMPLEMENTED; + case 502: + return HTTP_STATUS_502_BAD_GATEWAY; + case 503: + return HTTP_STATUS_503_SERVICE_UNAVAILABLE; + case 504: + return HTTP_STATUS_504_GATEWAY_TIME_OUT; + case 505: + return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED; + + default: + return HTTP_STATUS_UNSUPPORTED_STATUS; + } +} + +/** + Check whether the user's token or event has already + been enqueue on HTTP Tx or Rx Token list. + + @param[in] Map The container of either user's transmit or receive + token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP + @retval EFI_SUCCESS The current item isn't the same token/event as the + context. + +**/ +EFI_STATUS +EFIAPI +HttpTokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_HTTP_TOKEN *Token; + EFI_HTTP_TOKEN *TokenInItem; + + Token = (EFI_HTTP_TOKEN *) Context; + TokenInItem = (EFI_HTTP_TOKEN *) Item->Key; + + if (Token == TokenInItem || Token->Event == TokenInItem->Event) { + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + +/** + Check whether the HTTP message associated with Tx4Token or Tx6Token is already sent out. + + @param[in] Map The container of Tx4Token or Tx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_NOT_READY The HTTP message is still queued in the list. + @retval EFI_SUCCESS The HTTP message has been sent out. + +**/ +EFI_STATUS +EFIAPI +HttpTcpNotReady ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *ValueInItem; + + ValueInItem = (HTTP_TOKEN_WRAP *) Item->Value; + + if (!ValueInItem->TcpWrap.IsTxDone) { + return EFI_NOT_READY; + } + + return EFI_SUCCESS; +} + +/** + Transmit the HTTP mssage by processing the associated HTTP token. + + @param[in] Map The container of Tx4Token or Tx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit + queue. + +**/ +EFI_STATUS +EFIAPI +HttpTcpTransmit ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *ValueInItem; + EFI_STATUS Status; + CHAR8 *RequestStr; + CHAR8 *Url; + UINTN UrlSize; + + ValueInItem = (HTTP_TOKEN_WRAP *) Item->Value; + if (ValueInItem->TcpWrap.IsTxDone) { + return EFI_SUCCESS; + } + + // + // Parse the URI of the remote host. + // + UrlSize = StrLen (ValueInItem->HttpToken->Message->Data.Request->Url) + 1; + Url = AllocatePool (UrlSize); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStrS (ValueInItem->HttpToken->Message->Data.Request->Url, Url, UrlSize); + + // + // Create request message. + // + RequestStr = HttpGenRequestString ( + ValueInItem->HttpInstance, + ValueInItem->HttpToken->Message, + Url + ); + FreePool (Url); + if (RequestStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Transmit the request message. + // + Status = HttpTransmitTcp ( + ValueInItem->HttpInstance, + ValueInItem, + (UINT8*) RequestStr, + AsciiStrLen (RequestStr) + ); + FreePool (RequestStr); + return Status; +} + +/** + Receive the HTTP response by processing the associated HTTP token. + + @param[in] Map The container of Rx4Token or Rx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_SUCCESS The HTTP response is queued into TCP receive + queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +HttpTcpReceive ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + // + // Process the queued HTTP response. + // + return HttpResponseWorker ((HTTP_TOKEN_WRAP *) Item->Value); +} + +/** + Receive the HTTP header by processing the associated HTTP token. + + @param[in] HttpInstance The HTTP instance private data. + @param[in, out] SizeofHeaders The HTTP header length. + @param[in, out] BufferSize The size of buffer to cacahe the header message. + + @retval EFI_SUCCESS The HTTP header is received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveHeader ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT UINTN *SizeofHeaders, + IN OUT UINTN *BufferSize + ) +{ + EFI_STATUS Status; + EFI_TCP4_IO_TOKEN *Rx4Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP6_IO_TOKEN *Rx6Token; + EFI_TCP6_PROTOCOL *Tcp6; + CHAR8 **EndofHeader; + CHAR8 **HttpHeaders; + CHAR8 *Buffer; + + ASSERT (HttpInstance != NULL); + + EndofHeader = HttpInstance->EndofHeader; + HttpHeaders = HttpInstance->HttpHeaders; + Tcp4 = HttpInstance->Tcp4; + Tcp6 = HttpInstance->Tcp6; + Buffer = NULL; + Rx4Token = NULL; + Rx6Token = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + ASSERT (Tcp6 != NULL); + } else { + ASSERT (Tcp4 != NULL); + } + + if (!HttpInstance->LocalAddressIsIPv6) { + Rx4Token = &HttpInstance->Rx4Token; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + // + // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. + // + while (*EndofHeader == NULL) { + HttpInstance->IsRxDone = FALSE; + Rx4Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp4->Receive (Tcp4, Rx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone) { + Tcp4->Poll (Tcp4); + } + + Status = Rx4Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Append the response string. + // + *BufferSize = (*SizeofHeaders) + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength; + Buffer = AllocateZeroPool (*BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + if (*HttpHeaders != NULL) { + CopyMem (Buffer, *HttpHeaders, (*SizeofHeaders)); + FreePool (*HttpHeaders); + } + + CopyMem ( + Buffer + (*SizeofHeaders), + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer, + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength + ); + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + + // + // Check whether we received end of HTTP headers. + // + *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); + } + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + + } else { + Rx6Token = &HttpInstance->Rx6Token; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + // + // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. + // + while (*EndofHeader == NULL) { + HttpInstance->IsRxDone = FALSE; + Rx6Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp6->Receive (Tcp6, Rx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone) { + Tcp6->Poll (Tcp6); + } + + Status = Rx6Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Append the response string. + // + *BufferSize = (*SizeofHeaders) + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength; + Buffer = AllocateZeroPool (*BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + if (*HttpHeaders != NULL) { + CopyMem (Buffer, *HttpHeaders, (*SizeofHeaders)); + FreePool (*HttpHeaders); + } + + CopyMem ( + Buffer + (*SizeofHeaders), + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer, + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength + ); + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + + // + // Check whether we received end of HTTP headers. + // + *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); + + } + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + + // + // Skip the CRLF after the HTTP headers. + // + *EndofHeader = *EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR); + + return EFI_SUCCESS; +} + +/** + Receive the HTTP body by processing the associated HTTP token. + + @param[in] Wrap The HTTP token's wrap data. + @param[in] HttpMsg The HTTP message data. + + @retval EFI_SUCCESS The HTTP body is received. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveBody ( + IN HTTP_TOKEN_WRAP *Wrap, + IN EFI_HTTP_MESSAGE *HttpMsg + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + EFI_TCP6_PROTOCOL *Tcp6; + EFI_TCP6_IO_TOKEN *Rx6Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP4_IO_TOKEN *Rx4Token; + + HttpInstance = Wrap->HttpInstance; + Tcp4 = HttpInstance->Tcp4; + Tcp6 = HttpInstance->Tcp6; + Rx4Token = NULL; + Rx6Token = NULL; + + + if (HttpInstance->LocalAddressIsIPv6) { + ASSERT (Tcp6 != NULL); + } else { + ASSERT (Tcp4 != NULL); + } + + if (HttpInstance->LocalAddressIsIPv6) { + Rx6Token = &Wrap->TcpWrap.Rx6Token; + Rx6Token ->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; + Rx6Token ->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; + Rx6Token ->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; + Rx6Token->CompletionToken.Status = EFI_NOT_READY; + + Status = Tcp6->Receive (Tcp6, Rx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + + } else { + Rx4Token = &Wrap->TcpWrap.Rx4Token; + Rx4Token->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; + + Rx4Token->CompletionToken.Status = EFI_NOT_READY; + Status = Tcp4->Receive (Tcp4, Rx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + } + + return EFI_SUCCESS; + +} + +/** + Clean up Tcp Tokens while the Tcp transmission error occurs. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpTcpTokenCleanup ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_TCP4_IO_TOKEN *Rx4Token; + EFI_TCP6_IO_TOKEN *Rx6Token; + + ASSERT (Wrap != NULL); + HttpInstance = Wrap->HttpInstance; + Rx4Token = NULL; + Rx6Token = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } + + Rx6Token = &Wrap->TcpWrap.Rx6Token; + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + FreePool (Wrap); + + if (HttpInstance->Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx6Token.CompletionToken.Event); + HttpInstance->Rx6Token.CompletionToken.Event = NULL; + } + + Rx6Token = &HttpInstance->Rx6Token; + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + + } else { + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + Rx4Token = &Wrap->TcpWrap.Rx4Token; + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + FreePool (Wrap); + + if (HttpInstance->Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx4Token.CompletionToken.Event); + HttpInstance->Rx4Token.CompletionToken.Event = NULL; + } + + Rx4Token = &HttpInstance->Rx4Token; + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + } + +} + +/** + Generate HTTP request string. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Message Pointer to storage containing HTTP message data. + @param[in] Url The URL of a remote host. + + @return Pointer to the created HTTP request string. + @return NULL if any error occured. + +**/ +CHAR8 * +HttpGenRequestString ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_HTTP_MESSAGE *Message, + IN CHAR8 *Url + ) +{ + EFI_STATUS Status; + UINTN StrLength; + UINT8 *Request; + UINT8 *RequestPtr; + UINTN HttpHdrSize; + UINTN MsgSize; + BOOLEAN Success; + VOID *HttpHdr; + EFI_HTTP_HEADER **AppendList; + UINTN Index; + + ASSERT (HttpInstance != NULL); + ASSERT (Message != NULL); + + DEBUG ((EFI_D_ERROR, "HttpMethod - %x\n", Message->Data.Request->Method)); + + Request = NULL; + Success = FALSE; + HttpHdr = NULL; + AppendList = NULL; + + // + // Build AppendList + // + AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount)); + if (AppendList == NULL) { + return NULL; + } + + for(Index = 0; Index < Message->HeaderCount; Index++){ + AppendList[Index] = &Message->Headers[Index]; + } + + // + // Check whether the EFI_HTTP_UTILITIES_PROTOCOL is available. + // + if (mHttpUtilities == NULL) { + return NULL; + } + + // + // Build raw unformatted HTTP headers. + // + Status = mHttpUtilities->Build ( + mHttpUtilities, + 0, + NULL, + 0, + NULL, + Message->HeaderCount, + AppendList, + &HttpHdrSize, + &HttpHdr + ); + FreePool (AppendList); + if (EFI_ERROR (Status) || HttpHdr == NULL) { + return NULL; + } + + // + // Calculate HTTP message length. + // + MsgSize = Message->BodyLength + HTTP_MAXIMUM_METHOD_LEN + AsciiStrLen (Url) + + AsciiStrLen (HTTP_VERSION_CRLF_STR) + HttpHdrSize; + Request = AllocateZeroPool (MsgSize); + if (Request == NULL) { + goto Exit; + } + + RequestPtr = Request; + // + // Construct header request + // + switch (Message->Data.Request->Method) { + case HttpMethodGet: + StrLength = sizeof (HTTP_GET_STR) - 1; + CopyMem (RequestPtr, HTTP_GET_STR, StrLength); + RequestPtr += StrLength; + break; + case HttpMethodHead: + StrLength = sizeof (HTTP_HEAD_STR) - 1; + CopyMem (RequestPtr, HTTP_HEAD_STR, StrLength); + RequestPtr += StrLength; + break; + default: + ASSERT (FALSE); + goto Exit; + } + + StrLength = AsciiStrLen (Url); + CopyMem (RequestPtr, Url, StrLength); + RequestPtr += StrLength; + + StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1; + CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength); + RequestPtr += StrLength; + + // + // Construct header + // + CopyMem (RequestPtr, HttpHdr, HttpHdrSize); + RequestPtr += HttpHdrSize; + + // + // Construct body + // + if (Message->Body != NULL) { + CopyMem (RequestPtr, Message->Body, Message->BodyLength); + RequestPtr += Message->BodyLength; + } + + // + // Done + // + *RequestPtr = 0; + Success = TRUE; + +Exit: + + if (!Success) { + if (Request != NULL) { + FreePool (Request); + } + + Request = NULL; + } + + if (HttpHdr != NULL) { + FreePool (HttpHdr); + } + + return (CHAR8*) Request; +} diff --git a/Core/NetworkPkg/HttpDxe/HttpProto.h b/Core/NetworkPkg/HttpDxe/HttpProto.h new file mode 100644 index 0000000000..e43a2dc01c --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpProto.h @@ -0,0 +1,590 @@ +/** @file + The header files of miscellaneous routines for HttpDxe driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_PROTO_H__ +#define __EFI_HTTP_PROTO_H__ + +#define DEF_BUF_LEN 2048 + +#define HTTP_SERVICE_SIGNATURE SIGNATURE_32('H', 't', 't', 'S') + +#define HTTP_SERVICE_FROM_PROTOCOL(a) \ + CR ( \ + (a), \ + HTTP_SERVICE, \ + ServiceBinding, \ + HTTP_SERVICE_SIGNATURE \ + ) + + +// +// The state of HTTP protocol. It starts from UNCONFIGED. +// +#define HTTP_STATE_UNCONFIGED 0 +#define HTTP_STATE_HTTP_CONFIGED 1 +#define HTTP_STATE_TCP_CONFIGED 2 +#define HTTP_STATE_TCP_UNCONFIGED 3 +#define HTTP_STATE_TCP_CONNECTED 4 +#define HTTP_STATE_TCP_CLOSED 5 + +// +// TCP configured data. +// +#define HTTP_TOS_DEAULT 8 +#define HTTP_TTL_DEAULT 255 +#define HTTP_BUFFER_SIZE_DEAULT 65535 +#define HTTP_MAX_SYN_BACK_LOG 5 +#define HTTP_CONNECTION_TIMEOUT 60 +#define HTTP_DATA_RETRIES 12 +#define HTTP_FIN_TIMEOUT 2 +#define HTTP_KEEP_ALIVE_PROBES 6 +#define HTTP_KEEP_ALIVE_TIME 7200 +#define HTTP_KEEP_ALIVE_INTERVAL 30 + +#define HTTP_URL_BUFFER_LEN 4096 + +typedef struct _HTTP_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_HANDLE ImageHandle; + EFI_HANDLE ControllerHandle; + EFI_HANDLE Tcp4ChildHandle; + EFI_HANDLE Tcp6ChildHandle; + LIST_ENTRY ChildrenList; + UINTN ChildrenNumber; + INTN State; +} HTTP_SERVICE; + +typedef struct { + EFI_TCP4_IO_TOKEN Tx4Token; + EFI_TCP4_TRANSMIT_DATA Tx4Data; + EFI_TCP6_IO_TOKEN Tx6Token; + EFI_TCP6_TRANSMIT_DATA Tx6Data; + EFI_TCP4_IO_TOKEN Rx4Token; + EFI_TCP4_RECEIVE_DATA Rx4Data; + EFI_TCP6_IO_TOKEN Rx6Token; + EFI_TCP6_RECEIVE_DATA Rx6Data; + BOOLEAN IsTxDone; + BOOLEAN IsRxDone; + UINTN BodyLen; + EFI_HTTP_METHOD Method; +} HTTP_TCP_TOKEN_WRAP; + +typedef struct _HTTP_PROTOCOL { + UINT32 Signature; + EFI_HTTP_PROTOCOL Http; + EFI_HANDLE Handle; + HTTP_SERVICE *Service; + LIST_ENTRY Link; // Link to all HTTP instance from the service. + BOOLEAN InDestroy; + INTN State; + + EFI_HANDLE Tcp4ChildHandle; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP4_CONFIG_DATA Tcp4CfgData; + EFI_TCP4_OPTION Tcp4Option; + + EFI_TCP4_CONNECTION_TOKEN Tcp4ConnToken; + BOOLEAN IsTcp4ConnDone; + EFI_TCP4_CLOSE_TOKEN Tcp4CloseToken; + BOOLEAN IsTcp4CloseDone; + CHAR8 *RemoteHost; + UINT16 RemotePort; + EFI_IPv4_ADDRESS RemoteAddr; + + EFI_HANDLE Tcp6ChildHandle; + EFI_TCP6_PROTOCOL *Tcp6; + EFI_TCP6_CONFIG_DATA Tcp6CfgData; + EFI_TCP6_OPTION Tcp6Option; + + EFI_TCP6_CONNECTION_TOKEN Tcp6ConnToken; + BOOLEAN IsTcp6ConnDone; + EFI_TCP6_CLOSE_TOKEN Tcp6CloseToken; + BOOLEAN IsTcp6CloseDone; + EFI_IPv6_ADDRESS RemoteIpv6Addr; + + + + // + // Rx4Token or Rx6Token used for receiving HTTP header. + // + EFI_TCP4_IO_TOKEN Rx4Token; + EFI_TCP4_RECEIVE_DATA Rx4Data; + EFI_TCP6_IO_TOKEN Rx6Token; + EFI_TCP6_RECEIVE_DATA Rx6Data; + BOOLEAN IsRxDone; + + CHAR8 **EndofHeader; + CHAR8 **HttpHeaders; + CHAR8 *CacheBody; + CHAR8 *NextMsg; + UINTN CacheLen; + UINTN CacheOffset; + + // + // HTTP message-body parser. + // + VOID *MsgParser; + + EFI_HTTP_VERSION HttpVersion; + UINT32 TimeOutMillisec; + BOOLEAN LocalAddressIsIPv6; + + EFI_HTTPv4_ACCESS_POINT IPv4Node; + EFI_HTTPv6_ACCESS_POINT Ipv6Node; + + NET_MAP TxTokens; + NET_MAP RxTokens; + + CHAR8 *Url; +} HTTP_PROTOCOL; + +typedef struct { + EFI_HTTP_TOKEN *HttpToken; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP TcpWrap; +} HTTP_TOKEN_WRAP; + + +#define HTTP_PROTOCOL_SIGNATURE SIGNATURE_32('H', 't', 't', 'P') + +#define HTTP_INSTANCE_FROM_PROTOCOL(a) \ + CR ( \ + (a), \ + HTTP_PROTOCOL, \ + Http, \ + HTTP_PROTOCOL_SIGNATURE \ + ) + +/** + The common notify function used in HTTP driver. + + @param[in] Event The event signaled. + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Create events for the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events are created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Close events in the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +HttpCloseTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Create event for the TCP transmit token. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpTxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Create event for the TCP receive token which is used to receive HTTP header. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEventForHeader ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Create event for the TCP receive token which is used to receive HTTP body. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Close Events for Tcp Receive Tokens for HTTP body and HTTP header. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpCloseTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Intiialize the HTTP_PROTOCOL structure to the unconfigured state. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] IpVersion Indicate us TCP4 protocol or TCP6 protocol. + + @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitProtocol ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN BOOLEAN IpVersion + ); + +/** + Clean up the HTTP child, release all the resources used by it. + + @param[in] HttpInstance The HTTP child to clean up. + +**/ +VOID +HttpCleanProtocol ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Establish TCP connection with HTTP server. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateConnection ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Close existing TCP connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is closed. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCloseConnection ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Configure TCP4 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP4 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Configure TCP6 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP6 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Check existing TCP connection, if in error state, receover TCP4 connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP4 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp4 ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Check existing TCP connection, if in error state, recover TCP6 connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP6 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp6 ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Send the HTTP message through TCP4 or TCP6. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] TxString Buffer containing the HTTP message string. + @param[in] TxStringLen Length of the HTTP message string in bytes. + + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTransmitTcp ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN UINT8 *TxString, + IN UINTN TxStringLen + ); + +/** + Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined + in UEFI 2.5 specification. + + @param[in] StatusCode The status code value in HTTP message. + + @return Value defined in EFI_HTTP_STATUS_CODE . + +**/ +EFI_HTTP_STATUS_CODE +HttpMappingToStatusCode ( + IN UINTN StatusCode + ); + +/** + Check whether the user's token or event has already + been enqueue on HTTP Tx or Rx Token list. + + @param[in] Map The container of either user's transmit or receive + token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP + @retval EFI_SUCCESS The current item isn't the same token/event as the + context. + +**/ +EFI_STATUS +EFIAPI +HttpTokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + Check whether the HTTP message associated with TxToken or Tx6Token is already sent out. + + @param[in] Map The container of TxToken. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_NOT_READY The HTTP message is still queued in the list. + @retval EFI_SUCCESS The HTTP message has been sent out. + +**/ +EFI_STATUS +EFIAPI +HttpTcpNotReady ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + Initialize TCP related data. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] Configure The Flag indicates whether the first time to initialize Tcp. + + @retval EFI_SUCCESS The initialization of TCP instance is done. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitTcp ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN BOOLEAN Configure + ); + +/** + Transmit the HTTP mssage by processing the associated HTTP token. + + @param[in] Map The container of TxToken or Tx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit + queue. + +**/ +EFI_STATUS +EFIAPI +HttpTcpTransmit ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + Receive the HTTP response by processing the associated HTTP token. + + @param[in] Map The container of Rx4Token or Rx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_SUCCESS The HTTP response is queued into TCP receive + queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +HttpTcpReceive ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + Receive the HTTP header by processing the associated HTTP token. + + @param[in] HttpInstance The HTTP instance private data. + @param[in, out] SizeofHeaders The HTTP header length. + @param[in, out] BufferSize The size of buffer to cacahe the header message. + + @retval EFI_SUCCESS The HTTP header is received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveHeader ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT UINTN *SizeofHeaders, + IN OUT UINTN *BufferSize + ); + +/** + Receive the HTTP body by processing the associated HTTP token. + + @param[in] Wrap The HTTP token's wrap data. + @param[in] HttpMsg The HTTP message data. + + @retval EFI_SUCCESS The HTTP body is received. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveBody ( + IN HTTP_TOKEN_WRAP *Wrap, + IN EFI_HTTP_MESSAGE *HttpMsg + ); + +/** + Clean up Tcp Tokens while the Tcp transmission error occurs. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpTcpTokenCleanup ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Generate HTTP request string. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Message Pointer to storage containing HTTP message data. + @param[in] Url The URL of a remote host. + + @return Pointer to the created HTTP request string. + @return NULL if any error occured. + +**/ +CHAR8 * +HttpGenRequestString ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_HTTP_MESSAGE *Message, + IN CHAR8 *Url + ); + +/** + The work function of EfiHttpResponse(). + + @param[in] Wrap Pointer to HTTP token's wrap data. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources. + @retval EFI_NOT_READY Can't find a corresponding TxToken. + +**/ +EFI_STATUS +HttpResponseWorker ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +#endif diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.c b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.c new file mode 100644 index 0000000000..8c54874b9f --- /dev/null +++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.c @@ -0,0 +1,126 @@ +/** @file + The DriverEntryPoint and Unload for HttpUtilities driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpUtilitiesDxe.h" + + +/** + Unloads an image. + + @param ImageHandle Handle that identifies the image to be unloaded. + + @retval EFI_SUCCESS The image has been unloaded. + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. + +**/ +EFI_STATUS +EFIAPI +HttpUtilitiesDxeUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + UINTN HandleNum; + EFI_HANDLE *HandleBuffer; + UINT32 Index; + EFI_HTTP_UTILITIES_PROTOCOL *HttpUtilitiesProtocol; + + + HandleBuffer = NULL; + + // + // Locate all the handles with HttpUtilities protocol. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiHttpUtilitiesProtocolGuid, + NULL, + &HandleNum, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < HandleNum; Index++) { + // + // Firstly, find HttpUtilitiesProtocol interface + // + Status = gBS->OpenProtocol ( + HandleBuffer[Index], + &gEfiHttpUtilitiesProtocolGuid, + (VOID **) &HttpUtilitiesProtocol, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Then, uninstall HttpUtilities interface + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + HandleBuffer[Index], + &gEfiHttpUtilitiesProtocolGuid, HttpUtilitiesProtocol, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @param ImageHandle The firmware allocated handle for the UEFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. +**/ +EFI_STATUS +EFIAPI +HttpUtilitiesDxeDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + EFI_HANDLE Handle; + + Handle = NULL; + + // + // Install the HttpUtilities Protocol onto Handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiHttpUtilitiesProtocolGuid, + &mHttpUtilitiesProtocol, + NULL + ); + + return Status; +} + diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.h b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.h new file mode 100644 index 0000000000..70e993546c --- /dev/null +++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.h @@ -0,0 +1,212 @@ +/** @file + The header files of Http Utilities functions for HttpUtilities driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_HTTP_UTILITIES_DXE_H__ +#define __EFI_HTTP_UTILITIES_DXE_H__ + +#include + +// +// Libraries +// +#include +#include +#include +#include +#include +#include + +// +// Consumed Protocols +// +#include +#include + +// +// Protocol instances +// +extern EFI_HTTP_UTILITIES_PROTOCOL mHttpUtilitiesProtocol; + + +/** + Free existing HeaderFields. + + @param[in] HeaderFields Pointer to array of key/value header pairs waitting for free. + @param[in] FieldCount The number of header pairs in HeaderFields. + +**/ +VOID +FreeHeaderFields ( + IN EFI_HTTP_HEADER *HeaderFields, + IN UINTN FieldCount + ); + + +/** + Find required header field in HeaderFields. + + @param[in] HeaderFields Pointer to array of key/value header pairs. + @param[in] FieldCount The number of header pairs. + @param[in] FieldName Pointer to header field's name. + + @return Pointer to the queried header field. + @return NULL if not find this required header field. + +**/ +EFI_HTTP_HEADER * +FindHttpHeader ( + IN EFI_HTTP_HEADER *HeaderFields, + IN UINTN FieldCount, + IN CHAR8 *FieldName + ); + + +/** + Check whether header field called FieldName is in DeleteList. + + @param[in] DeleteList Pointer to array of key/value header pairs. + @param[in] DeleteCount The number of header pairs. + @param[in] FieldName Pointer to header field's name. + + @return TRUE if FieldName is not in DeleteList, that means this header field is valid. + @return FALSE if FieldName is in DeleteList, that means this header field is invalid. + +**/ +BOOLEAN +IsValidHttpHeader ( + IN CHAR8 *DeleteList[], + IN UINTN DeleteCount, + IN CHAR8 *FieldName + ); + + +/** + Set FieldName and FieldValue into specified HttpHeader. + + @param[in] HttpHeader Specified HttpHeader. + @param[in] FieldName FieldName of this HttpHeader. + @param[in] FieldValue FieldValue of this HttpHeader. + + + @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +SetFieldNameAndValue ( + IN EFI_HTTP_HEADER *HttpHeader, + IN CHAR8 *FieldName, + IN CHAR8 *FieldValue + ); + + +/** + Get one key/value header pair from the raw string. + + @param[in] String Pointer to the raw string. + @param[out] FieldName Pointer to header field's name. + @param[out] FieldValue Pointer to header field's value. + + @return Pointer to the next raw string. + @return NULL if no key/value header pair from this raw string. + +**/ +CHAR8 * +GetFieldNameAndValue ( + IN CHAR8 *String, + OUT CHAR8 **FieldName, + OUT CHAR8 **FieldValue + ); + + +/** + Create HTTP header based on a combination of seed header, fields + to delete, and fields to append. + + The Build() function is used to manage the headers portion of an + HTTP message by providing the ability to add, remove, or replace + HTTP headers. + + @param[in] This Pointer to EFI_HTTP_UTILITIES_PROTOCOL instance. + @param[in] SeedMessageSize Size of the initial HTTP header. This can be zero. + @param[in] SeedMessage Initial HTTP header to be used as a base for + building a new HTTP header. If NULL, + SeedMessageSize is ignored. + @param[in] DeleteCount Number of null-terminated HTTP header field names + in DeleteList. + @param[in] DeleteList List of null-terminated HTTP header field names to + remove from SeedMessage. Only the field names are + in this list because the field values are irrelevant + to this operation. + @param[in] AppendCount Number of header fields in AppendList. + @param[in] AppendList List of HTTP headers to populate NewMessage with. + If SeedMessage is not NULL, AppendList will be + appended to the existing list from SeedMessage in + NewMessage. + @param[out] NewMessageSize Pointer to number of header fields in NewMessage. + @param[out] NewMessage Pointer to a new list of HTTP headers based on. + + @retval EFI_SUCCESS Add, remove, and replace operations succeeded. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. +**/ +EFI_STATUS +EFIAPI +HttpUtilitiesBuild ( + IN EFI_HTTP_UTILITIES_PROTOCOL *This, + IN UINTN SeedMessageSize, + IN VOID *SeedMessage, OPTIONAL + IN UINTN DeleteCount, + IN CHAR8 *DeleteList[], OPTIONAL + IN UINTN AppendCount, + IN EFI_HTTP_HEADER *AppendList[], OPTIONAL + OUT UINTN *NewMessageSize, + OUT VOID **NewMessage + ); + + +/** + Parses HTTP header and produces an array of key/value pairs. + + The Parse() function is used to transform data stored in HttpHeader + into a list of fields paired with their corresponding values. + + @param[in] This Pointer to EFI_HTTP_UTILITIES_PROTOCOL instance. + @param[in] HttpMessage Contains raw unformatted HTTP header string. + @param[in] HttpMessageSize Size of HTTP header. + @param[out] HeaderFields Array of key/value header pairs. + @param[out] FieldCount Number of headers in HeaderFields. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been + initialized. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpMessage is NULL. + HeaderFields is NULL. + FieldCount is NULL. +**/ +EFI_STATUS +EFIAPI +HttpUtilitiesParse ( + IN EFI_HTTP_UTILITIES_PROTOCOL *This, + IN CHAR8 *HttpMessage, + IN UINTN HttpMessageSize, + OUT EFI_HTTP_HEADER **HeaderFields, + OUT UINTN *FieldCount + ); + +#endif diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf new file mode 100644 index 0000000000..c101e6f73c --- /dev/null +++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf @@ -0,0 +1,51 @@ +## @file +# Implementation of EFI Http Utilities Protocol interfaces. +# +# Copyright (c) 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HttpUtilitiesDxe + FILE_GUID = 22ea234f-e72a-11e4-91f9-28d2447c4829 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = HttpUtilitiesDxeDriverEntryPoint + UNLOAD_IMAGE = HttpUtilitiesDxeUnload + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[Sources] + HttpUtilitiesDxe.h + HttpUtilitiesDxe.c + HttpUtilitiesImpl.c + HttpUtilitiesProtocol.c + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + DebugLib + +[Protocols] + gEfiHttpUtilitiesProtocolGuid ## PRODUCES + +[Depex] + TRUE + +[Guids] + diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.uni b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.uni new file mode 100644 index 0000000000..83eeab345d Binary files /dev/null and b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.uni differ diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni new file mode 100644 index 0000000000..841ccda167 Binary files /dev/null and b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni differ diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesImpl.c b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesImpl.c new file mode 100644 index 0000000000..adb50af393 --- /dev/null +++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesImpl.c @@ -0,0 +1,279 @@ +/** @file + The functions for HttpUtilities driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpUtilitiesDxe.h" + + +/** + Get the next string, which is distinguished by specified seperator. + + @param[in] String Pointer to the string. + @param[in] Seperator Specified seperator used to distinguish where is the beginning + of next string. + + @return Pointer to the next string. + @return NULL if not find or String is NULL. + +**/ +CHAR8 * +AsciiStrGetNextToken ( + IN CONST CHAR8 *String, + IN CHAR8 Seperator + ) +{ + CONST CHAR8 *Token; + + Token = String; + while (TRUE) { + if (*Token == 0) { + return NULL; + } + if (*Token == Seperator) { + return (CHAR8 *)(Token + 1); + } + Token++; + } +} + + +/** + Free existing HeaderFields. + + @param[in] HeaderFields Pointer to array of key/value header pairs waitting for free. + @param[in] FieldCount The number of header pairs in HeaderFields. + +**/ +VOID +FreeHeaderFields ( + IN EFI_HTTP_HEADER *HeaderFields, + IN UINTN FieldCount + ) +{ + UINTN Index; + + if (HeaderFields != NULL) { + for (Index = 0; Index < FieldCount; Index++) { + if (HeaderFields[Index].FieldName != NULL) { + FreePool (HeaderFields[Index].FieldName); + } + if (HeaderFields[Index].FieldValue != NULL) { + FreePool (HeaderFields[Index].FieldValue); + } + } + + FreePool (HeaderFields); + } +} + + +/** + Find required header field in HeaderFields. + + @param[in] HeaderFields Pointer to array of key/value header pairs. + @param[in] FieldCount The number of header pairs. + @param[in] FieldName Pointer to header field's name. + + @return Pointer to the queried header field. + @return NULL if not find this required header field. + +**/ +EFI_HTTP_HEADER * +FindHttpHeader ( + IN EFI_HTTP_HEADER *HeaderFields, + IN UINTN FieldCount, + IN CHAR8 *FieldName + ) +{ + UINTN Index; + + for (Index = 0; Index < FieldCount; Index++) { + if (AsciiStrCmp (FieldName, HeaderFields[Index].FieldName) == 0) { + // + // Find the required header field. + // + return &HeaderFields[Index]; + } + } + return NULL; +} + + +/** + Check whether header field called FieldName is in DeleteList. + + @param[in] DeleteList Pointer to array of key/value header pairs. + @param[in] DeleteCount The number of header pairs. + @param[in] FieldName Pointer to header field's name. + + @return TRUE if FieldName is not in DeleteList, that means this header field is valid. + @return FALSE if FieldName is in DeleteList, that means this header field is invalid. + +**/ +BOOLEAN +IsValidHttpHeader ( + IN CHAR8 *DeleteList[], + IN UINTN DeleteCount, + IN CHAR8 *FieldName + ) +{ + UINTN Index; + + for (Index = 0; Index < DeleteCount; Index++) { + if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) { + return FALSE; + } + } + + return TRUE; +} + + +/** + Set FieldName and FieldValue into specified HttpHeader. + + @param[in] HttpHeader Specified HttpHeader. + @param[in] FieldName FieldName of this HttpHeader. + @param[in] FieldValue FieldValue of this HttpHeader. + + + @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +SetFieldNameAndValue ( + IN EFI_HTTP_HEADER *HttpHeader, + IN CHAR8 *FieldName, + IN CHAR8 *FieldValue + ) +{ + UINTN FieldNameSize; + UINTN FieldValueSize; + + if (HttpHeader->FieldName != NULL) { + FreePool (HttpHeader->FieldName); + } + if (HttpHeader->FieldValue != NULL) { + FreePool (HttpHeader->FieldValue); + } + + FieldNameSize = AsciiStrSize (FieldName); + HttpHeader->FieldName = AllocateZeroPool (FieldNameSize); + if (HttpHeader->FieldName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize); + HttpHeader->FieldName[FieldNameSize - 1] = 0; + + FieldValueSize = AsciiStrSize (FieldValue); + HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize); + if (HttpHeader->FieldValue == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize); + HttpHeader->FieldValue[FieldValueSize - 1] = 0; + + return EFI_SUCCESS; +} + + +/** + Get one key/value header pair from the raw string. + + @param[in] String Pointer to the raw string. + @param[out] FieldName Pointer to header field's name. + @param[out] FieldValue Pointer to header field's value. + + @return Pointer to the next raw string. + @return NULL if no key/value header pair from this raw string. + +**/ +CHAR8 * +GetFieldNameAndValue ( + IN CHAR8 *String, + OUT CHAR8 **FieldName, + OUT CHAR8 **FieldValue + ) +{ + CHAR8 *FieldNameStr; + CHAR8 *FieldValueStr; + CHAR8 *StrPtr; + + if (String == NULL || FieldName == NULL || FieldValue == NULL) { + return NULL; + } + + *FieldName = NULL; + *FieldValue = NULL; + FieldNameStr = NULL; + FieldValueStr = NULL; + StrPtr = NULL; + + // + // Each header field consists of a name followed by a colon (":") and the field value. + // + FieldNameStr = String; + FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':'); + if (FieldValueStr == NULL) { + return NULL; + } + + // + // Replace ':' with 0 + // + *(FieldValueStr - 1) = 0; + + // + // The field value MAY be preceded by any amount of LWS, though a single SP is preferred. + // + while (TRUE) { + if (*FieldValueStr == ' ' || *FieldValueStr == '\t') { + FieldValueStr ++; + } else if (*FieldValueStr == '\r' && *(FieldValueStr + 1) == '\n' && + (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t')) { + FieldValueStr = FieldValueStr + 3; + } else { + break; + } + } + + // + // Header fields can be extended over multiple lines by preceding each extra + // line with at least one SP or HT. + // + StrPtr = FieldValueStr; + do { + StrPtr = AsciiStrGetNextToken (StrPtr, '\r'); + if (StrPtr == NULL || *StrPtr != '\n') { + return NULL; + } + + StrPtr++; + } while (*StrPtr == ' ' || *StrPtr == '\t'); + + // + // Replace '\r' with 0 + // + *(StrPtr - 2) = 0; + + // + // Get FieldName and FieldValue. + // + *FieldName = FieldNameStr; + *FieldValue = FieldValueStr; + + return StrPtr; +} + diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesProtocol.c b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesProtocol.c new file mode 100644 index 0000000000..a83c9633fc --- /dev/null +++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesProtocol.c @@ -0,0 +1,393 @@ +/** @file + Implementation of EFI_HTTP_PROTOCOL protocol interfaces. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpUtilitiesDxe.h" + +EFI_HTTP_UTILITIES_PROTOCOL mHttpUtilitiesProtocol = { + HttpUtilitiesBuild, + HttpUtilitiesParse +}; + + +/** + Create HTTP header based on a combination of seed header, fields + to delete, and fields to append. + + The Build() function is used to manage the headers portion of an + HTTP message by providing the ability to add, remove, or replace + HTTP headers. + + @param[in] This Pointer to EFI_HTTP_UTILITIES_PROTOCOL instance. + @param[in] SeedMessageSize Size of the initial HTTP header. This can be zero. + @param[in] SeedMessage Initial HTTP header to be used as a base for + building a new HTTP header. If NULL, + SeedMessageSize is ignored. + @param[in] DeleteCount Number of null-terminated HTTP header field names + in DeleteList. + @param[in] DeleteList List of null-terminated HTTP header field names to + remove from SeedMessage. Only the field names are + in this list because the field values are irrelevant + to this operation. + @param[in] AppendCount Number of header fields in AppendList. + @param[in] AppendList List of HTTP headers to populate NewMessage with. + If SeedMessage is not NULL, AppendList will be + appended to the existing list from SeedMessage in + NewMessage. + @param[out] NewMessageSize Pointer to number of header fields in NewMessage. + @param[out] NewMessage Pointer to a new list of HTTP headers based on. + + @retval EFI_SUCCESS Add, remove, and replace operations succeeded. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. +**/ +EFI_STATUS +EFIAPI +HttpUtilitiesBuild ( + IN EFI_HTTP_UTILITIES_PROTOCOL *This, + IN UINTN SeedMessageSize, + IN VOID *SeedMessage, OPTIONAL + IN UINTN DeleteCount, + IN CHAR8 *DeleteList[], OPTIONAL + IN UINTN AppendCount, + IN EFI_HTTP_HEADER *AppendList[], OPTIONAL + OUT UINTN *NewMessageSize, + OUT VOID **NewMessage + ) +{ + EFI_STATUS Status; + EFI_HTTP_HEADER *SeedHeaderFields; + UINTN SeedFieldCount; + UINTN Index; + EFI_HTTP_HEADER *TempHeaderFields; + UINTN TempFieldCount; + EFI_HTTP_HEADER *NewHeaderFields; + UINTN NewFieldCount; + EFI_HTTP_HEADER *HttpHeader; + UINTN StrLength; + UINT8 *NewMessagePtr; + + SeedHeaderFields = NULL; + SeedFieldCount = 0; + TempHeaderFields = NULL; + TempFieldCount = 0; + NewHeaderFields = NULL; + NewFieldCount = 0; + + HttpHeader = NULL; + StrLength = 0; + NewMessagePtr = NULL; + *NewMessageSize = 0; + Status = EFI_SUCCESS; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (SeedMessage != NULL) { + Status = This->Parse ( + This, + SeedMessage, + SeedMessageSize, + &SeedHeaderFields, + &SeedFieldCount + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + // + // Handle DeleteList + // + if (SeedFieldCount != 0 && DeleteCount != 0) { + TempHeaderFields = AllocateZeroPool (SeedFieldCount * sizeof(EFI_HTTP_HEADER)); + if (TempHeaderFields == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + for (Index = 0, TempFieldCount = 0; Index < SeedFieldCount; Index++) { + // + // Check whether each SeedHeaderFields member is in DeleteList + // + if (IsValidHttpHeader( DeleteList, DeleteCount, SeedHeaderFields[Index].FieldName)) { + Status = SetFieldNameAndValue ( + &TempHeaderFields[TempFieldCount], + SeedHeaderFields[Index].FieldName, + SeedHeaderFields[Index].FieldValue + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + TempFieldCount++; + } + } + } else { + TempHeaderFields = SeedHeaderFields; + TempFieldCount = SeedFieldCount; + } + + // + // Handle AppendList + // + NewHeaderFields = AllocateZeroPool ((TempFieldCount + AppendCount) * sizeof (EFI_HTTP_HEADER)); + if (NewHeaderFields == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + for (Index = 0; Index < TempFieldCount; Index++) { + Status = SetFieldNameAndValue ( + &NewHeaderFields[Index], + TempHeaderFields[Index].FieldName, + TempHeaderFields[Index].FieldValue + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + NewFieldCount = TempFieldCount; + + for (Index = 0; Index < AppendCount; Index++) { + HttpHeader = FindHttpHeader (NewHeaderFields, NewFieldCount, AppendList[Index]->FieldName); + if (HttpHeader != NULL) { + Status = SetFieldNameAndValue ( + HttpHeader, + AppendList[Index]->FieldName, + AppendList[Index]->FieldValue + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } else { + Status = SetFieldNameAndValue ( + &NewHeaderFields[NewFieldCount], + AppendList[Index]->FieldName, + AppendList[Index]->FieldValue + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + NewFieldCount++; + } + } + + // + // Calculate NewMessageSize, then build NewMessage + // + for (Index = 0; Index < NewFieldCount; Index++) { + HttpHeader = &NewHeaderFields[Index]; + + StrLength = AsciiStrLen (HttpHeader->FieldName); + *NewMessageSize += StrLength; + + StrLength = sizeof(": ") - 1; + *NewMessageSize += StrLength; + + StrLength = AsciiStrLen (HttpHeader->FieldValue); + *NewMessageSize += StrLength; + + StrLength = sizeof("\r\n") - 1; + *NewMessageSize += StrLength; + } + StrLength = sizeof("\r\n") - 1; + *NewMessageSize += StrLength; + + // + // Final 0 for end flag + // + *NewMessageSize += 1; + + *NewMessage = AllocateZeroPool (*NewMessageSize); + if (*NewMessage == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NewMessagePtr = (UINT8 *)(*NewMessage); + + for (Index = 0; Index < NewFieldCount; Index++) { + HttpHeader = &NewHeaderFields[Index]; + + StrLength = AsciiStrLen (HttpHeader->FieldName); + CopyMem (NewMessagePtr, HttpHeader->FieldName, StrLength); + NewMessagePtr += StrLength; + + StrLength = sizeof(": ") - 1; + CopyMem (NewMessagePtr, ": ", StrLength); + NewMessagePtr += StrLength; + + StrLength = AsciiStrLen (HttpHeader->FieldValue); + CopyMem (NewMessagePtr, HttpHeader->FieldValue, StrLength); + NewMessagePtr += StrLength; + + StrLength = sizeof("\r\n") - 1; + CopyMem (NewMessagePtr, "\r\n", StrLength); + NewMessagePtr += StrLength; + } + StrLength = sizeof("\r\n") - 1; + CopyMem (NewMessagePtr, "\r\n", StrLength); + NewMessagePtr += StrLength; + + *NewMessagePtr = 0; + + ASSERT (*NewMessageSize == (UINTN)NewMessagePtr - (UINTN)(*NewMessage) + 1); + + // + // Free allocated buffer + // +ON_EXIT: + if (SeedHeaderFields != NULL) { + FreeHeaderFields(SeedHeaderFields, SeedFieldCount); + } + + if (TempHeaderFields != NULL) { + FreeHeaderFields(TempHeaderFields, TempFieldCount); + } + + if (NewHeaderFields != NULL) { + FreeHeaderFields(NewHeaderFields, NewFieldCount); + } + + return Status; +} + + +/** + Parses HTTP header and produces an array of key/value pairs. + + The Parse() function is used to transform data stored in HttpHeader + into a list of fields paired with their corresponding values. + + @param[in] This Pointer to EFI_HTTP_UTILITIES_PROTOCOL instance. + @param[in] HttpMessage Contains raw unformatted HTTP header string. + @param[in] HttpMessageSize Size of HTTP header. + @param[out] HeaderFields Array of key/value header pairs. + @param[out] FieldCount Number of headers in HeaderFields. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been + initialized. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpMessage is NULL. + HeaderFields is NULL. + FieldCount is NULL. +**/ +EFI_STATUS +EFIAPI +HttpUtilitiesParse ( + IN EFI_HTTP_UTILITIES_PROTOCOL *This, + IN CHAR8 *HttpMessage, + IN UINTN HttpMessageSize, + OUT EFI_HTTP_HEADER **HeaderFields, + OUT UINTN *FieldCount + ) +{ + EFI_STATUS Status; + CHAR8 *TempHttpMessage; + CHAR8 *Token; + CHAR8 *NextToken; + CHAR8 *FieldName; + CHAR8 *FieldValue; + UINTN Index; + + Status = EFI_SUCCESS; + TempHttpMessage = NULL; + Token = NULL; + NextToken = NULL; + FieldName = NULL; + FieldValue = NULL; + Index = 0; + + if (This == NULL || HttpMessage == NULL || HeaderFields == NULL || FieldCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + TempHttpMessage = AllocateZeroPool (HttpMessageSize); + if (TempHttpMessage == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize); + + // + // Get header number + // + *FieldCount = 0; + Token = TempHttpMessage; + while (TRUE) { + FieldName = NULL; + FieldValue = NULL; + NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue); + Token = NextToken; + if (FieldName == NULL || FieldValue == NULL) { + break; + } + + (*FieldCount)++; + } + + if (*FieldCount == 0) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + // + // Allocate buffer for header + // + *HeaderFields = AllocateZeroPool ((*FieldCount) * sizeof(EFI_HTTP_HEADER)); + if (*HeaderFields == NULL) { + *FieldCount = 0; + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize); + + // + // Set Field and Value to each header + // + Token = TempHttpMessage; + while (Index < *FieldCount) { + FieldName = NULL; + FieldValue = NULL; + NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue); + Token = NextToken; + if (FieldName == NULL || FieldValue == NULL) { + break; + } + + Status = SetFieldNameAndValue (&(*HeaderFields)[Index], FieldName, FieldValue); + if (EFI_ERROR (Status)) { + *FieldCount = 0; + FreeHeaderFields (*HeaderFields, Index); + goto ON_EXIT; + } + + Index++; + } + + // + // Free allocated buffer + // +ON_EXIT: + if (TempHttpMessage != NULL) { + FreePool (TempHttpMessage); + } + + return Status; +} \ No newline at end of file diff --git a/Core/NetworkPkg/IScsiDxe/ComponentName.c b/Core/NetworkPkg/IScsiDxe/ComponentName.c new file mode 100644 index 0000000000..46d89d855c --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/ComponentName.c @@ -0,0 +1,344 @@ +/** @file + UEFI Component Name(2) protocol implementation for iSCSI. + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIScsiComponentName = { + IScsiComponentNameGetDriverName, + IScsiComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIScsiComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IScsiComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IScsiComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIScsiDriverNameTable[] = { + { + "eng;en", + L"iSCSI Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gIScsiControllerNameTable = NULL; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IScsiComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mIScsiDriverNameTable, + DriverName, + (BOOLEAN) (This == &gIScsiComponentName) + ); +} + +/** + Update the component name for the iSCSI instance. + + @param[in] IScsiExtScsiPassThru A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param[in] Ipv6Flag TRUE if IP6 network stack is used. + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + @retval EFI_UNSUPPORTED Can't get the corresponding NIC info from the Controller handle. + +**/ +EFI_STATUS +UpdateName ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *IScsiExtScsiPassThru, + IN BOOLEAN Ipv6Flag + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[80]; + ISCSI_DRIVER_DATA *Private; + UINT8 NicIndex; + + if (IScsiExtScsiPassThru == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (IScsiExtScsiPassThru); + NicIndex = Private->Session->ConfigData->NicIndex; + + UnicodeSPrint ( + HandleName, + sizeof (HandleName), + L"iSCSI (%s, NicIndex=%d)", + Ipv6Flag ? L"IPv6" : L"IPv4", + NicIndex + ); + + if (gIScsiControllerNameTable != NULL) { + FreeUnicodeStringTable (gIScsiControllerNameTable); + gIScsiControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gIScsiComponentName.SupportedLanguages, + &gIScsiControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gIScsiComponentName2.SupportedLanguages, + &gIScsiControllerNameTable, + HandleName, + FALSE + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language, from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IScsiComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + EFI_HANDLE IScsiController; + BOOLEAN Ipv6Flag; + EFI_GUID *IScsiPrivateGuid; + ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier; + + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *IScsiExtScsiPassThru; + + if (ControllerHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Get the handle of the controller we are controling. + // + IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid); + if (IScsiController != NULL) { + IScsiPrivateGuid = &gIScsiV4PrivateGuid; + Ipv6Flag = FALSE; + } else { + IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp6ProtocolGuid); + if (IScsiController != NULL) { + IScsiPrivateGuid = &gIScsiV6PrivateGuid; + Ipv6Flag = TRUE; + } else { + return EFI_UNSUPPORTED; + } + } + + Status = gBS->OpenProtocol ( + IScsiController, + IScsiPrivateGuid, + (VOID **) &IScsiIdentifier, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if(ChildHandle != NULL) { + if(!Ipv6Flag) { + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiTcp4ProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiTcp6ProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiExtScsiPassThruProtocolGuid, + (VOID **)&IScsiExtScsiPassThru, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateName (IScsiExtScsiPassThru, Ipv6Flag); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gIScsiControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gIScsiComponentName) + ); +} diff --git a/Core/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c b/Core/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c new file mode 100644 index 0000000000..f37b403ae5 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c @@ -0,0 +1,67 @@ +/** @file + Implementation for EFI_AUTHENTICATION_INFO_PROTOCOL. Currently it is a + dummy support. + +Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +EFI_AUTHENTICATION_INFO_PROTOCOL gIScsiAuthenticationInfo = { + IScsiGetAuthenticationInfo, + IScsiSetAuthenticationInfo +}; + +/** + Retrieves the authentication information associated with a particular controller handle. + + @param[in] This Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL. + @param[in] ControllerHandle Handle to the Controller. + @param[out] Buffer Pointer to the authentication information. This function is + responsible for allocating the buffer and it is the caller's + responsibility to free buffer when the caller is finished with buffer. + + @retval EFI_DEVICE_ERROR The authentication information could not be + retrieved due to a hardware error. + +**/ +EFI_STATUS +EFIAPI +IScsiGetAuthenticationInfo ( + IN EFI_AUTHENTICATION_INFO_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + OUT VOID **Buffer + ) +{ + return EFI_DEVICE_ERROR; +} + +/** + Set the authentication information for a given controller handle. + + @param[in] This Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL. + @param[in] ControllerHandle Handle to the Controller. + @param[in] Buffer Pointer to the authentication information. + + @retval EFI_UNSUPPORTED If the platform policies do not allow setting of + the authentication information. + +**/ +EFI_STATUS +EFIAPI +IScsiSetAuthenticationInfo ( + IN EFI_AUTHENTICATION_INFO_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN VOID *Buffer + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/Core/NetworkPkg/IScsiDxe/IScsiCHAP.c b/Core/NetworkPkg/IScsiDxe/IScsiCHAP.c new file mode 100644 index 0000000000..18c49b952f --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiCHAP.c @@ -0,0 +1,477 @@ +/** @file + This file is for Challenge-Handshake Authentication Protocol (CHAP) Configuration. + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +/** + Initator calculates its own expected hash value. + + @param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator. + @param[in] ChapSecret iSCSI CHAP secret of the authenticator. + @param[in] SecretLength The length of iSCSI CHAP secret. + @param[in] ChapChallenge The challenge message sent by authenticator. + @param[in] ChallengeLength The length of iSCSI CHAP challenge message. + @param[out] ChapResponse The calculation of the expected hash value. + + @retval EFI_SUCCESS The expected hash value was calculatedly successfully. + @retval EFI_PROTOCOL_ERROR The length of the secret should be at least the + length of the hash value for the hashing algorithm chosen. + @retval EFI_PROTOCOL_ERROR MD5 hash operation fail. + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete MD5. + +**/ +EFI_STATUS +IScsiCHAPCalculateResponse ( + IN UINT32 ChapIdentifier, + IN CHAR8 *ChapSecret, + IN UINT32 SecretLength, + IN UINT8 *ChapChallenge, + IN UINT32 ChallengeLength, + OUT UINT8 *ChapResponse + ) +{ + UINTN Md5ContextSize; + VOID *Md5Ctx; + CHAR8 IdByte[1]; + EFI_STATUS Status; + + if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) { + return EFI_PROTOCOL_ERROR; + } + + Md5ContextSize = Md5GetContextSize (); + Md5Ctx = AllocatePool (Md5ContextSize); + if (Md5Ctx == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = EFI_PROTOCOL_ERROR; + + if (!Md5Init (Md5Ctx)) { + goto Exit; + } + + // + // Hash Identifier - Only calculate 1 byte data (RFC1994) + // + IdByte[0] = (CHAR8) ChapIdentifier; + if (!Md5Update (Md5Ctx, IdByte, 1)) { + goto Exit; + } + + // + // Hash Secret + // + if (!Md5Update (Md5Ctx, ChapSecret, SecretLength)) { + goto Exit; + } + + // + // Hash Challenge received from Target + // + if (!Md5Update (Md5Ctx, ChapChallenge, ChallengeLength)) { + goto Exit; + } + + if (Md5Final (Md5Ctx, ChapResponse)) { + Status = EFI_SUCCESS; + } + +Exit: + FreePool (Md5Ctx); + return Status; +} + +/** + The initator checks the CHAP response replied by target against its own + calculation of the expected hash value. + + @param[in] AuthData iSCSI CHAP authentication data. + @param[in] TargetResponse The response from target. + + @retval EFI_SUCCESS The response from target passed authentication. + @retval EFI_SECURITY_VIOLATION The response from target was not expected value. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiCHAPAuthTarget ( + IN ISCSI_CHAP_AUTH_DATA *AuthData, + IN UINT8 *TargetResponse + ) +{ + EFI_STATUS Status; + UINT32 SecretSize; + UINT8 VerifyRsp[ISCSI_CHAP_RSP_LEN]; + + Status = EFI_SUCCESS; + + SecretSize = (UINT32) AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret); + Status = IScsiCHAPCalculateResponse ( + AuthData->OutIdentifier, + AuthData->AuthConfig->ReverseCHAPSecret, + SecretSize, + AuthData->OutChallenge, + AuthData->OutChallengeLength, + VerifyRsp + ); + + if (CompareMem (VerifyRsp, TargetResponse, ISCSI_CHAP_RSP_LEN) != 0) { + Status = EFI_SECURITY_VIOLATION; + } + + return Status; +} + + +/** + This function checks the received iSCSI Login Response during the security + negotiation stage. + + @param[in] Conn The iSCSI connection. + + @retval EFI_SUCCESS The Login Response passed the CHAP validation. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiCHAPOnRspReceived ( + IN ISCSI_CONNECTION *Conn + ) +{ + EFI_STATUS Status; + ISCSI_SESSION *Session; + ISCSI_CHAP_AUTH_DATA *AuthData; + CHAR8 *Value; + UINT8 *Data; + UINT32 Len; + LIST_ENTRY *KeyValueList; + UINTN Algorithm; + CHAR8 *Identifier; + CHAR8 *Challenge; + CHAR8 *Name; + CHAR8 *Response; + UINT8 TargetRsp[ISCSI_CHAP_RSP_LEN]; + UINT32 RspLen; + UINTN Result; + + ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION); + ASSERT (Conn->RspQue.BufNum != 0); + + Session = Conn->Session; + AuthData = &Session->AuthData.CHAP; + Len = Conn->RspQue.BufSize; + Data = AllocateZeroPool (Len); + if (Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Copy the data in case the data spans over multiple PDUs. + // + NetbufQueCopy (&Conn->RspQue, 0, Len, Data); + + // + // Build the key-value list from the data segment of the Login Response. + // + KeyValueList = IScsiBuildKeyValueList ((CHAR8 *) Data, Len); + if (KeyValueList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = EFI_PROTOCOL_ERROR; + + switch (Conn->AuthStep) { + case ISCSI_AUTH_INITIAL: + // + // The first Login Response. + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG); + if (Value == NULL) { + goto ON_EXIT; + } + + Result = IScsiNetNtoi (Value); + if (Result > 0xFFFF) { + goto ON_EXIT; + } + + Session->TargetPortalGroupTag = (UINT16) Result; + + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD); + if (Value == NULL) { + goto ON_EXIT; + } + // + // Initiator mandates CHAP authentication but target replies without "CHAP", or + // initiator suggets "None" but target replies with some kind of auth method. + // + if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) { + if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) { + goto ON_EXIT; + } + } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) { + if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) { + goto ON_EXIT; + } + } else { + goto ON_EXIT; + } + + // + // Transit to CHAP step one. + // + Conn->AuthStep = ISCSI_CHAP_STEP_ONE; + Status = EFI_SUCCESS; + break; + + case ISCSI_CHAP_STEP_TWO: + // + // The Target replies with CHAP_A= CHAP_I= CHAP_C= + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_ALGORITHM); + if (Value == NULL) { + goto ON_EXIT; + } + + Algorithm = IScsiNetNtoi (Value); + if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) { + // + // Unsupported algorithm is chosen by target. + // + goto ON_EXIT; + } + + Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER); + if (Identifier == NULL) { + goto ON_EXIT; + } + + Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE); + if (Challenge == NULL) { + goto ON_EXIT; + } + // + // Process the CHAP identifier and CHAP Challenge from Target. + // Calculate Response value. + // + Result = IScsiNetNtoi (Identifier); + if (Result > 0xFF) { + goto ON_EXIT; + } + + AuthData->InIdentifier = (UINT32) Result; + AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN; + IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge); + Status = IScsiCHAPCalculateResponse ( + AuthData->InIdentifier, + AuthData->AuthConfig->CHAPSecret, + (UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret), + AuthData->InChallenge, + AuthData->InChallengeLength, + AuthData->CHAPResponse + ); + + // + // Transit to next step. + // + Conn->AuthStep = ISCSI_CHAP_STEP_THREE; + break; + + case ISCSI_CHAP_STEP_THREE: + // + // One way CHAP authentication and the target would like to + // authenticate us. + // + Status = EFI_SUCCESS; + break; + + case ISCSI_CHAP_STEP_FOUR: + ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL); + // + // The forth step, CHAP_N= CHAP_R= is received from Target. + // + Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME); + if (Name == NULL) { + goto ON_EXIT; + } + + Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE); + if (Response == NULL) { + goto ON_EXIT; + } + + RspLen = ISCSI_CHAP_RSP_LEN; + IScsiHexToBin (TargetRsp, &RspLen, Response); + + // + // Check the CHAP Name and Response replied by Target. + // + Status = IScsiCHAPAuthTarget (AuthData, TargetRsp); + break; + + default: + break; + } + +ON_EXIT: + + if (KeyValueList != NULL) { + IScsiFreeKeyValueList (KeyValueList); + } + + FreePool (Data); + + return Status; +} + + +/** + This function fills the CHAP authentication information into the login PDU + during the security negotiation stage in the iSCSI connection login. + + @param[in] Conn The iSCSI connection. + @param[in, out] Pdu The PDU to send out. + + @retval EFI_SUCCESS All check passed and the phase-related CHAP + authentication info is filled into the iSCSI PDU. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred. + +**/ +EFI_STATUS +IScsiCHAPToSendReq ( + IN ISCSI_CONNECTION *Conn, + IN OUT NET_BUF *Pdu + ) +{ + EFI_STATUS Status; + ISCSI_SESSION *Session; + ISCSI_LOGIN_REQUEST *LoginReq; + ISCSI_CHAP_AUTH_DATA *AuthData; + CHAR8 *Value; + CHAR8 ValueStr[256]; + CHAR8 *Response; + UINT32 RspLen; + CHAR8 *Challenge; + UINT32 ChallengeLen; + + ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION); + + Session = Conn->Session; + AuthData = &Session->AuthData.CHAP; + LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, 0); + if (LoginReq == NULL) { + return EFI_PROTOCOL_ERROR; + } + Status = EFI_SUCCESS; + + RspLen = 2 * ISCSI_CHAP_RSP_LEN + 3; + Response = AllocateZeroPool (RspLen); + if (Response == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ChallengeLen = 2 * ISCSI_CHAP_RSP_LEN + 3; + Challenge = AllocateZeroPool (ChallengeLen); + if (Challenge == NULL) { + FreePool (Response); + return EFI_OUT_OF_RESOURCES; + } + + switch (Conn->AuthStep) { + case ISCSI_AUTH_INITIAL: + // + // It's the initial Login Request. Fill in the key=value pairs mandatory + // for the initial Login Request. + // + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, mPrivate->InitiatorName); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal"); + IScsiAddKeyValuePair ( + Pdu, + ISCSI_KEY_TARGET_NAME, + Session->ConfigData->SessionConfigData.TargetName + ); + + if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) { + Value = ISCSI_KEY_VALUE_NONE; + ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT); + } else { + Value = ISCSI_AUTH_METHOD_CHAP; + } + + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value); + + break; + + case ISCSI_CHAP_STEP_ONE: + // + // First step, send the Login Request with CHAP_A= key-value pair. + // + AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr); + + Conn->AuthStep = ISCSI_CHAP_STEP_TWO; + break; + + case ISCSI_CHAP_STEP_THREE: + // + // Third step, send the Login Request with CHAP_N= CHAP_R= or + // CHAP_N= CHAP_R= CHAP_I= CHAP_C= if target authentication is + // required too. + // + // CHAP_N= + // + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig->CHAPName); + // + // CHAP_R= + // + IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response); + + if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) { + // + // CHAP_I= + // + IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1); + AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr); + // + // CHAP_C= + // + IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN); + AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN; + IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge); + + Conn->AuthStep = ISCSI_CHAP_STEP_FOUR; + } + // + // Set the stage transition flag. + // + ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT); + break; + + default: + Status = EFI_PROTOCOL_ERROR; + break; + } + + FreePool (Response); + FreePool (Challenge); + + return Status; +} diff --git a/Core/NetworkPkg/IScsiDxe/IScsiCHAP.h b/Core/NetworkPkg/IScsiDxe/IScsiCHAP.h new file mode 100644 index 0000000000..132461b84d --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiCHAP.h @@ -0,0 +1,108 @@ +/** @file + The header file of CHAP configuration. + +Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_CHAP_H_ +#define _ISCSI_CHAP_H_ + +#define ISCSI_AUTH_METHOD_CHAP "CHAP" + +#define ISCSI_KEY_CHAP_ALGORITHM "CHAP_A" +#define ISCSI_KEY_CHAP_IDENTIFIER "CHAP_I" +#define ISCSI_KEY_CHAP_CHALLENGE "CHAP_C" +#define ISCSI_KEY_CHAP_NAME "CHAP_N" +#define ISCSI_KEY_CHAP_RESPONSE "CHAP_R" + +#define ISCSI_CHAP_ALGORITHM_MD5 5 + +#define ISCSI_CHAP_AUTH_MAX_LEN 1024 +/// +/// MD5_HASHSIZE +/// +#define ISCSI_CHAP_RSP_LEN 16 + +#define ISCSI_CHAP_STEP_ONE 1 +#define ISCSI_CHAP_STEP_TWO 2 +#define ISCSI_CHAP_STEP_THREE 3 +#define ISCSI_CHAP_STEP_FOUR 4 + + +#pragma pack(1) + +typedef struct _ISCSI_CHAP_AUTH_CONFIG_NVDATA { + UINT8 CHAPType; + CHAR8 CHAPName[ISCSI_CHAP_NAME_STORAGE]; + CHAR8 CHAPSecret[ISCSI_CHAP_SECRET_STORAGE]; + CHAR8 ReverseCHAPName[ISCSI_CHAP_NAME_STORAGE]; + CHAR8 ReverseCHAPSecret[ISCSI_CHAP_SECRET_STORAGE]; +} ISCSI_CHAP_AUTH_CONFIG_NVDATA; + +#pragma pack() + +/// +/// ISCSI CHAP Authentication Data +/// +typedef struct _ISCSI_CHAP_AUTH_DATA { + ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig; + UINT32 InIdentifier; + UINT8 InChallenge[ISCSI_CHAP_AUTH_MAX_LEN]; + UINT32 InChallengeLength; + // + // Calculated CHAP Response (CHAP_R) value. + // + UINT8 CHAPResponse[ISCSI_CHAP_RSP_LEN]; + + // + // Auth-data to be sent out for mutual authentication. + // + UINT32 OutIdentifier; + UINT8 OutChallenge[ISCSI_CHAP_AUTH_MAX_LEN]; + UINT32 OutChallengeLength; +} ISCSI_CHAP_AUTH_DATA; + +/** + This function checks the received iSCSI Login Response during the security + negotiation stage. + + @param[in] Conn The iSCSI connection. + + @retval EFI_SUCCESS The Login Response passed the CHAP validation. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiCHAPOnRspReceived ( + IN ISCSI_CONNECTION *Conn + ); +/** + This function fills the CHAP authentication information into the login PDU + during the security negotiation stage in the iSCSI connection login. + + @param[in] Conn The iSCSI connection. + @param[in, out] Pdu The PDU to send out. + + @retval EFI_SUCCESS All check passed and the phase-related CHAP + authentication info is filled into the iSCSI PDU. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred. + +**/ +EFI_STATUS +IScsiCHAPToSendReq ( + IN ISCSI_CONNECTION *Conn, + IN OUT NET_BUF *Pdu + ); + +#endif diff --git a/Core/NetworkPkg/IScsiDxe/IScsiConfig.c b/Core/NetworkPkg/IScsiDxe/IScsiConfig.c new file mode 100644 index 0000000000..a523e10306 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiConfig.c @@ -0,0 +1,2661 @@ +/** @file + Helper functions for configuring or getting the parameters relating to iSCSI. + +Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +CHAR16 mVendorStorageName[] = L"ISCSI_CONFIG_IFR_NVDATA"; +BOOLEAN mIScsiDeviceListUpdated = FALSE; +UINTN mNumberOfIScsiDevices = 0; +ISCSI_FORM_CALLBACK_INFO *mCallbackInfo = NULL; + +HII_VENDOR_DEVICE_PATH mIScsiHiiVendorDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + ISCSI_CONFIG_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + + +/** + Convert the IP address into a dotted string. + + @param[in] Ip The IP address. + @param[in] Ipv6Flag Indicates whether the IP address is version 4 or version 6. + @param[out] Str The formatted IP string. + +**/ +VOID +IScsiIpToStr ( + IN EFI_IP_ADDRESS *Ip, + IN BOOLEAN Ipv6Flag, + OUT CHAR16 *Str + ) +{ + EFI_IPv4_ADDRESS *Ip4; + EFI_IPv6_ADDRESS *Ip6; + UINTN Index; + BOOLEAN Short; + UINTN Number; + CHAR16 FormatString[8]; + + if (!Ipv6Flag) { + Ip4 = &Ip->v4; + + UnicodeSPrint ( + Str, + (UINTN) 2 * IP4_STR_MAX_SIZE, + L"%d.%d.%d.%d", + (UINTN) Ip4->Addr[0], + (UINTN) Ip4->Addr[1], + (UINTN) Ip4->Addr[2], + (UINTN) Ip4->Addr[3] + ); + + return ; + } + + Ip6 = &Ip->v6; + Short = FALSE; + + for (Index = 0; Index < 15; Index = Index + 2) { + if (!Short && + Index % 2 == 0 && + Ip6->Addr[Index] == 0 && + Ip6->Addr[Index + 1] == 0 + ) { + // + // Deal with the case of ::. + // + if (Index == 0) { + *Str = L':'; + *(Str + 1) = L':'; + Str = Str + 2; + } else { + *Str = L':'; + Str = Str + 1; + } + + while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) { + Index = Index + 2; + } + + Short = TRUE; + + if (Index == 16) { + // + // :: is at the end of the address. + // + *Str = L'\0'; + break; + } + } + + ASSERT (Index < 15); + + if (Ip6->Addr[Index] == 0) { + Number = UnicodeSPrint (Str, 2 * IP_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]); + } else { + if (Ip6->Addr[Index + 1] < 0x10) { + CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:")); + } else { + CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:")); + } + + Number = UnicodeSPrint ( + Str, + 2 * IP_STR_MAX_SIZE, + (CONST CHAR16 *) FormatString, + (UINTN) Ip6->Addr[Index], + (UINTN) Ip6->Addr[Index + 1] + ); + } + + Str = Str + Number; + + if (Index + 2 == 16) { + *Str = L'\0'; + if (*(Str - 1) == L':') { + *(Str - 1) = L'\0'; + } + } + } +} + +/** + Check whether the input IP address is valid. + + @param[in] Ip The IP address. + @param[in] IpMode Indicates iSCSI running on IP4 or IP6 stack. + + @retval TRUE The input IP address is valid. + @retval FALSE Otherwise + +**/ +BOOLEAN +IpIsUnicast ( + IN EFI_IP_ADDRESS *Ip, + IN UINT8 IpMode + ) +{ + if (IpMode == IP_MODE_IP4) { + return NetIp4IsUnicast (NTOHL (Ip->Addr[0]), 0); + } else if (IpMode == IP_MODE_IP6) { + return NetIp6IsValidUnicast (&Ip->v6); + } else { + DEBUG ((DEBUG_ERROR, "IpMode %d is invalid when configuring the iSCSI target IP!\n", IpMode)); + return FALSE; + } +} + +/** + Parse IsId in string format and convert it to binary. + + @param[in] String The buffer of the string to be parsed. + @param[in, out] IsId The buffer to store IsId. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +IScsiParseIsIdFromString ( + IN CONST CHAR16 *String, + IN OUT UINT8 *IsId + ) +{ + UINT8 Index; + CHAR16 *IsIdStr; + CHAR16 TempStr[3]; + UINTN NodeVal; + CHAR16 PortString[ISCSI_NAME_IFR_MAX_SIZE]; + EFI_INPUT_KEY Key; + + if ((String == NULL) || (IsId == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IsIdStr = (CHAR16 *) String; + + if (StrLen (IsIdStr) != 6) { + UnicodeSPrint ( + PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"Error! Input is incorrect, please input 6 hex numbers!\n" + ); + + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + PortString, + NULL + ); + + return EFI_INVALID_PARAMETER; + } + + for (Index = 3; Index < 6; Index++) { + CopyMem (TempStr, IsIdStr, sizeof (TempStr)); + TempStr[2] = L'\0'; + + // + // Convert the string to IsId. StrHexToUintn stops at the first character + // that is not a valid hex character, '\0' here. + // + NodeVal = StrHexToUintn (TempStr); + + IsId[Index] = (UINT8) NodeVal; + + IsIdStr = IsIdStr + 2; + } + + return EFI_SUCCESS; +} + +/** + Convert IsId from binary to string format. + + @param[out] String The buffer to store the converted string. + @param[in] IsId The buffer to store IsId. + + @retval EFI_SUCCESS The string converted successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +IScsiConvertIsIdToString ( + OUT CHAR16 *String, + IN UINT8 *IsId + ) +{ + UINT8 Index; + UINTN Number; + + if ((String == NULL) || (IsId == NULL)) { + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < 6; Index++) { + if (IsId[Index] <= 0xF) { + Number = UnicodeSPrint ( + String, + 2 * ISID_CONFIGURABLE_STORAGE, + L"0%X", + (UINTN) IsId[Index] + ); + } else { + Number = UnicodeSPrint ( + String, + 2 * ISID_CONFIGURABLE_STORAGE, + L"%X", + (UINTN) IsId[Index] + ); + + } + + String = String + Number; + } + + *String = L'\0'; + + return EFI_SUCCESS; +} + +/** + Get the attempt config data from global structure by the ConfigIndex. + + @param[in] AttemptConfigIndex The unique index indicates the attempt. + + @return Pointer to the attempt config data. + @retval NULL The attempt configuration data cannot be found. + +**/ +ISCSI_ATTEMPT_CONFIG_NVDATA * +IScsiConfigGetAttemptByConfigIndex ( + IN UINT8 AttemptConfigIndex + ) +{ + LIST_ENTRY *Entry; + ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt; + + NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) { + Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + if (Attempt->AttemptConfigIndex == AttemptConfigIndex) { + return Attempt; + } + } + + return NULL; +} + + +/** + Get the existing attempt config data from global structure by the NicIndex. + + @param[in] NewAttempt The created new attempt + @param[in] IScsiMode The IScsi Mode of the new attempt, Enabled or + Enabled for MPIO. + + @return Pointer to the existing attempt config data which + has the same NICIndex as the new created attempt. + @retval NULL The attempt with NicIndex does not exist. + +**/ +ISCSI_ATTEMPT_CONFIG_NVDATA * +IScsiConfigGetAttemptByNic ( + IN ISCSI_ATTEMPT_CONFIG_NVDATA *NewAttempt, + IN UINT8 IScsiMode + ) +{ + LIST_ENTRY *Entry; + ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt; + + NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) { + Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + if (Attempt != NewAttempt && Attempt->NicIndex == NewAttempt->NicIndex && + Attempt->SessionConfigData.Enabled == IScsiMode) { + return Attempt; + } + } + + return NULL; +} + + +/** + Convert the iSCSI configuration data into the IFR data. + + @param[in] Attempt The iSCSI attempt config data. + @param[in, out] IfrNvData The IFR nv data. + +**/ +VOID +IScsiConvertAttemptConfigDataToIfrNvData ( + IN ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt, + IN OUT ISCSI_CONFIG_IFR_NVDATA *IfrNvData + ) +{ + ISCSI_SESSION_CONFIG_NVDATA *SessionConfigData; + ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData; + EFI_IP_ADDRESS Ip; + + // + // Normal session configuration parameters. + // + SessionConfigData = &Attempt->SessionConfigData; + IfrNvData->Enabled = SessionConfigData->Enabled; + IfrNvData->IpMode = SessionConfigData->IpMode; + + IfrNvData->InitiatorInfoFromDhcp = SessionConfigData->InitiatorInfoFromDhcp; + IfrNvData->TargetInfoFromDhcp = SessionConfigData->TargetInfoFromDhcp; + IfrNvData->TargetPort = SessionConfigData->TargetPort; + + if (IfrNvData->IpMode == IP_MODE_IP4) { + CopyMem (&Ip.v4, &SessionConfigData->LocalIp, sizeof (EFI_IPv4_ADDRESS)); + IScsiIpToStr (&Ip, FALSE, IfrNvData->LocalIp); + CopyMem (&Ip.v4, &SessionConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + IScsiIpToStr (&Ip, FALSE, IfrNvData->SubnetMask); + CopyMem (&Ip.v4, &SessionConfigData->Gateway, sizeof (EFI_IPv4_ADDRESS)); + IScsiIpToStr (&Ip, FALSE, IfrNvData->Gateway); + CopyMem (&Ip.v4, &SessionConfigData->TargetIp, sizeof (EFI_IPv4_ADDRESS)); + IScsiIpToStr (&Ip, FALSE, IfrNvData->TargetIp); + } else if (IfrNvData->IpMode == IP_MODE_IP6) { + ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp)); + IP6_COPY_ADDRESS (&Ip.v6, &SessionConfigData->TargetIp); + IScsiIpToStr (&Ip, TRUE, IfrNvData->TargetIp); + } + + AsciiStrToUnicodeStrS ( + SessionConfigData->TargetName, + IfrNvData->TargetName, + sizeof (IfrNvData->TargetName) / sizeof (IfrNvData->TargetName[0]) + ); + IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->BootLun); + IScsiConvertIsIdToString (IfrNvData->IsId, SessionConfigData->IsId); + + IfrNvData->ConnectRetryCount = SessionConfigData->ConnectRetryCount; + IfrNvData->ConnectTimeout = SessionConfigData->ConnectTimeout; + + // + // Authentication parameters. + // + IfrNvData->AuthenticationType = Attempt->AuthenticationType; + + if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) { + AuthConfigData = &Attempt->AuthConfigData.CHAP; + IfrNvData->CHAPType = AuthConfigData->CHAPType; + AsciiStrToUnicodeStrS ( + AuthConfigData->CHAPName, + IfrNvData->CHAPName, + sizeof (IfrNvData->CHAPName) / sizeof (IfrNvData->CHAPName[0]) + ); + AsciiStrToUnicodeStrS ( + AuthConfigData->CHAPSecret, + IfrNvData->CHAPSecret, + sizeof (IfrNvData->CHAPSecret) / sizeof (IfrNvData->CHAPSecret[0]) + ); + AsciiStrToUnicodeStrS ( + AuthConfigData->ReverseCHAPName, + IfrNvData->ReverseCHAPName, + sizeof (IfrNvData->ReverseCHAPName) / sizeof (IfrNvData->ReverseCHAPName[0]) + ); + AsciiStrToUnicodeStrS ( + AuthConfigData->ReverseCHAPSecret, + IfrNvData->ReverseCHAPSecret, + sizeof (IfrNvData->ReverseCHAPSecret) / sizeof (IfrNvData->ReverseCHAPSecret[0]) + ); + } + + // + // Other parameters. + // + AsciiStrToUnicodeStrS ( + Attempt->AttemptName, + IfrNvData->AttemptName, + sizeof (IfrNvData->AttemptName) / sizeof (IfrNvData->AttemptName[0]) + ); +} + +/** + Convert the IFR data to iSCSI configuration data. + + @param[in] IfrNvData Point to ISCSI_CONFIG_IFR_NVDATA. + @param[in, out] Attempt The iSCSI attempt config data. + + @retval EFI_INVALID_PARAMETER Any input or configured parameter is invalid. + @retval EFI_NOT_FOUND Cannot find the corresponding variable. + @retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources. + @retval EFI_ABORTED The operation is aborted. + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +EFI_STATUS +IScsiConvertIfrNvDataToAttemptConfigData ( + IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt + ) +{ + EFI_IP_ADDRESS HostIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS Gateway; + CHAR16 *MacString; + CHAR16 *AttemptName1; + CHAR16 *AttemptName2; + ISCSI_ATTEMPT_CONFIG_NVDATA *ExistAttempt; + ISCSI_ATTEMPT_CONFIG_NVDATA *SameNicAttempt; + CHAR16 IScsiMode[64]; + CHAR16 IpMode[64]; + ISCSI_NIC_INFO *NicInfo; + EFI_INPUT_KEY Key; + UINT8 *AttemptConfigOrder; + UINTN AttemptConfigOrderSize; + UINT8 *AttemptOrderTmp; + UINTN TotalNumber; + EFI_STATUS Status; + + if (IfrNvData == NULL || Attempt == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Update those fields which don't have INTERACTIVE attribute. + // + Attempt->SessionConfigData.ConnectRetryCount = IfrNvData->ConnectRetryCount; + Attempt->SessionConfigData.ConnectTimeout = IfrNvData->ConnectTimeout; + Attempt->SessionConfigData.IpMode = IfrNvData->IpMode; + + if (IfrNvData->IpMode < IP_MODE_AUTOCONFIG) { + Attempt->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp; + Attempt->SessionConfigData.TargetPort = IfrNvData->TargetPort; + + if (Attempt->SessionConfigData.TargetPort == 0) { + Attempt->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT; + } + + Attempt->SessionConfigData.TargetInfoFromDhcp = IfrNvData->TargetInfoFromDhcp; + } + + Attempt->AuthenticationType = IfrNvData->AuthenticationType; + + if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) { + Attempt->AuthConfigData.CHAP.CHAPType = IfrNvData->CHAPType; + } + + // + // Only do full parameter validation if iSCSI is enabled on this device. + // + if (IfrNvData->Enabled != ISCSI_DISABLED) { + if (Attempt->SessionConfigData.ConnectTimeout < CONNECT_MIN_TIMEOUT) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Connection Establishing Timeout is less than minimum value 100ms.", + NULL + ); + + return EFI_INVALID_PARAMETER; + } + + // + // Validate the address configuration of the Initiator if DHCP isn't + // deployed. + // + if (!Attempt->SessionConfigData.InitiatorInfoFromDhcp) { + CopyMem (&HostIp.v4, &Attempt->SessionConfigData.LocalIp, sizeof (HostIp.v4)); + CopyMem (&SubnetMask.v4, &Attempt->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4)); + CopyMem (&Gateway.v4, &Attempt->SessionConfigData.Gateway, sizeof (Gateway.v4)); + + if ((Gateway.Addr[0] != 0)) { + if (SubnetMask.Addr[0] == 0) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Gateway address is set but subnet mask is zero.", + NULL + ); + + return EFI_INVALID_PARAMETER; + } else if (!IP4_NET_EQUAL (HostIp.Addr[0], Gateway.Addr[0], SubnetMask.Addr[0])) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Local IP and Gateway are not in the same subnet.", + NULL + ); + + return EFI_INVALID_PARAMETER; + } + } + } + // + // Validate target configuration if DHCP isn't deployed. + // + if (!Attempt->SessionConfigData.TargetInfoFromDhcp && Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) { + if (!IpIsUnicast (&Attempt->SessionConfigData.TargetIp, IfrNvData->IpMode)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Target IP is invalid!", + NULL + ); + return EFI_INVALID_PARAMETER; + } + + // + // Validate iSCSI target name configuration again: + // The format of iSCSI target name is already verified in IScsiFormCallback() when + // user input the name; here we only check the case user does not input the name. + // + if (Attempt->SessionConfigData.TargetName[0] == '\0') { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"iSCSI target name is NULL!", + NULL + ); + return EFI_INVALID_PARAMETER; + } + } + + + // + // Validate the authentication info. + // + if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) { + if ((IfrNvData->CHAPName[0] == '\0') || (IfrNvData->CHAPSecret[0] == '\0')) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"CHAP Name or CHAP Secret is invalid!", + NULL + ); + + return EFI_INVALID_PARAMETER; + } + + if ((IfrNvData->CHAPType == ISCSI_CHAP_MUTUAL) && + ((IfrNvData->ReverseCHAPName[0] == '\0') || (IfrNvData->ReverseCHAPSecret[0] == '\0')) + ) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Reverse CHAP Name or Reverse CHAP Secret is invalid!", + NULL + ); + return EFI_INVALID_PARAMETER; + } + } + + // + // Check whether this attempt uses NIC which is already used by existing attempt. + // + SameNicAttempt = IScsiConfigGetAttemptByNic (Attempt, IfrNvData->Enabled); + if (SameNicAttempt != NULL) { + AttemptName1 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_MAX_SIZE * sizeof (CHAR16)); + if (AttemptName1 == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AttemptName2 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_MAX_SIZE * sizeof (CHAR16)); + if (AttemptName2 == NULL) { + FreePool (AttemptName1); + return EFI_OUT_OF_RESOURCES; + } + + AsciiStrToUnicodeStrS (Attempt->AttemptName, AttemptName1, ATTEMPT_NAME_MAX_SIZE); + if (StrLen (AttemptName1) > ATTEMPT_NAME_SIZE) { + CopyMem (&AttemptName1[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16)); + } + + AsciiStrToUnicodeStrS (SameNicAttempt->AttemptName, AttemptName2, ATTEMPT_NAME_MAX_SIZE); + if (StrLen (AttemptName2) > ATTEMPT_NAME_SIZE) { + CopyMem (&AttemptName2[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16)); + } + + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"Warning! Attempt \"%s\" uses same NIC as Attempt \"%s\".", + AttemptName1, + AttemptName2 + ); + + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + mPrivate->PortString, + NULL + ); + + FreePool (AttemptName1); + FreePool (AttemptName2); + } + } + + // + // Update the iSCSI Mode data and record it in attempt help info. + // + Attempt->SessionConfigData.Enabled = IfrNvData->Enabled; + if (IfrNvData->Enabled == ISCSI_DISABLED) { + UnicodeSPrint (IScsiMode, 64, L"Disabled"); + } else if (IfrNvData->Enabled == ISCSI_ENABLED) { + UnicodeSPrint (IScsiMode, 64, L"Enabled"); + } else if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) { + UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO"); + } + + if (IfrNvData->IpMode == IP_MODE_IP4) { + UnicodeSPrint (IpMode, 64, L"IP4"); + } else if (IfrNvData->IpMode == IP_MODE_IP6) { + UnicodeSPrint (IpMode, 64, L"IP6"); + } else if (IfrNvData->IpMode == IP_MODE_AUTOCONFIG) { + UnicodeSPrint (IpMode, 64, L"Autoconfigure"); + } + + NicInfo = IScsiGetNicInfoByIndex (Attempt->NicIndex); + if (NicInfo == NULL) { + return EFI_NOT_FOUND; + } + + MacString = (CHAR16 *) AllocateZeroPool (ISCSI_MAX_MAC_STRING_LEN * sizeof (CHAR16)); + if (MacString == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AsciiStrToUnicodeStrS (Attempt->MacString, MacString, ISCSI_MAX_MAC_STRING_LEN); + + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s", + MacString, + NicInfo->BusNumber, + NicInfo->DeviceNumber, + NicInfo->FunctionNumber, + IScsiMode, + IpMode + ); + + Attempt->AttemptTitleHelpToken = HiiSetString ( + mCallbackInfo->RegisteredHandle, + Attempt->AttemptTitleHelpToken, + mPrivate->PortString, + NULL + ); + if (Attempt->AttemptTitleHelpToken == 0) { + FreePool (MacString); + return EFI_OUT_OF_RESOURCES; + } + + // + // Check whether this attempt is an existing one. + // + ExistAttempt = IScsiConfigGetAttemptByConfigIndex (Attempt->AttemptConfigIndex); + if (ExistAttempt != NULL) { + ASSERT (ExistAttempt == Attempt); + + if (IfrNvData->Enabled == ISCSI_DISABLED && + Attempt->SessionConfigData.Enabled != ISCSI_DISABLED) { + + // + // User updates the Attempt from "Enabled"/"Enabled for MPIO" to "Disabled". + // + if (Attempt->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) { + if (mPrivate->MpioCount < 1) { + return EFI_ABORTED; + } + + if (--mPrivate->MpioCount == 0) { + mPrivate->EnableMpio = FALSE; + } + } else if (Attempt->SessionConfigData.Enabled == ISCSI_ENABLED) { + if (mPrivate->SinglePathCount < 1) { + return EFI_ABORTED; + } + mPrivate->SinglePathCount--; + } + + } else if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO && + Attempt->SessionConfigData.Enabled == ISCSI_ENABLED) { + // + // User updates the Attempt from "Enabled" to "Enabled for MPIO". + // + if (mPrivate->SinglePathCount < 1) { + return EFI_ABORTED; + } + + mPrivate->EnableMpio = TRUE; + mPrivate->MpioCount++; + mPrivate->SinglePathCount--; + + } else if (IfrNvData->Enabled == ISCSI_ENABLED && + Attempt->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) { + // + // User updates the Attempt from "Enabled for MPIO" to "Enabled". + // + if (mPrivate->MpioCount < 1) { + return EFI_ABORTED; + } + + if (--mPrivate->MpioCount == 0) { + mPrivate->EnableMpio = FALSE; + } + mPrivate->SinglePathCount++; + + } else if (IfrNvData->Enabled != ISCSI_DISABLED && + Attempt->SessionConfigData.Enabled == ISCSI_DISABLED) { + // + // User updates the Attempt from "Disabled" to "Enabled"/"Enabled for MPIO". + // + if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) { + mPrivate->EnableMpio = TRUE; + mPrivate->MpioCount++; + + } else if (IfrNvData->Enabled == ISCSI_ENABLED) { + mPrivate->SinglePathCount++; + } + } + + } else if (ExistAttempt == NULL) { + // + // When a new attempt is created, pointer of the attempt is saved to + // mPrivate->NewAttempt, and also saved to mCallbackInfo->Current in + // IScsiConfigProcessDefault. If input Attempt does not match any existing + // attempt, it should be a new created attempt. Save it to system now. + // + ASSERT (Attempt == mPrivate->NewAttempt); + + // + // Save current order number for this attempt. + // + AttemptConfigOrder = IScsiGetVariableAndSize ( + L"AttemptOrder", + &gIScsiConfigGuid, + &AttemptConfigOrderSize + ); + + TotalNumber = AttemptConfigOrderSize / sizeof (UINT8); + TotalNumber++; + + // + // Append the new created attempt order to the end. + // + AttemptOrderTmp = AllocateZeroPool (TotalNumber * sizeof (UINT8)); + if (AttemptOrderTmp == NULL) { + if (AttemptConfigOrder != NULL) { + FreePool (AttemptConfigOrder); + } + return EFI_OUT_OF_RESOURCES; + } + + if (AttemptConfigOrder != NULL) { + CopyMem (AttemptOrderTmp, AttemptConfigOrder, AttemptConfigOrderSize); + FreePool (AttemptConfigOrder); + } + + AttemptOrderTmp[TotalNumber - 1] = Attempt->AttemptConfigIndex; + AttemptConfigOrder = AttemptOrderTmp; + AttemptConfigOrderSize = TotalNumber * sizeof (UINT8); + + Status = gRT->SetVariable ( + L"AttemptOrder", + &gIScsiConfigGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + AttemptConfigOrderSize, + AttemptConfigOrder + ); + FreePool (AttemptConfigOrder); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Insert new created attempt to array. + // + InsertTailList (&mPrivate->AttemptConfigs, &Attempt->Link); + mPrivate->AttemptCount++; + // + // Reset mPrivate->NewAttempt to NULL, which indicates none attempt is created + // but not saved now. + // + mPrivate->NewAttempt = NULL; + + if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) { + // + // This new Attempt is enabled for MPIO; enable the multipath mode. + // + mPrivate->EnableMpio = TRUE; + mPrivate->MpioCount++; + } else if (IfrNvData->Enabled == ISCSI_ENABLED) { + mPrivate->SinglePathCount++; + } + + IScsiConfigUpdateAttempt (); + } + + // + // Record the user configuration information in NVR. + // + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"%s%d", + MacString, + (UINTN) Attempt->AttemptConfigIndex + ); + + FreePool (MacString); + + return gRT->SetVariable ( + mPrivate->PortString, + &gEfiIScsiInitiatorNameProtocolGuid, + ISCSI_CONFIG_VAR_ATTR, + sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA), + Attempt + ); +} + +/** + Create Hii Extend Label OpCode as the start opcode and end opcode. It is + a help function. + + @param[in] StartLabelNumber The number of start label. + @param[out] StartOpCodeHandle Points to the start opcode handle. + @param[out] StartLabel Points to the created start opcode. + @param[out] EndOpCodeHandle Points to the end opcode handle. + @param[out] EndLabel Points to the created end opcode. + + @retval EFI_OUT_OF_RESOURCES Do not have sufficient resource to finish this + operation. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +EFI_STATUS +IScsiCreateOpCode ( + IN UINT16 StartLabelNumber, + OUT VOID **StartOpCodeHandle, + OUT EFI_IFR_GUID_LABEL **StartLabel, + OUT VOID **EndOpCodeHandle, + OUT EFI_IFR_GUID_LABEL **EndLabel + ) +{ + EFI_STATUS Status; + EFI_IFR_GUID_LABEL *InternalStartLabel; + EFI_IFR_GUID_LABEL *InternalEndLabel; + + if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) { + return EFI_INVALID_PARAMETER; + } + + *StartOpCodeHandle = NULL; + *EndOpCodeHandle = NULL; + Status = EFI_OUT_OF_RESOURCES; + + // + // Initialize the container for dynamic opcodes. + // + *StartOpCodeHandle = HiiAllocateOpCodeHandle (); + if (*StartOpCodeHandle == NULL) { + return Status; + } + + *EndOpCodeHandle = HiiAllocateOpCodeHandle (); + if (*EndOpCodeHandle == NULL) { + goto Exit; + } + + // + // Create Hii Extend Label OpCode as the start opcode. + // + InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + *StartOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + if (InternalStartLabel == NULL) { + goto Exit; + } + + InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + InternalStartLabel->Number = StartLabelNumber; + + // + // Create Hii Extend Label OpCode as the end opcode. + // + InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + *EndOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + if (InternalEndLabel == NULL) { + goto Exit; + } + + InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + InternalEndLabel->Number = LABEL_END; + + *StartLabel = InternalStartLabel; + *EndLabel = InternalEndLabel; + + return EFI_SUCCESS; + +Exit: + + if (*StartOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (*StartOpCodeHandle); + } + + if (*EndOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (*EndOpCodeHandle); + } + + return Status; +} + +/** + Callback function when user presses "Add an Attempt". + + @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this + operation. + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +EFI_STATUS +IScsiConfigAddAttempt ( + VOID + ) +{ + LIST_ENTRY *Entry; + ISCSI_NIC_INFO *NicInfo; + EFI_STRING_ID PortTitleToken; + EFI_STRING_ID PortTitleHelpToken; + CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN]; + EFI_STATUS Status; + VOID *StartOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *EndLabel; + + Status = IScsiCreateOpCode ( + MAC_ENTRY_LABEL, + &StartOpCodeHandle, + &StartLabel, + &EndOpCodeHandle, + &EndLabel + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Ask user to select a MAC for this attempt. + // + NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) { + NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link); + IScsiMacAddrToStr ( + &NicInfo->PermanentAddress, + NicInfo->HwAddressSize, + NicInfo->VlanId, + MacString + ); + + UnicodeSPrint (mPrivate->PortString, (UINTN) ISCSI_NAME_IFR_MAX_SIZE, L"MAC %s", MacString); + PortTitleToken = HiiSetString ( + mCallbackInfo->RegisteredHandle, + 0, + mPrivate->PortString, + NULL + ); + if (PortTitleToken == 0) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"PFA: Bus %d | Dev %d | Func %d", + NicInfo->BusNumber, + NicInfo->DeviceNumber, + NicInfo->FunctionNumber + ); + PortTitleHelpToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, mPrivate->PortString, NULL); + if (PortTitleHelpToken == 0) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + HiiCreateGotoOpCode ( + StartOpCodeHandle, // Container for dynamic created opcodes + FORMID_ATTEMPT_FORM, + PortTitleToken, + PortTitleHelpToken, + EFI_IFR_FLAG_CALLBACK, // Question flag + (UINT16) (KEY_MAC_ENTRY_BASE + NicInfo->NicIndex) + ); + } + + Status = HiiUpdateForm ( + mCallbackInfo->RegisteredHandle, // HII handle + &gIScsiConfigGuid, // Formset GUID + FORMID_MAC_FORM, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + EndOpCodeHandle // Replace data + ); + +Exit: + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); + + return Status; +} + + +/** + Update the MAIN form to display the configured attempts. + +**/ +VOID +IScsiConfigUpdateAttempt ( + VOID + ) +{ + CHAR16 AttemptName[ATTEMPT_NAME_MAX_SIZE]; + LIST_ENTRY *Entry; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + VOID *StartOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *EndLabel; + EFI_STATUS Status; + + Status = IScsiCreateOpCode ( + ATTEMPT_ENTRY_LABEL, + &StartOpCodeHandle, + &StartLabel, + &EndOpCodeHandle, + &EndLabel + ); + if (EFI_ERROR (Status)) { + return ; + } + + NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) { + AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + + AsciiStrToUnicodeStrS (AttemptConfigData->AttemptName, AttemptName, sizeof (AttemptName) / sizeof (AttemptName[0])); + UnicodeSPrint (mPrivate->PortString, (UINTN) 128, L"Attempt %s", AttemptName); + AttemptConfigData->AttemptTitleToken = HiiSetString ( + mCallbackInfo->RegisteredHandle, + 0, + mPrivate->PortString, + NULL + ); + if (AttemptConfigData->AttemptTitleToken == 0) { + return ; + } + + HiiCreateGotoOpCode ( + StartOpCodeHandle, // Container for dynamic created opcodes + FORMID_ATTEMPT_FORM, // Form ID + AttemptConfigData->AttemptTitleToken, // Prompt text + AttemptConfigData->AttemptTitleHelpToken, // Help text + EFI_IFR_FLAG_CALLBACK, // Question flag + (UINT16) (KEY_ATTEMPT_ENTRY_BASE + AttemptConfigData->AttemptConfigIndex) // Question ID + ); + } + + HiiUpdateForm ( + mCallbackInfo->RegisteredHandle, // HII handle + &gIScsiConfigGuid, // Formset GUID + FORMID_MAIN_FORM, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + EndOpCodeHandle // Replace data + ); + + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); +} + + +/** + Callback function when user presses "Commit Changes and Exit" in Delete Attempts. + + @param[in] IfrNvData The IFR NV data. + + @retval EFI_NOT_FOUND Cannot find the corresponding variable. + @retval EFI_SUCCESS The operation is completed successfully. + @retval EFI_ABOTRED This operation is aborted cause of error + configuration. + @retval EFI_OUT_OF_RESOURCES Fail to finish the operation due to lack of + resources. + +**/ +EFI_STATUS +IScsiConfigDeleteAttempts ( + IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN NewIndex; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + UINT8 *AttemptConfigOrder; + UINTN AttemptConfigOrderSize; + UINT8 *AttemptNewOrder; + UINT32 Attribute; + UINTN Total; + UINTN NewTotal; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN]; + + AttemptConfigOrder = IScsiGetVariableAndSize ( + L"AttemptOrder", + &gIScsiConfigGuid, + &AttemptConfigOrderSize + ); + if ((AttemptConfigOrder == NULL) || (AttemptConfigOrderSize == 0)) { + return EFI_NOT_FOUND; + } + + AttemptNewOrder = AllocateZeroPool (AttemptConfigOrderSize); + if (AttemptNewOrder == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + Total = AttemptConfigOrderSize / sizeof (UINT8); + NewTotal = Total; + Index = 0; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) { + if (IfrNvData->DeleteAttemptList[Index] == 0) { + Index++; + continue; + } + + // + // Delete the attempt. + // + + AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + if (AttemptConfigData == NULL) { + Status = EFI_NOT_FOUND; + goto Error; + } + + // + // Remove this attempt from UI configured attempt list. + // + RemoveEntryList (&AttemptConfigData->Link); + mPrivate->AttemptCount--; + + if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) { + if (mPrivate->MpioCount < 1) { + Status = EFI_ABORTED; + goto Error; + } + + // + // No more attempt is enabled for MPIO. Transit the iSCSI mode to single path. + // + if (--mPrivate->MpioCount == 0) { + mPrivate->EnableMpio = FALSE; + } + } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) { + if (mPrivate->SinglePathCount < 1) { + Status = EFI_ABORTED; + goto Error; + } + + mPrivate->SinglePathCount--; + } + + AsciiStrToUnicodeStrS (AttemptConfigData->MacString, MacString, sizeof (MacString) / sizeof (MacString[0])); + + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) 128, + L"%s%d", + MacString, + (UINTN) AttemptConfigData->AttemptConfigIndex + ); + + gRT->SetVariable ( + mPrivate->PortString, + &gEfiIScsiInitiatorNameProtocolGuid, + 0, + 0, + NULL + ); + + // + // Mark the attempt order in NVR to be deleted - 0. + // + for (NewIndex = 0; NewIndex < Total; NewIndex++) { + if (AttemptConfigOrder[NewIndex] == AttemptConfigData->AttemptConfigIndex) { + AttemptConfigOrder[NewIndex] = 0; + break; + } + } + + NewTotal--; + FreePool (AttemptConfigData); + + // + // Check next Attempt. + // + Index++; + } + + // + // Construct AttemptNewOrder. + // + for (Index = 0, NewIndex = 0; Index < Total; Index++) { + if (AttemptConfigOrder[Index] != 0) { + AttemptNewOrder[NewIndex] = AttemptConfigOrder[Index]; + NewIndex++; + } + } + + Attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE; + + // + // Update AttemptOrder in NVR. + // + Status = gRT->SetVariable ( + L"AttemptOrder", + &gIScsiConfigGuid, + Attribute, + NewTotal * sizeof (UINT8), + AttemptNewOrder + ); + +Error: + if (AttemptConfigOrder != NULL) { + FreePool (AttemptConfigOrder); + } + + if (AttemptNewOrder != NULL) { + FreePool (AttemptNewOrder); + } + + return Status; +} + + +/** + Callback function when user presses "Delete Attempts". + + @param[in] IfrNvData The IFR nv data. + + @retval EFI_INVALID_PARAMETER Any parameter is invalid. + @retval EFI_BUFFER_TOO_SMALL The buffer in UpdateData is too small. + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +EFI_STATUS +IScsiConfigDisplayDeleteAttempts ( + IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData + ) +{ + + UINT8 *AttemptConfigOrder; + UINTN AttemptConfigOrderSize; + LIST_ENTRY *Entry; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + UINT8 Index; + VOID *StartOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *EndLabel; + EFI_STATUS Status; + + Status = IScsiCreateOpCode ( + DELETE_ENTRY_LABEL, + &StartOpCodeHandle, + &StartLabel, + &EndOpCodeHandle, + &EndLabel + ); + if (EFI_ERROR (Status)) { + return Status; + } + + AttemptConfigOrder = IScsiGetVariableAndSize ( + L"AttemptOrder", + &gIScsiConfigGuid, + &AttemptConfigOrderSize + ); + if (AttemptConfigOrder != NULL) { + // + // Create the check box opcode to be deleted. + // + Index = 0; + + NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) { + AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + IfrNvData->DeleteAttemptList[Index] = 0x00; + + HiiCreateCheckBoxOpCode( + StartOpCodeHandle, + (EFI_QUESTION_ID) (ATTEMPT_DEL_QUESTION_ID + Index), + CONFIGURATION_VARSTORE_ID, + (UINT16) (ATTEMPT_DEL_VAR_OFFSET + Index), + AttemptConfigData->AttemptTitleToken, + AttemptConfigData->AttemptTitleHelpToken, + 0, + 0, + NULL + ); + + Index++; + + if (Index == ISCSI_MAX_ATTEMPTS_NUM) { + break; + } + } + + FreePool (AttemptConfigOrder); + } + + Status = HiiUpdateForm ( + mCallbackInfo->RegisteredHandle, // HII handle + &gIScsiConfigGuid, // Formset GUID + FORMID_DELETE_FORM, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + EndOpCodeHandle // Replace data + ); + + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); + + return Status; +} + + +/** + Callback function when user presses "Change Attempt Order". + + @retval EFI_INVALID_PARAMETER Any parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this + operation. + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +EFI_STATUS +IScsiConfigDisplayOrderAttempts ( + VOID + ) +{ + EFI_STATUS Status; + UINT8 Index; + LIST_ENTRY *Entry; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + VOID *StartOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *EndLabel; + VOID *OptionsOpCodeHandle; + + Status = IScsiCreateOpCode ( + ORDER_ENTRY_LABEL, + &StartOpCodeHandle, + &StartLabel, + &EndOpCodeHandle, + &EndLabel + ); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (StartOpCodeHandle != NULL); + + OptionsOpCodeHandle = NULL; + + // + // If no attempt to be ordered, update the original form and exit. + // + if (mPrivate->AttemptCount == 0) { + goto Exit; + } + + // + // Create Option OpCode. + // + OptionsOpCodeHandle = HiiAllocateOpCodeHandle (); + if (OptionsOpCodeHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + Index = 0; + + NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) { + AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + HiiCreateOneOfOptionOpCode ( + OptionsOpCodeHandle, + AttemptConfigData->AttemptTitleToken, + 0, + EFI_IFR_NUMERIC_SIZE_1, + AttemptConfigData->AttemptConfigIndex + ); + Index++; + } + + ASSERT (Index == mPrivate->AttemptCount); + + HiiCreateOrderedListOpCode ( + StartOpCodeHandle, // Container for dynamic created opcodes + DYNAMIC_ORDERED_LIST_QUESTION_ID, // Question ID + CONFIGURATION_VARSTORE_ID, // VarStore ID + DYNAMIC_ORDERED_LIST_VAR_OFFSET, // Offset in Buffer Storage + STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY), // Question prompt text + STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY), // Question help text + 0, // Question flag + EFI_IFR_UNIQUE_SET, // Ordered list flag, e.g. EFI_IFR_UNIQUE_SET + EFI_IFR_NUMERIC_SIZE_1, // Data type of Question value + ISCSI_MAX_ATTEMPTS_NUM, // Maximum container + OptionsOpCodeHandle, // Option Opcode list + NULL // Default Opcode is NULL + ); + +Exit: + Status = HiiUpdateForm ( + mCallbackInfo->RegisteredHandle, // HII handle + &gIScsiConfigGuid, // Formset GUID + FORMID_ORDER_FORM, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + EndOpCodeHandle // Replace data + ); + +Error: + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); + if (OptionsOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (OptionsOpCodeHandle); + } + + return Status; +} + + +/** + Callback function when user presses "Commit Changes and Exit" in Change Attempt Order. + + @param[in] IfrNvData The IFR nv data. + + @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this + operation. + @retval EFI_NOT_FOUND Cannot find the corresponding variable. + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +EFI_STATUS +IScsiConfigOrderAttempts ( + IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN Indexj; + UINT8 AttemptConfigIndex; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + UINT8 *AttemptConfigOrder; + UINT8 *AttemptConfigOrderTmp; + UINTN AttemptConfigOrderSize; + + AttemptConfigOrder = IScsiGetVariableAndSize ( + L"AttemptOrder", + &gIScsiConfigGuid, + &AttemptConfigOrderSize + ); + if (AttemptConfigOrder == NULL) { + return EFI_NOT_FOUND; + } + + AttemptConfigOrderTmp = AllocateZeroPool (AttemptConfigOrderSize); + if (AttemptConfigOrderTmp == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + for (Index = 0; Index < ISCSI_MAX_ATTEMPTS_NUM; Index++) { + // + // The real content ends with 0. + // + if (IfrNvData->DynamicOrderedList[Index] == 0) { + break; + } + + AttemptConfigIndex = IfrNvData->DynamicOrderedList[Index]; + AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (AttemptConfigIndex); + if (AttemptConfigData == NULL) { + Status = EFI_NOT_FOUND; + goto Exit; + } + + // + // Reorder the Attempt List. + // + RemoveEntryList (&AttemptConfigData->Link); + InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link); + + AttemptConfigOrderTmp[Index] = AttemptConfigIndex; + + // + // Mark it to be deleted - 0. + // + for (Indexj = 0; Indexj < AttemptConfigOrderSize / sizeof (UINT8); Indexj++) { + if (AttemptConfigOrder[Indexj] == AttemptConfigIndex) { + AttemptConfigOrder[Indexj] = 0; + break; + } + } + } + + // + // Adjust the attempt order in NVR. + // + for (; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) { + for (Indexj = 0; Indexj < AttemptConfigOrderSize / sizeof (UINT8); Indexj++) { + if (AttemptConfigOrder[Indexj] != 0) { + AttemptConfigOrderTmp[Index] = AttemptConfigOrder[Indexj]; + AttemptConfigOrder[Indexj] = 0; + continue; + } + } + } + + Status = gRT->SetVariable ( + L"AttemptOrder", + &gIScsiConfigGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + AttemptConfigOrderSize, + AttemptConfigOrderTmp + ); + +Exit: + if (AttemptConfigOrderTmp != NULL) { + FreePool (AttemptConfigOrderTmp); + } + + FreePool (AttemptConfigOrder); + return Status; +} + + +/** + Callback function when a user presses "Attempt *" or when a user selects a NIC to + create the new attempt. + + @param[in] KeyValue A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. + @param[in] IfrNvData The IFR nv data. + + @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this + operation. + @retval EFI_NOT_FOUND Cannot find the corresponding variable. + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +EFI_STATUS +IScsiConfigProcessDefault ( + IN EFI_QUESTION_ID KeyValue, + IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData + ) +{ + BOOLEAN NewAttempt; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + ISCSI_SESSION_CONFIG_NVDATA *ConfigData; + UINT8 CurrentAttemptConfigIndex; + ISCSI_NIC_INFO *NicInfo; + UINT8 NicIndex; + CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN]; + UINT8 *AttemptConfigOrder; + UINTN AttemptConfigOrderSize; + UINTN TotalNumber; + UINTN Index; + + // + // Is User creating a new attempt? + // + NewAttempt = FALSE; + + if ((KeyValue >= KEY_MAC_ENTRY_BASE) && + (KeyValue <= (UINT16) (mPrivate->MaxNic + KEY_MAC_ENTRY_BASE))) { + // + // User has pressed "Add an Attempt" and then selects a NIC. + // + NewAttempt = TRUE; + } else if ((KeyValue >= KEY_ATTEMPT_ENTRY_BASE) && + (KeyValue < (ISCSI_MAX_ATTEMPTS_NUM + KEY_ATTEMPT_ENTRY_BASE))) { + + // + // User has pressed "Attempt *". + // + NewAttempt = FALSE; + } else { + // + // Don't process anything. + // + return EFI_SUCCESS; + } + + // + // Free any attempt that is previously created but not saved to system. + // + if (mPrivate->NewAttempt != NULL) { + FreePool (mPrivate->NewAttempt); + mPrivate->NewAttempt = NULL; + } + + if (NewAttempt) { + // + // Determine which NIC user has selected for the new created attempt. + // + NicIndex = (UINT8) (KeyValue - KEY_MAC_ENTRY_BASE); + NicInfo = IScsiGetNicInfoByIndex (NicIndex); + if (NicInfo == NULL) { + return EFI_NOT_FOUND; + } + + // + // Create new attempt. + // + + AttemptConfigData = AllocateZeroPool (sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA)); + if (AttemptConfigData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ConfigData = &AttemptConfigData->SessionConfigData; + ConfigData->TargetPort = ISCSI_WELL_KNOWN_PORT; + ConfigData->ConnectTimeout = CONNECT_DEFAULT_TIMEOUT; + ConfigData->ConnectRetryCount = CONNECT_MIN_RETRY; + + AttemptConfigData->AuthenticationType = ISCSI_AUTH_TYPE_CHAP; + AttemptConfigData->AuthConfigData.CHAP.CHAPType = ISCSI_CHAP_UNI; + + // + // Get current order number for this attempt. + // + AttemptConfigOrder = IScsiGetVariableAndSize ( + L"AttemptOrder", + &gIScsiConfigGuid, + &AttemptConfigOrderSize + ); + + TotalNumber = AttemptConfigOrderSize / sizeof (UINT8); + + if (AttemptConfigOrder == NULL) { + CurrentAttemptConfigIndex = 1; + } else { + // + // Get the max attempt config index. + // + CurrentAttemptConfigIndex = AttemptConfigOrder[0]; + for (Index = 1; Index < TotalNumber; Index++) { + if (CurrentAttemptConfigIndex < AttemptConfigOrder[Index]) { + CurrentAttemptConfigIndex = AttemptConfigOrder[Index]; + } + } + + CurrentAttemptConfigIndex++; + } + + TotalNumber++; + + // + // Record the mapping between attempt order and attempt's configdata. + // + AttemptConfigData->AttemptConfigIndex = CurrentAttemptConfigIndex; + + if (AttemptConfigOrder != NULL) { + FreePool (AttemptConfigOrder); + } + + // + // Record the MAC info in Config Data. + // + IScsiMacAddrToStr ( + &NicInfo->PermanentAddress, + NicInfo->HwAddressSize, + NicInfo->VlanId, + MacString + ); + + UnicodeStrToAsciiStrS (MacString, AttemptConfigData->MacString, sizeof (AttemptConfigData->MacString)); + AttemptConfigData->NicIndex = NicIndex; + + // + // Generate OUI-format ISID based on MAC address. + // + CopyMem (AttemptConfigData->SessionConfigData.IsId, &NicInfo->PermanentAddress, 6); + AttemptConfigData->SessionConfigData.IsId[0] = + (UINT8) (AttemptConfigData->SessionConfigData.IsId[0] & 0x3F); + + // + // Add the help info for the new attempt. + // + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"MAC: %s, PFA: Bus %d | Dev %d | Func %d", + MacString, + NicInfo->BusNumber, + NicInfo->DeviceNumber, + NicInfo->FunctionNumber + ); + + AttemptConfigData->AttemptTitleHelpToken = HiiSetString ( + mCallbackInfo->RegisteredHandle, + 0, + mPrivate->PortString, + NULL + ); + if (AttemptConfigData->AttemptTitleHelpToken == 0) { + FreePool (AttemptConfigData); + return EFI_INVALID_PARAMETER; + } + + // + // Set the attempt name to default. + // + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) 128, + L"%d", + (UINTN) AttemptConfigData->AttemptConfigIndex + ); + UnicodeStrToAsciiStrS (mPrivate->PortString, AttemptConfigData->AttemptName, sizeof (AttemptConfigData->AttemptName)); + + // + // Save the created Attempt temporarily. If user does not save the attempt + // by press 'KEY_SAVE_ATTEMPT_CONFIG' later, iSCSI driver would know that + // and free resources. + // + mPrivate->NewAttempt = (VOID *) AttemptConfigData; + + } else { + // + // Determine which Attempt user has selected to configure. + // Get the attempt configuration data. + // + CurrentAttemptConfigIndex = (UINT8) (KeyValue - KEY_ATTEMPT_ENTRY_BASE); + + AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (CurrentAttemptConfigIndex); + if (AttemptConfigData == NULL) { + DEBUG ((DEBUG_ERROR, "Corresponding configuration data can not be retrieved!\n")); + return EFI_NOT_FOUND; + } + } + + // + // Clear the old IFR data to avoid sharing it with other attempts. + // + if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) { + ZeroMem (IfrNvData->CHAPName, sizeof (IfrNvData->CHAPName)); + ZeroMem (IfrNvData->CHAPSecret, sizeof (IfrNvData->CHAPSecret)); + ZeroMem (IfrNvData->ReverseCHAPName, sizeof (IfrNvData->ReverseCHAPName)); + ZeroMem (IfrNvData->ReverseCHAPSecret, sizeof (IfrNvData->ReverseCHAPSecret)); + } + + IScsiConvertAttemptConfigDataToIfrNvData (AttemptConfigData, IfrNvData); + + // + // Update current attempt to be a new created attempt or an existing attempt. + // + mCallbackInfo->Current = AttemptConfigData; + + return EFI_SUCCESS; +} + + +/** + + This function allows the caller to request the current + configuration for one or more named elements. The resulting + string is in format. Also, any and all alternative + configuration strings shall be appended to the end of the + current configuration string. If they are, they must appear + after the current configuration. They must contain the same + routing (GUID, NAME, PATH) as the current configuration string. + They must have an additional description indicating the type of + alternative configuration the string represents, + "ALTCFG=". That (when + converted from Hex UNICODE to binary) is a reference to a + string in the associated string pack. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + + @param[in] Request A null-terminated Unicode string in + format. Note that this + includes the routing information as well as + the configurable name / value pairs. It is + invalid for this string to be in + format. + + @param[out] Progress On return, points to a character in the + Request string. Points to the string's null + terminator if request was successful. Points + to the most recent "&" before the first + failing name / value pair (or the beginning + of the string if the failure is in the first + name / value pair) if the request was not successful. + + @param[out] Results A null-terminated Unicode string in + format which has all values + filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_SUCCESS The Results string is filled with the + values corresponding to all requested + names. + + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + + @retval EFI_INVALID_PARAMETER For example, passing in a NULL + for the Request parameter + would result in this type of + error. In this case, the + Progress parameter would be + set to NULL. + + @retval EFI_NOT_FOUND Routing data doesn't match any + known driver. Progress set to the + first character in the routing header. + Note: There is no requirement that the + driver validate the routing data. It + must skip the in order to + process the names. + + @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set + to most recent "&" before the + error or the beginning of the + string. + + @retval EFI_INVALID_PARAMETER Unknown name. Progress points + to the & before the name in + question. + +**/ +EFI_STATUS +EFIAPI +IScsiFormExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + EFI_STATUS Status; + CHAR8 *InitiatorName; + UINTN BufferSize; + ISCSI_CONFIG_IFR_NVDATA *IfrNvData; + ISCSI_FORM_CALLBACK_INFO *Private; + EFI_STRING ConfigRequestHdr; + EFI_STRING ConfigRequest; + BOOLEAN AllocatedRequest; + UINTN Size; + + if (This == NULL || Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Request; + if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gIScsiConfigGuid, mVendorStorageName)) { + return EFI_NOT_FOUND; + } + + ConfigRequestHdr = NULL; + ConfigRequest = NULL; + AllocatedRequest = FALSE; + Size = 0; + + Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This); + IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA)); + if (IfrNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (Private->Current != NULL) { + IScsiConvertAttemptConfigDataToIfrNvData (Private->Current, IfrNvData); + } + + BufferSize = ISCSI_NAME_MAX_SIZE; + InitiatorName = (CHAR8 *) AllocateZeroPool (BufferSize); + if (InitiatorName == NULL) { + FreePool (IfrNvData); + return EFI_OUT_OF_RESOURCES; + } + + Status = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName); + if (EFI_ERROR (Status)) { + IfrNvData->InitiatorName[0] = L'\0'; + } else { + AsciiStrToUnicodeStrS ( + InitiatorName, + IfrNvData->InitiatorName, + sizeof (IfrNvData->InitiatorName) / sizeof (IfrNvData->InitiatorName[0]) + ); + } + + // + // Convert buffer data to by helper function BlockToConfig(). + // + BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA); + ConfigRequest = Request; + if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { + // + // Request has no request element, construct full request string. + // Allocate and fill a buffer large enough to hold the template + // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator + // + ConfigRequestHdr = HiiConstructConfigHdr (&gIScsiConfigGuid, mVendorStorageName, Private->DriverHandle); + Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + ASSERT (ConfigRequest != NULL); + AllocatedRequest = TRUE; + UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); + FreePool (ConfigRequestHdr); + } + + Status = gHiiConfigRouting->BlockToConfig ( + gHiiConfigRouting, + ConfigRequest, + (UINT8 *) IfrNvData, + BufferSize, + Results, + Progress + ); + FreePool (IfrNvData); + FreePool (InitiatorName); + + // + // Free the allocated config request string. + // + if (AllocatedRequest) { + FreePool (ConfigRequest); + ConfigRequest = NULL; + } + // + // Set Progress string to the original request string. + // + if (Request == NULL) { + *Progress = NULL; + } else if (StrStr (Request, L"OFFSET") == NULL) { + *Progress = Request + StrLen (Request); + } + + return Status; +} + + +/** + + This function applies changes in a driver's configuration. + Input is a Configuration, which has the routing data for this + driver followed by name / value configuration pairs. The driver + must apply those pairs to its configurable storage. If the + driver's configuration is stored in a linear block of data + and the driver's name / value pairs are in + format, it may use the ConfigToBlock helper function (above) to + simplify the job. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + + @param[in] Configuration A null-terminated Unicode string in + format. + + @param[out] Progress A pointer to a string filled in with the + offset of the most recent '&' before the + first failing name / value pair (or the + beginning of the string if the failure + is in the first name / value pair) or + the terminating NULL if all was + successful. + + @retval EFI_SUCCESS The results have been distributed or are + awaiting distribution. + + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + + @retval EFI_INVALID_PARAMETERS Passing in a NULL for the + Results parameter would result + in this type of error. + + @retval EFI_NOT_FOUND Target for the specified routing data + was not found. + +**/ +EFI_STATUS +EFIAPI +IScsiFormRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + if (This == NULL || Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check routing data in . + // Note: if only one Storage is used, then this checking could be skipped. + // + if (!HiiIsConfigHdrMatch (Configuration, &gIScsiConfigGuid, mVendorStorageName)) { + *Progress = Configuration; + return EFI_NOT_FOUND; + } + + *Progress = Configuration + StrLen (Configuration); + return EFI_SUCCESS; +} + + +/** + + This function is called to provide results data to the driver. + This data consists of a unique key that is used to identify + which data is either being passed back or being asked for. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Action Specifies the type of action taken by the browser. + @param[in] QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. The format of the data tends to + vary based on the opcode that generated the callback. + @param[in] Type The type of value for the question. + @param[in, out] Value A pointer to the data being sent to the original + exporting driver. + @param[out] ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. +**/ +EFI_STATUS +EFIAPI +IScsiFormCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN OUT EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + ISCSI_FORM_CALLBACK_INFO *Private; + UINTN BufferSize; + CHAR8 *IScsiName; + CHAR8 IpString[IP_STR_MAX_SIZE]; + CHAR8 LunString[ISCSI_LUN_STR_MAX_LEN]; + UINT64 Lun; + EFI_IP_ADDRESS HostIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS Gateway; + ISCSI_CONFIG_IFR_NVDATA *IfrNvData; + ISCSI_CONFIG_IFR_NVDATA OldIfrNvData; + EFI_STATUS Status; + CHAR16 AttemptName[ATTEMPT_NAME_SIZE + 4]; + EFI_INPUT_KEY Key; + + if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) || (Action == EFI_BROWSER_ACTION_FORM_CLOSE)) { + // + // Do nothing for UEFI OPEN/CLOSE Action + // + return EFI_SUCCESS; + } + + if ((Action != EFI_BROWSER_ACTION_CHANGING) && (Action != EFI_BROWSER_ACTION_CHANGED)) { + // + // All other type return unsupported. + // + return EFI_UNSUPPORTED; + } + + if ((Value == NULL) || (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This); + + // + // Retrieve uncommitted data from Browser + // + + BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA); + IfrNvData = AllocateZeroPool (BufferSize); + if (IfrNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IScsiName = (CHAR8 *) AllocateZeroPool (ISCSI_NAME_MAX_SIZE); + if (IScsiName == NULL) { + FreePool (IfrNvData); + return EFI_OUT_OF_RESOURCES; + } + + Status = EFI_SUCCESS; + + ZeroMem (&OldIfrNvData, BufferSize); + + HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData); + + CopyMem (&OldIfrNvData, IfrNvData, BufferSize); + + if (Action == EFI_BROWSER_ACTION_CHANGING) { + switch (QuestionId) { + case KEY_ADD_ATTEMPT: + // + // Check whether iSCSI initiator name is configured already. + // + mPrivate->InitiatorNameLength = ISCSI_NAME_MAX_SIZE; + Status = gIScsiInitiatorName.Get ( + &gIScsiInitiatorName, + &mPrivate->InitiatorNameLength, + mPrivate->InitiatorName + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Error: please configure iSCSI initiator name first!", + NULL + ); + break; + } + + Status = IScsiConfigAddAttempt (); + break; + + case KEY_DELETE_ATTEMPT: + CopyMem ( + OldIfrNvData.DeleteAttemptList, + IfrNvData->DeleteAttemptList, + sizeof (IfrNvData->DeleteAttemptList) + ); + Status = IScsiConfigDisplayDeleteAttempts (IfrNvData); + break; + + case KEY_ORDER_ATTEMPT_CONFIG: + // + // Order the attempt according to user input. + // + CopyMem ( + OldIfrNvData.DynamicOrderedList, + IfrNvData->DynamicOrderedList, + sizeof (IfrNvData->DynamicOrderedList) + ); + IScsiConfigDisplayOrderAttempts (); + break; + + default: + Status = IScsiConfigProcessDefault (QuestionId, IfrNvData); + break; + } + } else if (Action == EFI_BROWSER_ACTION_CHANGED) { + switch (QuestionId) { + case KEY_INITIATOR_NAME: + UnicodeStrToAsciiStrS (IfrNvData->InitiatorName, IScsiName, ISCSI_NAME_MAX_SIZE); + BufferSize = AsciiStrSize (IScsiName); + + Status = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, IScsiName); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid iSCSI Name!", + NULL + ); + } + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + break; + case KEY_ATTEMPT_NAME: + if (StrLen (IfrNvData->AttemptName) > ATTEMPT_NAME_SIZE) { + CopyMem (AttemptName, IfrNvData->AttemptName, ATTEMPT_NAME_SIZE * sizeof (CHAR16)); + CopyMem (&AttemptName[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16)); + } else { + CopyMem ( + AttemptName, + IfrNvData->AttemptName, + (StrLen (IfrNvData->AttemptName) + 1) * sizeof (CHAR16) + ); + } + + UnicodeStrToAsciiStrS (IfrNvData->AttemptName, Private->Current->AttemptName, sizeof (Private->Current->AttemptName)); + + IScsiConfigUpdateAttempt (); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + break; + + case KEY_SAVE_ATTEMPT_CONFIG: + Status = IScsiConvertIfrNvDataToAttemptConfigData (IfrNvData, Private->Current); + if (EFI_ERROR (Status)) { + break; + } + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY; + break; + + case KEY_SAVE_ORDER_CHANGES: + // + // Sync the Attempt Order to NVR. + // + Status = IScsiConfigOrderAttempts (IfrNvData); + if (EFI_ERROR (Status)) { + break; + } + + IScsiConfigUpdateAttempt (); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT; + break; + + case KEY_IGNORE_ORDER_CHANGES: + CopyMem ( + IfrNvData->DynamicOrderedList, + OldIfrNvData.DynamicOrderedList, + sizeof (IfrNvData->DynamicOrderedList) + ); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; + break; + + case KEY_SAVE_DELETE_ATTEMPT: + // + // Delete the Attempt Order from NVR + // + Status = IScsiConfigDeleteAttempts (IfrNvData); + if (EFI_ERROR (Status)) { + break; + } + + IScsiConfigUpdateAttempt (); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT; + break; + + case KEY_IGNORE_DELETE_ATTEMPT: + CopyMem ( + IfrNvData->DeleteAttemptList, + OldIfrNvData.DeleteAttemptList, + sizeof (IfrNvData->DeleteAttemptList) + ); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; + break; + + case KEY_IP_MODE: + switch (Value->u8) { + case IP_MODE_IP6: + ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp)); + IScsiIpToStr (&Private->Current->SessionConfigData.TargetIp, TRUE, IfrNvData->TargetIp); + Private->Current->AutoConfigureMode = 0; + break; + + case IP_MODE_IP4: + ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp)); + IScsiIpToStr (&Private->Current->SessionConfigData.TargetIp, FALSE, IfrNvData->TargetIp); + Private->Current->AutoConfigureMode = 0; + + break; + } + + break; + + case KEY_LOCAL_IP: + Status = NetLibStrToIp4 (IfrNvData->LocalIp, &HostIp.v4); + if (EFI_ERROR (Status) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid IP address!", + NULL + ); + + Status = EFI_INVALID_PARAMETER; + } else { + CopyMem (&Private->Current->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4)); + } + + break; + + case KEY_SUBNET_MASK: + Status = NetLibStrToIp4 (IfrNvData->SubnetMask, &SubnetMask.v4); + if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (IScsiGetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Subnet Mask!", + NULL + ); + + Status = EFI_INVALID_PARAMETER; + } else { + CopyMem (&Private->Current->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4)); + } + + break; + + case KEY_GATE_WAY: + Status = NetLibStrToIp4 (IfrNvData->Gateway, &Gateway.v4); + if (EFI_ERROR (Status) || ((Gateway.Addr[0] != 0) && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), 0))) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Gateway!", + NULL + ); + Status = EFI_INVALID_PARAMETER; + } else { + CopyMem (&Private->Current->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4)); + } + + break; + + case KEY_TARGET_IP: + UnicodeStrToAsciiStrS (IfrNvData->TargetIp, IpString, sizeof (IpString)); + Status = IScsiAsciiStrToIp (IpString, IfrNvData->IpMode, &HostIp); + if (EFI_ERROR (Status) || !IpIsUnicast (&HostIp, IfrNvData->IpMode)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid IP address!", + NULL + ); + Status = EFI_INVALID_PARAMETER; + } else { + CopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp, sizeof (HostIp)); + } + + break; + + case KEY_TARGET_NAME: + UnicodeStrToAsciiStrS (IfrNvData->TargetName, IScsiName, ISCSI_NAME_MAX_SIZE); + Status = IScsiNormalizeName (IScsiName, AsciiStrLen (IScsiName)); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid iSCSI Name!", + NULL + ); + } else { + AsciiStrCpyS (Private->Current->SessionConfigData.TargetName, ISCSI_NAME_MAX_SIZE, IScsiName); + } + + break; + + case KEY_DHCP_ENABLE: + if (IfrNvData->InitiatorInfoFromDhcp == 0) { + IfrNvData->TargetInfoFromDhcp = 0; + } + + break; + + case KEY_BOOT_LUN: + UnicodeStrToAsciiStrS (IfrNvData->BootLun, LunString, sizeof (LunString)); + Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid LUN string!", + NULL + ); + } else { + CopyMem (Private->Current->SessionConfigData.BootLun, &Lun, sizeof (Lun)); + } + + break; + + case KEY_AUTH_TYPE: + switch (Value->u8) { + case ISCSI_AUTH_TYPE_CHAP: + IfrNvData->CHAPType = ISCSI_CHAP_UNI; + break; + default: + break; + } + + break; + + case KEY_CHAP_NAME: + UnicodeStrToAsciiStrS ( + IfrNvData->CHAPName, + Private->Current->AuthConfigData.CHAP.CHAPName, + sizeof (Private->Current->AuthConfigData.CHAP.CHAPName) + ); + break; + + case KEY_CHAP_SECRET: + UnicodeStrToAsciiStrS ( + IfrNvData->CHAPSecret, + Private->Current->AuthConfigData.CHAP.CHAPSecret, + sizeof (Private->Current->AuthConfigData.CHAP.CHAPSecret) + ); + break; + + case KEY_REVERSE_CHAP_NAME: + UnicodeStrToAsciiStrS ( + IfrNvData->ReverseCHAPName, + Private->Current->AuthConfigData.CHAP.ReverseCHAPName, + sizeof (Private->Current->AuthConfigData.CHAP.ReverseCHAPName) + ); + break; + + case KEY_REVERSE_CHAP_SECRET: + UnicodeStrToAsciiStrS ( + IfrNvData->ReverseCHAPSecret, + Private->Current->AuthConfigData.CHAP.ReverseCHAPSecret, + sizeof (Private->Current->AuthConfigData.CHAP.ReverseCHAPSecret) + ); + break; + + case KEY_CONFIG_ISID: + IScsiParseIsIdFromString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId); + IScsiConvertIsIdToString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId); + + break; + + default: + break; + } + } + + if (!EFI_ERROR (Status)) { + // + // Pass changed uncommitted data back to Form Browser. + // + BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA); + HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL); + } + + FreePool (IfrNvData); + FreePool (IScsiName); + + return Status; +} + + +/** + Initialize the iSCSI configuration form. + + @param[in] DriverBindingHandle The iSCSI driverbinding handle. + + @retval EFI_SUCCESS The iSCSI configuration form is initialized. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +IScsiConfigFormInit ( + IN EFI_HANDLE DriverBindingHandle + ) +{ + EFI_STATUS Status; + ISCSI_FORM_CALLBACK_INFO *CallbackInfo; + + CallbackInfo = (ISCSI_FORM_CALLBACK_INFO *) AllocateZeroPool (sizeof (ISCSI_FORM_CALLBACK_INFO)); + if (CallbackInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CallbackInfo->Signature = ISCSI_FORM_CALLBACK_INFO_SIGNATURE; + CallbackInfo->Current = NULL; + + CallbackInfo->ConfigAccess.ExtractConfig = IScsiFormExtractConfig; + CallbackInfo->ConfigAccess.RouteConfig = IScsiFormRouteConfig; + CallbackInfo->ConfigAccess.Callback = IScsiFormCallback; + + // + // Install Device Path Protocol and Config Access protocol to driver handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &CallbackInfo->DriverHandle, + &gEfiDevicePathProtocolGuid, + &mIScsiHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &CallbackInfo->ConfigAccess, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Publish our HII data. + // + CallbackInfo->RegisteredHandle = HiiAddPackages ( + &gIScsiConfigGuid, + CallbackInfo->DriverHandle, + IScsiDxeStrings, + IScsiConfigVfrBin, + NULL + ); + if (CallbackInfo->RegisteredHandle == NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + &CallbackInfo->DriverHandle, + &gEfiDevicePathProtocolGuid, + &mIScsiHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &CallbackInfo->ConfigAccess, + NULL + ); + FreePool(CallbackInfo); + return EFI_OUT_OF_RESOURCES; + } + + mCallbackInfo = CallbackInfo; + + return EFI_SUCCESS; +} + + +/** + Unload the iSCSI configuration form, this includes: delete all the iSCSI + configuration entries, uninstall the form callback protocol, and + free the resources used. + + @param[in] DriverBindingHandle The iSCSI driverbinding handle. + + @retval EFI_SUCCESS The iSCSI configuration form is unloaded. + @retval Others Failed to unload the form. + +**/ +EFI_STATUS +IScsiConfigFormUnload ( + IN EFI_HANDLE DriverBindingHandle + ) +{ + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + ISCSI_NIC_INFO *NicInfo; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + while (!IsListEmpty (&mPrivate->AttemptConfigs)) { + Entry = NetListRemoveHead (&mPrivate->AttemptConfigs); + AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + FreePool (AttemptConfigData); + mPrivate->AttemptCount--; + } + + ASSERT (mPrivate->AttemptCount == 0); + + while (!IsListEmpty (&mPrivate->NicInfoList)) { + Entry = NetListRemoveHead (&mPrivate->NicInfoList); + NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link); + FreePool (NicInfo); + mPrivate->NicCount--; + } + + ASSERT (mPrivate->NicCount == 0); + + // + // Free attempt is created but not saved to system. + // + if (mPrivate->NewAttempt != NULL) { + FreePool (mPrivate->NewAttempt); + } + + FreePool (mPrivate); + mPrivate = NULL; + + // + // Remove HII package list. + // + HiiRemovePackages (mCallbackInfo->RegisteredHandle); + + // + // Uninstall Device Path Protocol and Config Access protocol. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + mCallbackInfo->DriverHandle, + &gEfiDevicePathProtocolGuid, + &mIScsiHiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &mCallbackInfo->ConfigAccess, + NULL + ); + + FreePool (mCallbackInfo); + + return Status; +} diff --git a/Core/NetworkPkg/IScsiDxe/IScsiConfig.h b/Core/NetworkPkg/IScsiDxe/IScsiConfig.h new file mode 100644 index 0000000000..daa0d34382 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiConfig.h @@ -0,0 +1,157 @@ +/** @file + The header file of functions for configuring or getting the parameters + relating to iSCSI. + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_CONFIG_H_ +#define _ISCSI_CONFIG_H_ + +#include "IScsiConfigNVDataStruc.h" + +typedef struct _ISCSI_FORM_CALLBACK_INFO ISCSI_FORM_CALLBACK_INFO; + +extern UINT8 IScsiConfigVfrBin[]; +extern UINT8 IScsiDxeStrings[]; +extern ISCSI_FORM_CALLBACK_INFO *mCallbackInfo; + + +#define VAR_OFFSET(Field) \ + ((UINT16) ((UINTN) &(((ISCSI_CONFIG_IFR_NVDATA *) 0)->Field))) + +#define QUESTION_ID(Field) \ + ((UINT16) (VAR_OFFSET (Field) + CONFIG_OPTION_OFFSET)) + + +#define DYNAMIC_ONE_OF_VAR_OFFSET VAR_OFFSET (Enabled) +#define DYNAMIC_ORDERED_LIST_QUESTION_ID QUESTION_ID (DynamicOrderedList) +#define DYNAMIC_ORDERED_LIST_VAR_OFFSET VAR_OFFSET (DynamicOrderedList) +#define ATTEMPT_DEL_QUESTION_ID QUESTION_ID (DeleteAttemptList) +#define ATTEMPT_DEL_VAR_OFFSET VAR_OFFSET (DeleteAttemptList) + +// +// sizeof (EFI_MAC_ADDRESS) * 3 +// +#define ISCSI_MAX_MAC_STRING_LEN 96 + +#define ISCSI_INITATOR_NAME_VAR_NAME L"I_NAME" + +#define ISCSI_CONFIG_VAR_ATTR (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE) + +#define ISCSI_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'f', 'c', 'i') + +#define ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK(Callback) \ + CR ( \ + Callback, \ + ISCSI_FORM_CALLBACK_INFO, \ + ConfigAccess, \ + ISCSI_FORM_CALLBACK_INFO_SIGNATURE \ + ) + +#pragma pack(1) +struct _ISCSI_ATTEMPT_CONFIG_NVDATA { + LIST_ENTRY Link; + UINT8 NicIndex; + UINT8 AttemptConfigIndex; + BOOLEAN DhcpSuccess; + BOOLEAN ValidiBFTPath; + BOOLEAN ValidPath; + UINT8 AutoConfigureMode; + EFI_STRING_ID AttemptTitleToken; + EFI_STRING_ID AttemptTitleHelpToken; + CHAR8 AttemptName[ATTEMPT_NAME_MAX_SIZE]; + CHAR8 MacString[ISCSI_MAX_MAC_STRING_LEN]; + EFI_IP_ADDRESS PrimaryDns; + EFI_IP_ADDRESS SecondaryDns; + EFI_IP_ADDRESS DhcpServer; + ISCSI_SESSION_CONFIG_NVDATA SessionConfigData; + UINT8 AuthenticationType; + union { + ISCSI_CHAP_AUTH_CONFIG_NVDATA CHAP; + } AuthConfigData; + BOOLEAN AutoConfigureSuccess; +}; + +/// +/// HII specific Vendor Device Path definition. +/// +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; + +#pragma pack() + +struct _ISCSI_FORM_CALLBACK_INFO { + UINT32 Signature; + EFI_HANDLE DriverHandle; + EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess; + UINT16 *KeyList; + VOID *FormBuffer; + EFI_HII_HANDLE RegisteredHandle; + ISCSI_ATTEMPT_CONFIG_NVDATA *Current; +}; + +/** + Initialize the iSCSI configuration form. + + @param[in] DriverBindingHandle The iSCSI driverbinding handle. + + @retval EFI_SUCCESS The iSCSI configuration form is initialized. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +IScsiConfigFormInit ( + IN EFI_HANDLE DriverBindingHandle + ); + +/** + Unload the iSCSI configuration form, this includes: delete all the iSCSI + configuration entries, uninstall the form callback protocol, and + free the resources used. + + @param[in] DriverBindingHandle The iSCSI driverbinding handle. + + @retval EFI_SUCCESS The iSCSI configuration form is unloaded. + @retval Others Failed to unload the form. + +**/ +EFI_STATUS +IScsiConfigFormUnload ( + IN EFI_HANDLE DriverBindingHandle + ); + +/** + Update the MAIN form to display the configured attempts. + +**/ +VOID +IScsiConfigUpdateAttempt ( + VOID + ); + +/** + Get the attempt config data from global structure by the ConfigIndex. + + @param[in] AttemptConfigIndex The unique index indicates the attempt. + + @return Pointer to the attempt config data. + @retval NULL The attempt configuration data can not be found. + +**/ +ISCSI_ATTEMPT_CONFIG_NVDATA * +IScsiConfigGetAttemptByConfigIndex ( + IN UINT8 AttemptConfigIndex + ); + +#endif diff --git a/Core/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h b/Core/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h new file mode 100644 index 0000000000..56ebb503c0 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h @@ -0,0 +1,185 @@ +/** @file + Define NVData structures used by the iSCSI configuration component. + +Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_NVDATASTRUC_H_ +#define _ISCSI_NVDATASTRUC_H_ + +#include + +#define VAR_EQ_TEST_NAME 0x100 +#define CONFIGURATION_VARSTORE_ID 0x6666 + +#define FORMID_MAIN_FORM 1 +#define FORMID_MAC_FORM 2 +#define FORMID_ATTEMPT_FORM 3 +#define FORMID_ORDER_FORM 4 +#define FORMID_DELETE_FORM 5 + +#define ISCSI_NAME_IFR_MIN_SIZE 4 +#define ISCSI_NAME_IFR_MAX_SIZE 223 +#define ISCSI_NAME_MAX_SIZE 224 + +#define ATTEMPT_NAME_MAX_SIZE 96 +#define ATTEMPT_NAME_SIZE 10 + +#define CONNECT_MIN_RETRY 0 +#define CONNECT_MAX_RETRY 16 + +#define CONNECT_MIN_TIMEOUT 100 +#define CONNECT_MAX_TIMEOUT 20000 +#define CONNECT_DEFAULT_TIMEOUT 1000 + +#define ISCSI_MAX_ATTEMPTS_NUM 255 + +#define ISCSI_DISABLED 0 +#define ISCSI_ENABLED 1 +#define ISCSI_ENABLED_FOR_MPIO 2 + +#define IP_MODE_IP4 0 +#define IP_MODE_IP6 1 +#define IP_MODE_AUTOCONFIG 2 + +#define ISCSI_AUTH_TYPE_NONE 0 +#define ISCSI_AUTH_TYPE_CHAP 1 +#define ISCSI_AUTH_TYPE_KRB 2 + +#define IP4_MIN_SIZE 7 +#define IP4_MAX_SIZE 15 +#define IP4_STR_MAX_SIZE 16 + +// +// Macros used for an IPv4 or an IPv6 address. +// +#define IP_MIN_SIZE 2 +#define IP_MAX_SIZE 39 +#define IP_STR_MAX_SIZE 40 + +#define LUN_MIN_SIZE 1 +#define LUN_MAX_SIZE 20 + +#define ISCSI_CHAP_UNI 0 +#define ISCSI_CHAP_MUTUAL 1 + +#define TARGET_PORT_MIN_NUM 0 +#define TARGET_PORT_MAX_NUM 65535 +#define LABEL_END 0xffff + +#define KEY_INITIATOR_NAME 0x101 +#define KEY_DHCP_ENABLE 0x102 +#define KEY_LOCAL_IP 0x103 +#define KEY_SUBNET_MASK 0x104 +#define KEY_GATE_WAY 0x105 +#define KEY_TARGET_IP 0x106 +#define KEY_CHAP_NAME 0x107 +#define KEY_CHAP_SECRET 0x108 +#define KEY_REVERSE_CHAP_NAME 0x109 +#define KEY_REVERSE_CHAP_SECRET 0x10a +#define KEY_SAVE_CHANGES 0x10b +#define KEY_TARGET_NAME 0x10c +#define KEY_BOOT_LUN 0x10d + +#define KEY_ADD_ATTEMPT 0x10e +#define KEY_SAVE_ATTEMPT_CONFIG 0x10f +#define KEY_ORDER_ATTEMPT_CONFIG 0x110 +#define KEY_SAVE_ORDER_CHANGES 0x111 +#define KEY_IGNORE_ORDER_CHANGES 0x112 +#define KEY_ATTEMPT_NAME 0x113 +#define KEY_SAVE_DELETE_ATTEMPT 0x114 +#define KEY_IGNORE_DELETE_ATTEMPT 0x115 +#define KEY_DELETE_ATTEMPT 0x116 + +#define KEY_IP_MODE 0x11c +#define KEY_AUTH_TYPE 0x11d +#define KEY_CONFIG_ISID 0x11e + +#define ATTEMPT_ENTRY_LABEL 0x9000 +#define KEY_ATTEMPT_ENTRY_BASE 0xa000 +#define KEY_DE_ATTEMPT_ENTRY_BASE 0xb000 + +#define KEY_DEVICE_ENTRY_BASE 0x1000 +#define KEY_MAC_ENTRY_BASE 0x2000 +#define MAC_ENTRY_LABEL 0x3000 +#define ORDER_ENTRY_LABEL 0x4000 +#define DELETE_ENTRY_LABEL 0x5000 +#define CONFIG_OPTION_OFFSET 0x9000 + +#define ISCSI_LUN_STR_MAX_LEN 21 +#define ISCSI_CHAP_SECRET_MIN_LEN 12 +#define ISCSI_CHAP_SECRET_MAX_LEN 16 +// +// ISCSI_CHAP_SECRET_STORAGE = ISCSI_CHAP_SECRET_MAX_LEN + sizeof (NULL-Terminator) +// +#define ISCSI_CHAP_SECRET_STORAGE 17 +#define ISCSI_CHAP_NAME_MAX_LEN 126 +#define ISCSI_CHAP_NAME_STORAGE 127 + +#define KERBEROS_SECRET_MIN_LEN 12 +#define KERBEROS_SECRET_MAX_LEN 16 +#define KERBEROS_SECRET_STORAGE 17 +#define KERBEROS_NAME_MAX_LEN 96 +#define KERBEROS_KDC_PORT_MIN_NUM 0 +#define KERBEROS_KDC_PORT_MAX_NUM 65535 + +#define ISID_CONFIGURABLE_MIN_LEN 6 +#define ISID_CONFIGURABLE_MAX_LEN 12 +#define ISID_CONFIGURABLE_STORAGE 13 + +#pragma pack(1) +typedef struct _ISCSI_CONFIG_IFR_NVDATA { + CHAR16 InitiatorName[ISCSI_NAME_MAX_SIZE]; + CHAR16 AttemptName[ATTEMPT_NAME_MAX_SIZE]; + + UINT8 Enabled; + UINT8 IpMode; + + UINT8 ConnectRetryCount; + UINT8 Padding1; + UINT16 ConnectTimeout; // Timeout value in milliseconds. + + UINT8 InitiatorInfoFromDhcp; + UINT8 TargetInfoFromDhcp; + CHAR16 LocalIp[IP4_STR_MAX_SIZE]; + CHAR16 SubnetMask[IP4_STR_MAX_SIZE]; + CHAR16 Gateway[IP4_STR_MAX_SIZE]; + + CHAR16 TargetName[ISCSI_NAME_MAX_SIZE]; + CHAR16 TargetIp[IP_STR_MAX_SIZE]; + UINT16 TargetPort; + CHAR16 BootLun[ISCSI_LUN_STR_MAX_LEN]; + + UINT8 AuthenticationType; + + UINT8 CHAPType; + CHAR16 CHAPName[ISCSI_CHAP_NAME_STORAGE]; + CHAR16 CHAPSecret[ISCSI_CHAP_SECRET_STORAGE]; + CHAR16 ReverseCHAPName[ISCSI_CHAP_NAME_STORAGE]; + CHAR16 ReverseCHAPSecret[ISCSI_CHAP_SECRET_STORAGE]; + + BOOLEAN MutualRequired; + UINT8 Padding2; + CHAR16 KerberosUserName[KERBEROS_NAME_MAX_LEN]; + CHAR16 KerberosUserSecret[KERBEROS_SECRET_STORAGE]; + CHAR16 KerberosKDCName[KERBEROS_NAME_MAX_LEN]; + CHAR16 KerberosKDCRealm[KERBEROS_NAME_MAX_LEN]; + CHAR16 KerberosKDCIp[IP_STR_MAX_SIZE]; + UINT16 KerberosKDCPort; + + UINT8 DynamicOrderedList[ISCSI_MAX_ATTEMPTS_NUM]; + UINT8 DeleteAttemptList[ISCSI_MAX_ATTEMPTS_NUM]; + + CHAR16 IsId[ISID_CONFIGURABLE_STORAGE]; +} ISCSI_CONFIG_IFR_NVDATA; +#pragma pack() + +#endif diff --git a/Core/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni b/Core/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni new file mode 100644 index 0000000000..57ef9c8d77 Binary files /dev/null and b/Core/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni differ diff --git a/Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr b/Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr new file mode 100644 index 0000000000..db77c0f215 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr @@ -0,0 +1,361 @@ +/** @file + VFR file used by the iSCSI configuration component. + +Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiConfigNVDataStruc.h" + +#define EFI_NETWORK_DEVICE_CLASS 0x04 + +formset + guid = ISCSI_CONFIG_GUID, + title = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_TITLE), + help = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_HELP), + + varstore ISCSI_CONFIG_IFR_NVDATA, + varid = CONFIGURATION_VARSTORE_ID, + name = ISCSI_CONFIG_IFR_NVDATA, + guid = ISCSI_CONFIG_GUID; + + form formid = FORMID_MAIN_FORM, + title = STRING_TOKEN(STR_ISCSI_MAIN_FORM_TITLE); + + string varid = ISCSI_CONFIG_IFR_NVDATA.InitiatorName, + prompt = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME), + help = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME_HELP), + flags = INTERACTIVE, + key = KEY_INITIATOR_NAME, + minsize = ISCSI_NAME_IFR_MIN_SIZE, + maxsize = ISCSI_NAME_IFR_MAX_SIZE, + endstring; + + subtitle text = STRING_TOKEN(STR_NULL); + + goto FORMID_MAC_FORM, + prompt = STRING_TOKEN(STR_ADD_ATTEMPT_ENTRY), + help = STRING_TOKEN(STR_ADD_ATTEMPT_ENTRY), + flags = INTERACTIVE, + key = KEY_ADD_ATTEMPT; + + label ATTEMPT_ENTRY_LABEL; + label LABEL_END; + + subtitle text = STRING_TOKEN(STR_NULL); + + goto FORMID_DELETE_FORM, + prompt = STRING_TOKEN (STR_DEL_ATTEMPT_ENTRY), + help = STRING_TOKEN (STR_DEL_ATTEMPT_ENTRY_HELP), + flags = INTERACTIVE, + key = KEY_DELETE_ATTEMPT; + + subtitle text = STRING_TOKEN(STR_NULL); + + goto FORMID_ORDER_FORM, + prompt = STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY), + help = STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY), + flags = INTERACTIVE, + key = KEY_ORDER_ATTEMPT_CONFIG; + + subtitle text = STRING_TOKEN(STR_NULL); + + endform; + + form formid = FORMID_MAC_FORM, + title = STRING_TOKEN(STR_ISCSI_MAC_FORM_TITLE); + + label MAC_ENTRY_LABEL; + label LABEL_END; + + endform; + + form formid = FORMID_ORDER_FORM, + title = STRING_TOKEN(STR_ORDER_ATTEMPT_ENTRY); + + label ORDER_ENTRY_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN (STR_SAVE_AND_EXIT), + text = STRING_TOKEN (STR_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_SAVE_ORDER_CHANGES; + + text + help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + text = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_IGNORE_ORDER_CHANGES; + endform; + + form formid = FORMID_DELETE_FORM, + title = STRING_TOKEN(STR_DEL_ATTEMPT_ENTRY); + + label DELETE_ENTRY_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN (STR_SAVE_AND_EXIT), + text = STRING_TOKEN (STR_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_SAVE_DELETE_ATTEMPT; + + text + help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + text = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_IGNORE_DELETE_ATTEMPT; + endform; + + form formid = FORMID_ATTEMPT_FORM, + title = STRING_TOKEN(STR_ISCSI_ATTEMPT_FORM_TITLE); + + string varid = ISCSI_CONFIG_IFR_NVDATA.AttemptName, + prompt = STRING_TOKEN(STR_ISCSI_ATTEMPT_NAME), + help = STRING_TOKEN(STR_ISCSI_ATTEMPT_NAME_HELP), + flags = INTERACTIVE, + key = KEY_ATTEMPT_NAME, + minsize = 0, + maxsize = ATTEMPT_NAME_MAX_SIZE, + endstring; + + subtitle text = STRING_TOKEN(STR_NULL); + + oneof varid = ISCSI_CONFIG_IFR_NVDATA.Enabled, + prompt = STRING_TOKEN(STR_ISCSI_MODE_PROMPT), + help = STRING_TOKEN(STR_ISCSI_MODE_HELP), + option text = STRING_TOKEN(STR_ISCSI_MODE_DISABLED), value = ISCSI_DISABLED, flags = DEFAULT; + option text = STRING_TOKEN(STR_ISCSI_MODE_ENABLED), value = ISCSI_ENABLED, flags = 0; + option text = STRING_TOKEN(STR_ISCSI_MODE_ENABLED_FOR_MPIO), value = ISCSI_ENABLED_FOR_MPIO, flags = 0; + endoneof; + + subtitle text = STRING_TOKEN(STR_NULL); + + oneof varid = ISCSI_CONFIG_IFR_NVDATA.IpMode, + questionid = KEY_IP_MODE, + prompt = STRING_TOKEN(STR_IP_MODE_PROMPT), + help = STRING_TOKEN(STR_IP_MODE_HELP), + option text = STRING_TOKEN(STR_IP_MODE_IP4), value = IP_MODE_IP4, flags = INTERACTIVE; + option text = STRING_TOKEN(STR_IP_MODE_IP6), value = IP_MODE_IP6, flags = INTERACTIVE; + option text = STRING_TOKEN(STR_IP_MODE_AUTOCONFIG), value = IP_MODE_AUTOCONFIG, flags = INTERACTIVE; + endoneof; + + subtitle text = STRING_TOKEN(STR_NULL); + + numeric varid = ISCSI_CONFIG_IFR_NVDATA.ConnectRetryCount, + prompt = STRING_TOKEN(STR_ISCSI_CONFIG_RETRY), + help = STRING_TOKEN(STR_ISCSI_CONFIG_RETRY_HELP), + flags = 0, + minimum = CONNECT_MIN_RETRY, + maximum = CONNECT_MAX_RETRY, + step = 0, + endnumeric; + + numeric varid = ISCSI_CONFIG_IFR_NVDATA.ConnectTimeout, + prompt = STRING_TOKEN(STR_ISCSI_CONFIG_TIMEOUT), + help = STRING_TOKEN(STR_ISCSI_CONFIG_TIMEOUT_HELP), + flags = 0, + minimum = CONNECT_MIN_TIMEOUT, + maximum = CONNECT_MAX_TIMEOUT, + step = 0, + default = CONNECT_DEFAULT_TIMEOUT, + endnumeric; + + subtitle text = STRING_TOKEN(STR_NULL); + + string varid = ISCSI_CONFIG_IFR_NVDATA.IsId, + prompt = STRING_TOKEN(STR_ISCSI_CONFIG_ISID), + help = STRING_TOKEN(STR_ISCSI_CONFIG_ISID_HELP), + flags = INTERACTIVE, + key = KEY_CONFIG_ISID, + minsize = ISID_CONFIGURABLE_MIN_LEN, + maxsize = ISID_CONFIGURABLE_MAX_LEN, + endstring; + + subtitle text = STRING_TOKEN(STR_NULL); + + suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG; + checkbox varid = ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp, + prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP), + help = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP), + flags = INTERACTIVE, + key = KEY_DHCP_ENABLE, + endcheckbox; + endif; + + suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x01 OR + ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_IP6 OR + ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG; + string varid = ISCSI_CONFIG_IFR_NVDATA.LocalIp, + prompt = STRING_TOKEN(STR_ISCSI_LOCAL_IP_ADDRESS), + help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_LOCAL_IP, + minsize = IP4_MIN_SIZE, + maxsize = IP4_MAX_SIZE, + endstring; + + string varid = ISCSI_CONFIG_IFR_NVDATA.SubnetMask, + prompt = STRING_TOKEN(STR_ISCSI_LOCAL_MASK), + help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_SUBNET_MASK, + minsize = IP4_MIN_SIZE, + maxsize = IP4_MAX_SIZE, + endstring; + + string varid = ISCSI_CONFIG_IFR_NVDATA.Gateway, + prompt = STRING_TOKEN(STR_ISCSI_LOCAL_GATEWAY), + help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_GATE_WAY, + minsize = IP4_MIN_SIZE, + maxsize = IP4_MAX_SIZE, + endstring; + + endif; + + suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG; + subtitle text = STRING_TOKEN(STR_NULL); + endif; + + suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG OR + ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x00; + checkbox varid = ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp, + prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET), + help = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET), + flags = 0, + endcheckbox; + endif; + + suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG OR + ideqval ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp == 0x01; + + string varid = ISCSI_CONFIG_IFR_NVDATA.TargetName, + prompt = STRING_TOKEN(STR_ISCSI_TARGET_NAME), + help = STRING_TOKEN(STR_ISCSI_TARGET_NAME_HELP), + flags = INTERACTIVE, + key = KEY_TARGET_NAME, + minsize = ISCSI_NAME_IFR_MIN_SIZE, + maxsize = ISCSI_NAME_IFR_MAX_SIZE, + endstring; + + string varid = ISCSI_CONFIG_IFR_NVDATA.TargetIp, + prompt = STRING_TOKEN(STR_ISCSI_TARGET_IP_ADDRESS), + help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_TARGET_IP, + minsize = IP_MIN_SIZE, + maxsize = IP_MAX_SIZE, + endstring; + + numeric varid = ISCSI_CONFIG_IFR_NVDATA.TargetPort, + prompt = STRING_TOKEN(STR_ISCSI_TARGET_PORT), + help = STRING_TOKEN(STR_ISCSI_TARGET_PORT), + flags = 0, + minimum = TARGET_PORT_MIN_NUM, + maximum = TARGET_PORT_MAX_NUM, + step = 0, + endnumeric; + + string varid = ISCSI_CONFIG_IFR_NVDATA.BootLun, + prompt = STRING_TOKEN(STR_ISCSI_BOOT_LUN), + help = STRING_TOKEN(STR_ISCSI_BOOT_LUN_HELP), + flags = INTERACTIVE, + key = KEY_BOOT_LUN, + minsize = LUN_MIN_SIZE, + maxsize = LUN_MAX_SIZE, + endstring; + + endif; + + suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG; + subtitle text = STRING_TOKEN(STR_NULL); + endif; + + oneof varid = ISCSI_CONFIG_IFR_NVDATA.AuthenticationType, + questionid = KEY_AUTH_TYPE, + prompt = STRING_TOKEN(STR_AUTHEN_TYPE_PROMPT), + help = STRING_TOKEN(STR_AUTHEN_TYPE_HELP), + option text = STRING_TOKEN(STR_AUTHEN_TYPE_CHAP), value = ISCSI_AUTH_TYPE_CHAP, flags = 0; + option text = STRING_TOKEN(STR_AUTHEN_TYPE_NONE), value = ISCSI_AUTH_TYPE_NONE, flags = DEFAULT; + endoneof; + + suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_CHAP; + oneof varid = ISCSI_CONFIG_IFR_NVDATA.CHAPType, + prompt = STRING_TOKEN(STR_CHAP_TYPE_PROMPT), + help = STRING_TOKEN(STR_CHAP_TYPE_HELP), + option text = STRING_TOKEN(STR_CHAP_TYPE_UNI), value = ISCSI_CHAP_UNI, flags = 0; + option text = STRING_TOKEN(STR_CHAP_TYPE_MUTUAL), value = ISCSI_CHAP_MUTUAL, flags = DEFAULT; + endoneof; + endif; + + suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_CHAP; + string varid = ISCSI_CONFIG_IFR_NVDATA.CHAPName, + prompt = STRING_TOKEN(STR_ISCSI_CHAP_NAME), + help = STRING_TOKEN(STR_ISCSI_CHAP_NAME), + flags = INTERACTIVE, + key = KEY_CHAP_NAME, + minsize = 0, + maxsize = ISCSI_CHAP_NAME_MAX_LEN, + endstring; + + string varid = ISCSI_CONFIG_IFR_NVDATA.CHAPSecret, + prompt = STRING_TOKEN(STR_ISCSI_CHAP_SECRET), + help = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP), + flags = INTERACTIVE, + key = KEY_CHAP_SECRET, + minsize = ISCSI_CHAP_SECRET_MIN_LEN, + maxsize = ISCSI_CHAP_SECRET_MAX_LEN, + endstring; + + endif; + + suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_CHAP OR + NOT ideqval ISCSI_CONFIG_IFR_NVDATA.CHAPType == ISCSI_CHAP_MUTUAL; + + string varid = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPName, + prompt = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME), + help = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME), + flags = INTERACTIVE, + key = KEY_REVERSE_CHAP_NAME, + minsize = 0, + maxsize = ISCSI_CHAP_NAME_MAX_LEN, + endstring; + + string varid = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPSecret, + prompt = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_SECRET), + help = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP), + flags = INTERACTIVE, + key = KEY_REVERSE_CHAP_SECRET, + minsize = ISCSI_CHAP_SECRET_MIN_LEN, + maxsize = ISCSI_CHAP_SECRET_MAX_LEN, + endstring; + + endif; + + subtitle text = STRING_TOKEN(STR_NULL); + + text + help = STRING_TOKEN (STR_SAVE_CHANGES_HELP), + text = STRING_TOKEN (STR_SAVE_CHANGES), + flags = INTERACTIVE, + key = KEY_SAVE_ATTEMPT_CONFIG; + + goto FORMID_MAIN_FORM, + prompt = STRING_TOKEN (STR_RETURN_MAIN_FORM), + help = STRING_TOKEN (STR_RETURN_MAIN_FORM), + flags = 0; + + endform; + +endformset; + diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDhcp.c b/Core/NetworkPkg/IScsiDxe/IScsiDhcp.c new file mode 100644 index 0000000000..fa2412ebd8 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiDhcp.c @@ -0,0 +1,498 @@ +/** @file + iSCSI DHCP4 related configuration routines. + +Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + + +/** + Extract the Root Path option and get the required target information. + + @param[in] RootPath The RootPath. + @param[in] Length Length of the RootPath option payload. + @param[in, out] ConfigData The iSCSI attempt configuration data read + from a nonvolatile device. + + @retval EFI_SUCCESS All required information is extracted from the RootPath option. + @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_INVALID_PARAMETER The RootPath is malformatted. + +**/ +EFI_STATUS +IScsiDhcpExtractRootPath ( + IN CHAR8 *RootPath, + IN UINT8 Length, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ) +{ + EFI_STATUS Status; + UINT8 IScsiRootPathIdLen; + CHAR8 *TmpStr; + ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX]; + ISCSI_ROOT_PATH_FIELD *Field; + UINT32 FieldIndex; + UINT8 Index; + ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData; + EFI_IP_ADDRESS Ip; + UINT8 IpMode; + + ConfigNvData = &ConfigData->SessionConfigData; + + // + // "iscsi:"":"":"":"":" + // + IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID); + + if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) { + return EFI_NOT_FOUND; + } + // + // Skip the iSCSI RootPath ID "iscsi:". + // + RootPath += IScsiRootPathIdLen; + Length = (UINT8) (Length - IScsiRootPathIdLen); + + TmpStr = (CHAR8 *) AllocatePool (Length + 1); + if (TmpStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (TmpStr, RootPath, Length); + TmpStr[Length] = '\0'; + + Index = 0; + FieldIndex = RP_FIELD_IDX_SERVERNAME; + ZeroMem (&Fields[0], sizeof (Fields)); + + // + // Extract the fields in the Root Path option string. + // + for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) { + if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) { + Fields[FieldIndex].Str = &TmpStr[Index]; + } + + while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) { + Index++; + } + + if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) { + if (FieldIndex != RP_FIELD_IDX_TARGETNAME) { + TmpStr[Index] = '\0'; + Index++; + } + + if (Fields[FieldIndex].Str != NULL) { + Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str); + } + } + } + + if (FieldIndex != RP_FIELD_IDX_MAX) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) || + (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) || + (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1) + ) { + + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // Get the IP address of the target. + // + Field = &Fields[RP_FIELD_IDX_SERVERNAME]; + + if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) { + IpMode = ConfigNvData->IpMode; + } else { + IpMode = ConfigData->AutoConfigureMode; + } + + Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip); + CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS)); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Check the protocol type. + // + Field = &Fields[RP_FIELD_IDX_PROTOCOL]; + if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // Get the port of the iSCSI target. + // + Field = &Fields[RP_FIELD_IDX_PORT]; + if (Field->Str != NULL) { + ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str); + } else { + ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT; + } + // + // Get the LUN. + // + Field = &Fields[RP_FIELD_IDX_LUN]; + if (Field->Str != NULL) { + Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } else { + ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun)); + } + // + // Get the target iSCSI Name. + // + Field = &Fields[RP_FIELD_IDX_TARGETNAME]; + + if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // Validate the iSCSI name. + // + Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str)); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str); + +ON_EXIT: + + FreePool (TmpStr); + + return Status; +} + +/** + The callback function registerd to the DHCP4 instance that is used to select + the qualified DHCP OFFER. + + @param[in] This The DHCP4 protocol. + @param[in] Context The context set when configuring the DHCP4 protocol. + @param[in] CurrentState The current state of the DHCP4 protocol. + @param[in] Dhcp4Event The event occurs in the current state. + @param[in] Packet The DHCP packet that is to be sent or was already received. + @param[out] NewPacket The packet used to replace the above Packet. + + @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted + in the Dhcp4Event. + @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +IScsiDhcpSelectOffer ( + IN EFI_DHCP4_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP4_STATE CurrentState, + IN EFI_DHCP4_EVENT Dhcp4Event, + IN EFI_DHCP4_PACKET *Packet, OPTIONAL + OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL + ) +{ + EFI_STATUS Status; + UINT32 OptionCount; + EFI_DHCP4_PACKET_OPTION **OptionList; + UINT32 Index; + + if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) { + return EFI_SUCCESS; + } + + OptionCount = 0; + + Status = This->Parse (This, Packet, &OptionCount, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_READY; + } + + OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *)); + if (OptionList == NULL) { + return EFI_NOT_READY; + } + + Status = This->Parse (This, Packet, &OptionCount, OptionList); + if (EFI_ERROR (Status)) { + FreePool (OptionList); + return EFI_NOT_READY; + } + + for (Index = 0; Index < OptionCount; Index++) { + if (OptionList[Index]->OpCode != DHCP4_TAG_ROOT_PATH) { + continue; + } + + Status = IScsiDhcpExtractRootPath ( + (CHAR8 *) &OptionList[Index]->Data[0], + OptionList[Index]->Length, + (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context + ); + + break; + } + + if ((Index == OptionCount)) { + Status = EFI_NOT_READY; + } + + FreePool (OptionList); + + return Status; +} + +/** + Parse the DHCP ACK to get the address configuration and DNS information. + + @param[in] Dhcp4 The DHCP4 protocol. + @param[in, out] ConfigData The session configuration data. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_NO_MAPPING DHCP failed to acquire address and other information. + @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted. + @retval EFI_DEVICE_ERROR Other errors as indicated. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +IScsiParseDhcpAck ( + IN EFI_DHCP4_PROTOCOL *Dhcp4, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ) +{ + EFI_STATUS Status; + EFI_DHCP4_MODE_DATA Dhcp4ModeData; + UINT32 OptionCount; + EFI_DHCP4_PACKET_OPTION **OptionList; + UINT32 Index; + ISCSI_SESSION_CONFIG_NVDATA *NvData; + + Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Dhcp4ModeData.State != Dhcp4Bound) { + return EFI_NO_MAPPING; + } + + NvData = &ConfigData->SessionConfigData; + + CopyMem (&NvData->LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&NvData->SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&NvData->Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); + + OptionCount = 0; + OptionList = NULL; + + Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_DEVICE_ERROR; + } + + OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *)); + if (OptionList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList); + if (EFI_ERROR (Status)) { + FreePool (OptionList); + return EFI_DEVICE_ERROR; + } + + for (Index = 0; Index < OptionCount; Index++) { + // + // Get DNS server addresses and DHCP server address from this offer. + // + if (OptionList[Index]->OpCode == DHCP4_TAG_DNS) { + + if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) { + Status = EFI_INVALID_PARAMETER; + break; + } + // + // Primary DNS server address. + // + CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS)); + + if (OptionList[Index]->Length > 4) { + // + // Secondary DNS server address. + // + CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS)); + } + } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) { + if (OptionList[Index]->Length != 4) { + Status = EFI_INVALID_PARAMETER; + break; + } + + CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS)); + } + } + + FreePool (OptionList); + + return Status; +} + + +/** + Parse the DHCP ACK to get the address configuration and DNS information. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller. + @param[in, out] ConfigData The attempt configuration data. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NO_MEDIA There was a media error. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiDoDhcp ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ) +{ + EFI_HANDLE Dhcp4Handle; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_STATUS Status; + EFI_DHCP4_PACKET_OPTION *ParaList; + EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData; + ISCSI_SESSION_CONFIG_NVDATA *NvData; + BOOLEAN MediaPresent; + + Dhcp4Handle = NULL; + Dhcp4 = NULL; + ParaList = NULL; + + // + // Check media status before doing DHCP. + // + MediaPresent = TRUE; + NetLibDetectMedia (Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Create a DHCP4 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiDhcp4ServiceBindingProtocolGuid, + &Dhcp4Handle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Dhcp4Handle, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Dhcp4, + Image, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + NvData = &ConfigData->SessionConfigData; + + ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3); + if (ParaList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Ask the server to reply with Netmask, Router, DNS, and RootPath options. + // + ParaList->OpCode = DHCP4_TAG_PARA_LIST; + ParaList->Length = (UINT8) (NvData->TargetInfoFromDhcp ? 4 : 3); + ParaList->Data[0] = DHCP4_TAG_NETMASK; + ParaList->Data[1] = DHCP4_TAG_ROUTER; + ParaList->Data[2] = DHCP4_TAG_DNS; + ParaList->Data[3] = DHCP4_TAG_ROOT_PATH; + + ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA)); + Dhcp4ConfigData.OptionCount = 1; + Dhcp4ConfigData.OptionList = &ParaList; + + if (NvData->TargetInfoFromDhcp) { + // + // Use callback to select an offer that contains target information. + // + Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer; + Dhcp4ConfigData.CallbackContext = ConfigData; + } + + Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Dhcp4->Start (Dhcp4, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Parse the ACK to get required information. + // + Status = IScsiParseDhcpAck (Dhcp4, ConfigData); + +ON_EXIT: + + if (ParaList != NULL) { + FreePool (ParaList); + } + + if (Dhcp4 != NULL) { + Dhcp4->Stop (Dhcp4); + Dhcp4->Configure (Dhcp4, NULL); + + gBS->CloseProtocol ( + Dhcp4Handle, + &gEfiDhcp4ProtocolGuid, + Image, + Controller + ); + } + + NetLibDestroyServiceChild ( + Controller, + Image, + &gEfiDhcp4ServiceBindingProtocolGuid, + Dhcp4Handle + ); + + return Status; +} diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDhcp.h b/Core/NetworkPkg/IScsiDxe/IScsiDhcp.h new file mode 100644 index 0000000000..3165100099 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiDhcp.h @@ -0,0 +1,62 @@ +/** @file + The head file of iSCSI DHCP4 related configuration routines. + +Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_DHCP_H_ +#define _ISCSI_DHCP_H_ + + +#define DHCP4_TAG_PARA_LIST 55 +#define DHCP4_TAG_NETMASK 1 +#define DHCP4_TAG_ROUTER 3 +#define DHCP4_TAG_DNS 6 +#define DHCP4_TAG_SERVER_ID 54 +#define DHCP4_TAG_ROOT_PATH 17 +#define ISCSI_ROOT_PATH_ID "iscsi:" +#define ISCSI_ROOT_PATH_FIELD_DELIMITER ':' + +#define RP_FIELD_IDX_SERVERNAME 0 +#define RP_FIELD_IDX_PROTOCOL 1 +#define RP_FIELD_IDX_PORT 2 +#define RP_FIELD_IDX_LUN 3 +#define RP_FIELD_IDX_TARGETNAME 4 +#define RP_FIELD_IDX_MAX 5 + +typedef struct _ISCSI_ATTEMPT_CONFIG_NVDATA ISCSI_ATTEMPT_CONFIG_NVDATA; + +typedef struct _ISCSI_ROOT_PATH_FIELD { + CHAR8 *Str; + UINT8 Len; +} ISCSI_ROOT_PATH_FIELD; + +/** + Parse the DHCP ACK to get the address configuration and DNS information. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller. + @param[in, out] ConfigData The attempt configuration data. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NO_MEDIA There was a media error. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiDoDhcp ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ); + +#endif diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDhcp6.c b/Core/NetworkPkg/IScsiDxe/IScsiDhcp6.c new file mode 100644 index 0000000000..331b0f161b --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiDhcp6.c @@ -0,0 +1,525 @@ +/** @file + iSCSI DHCP6 related configuration routines. + +Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + + +/** + Extract the Root Path option and get the required target information from + Boot File Uniform Resource Locator (URL) Option. + + @param[in] RootPath The RootPath string. + @param[in] Length Length of the RootPath option payload. + @param[in, out] ConfigData The iSCSI session configuration data read from + nonvolatile device. + + @retval EFI_SUCCESS All required information is extracted from the + RootPath option. + @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_INVALID_PARAMETER The RootPath is malformatted. + +**/ +EFI_STATUS +IScsiDhcp6ExtractRootPath ( + IN CHAR8 *RootPath, + IN UINT16 Length, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ) +{ + EFI_STATUS Status; + UINT16 IScsiRootPathIdLen; + CHAR8 *TmpStr; + ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX]; + ISCSI_ROOT_PATH_FIELD *Field; + UINT32 FieldIndex; + UINT8 Index; + ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData; + EFI_IP_ADDRESS Ip; + UINT8 IpMode; + + ConfigNvData = &ConfigData->SessionConfigData; + + // + // "iscsi:"":"":"":"":" + // + IScsiRootPathIdLen = (UINT16) AsciiStrLen (ISCSI_ROOT_PATH_ID); + + if ((Length <= IScsiRootPathIdLen) || + (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) { + return EFI_NOT_FOUND; + } + // + // Skip the iSCSI RootPath ID "iscsi:". + // + RootPath = RootPath + IScsiRootPathIdLen; + Length = (UINT16) (Length - IScsiRootPathIdLen); + + TmpStr = (CHAR8 *) AllocatePool (Length + 1); + if (TmpStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (TmpStr, RootPath, Length); + TmpStr[Length] = '\0'; + + Index = 0; + FieldIndex = 0; + ZeroMem (&Fields[0], sizeof (Fields)); + + // + // Extract SERVERNAME field in the Root Path option. + // + if (TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } else { + Index++; + } + + Fields[RP_FIELD_IDX_SERVERNAME].Str = &TmpStr[Index]; + + while ((TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER) && (Index < Length)) { + Index++; + } + + // + // Skip ']' and ':'. + // + TmpStr[Index] = '\0'; + Index += 2; + + Fields[RP_FIELD_IDX_SERVERNAME].Len = (UINT8) AsciiStrLen (Fields[RP_FIELD_IDX_SERVERNAME].Str); + + // + // Extract others fields in the Root Path option string. + // + for (FieldIndex = 1; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) { + + if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) { + Fields[FieldIndex].Str = &TmpStr[Index]; + } + + while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) { + Index++; + } + + if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) { + if (FieldIndex != RP_FIELD_IDX_TARGETNAME) { + TmpStr[Index] = '\0'; + Index++; + } + + if (Fields[FieldIndex].Str != NULL) { + Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str); + } + } + } + + if (FieldIndex != RP_FIELD_IDX_MAX) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) || + (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) || + (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1) + ) { + + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // Get the IP address of the target. + // + Field = &Fields[RP_FIELD_IDX_SERVERNAME]; + if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) { + IpMode = ConfigNvData->IpMode; + } else { + IpMode = ConfigData->AutoConfigureMode; + } + + Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip); + CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS)); + + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Check the protocol type. + // + Field = &Fields[RP_FIELD_IDX_PROTOCOL]; + if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // Get the port of the iSCSI target. + // + Field = &Fields[RP_FIELD_IDX_PORT]; + if (Field->Str != NULL) { + ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str); + } else { + ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT; + } + // + // Get the LUN. + // + Field = &Fields[RP_FIELD_IDX_LUN]; + if (Field->Str != NULL) { + Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } else { + ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun)); + } + // + // Get the target iSCSI Name. + // + Field = &Fields[RP_FIELD_IDX_TARGETNAME]; + + if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + // + // Validate the iSCSI name. + // + Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str)); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + AsciiStrCpyS (ConfigNvData->TargetName, ISCSI_NAME_MAX_SIZE, Field->Str); + +ON_EXIT: + + FreePool (TmpStr); + + return Status; +} + +/** + EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol + instance to intercept events that occurs in the DHCPv6 Information Request + exchange process. + + @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that + is used to configure this callback function. + @param[in] Context Pointer to the context that is initialized in + the EFI_DHCP6_PROTOCOL.InfoRequest(). + @param[in] Packet Pointer to Reply packet that has been received. + The EFI DHCPv6 Protocol instance is responsible + for freeing the buffer. + + @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish + Information Request exchange process. + @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue + Information Request exchange process. + @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort + the Information Request exchange process. + @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish + the Information Request exchange process because some + request information are not received. + +**/ +EFI_STATUS +EFIAPI +IScsiDhcp6ParseReply ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT32 Index; + UINT32 OptionCount; + EFI_DHCP6_PACKET_OPTION *BootFileOpt; + EFI_DHCP6_PACKET_OPTION **OptionList; + ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData; + UINT16 ParaLen; + + OptionCount = 0; + BootFileOpt = NULL; + + Status = This->Parse (This, Packet, &OptionCount, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_READY; + } + + OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *)); + if (OptionList == NULL) { + return EFI_NOT_READY; + } + + Status = This->Parse (This, Packet, &OptionCount, OptionList); + if (EFI_ERROR (Status)) { + Status = EFI_NOT_READY; + goto Exit; + } + + ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context; + + for (Index = 0; Index < OptionCount; Index++) { + OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode); + OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen); + + // + // Get DNS server addresses from this reply packet. + // + if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) { + + if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + // + // Primary DNS server address. + // + CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS)); + + if (OptionList[Index]->OpLen > 16) { + // + // Secondary DNS server address + // + CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS)); + } + + } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) { + // + // The server sends this option to inform the client about an URL to a boot file. + // + BootFileOpt = OptionList[Index]; + } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_PARA) { + // + // The server sends this option to inform the client about DHCP6 server address. + // + if (OptionList[Index]->OpLen < 18) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + // + // Check param-len 1, should be 16 bytes. + // + CopyMem (&ParaLen, &OptionList[Index]->Data[0], sizeof (UINT16)); + if (NTOHS (ParaLen) != 16) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[2], sizeof (EFI_IPv6_ADDRESS)); + } + } + + if (BootFileOpt == NULL) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option + // + Status = IScsiDhcp6ExtractRootPath ( + (CHAR8 *) BootFileOpt->Data, + BootFileOpt->OpLen, + ConfigData + ); + +Exit: + + FreePool (OptionList); + return Status; +} + + +/** + Parse the DHCP ACK to get the address configuration and DNS information. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller; + @param[in, out] ConfigData The attempt configuration data. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_NO_MAPPING DHCP failed to acquire address and other + information. + @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted. + @retval EFI_DEVICE_ERROR Some unexpected error occurred. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the + operation. + @retval EFI_NO_MEDIA There was a media error. + +**/ +EFI_STATUS +IScsiDoDhcp6 ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ) +{ + EFI_HANDLE Dhcp6Handle; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_STATUS Status; + EFI_STATUS TimerStatus; + EFI_DHCP6_PACKET_OPTION *Oro; + EFI_DHCP6_RETRANSMISSION InfoReqReXmit; + EFI_EVENT Timer; + BOOLEAN MediaPresent; + + // + // Check media status before doing DHCP. + // + MediaPresent = TRUE; + NetLibDetectMedia (Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // iSCSI will only request target info from DHCPv6 server. + // + if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) { + return EFI_SUCCESS; + } + + Dhcp6Handle = NULL; + Dhcp6 = NULL; + Oro = NULL; + Timer = NULL; + + // + // Create a DHCP6 child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Controller, + Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Dhcp6Handle + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Dhcp6, + Image, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 5); + if (Oro == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Ask the server to reply with DNS and Boot File URL options by info request. + // All members in EFI_DHCP6_PACKET_OPTION are in network order. + // + Oro->OpCode = HTONS (DHCP6_OPT_REQUEST_OPTION); + Oro->OpLen = HTONS (2 * 3); + Oro->Data[1] = DHCP6_OPT_DNS_SERVERS; + Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL; + Oro->Data[5] = DHCP6_OPT_BOOT_FILE_PARA; + + InfoReqReXmit.Irt = 4; + InfoReqReXmit.Mrc = 1; + InfoReqReXmit.Mrt = 10; + InfoReqReXmit.Mrd = 30; + + Status = Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + NULL, + IScsiDhcp6ParseReply, + ConfigData + ); + if (Status == EFI_NO_MAPPING) { + Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + Timer, + TimerRelative, + ISCSI_GET_MAPPING_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + do { + + TimerStatus = gBS->CheckEvent (Timer); + + if (!EFI_ERROR (TimerStatus)) { + Status = Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + NULL, + IScsiDhcp6ParseReply, + ConfigData + ); + } + + } while (TimerStatus == EFI_NOT_READY); + + } + +ON_EXIT: + + if (Oro != NULL) { + FreePool (Oro); + } + + if (Timer != NULL) { + gBS->CloseEvent (Timer); + } + + if (Dhcp6 != NULL) { + gBS->CloseProtocol ( + Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + Image, + Controller + ); + } + + NetLibDestroyServiceChild ( + Controller, + Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + Dhcp6Handle + ); + + return Status; +} + diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDhcp6.h b/Core/NetworkPkg/IScsiDxe/IScsiDhcp6.h new file mode 100644 index 0000000000..4ca25bd494 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiDhcp6.h @@ -0,0 +1,80 @@ +/** @file + The header file of iSCSI DHCP6 related configuration routines. + +Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_DHCP6_H_ +#define _ISCSI_DHCP6_H_ + +#define DHCP6_OPT_REQUEST_OPTION 6 +#define DHCP6_OPT_VENDOR_INFO 17 +#define DHCP6_OPT_DNS_SERVERS 23 +/// +/// Assigned by IANA, RFC 5970 +/// +#define DHCP6_OPT_BOOT_FILE_URL 59 +#define DHCP6_OPT_BOOT_FILE_PARA 60 + +#define ISCSI_ROOT_PATH_ID "iscsi:" +#define ISCSI_ROOT_PATH_FIELD_DELIMITER ':' +#define ISCSI_ROOT_PATH_ADDR_START_DELIMITER '[' +#define ISCSI_ROOT_PATH_ADDR_END_DELIMITER ']' + + +/** + Extract the Root Path option and get the required target information from + Boot File Uniform Resource Locator (URL) Option. + + @param[in] RootPath The RootPath string. + @param[in] Length Length of the RootPath option payload. + @param[in, out] ConfigData The iSCSI session configuration data read from + nonvolatile device. + + @retval EFI_SUCCESS All required information is extracted from the + RootPath option. + @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_INVALID_PARAMETER The RootPath is malformatted. + +**/ +EFI_STATUS +IScsiDhcp6ExtractRootPath ( + IN CHAR8 *RootPath, + IN UINT16 Length, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ); + +/** + Parse the DHCP ACK to get the address configuration and DNS information. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller; + @param[in, out] ConfigData The attempt configuration data. + + @retval EFI_SUCCESS The DNS information is got from the DHCP ACK. + @retval EFI_NO_MAPPING DHCP failed to acquire address and other + information. + @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted. + @retval EFI_DEVICE_ERROR Some unexpected error happened. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the + operation. + @retval EFI_NO_MEDIA There was a media error. + +**/ +EFI_STATUS +IScsiDoDhcp6 ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller, + IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData + ); + +#endif diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDriver.c b/Core/NetworkPkg/IScsiDxe/IScsiDriver.c new file mode 100644 index 0000000000..0d9406e727 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiDriver.c @@ -0,0 +1,1647 @@ +/** @file + The entry point of IScsi driver. + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +EFI_DRIVER_BINDING_PROTOCOL gIScsiIp4DriverBinding = { + IScsiIp4DriverBindingSupported, + IScsiIp4DriverBindingStart, + IScsiIp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gIScsiIp6DriverBinding = { + IScsiIp6DriverBindingSupported, + IScsiIp6DriverBindingStart, + IScsiIp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_GUID gIScsiV4PrivateGuid = ISCSI_V4_PRIVATE_GUID; +EFI_GUID gIScsiV6PrivateGuid = ISCSI_V6_PRIVATE_GUID; +ISCSI_PRIVATE_DATA *mPrivate = NULL; + +/** + Tests to see if this driver supports the RemainingDevicePath. + + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The RemainingDevicePath is supported or NULL. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +IScsiIsDevicePathSupported ( + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_DEVICE_PATH_PROTOCOL *CurrentDevicePath; + + CurrentDevicePath = RemainingDevicePath; + if (CurrentDevicePath != NULL) { + while (!IsDevicePathEnd (CurrentDevicePath)) { + if ((CurrentDevicePath->Type == MESSAGING_DEVICE_PATH) && (CurrentDevicePath->SubType == MSG_ISCSI_DP)) { + return EFI_SUCCESS; + } + + CurrentDevicePath = NextDevicePathNode (CurrentDevicePath); + } + + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + + +/** + Tests to see if this driver supports a given controller. This is the worker function for + IScsiIp4(6)DriverBindingSupported. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +IScsiSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *IScsiServiceBindingGuid; + EFI_GUID *TcpServiceBindingGuid; + EFI_GUID *DhcpServiceBindingGuid; + + if (IpVersion == IP_VERSION_4) { + IScsiServiceBindingGuid = &gIScsiV4PrivateGuid; + TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + DhcpServiceBindingGuid = &gEfiDhcp4ServiceBindingProtocolGuid; + } else { + IScsiServiceBindingGuid = &gIScsiV6PrivateGuid; + TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + DhcpServiceBindingGuid = &gEfiDhcp6ServiceBindingProtocolGuid; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + IScsiServiceBindingGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + TcpServiceBindingGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = IScsiIsDevicePathSupported (RemainingDevicePath); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + if (IScsiDhcpIsConfigured (ControllerHandle, IpVersion)) { + Status = gBS->OpenProtocol ( + ControllerHandle, + DhcpServiceBindingGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + + +/** + Start to manage the controller. This is the worker function for + IScsiIp4(6)DriverBindingStart. + + @param[in] Image Handle of the image. + @param[in] ControllerHandle Handle of the controller. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCES This driver was started. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_NOT_FOUND There is no sufficient information to establish + the iScsi session. + @retval EFI_DEVICE_ERROR Failed to get TCP connection device path. + +**/ +EFI_STATUS +IScsiStart ( + IN EFI_HANDLE Image, + IN EFI_HANDLE ControllerHandle, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + ISCSI_DRIVER_DATA *Private; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + ISCSI_SESSION *Session; + UINT8 Index; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExistIScsiExtScsiPassThru; + ISCSI_DRIVER_DATA *ExistPrivate; + UINT8 *AttemptConfigOrder; + UINTN AttemptConfigOrderSize; + UINT8 BootSelected; + EFI_HANDLE *HandleBuffer; + UINTN NumberOfHandles; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_GUID *IScsiPrivateGuid; + EFI_GUID *TcpServiceBindingGuid; + CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN]; + BOOLEAN NeedUpdate; + VOID *Interface; + EFI_GUID *ProtocolGuid; + + // + // Test to see if iSCSI driver supports the given controller. + // + + if (IpVersion == IP_VERSION_4) { + IScsiPrivateGuid = &gIScsiV4PrivateGuid; + TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + ProtocolGuid = &gEfiTcp4ProtocolGuid; + } else if (IpVersion == IP_VERSION_6) { + IScsiPrivateGuid = &gIScsiV6PrivateGuid; + TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + ProtocolGuid = &gEfiTcp6ProtocolGuid; + } else { + return EFI_INVALID_PARAMETER; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + IScsiPrivateGuid, + NULL, + Image, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + TcpServiceBindingGuid, + NULL, + Image, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Record the incoming NIC info. + // + Status = IScsiAddNic (ControllerHandle); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create the instance private data. + // + Private = IScsiCreateDriverData (Image, ControllerHandle); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create a underlayer child instance, but not need to configure it. Just open ChildHandle + // via BY_DRIVER. That is, establishing the relationship between ControllerHandle and ChildHandle. + // Therefore, when DisconnectController(), especially VLAN virtual controller handle, + // IScsiDriverBindingStop() will be called. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + Image, + TcpServiceBindingGuid, + &Private->ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->ChildHandle, /// Default Tcp child + ProtocolGuid, + &Interface, + Image, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Always install private protocol no matter what happens later. We need to + // keep the relationship between ControllerHandle and ChildHandle. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + IScsiPrivateGuid, + EFI_NATIVE_INTERFACE, + &Private->IScsiIdentifier + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (IpVersion == IP_VERSION_4) { + mPrivate->Ipv6Flag = FALSE; + } else { + mPrivate->Ipv6Flag = TRUE; + } + + // + // Get the current iSCSI configuration data. + // + Status = IScsiGetConfigData (Private); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // If there is already a successul attempt, check whether this attempt is the + // first "enabled for MPIO" attempt. If not, still try the first attempt. + // In single path mode, try all attempts. + // + ExistPrivate = NULL; + Status = EFI_NOT_FOUND; + + if (mPrivate->OneSessionEstablished && mPrivate->EnableMpio) { + AttemptConfigData = NULL; + NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) { + AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) { + break; + } + } + + if (AttemptConfigData == NULL) { + goto ON_ERROR; + } + + if (AttemptConfigData->AttemptConfigIndex == mPrivate->BootSelectedIndex) { + goto ON_EXIT; + } + + // + // Uninstall the original ExtScsiPassThru first. + // + + // + // Locate all ExtScsiPassThru protocol instances. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiExtScsiPassThruProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Find ExtScsiPassThru protocol instance produced by this driver. + // + ExistIScsiExtScsiPassThru = NULL; + for (Index = 0; Index < NumberOfHandles && ExistIScsiExtScsiPassThru == NULL; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath + ); + if (EFI_ERROR (Status)) { + continue; + } + + while (!IsDevicePathEnd (DevicePath)) { + if ((DevicePath->Type == MESSAGING_DEVICE_PATH) && (DevicePath->SubType == MSG_MAC_ADDR_DP)) { + // + // Get the ExtScsiPassThru protocol instance. + // + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiExtScsiPassThruProtocolGuid, + (VOID **) &ExistIScsiExtScsiPassThru + ); + ASSERT_EFI_ERROR (Status); + break; + } + + DevicePath = NextDevicePathNode (DevicePath); + } + } + + FreePool (HandleBuffer); + + if (ExistIScsiExtScsiPassThru == NULL) { + Status = EFI_NOT_FOUND; + goto ON_ERROR; + } + + ExistPrivate = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (ExistIScsiExtScsiPassThru); + + Status = gBS->UninstallProtocolInterface ( + ExistPrivate->ExtScsiPassThruHandle, + &gEfiExtScsiPassThruProtocolGuid, + &ExistPrivate->IScsiExtScsiPassThru + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Install the Ext SCSI PASS THRU protocol. + // + Status = gBS->InstallProtocolInterface ( + &Private->ExtScsiPassThruHandle, + &gEfiExtScsiPassThruProtocolGuid, + EFI_NATIVE_INTERFACE, + &Private->IScsiExtScsiPassThru + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + BootSelected = 0; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) { + AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + // + // Don't process the attempt that does not associate with the current NIC or + // this attempt is disabled or established. + // + if (AttemptConfigData->NicIndex != mPrivate->CurrentNic || + AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED || + AttemptConfigData->ValidPath) { + continue; + } + + // + // In multipath mode, don't process attempts configured for single path. + // In default single path mode, don't process attempts configured for multipath. + // + if ((mPrivate->EnableMpio && + AttemptConfigData->SessionConfigData.Enabled != ISCSI_ENABLED_FOR_MPIO) || + (!mPrivate->EnableMpio && + AttemptConfigData->SessionConfigData.Enabled != ISCSI_ENABLED)) { + continue; + } + + // + // Don't process the attempt that fails to get the init/target information from DHCP. + // + if (AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp && + !AttemptConfigData->DhcpSuccess) { + if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) { + mPrivate->ValidSinglePathCount--; + } + continue; + } + + // + // Don't process the autoconfigure path if it is already established. + // + if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG && + AttemptConfigData->AutoConfigureSuccess) { + continue; + } + + // + // Don't process the attempt if its IP mode is not in the current IP version. + // + if (!mPrivate->Ipv6Flag) { + if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) { + continue; + } + if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG && + AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) { + continue; + } + } else { + if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) { + continue; + } + if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG && + AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) { + continue; + } + } + + // + // Fill in the Session and init it. + // + Session = (ISCSI_SESSION *) AllocateZeroPool (sizeof (ISCSI_SESSION)); + if (Session == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Session->Private = Private; + Session->ConfigData = AttemptConfigData; + Session->AuthType = AttemptConfigData->AuthenticationType; + + AsciiStrToUnicodeStrS (AttemptConfigData->MacString, MacString, sizeof (MacString) / sizeof (MacString[0])); + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"%s%d", + MacString, + (UINTN) AttemptConfigData->AttemptConfigIndex + ); + + if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) { + Session->AuthData.CHAP.AuthConfig = &AttemptConfigData->AuthConfigData.CHAP; + } + + IScsiSessionInit (Session, FALSE); + + // + // Try to login and create an iSCSI session according to the configuration. + // + Status = IScsiSessionLogin (Session); + if (Status == EFI_MEDIA_CHANGED) { + // + // The specified target is not available, and the redirection information is + // received. Login the session again with the updated target address. + // + Status = IScsiSessionLogin (Session); + } else if (Status == EFI_NOT_READY) { + Status = IScsiSessionReLogin (Session); + } + + if (EFI_ERROR (Status)) { + // + // In Single path mode, only the successful attempt will be recorded in iBFT; + // in multi-path mode, all the attempt entries in MPIO will be recorded in iBFT. + // + if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) { + mPrivate->ValidSinglePathCount--; + } + + FreePool (Session); + + } else { + AttemptConfigData->ValidPath = TRUE; + + // + // Do not record the attempt in iBFT if it login with KRB5. + // TODO: record KRB5 attempt information in the iSCSI device path. + // + if (Session->AuthType == ISCSI_AUTH_TYPE_KRB) { + if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) { + mPrivate->ValidSinglePathCount--; + } + + AttemptConfigData->ValidiBFTPath = FALSE; + } else { + AttemptConfigData->ValidiBFTPath = TRUE; + } + + // + // IScsi session success. Update the attempt state to NVR. + // + if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) { + AttemptConfigData->AutoConfigureSuccess = TRUE; + } + + gRT->SetVariable ( + mPrivate->PortString, + &gEfiIScsiInitiatorNameProtocolGuid, + ISCSI_CONFIG_VAR_ATTR, + sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA), + AttemptConfigData + ); + + // + // Select the first login session. Abort others. + // + if (Private->Session == NULL) { + Private->Session = Session; + BootSelected = AttemptConfigData->AttemptConfigIndex; + // + // Don't validate other attempt in multipath mode if one is success. + // + if (mPrivate->EnableMpio) { + break; + } + } else { + IScsiSessionAbort (Session); + FreePool (Session); + } + } + } + + // + // All attempts configured for this driver instance are not valid. + // + if (Private->Session == NULL) { + Status = gBS->UninstallProtocolInterface ( + Private->ExtScsiPassThruHandle, + &gEfiExtScsiPassThruProtocolGuid, + &Private->IScsiExtScsiPassThru + ); + ASSERT_EFI_ERROR (Status); + Private->ExtScsiPassThruHandle = NULL; + + // + // Reinstall the original ExtScsiPassThru back. + // + if (mPrivate->OneSessionEstablished && ExistPrivate != NULL) { + Status = gBS->InstallProtocolInterface ( + &ExistPrivate->ExtScsiPassThruHandle, + &gEfiExtScsiPassThruProtocolGuid, + EFI_NATIVE_INTERFACE, + &ExistPrivate->IScsiExtScsiPassThru + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + goto ON_EXIT; + } + + Status = EFI_NOT_FOUND; + + goto ON_ERROR; + } + + NeedUpdate = TRUE; + // + // More than one attempt successes. + // + if (Private->Session != NULL && mPrivate->OneSessionEstablished) { + + AttemptConfigOrder = IScsiGetVariableAndSize ( + L"AttemptOrder", + &gIScsiConfigGuid, + &AttemptConfigOrderSize + ); + if (AttemptConfigOrder == NULL) { + goto ON_ERROR; + } + for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) { + if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex || + AttemptConfigOrder[Index] == BootSelected) { + break; + } + } + + if (mPrivate->EnableMpio) { + // + // Use the attempt in earlier order. Abort the later one in MPIO. + // + if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex) { + IScsiSessionAbort (Private->Session); + FreePool (Private->Session); + Private->Session = NULL; + gBS->UninstallProtocolInterface ( + Private->ExtScsiPassThruHandle, + &gEfiExtScsiPassThruProtocolGuid, + &Private->IScsiExtScsiPassThru + ); + Private->ExtScsiPassThruHandle = NULL; + + // + // Reinstall the original ExtScsiPassThru back. + // + Status = gBS->InstallProtocolInterface ( + &ExistPrivate->ExtScsiPassThruHandle, + &gEfiExtScsiPassThruProtocolGuid, + EFI_NATIVE_INTERFACE, + &ExistPrivate->IScsiExtScsiPassThru + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + goto ON_EXIT; + } else { + if (AttemptConfigOrder[Index] != BootSelected) { + goto ON_ERROR; + } + mPrivate->BootSelectedIndex = BootSelected; + // + // Clear the resource in ExistPrivate. + // + gBS->UninstallProtocolInterface ( + ExistPrivate->Controller, + IScsiPrivateGuid, + &ExistPrivate->IScsiIdentifier + ); + + IScsiRemoveNic (ExistPrivate->Controller); + if (ExistPrivate->Session != NULL) { + IScsiSessionAbort (ExistPrivate->Session); + } + + IScsiCleanDriverData (ExistPrivate); + } + } else { + // + // Use the attempt in earlier order as boot selected in single path mode. + // + if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex) { + NeedUpdate = FALSE; + } + } + + } + + if (NeedUpdate) { + mPrivate->OneSessionEstablished = TRUE; + mPrivate->BootSelectedIndex = BootSelected; + } + + // + // Duplicate the Session's tcp connection device path. The source port field + // will be set to zero as one iSCSI session is comprised of several iSCSI + // connections. + // + Private->DevicePath = IScsiGetTcpConnDevicePath (Private->Session); + if (Private->DevicePath == NULL) { + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + // + // Install the updated device path onto the ExtScsiPassThruHandle. + // + Status = gBS->InstallProtocolInterface ( + &Private->ExtScsiPassThruHandle, + &gEfiDevicePathProtocolGuid, + EFI_NATIVE_INTERFACE, + Private->DevicePath + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // ISCSI children should share the default Tcp child, just open the default Tcp child via BY_CHILD_CONTROLLER. + // + Status = gBS->OpenProtocol ( + Private->ChildHandle, /// Default Tcp child + ProtocolGuid, + &Interface, + Image, + Private->ExtScsiPassThruHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Private->ExtScsiPassThruHandle, + &gEfiExtScsiPassThruProtocolGuid, + &Private->IScsiExtScsiPassThru, + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + NULL + ); + + goto ON_ERROR; + } + +ON_EXIT: + + // + // Update/Publish the iSCSI Boot Firmware Table. + // + if (mPrivate->BootSelectedIndex != 0) { + IScsiPublishIbft (); + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (Private->Session != NULL) { + IScsiSessionAbort (Private->Session); + } + + return Status; +} + +/** + Stops a device controller or a bus controller. This is the worker function for + IScsiIp4(6)DriverBindingStop. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +IScsiStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_HANDLE IScsiController; + EFI_STATUS Status; + ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier; + ISCSI_DRIVER_DATA *Private; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru; + ISCSI_CONNECTION *Conn; + EFI_GUID *ProtocolGuid; + EFI_GUID *TcpServiceBindingGuid; + EFI_GUID *TcpProtocolGuid; + + + if (NumberOfChildren != 0) { + // + // We should have only one child. + // + Status = gBS->OpenProtocol ( + ChildHandleBuffer[0], + &gEfiExtScsiPassThruProtocolGuid, + (VOID **) &PassThru, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru); + Conn = NET_LIST_HEAD (&Private->Session->Conns, ISCSI_CONNECTION, Link); + + // + // Previously the TCP protocol is opened BY_CHILD_CONTROLLER. Just close + // the protocol here, but do not uninstall the device path protocol and + // EXT SCSI PASS THRU protocol installed on ExtScsiPassThruHandle. + // + if (IpVersion == IP_VERSION_4) { + ProtocolGuid = &gEfiTcp4ProtocolGuid; + } else { + ProtocolGuid = &gEfiTcp6ProtocolGuid; + } + + gBS->CloseProtocol ( + Private->ChildHandle, + ProtocolGuid, + Private->Image, + Private->ExtScsiPassThruHandle + ); + + gBS->CloseProtocol ( + Conn->TcpIo.Handle, + ProtocolGuid, + Private->Image, + Private->ExtScsiPassThruHandle + ); + + return EFI_SUCCESS; + } + + // + // Get the handle of the controller we are controling. + // + if (IpVersion == IP_VERSION_4) { + ProtocolGuid = &gIScsiV4PrivateGuid; + TcpProtocolGuid = &gEfiTcp4ProtocolGuid; + TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + ProtocolGuid = &gIScsiV6PrivateGuid; + TcpProtocolGuid = &gEfiTcp6ProtocolGuid; + TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + IScsiController = NetLibGetNicHandle (ControllerHandle, TcpProtocolGuid); + if (IScsiController == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + IScsiController, + ProtocolGuid, + (VOID **) &IScsiIdentifier, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Private = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier); + ASSERT (Private != NULL); + + if (Private->ChildHandle != NULL) { + Status = gBS->CloseProtocol ( + Private->ChildHandle, + TcpProtocolGuid, + This->DriverBindingHandle, + IScsiController + ); + + ASSERT (!EFI_ERROR (Status)); + + Status = NetLibDestroyServiceChild ( + IScsiController, + This->DriverBindingHandle, + TcpServiceBindingGuid, + Private->ChildHandle + ); + + ASSERT (!EFI_ERROR (Status)); + } + + gBS->UninstallProtocolInterface ( + IScsiController, + ProtocolGuid, + &Private->IScsiIdentifier + ); + + // + // Remove this NIC. + // + IScsiRemoveNic (IScsiController); + + // + // Update the iSCSI Boot Firware Table. + // + IScsiPublishIbft (); + + if (Private->Session != NULL) { + IScsiSessionAbort (Private->Session); + } + + IScsiCleanDriverData (Private); + + return EFI_SUCCESS; +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +IScsiIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return IScsiSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error. Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +IScsiIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + Status = IScsiStart (This->DriverBindingHandle, ControllerHandle, IP_VERSION_4); + if (Status == EFI_ALREADY_STARTED) { + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +IScsiIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + return IScsiStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_4 + ); +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +IScsiIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return IScsiSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error. Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +IScsiIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + Status = IScsiStart (This->DriverBindingHandle, ControllerHandle, IP_VERSION_6); + if (Status == EFI_ALREADY_STARTED) { + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +IScsiIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + return IScsiStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_6 + ); +} + +/** + Unload the iSCSI driver. + + @param[in] ImageHandle The handle of the driver image. + + @retval EFI_SUCCESS The driver is unloaded. + @retval EFI_DEVICE_ERROR An unexpected error occurred. + +**/ +EFI_STATUS +EFIAPI +IScsiUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + UINTN DeviceHandleCount; + EFI_HANDLE *DeviceHandleBuffer; + UINTN Index; + EFI_COMPONENT_NAME_PROTOCOL *ComponentName; + EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2; + + // + // Try to disonnect the driver from the devices it's controlling. + // + Status = gBS->LocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &DeviceHandleCount, + &DeviceHandleBuffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Disconnect the iSCSI4 driver from the controlled device. + // + for (Index = 0; Index < DeviceHandleCount; Index++) { + Status = IScsiTestManagedDevice ( + DeviceHandleBuffer[Index], + gIScsiIp4DriverBinding.DriverBindingHandle, + &gEfiTcp4ProtocolGuid) + ; + if (EFI_ERROR (Status)) { + continue; + } + Status = gBS->DisconnectController ( + DeviceHandleBuffer[Index], + gIScsiIp4DriverBinding.DriverBindingHandle, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + // + // Disconnect the iSCSI6 driver from the controlled device. + // + for (Index = 0; Index < DeviceHandleCount; Index++) { + Status = IScsiTestManagedDevice ( + DeviceHandleBuffer[Index], + gIScsiIp6DriverBinding.DriverBindingHandle, + &gEfiTcp6ProtocolGuid + ); + if (EFI_ERROR (Status)) { + continue; + } + Status = gBS->DisconnectController ( + DeviceHandleBuffer[Index], + gIScsiIp6DriverBinding.DriverBindingHandle, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + // + // Unload the iSCSI configuration form. + // + Status = IScsiConfigFormUnload (gIScsiIp4DriverBinding.DriverBindingHandle); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Uninstall the protocols installed by iSCSI driver. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiAuthenticationInfoProtocolGuid, + &gIScsiAuthenticationInfo, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (gIScsiControllerNameTable!= NULL) { + Status = FreeUnicodeStringTable (gIScsiControllerNameTable); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + gIScsiControllerNameTable = NULL; + } + + // + // Uninstall the ComponentName and ComponentName2 protocol from iSCSI4 driver binding handle + // if it has been installed. + // + Status = gBS->HandleProtocol ( + gIScsiIp4DriverBinding.DriverBindingHandle, + &gEfiComponentNameProtocolGuid, + (VOID **) &ComponentName + ); + if (!EFI_ERROR (Status)) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + gIScsiIp4DriverBinding.DriverBindingHandle, + &gEfiComponentNameProtocolGuid, + ComponentName, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + Status = gBS->HandleProtocol ( + gIScsiIp4DriverBinding.DriverBindingHandle, + &gEfiComponentName2ProtocolGuid, + (VOID **) &ComponentName2 + ); + if (!EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + gIScsiIp4DriverBinding.DriverBindingHandle, + &gEfiComponentName2ProtocolGuid, + ComponentName2, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + // + // Uninstall the ComponentName and ComponentName2 protocol from iSCSI6 driver binding handle + // if it has been installed. + // + Status = gBS->HandleProtocol ( + gIScsiIp6DriverBinding.DriverBindingHandle, + &gEfiComponentNameProtocolGuid, + (VOID **) &ComponentName + ); + if (!EFI_ERROR (Status)) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + gIScsiIp6DriverBinding.DriverBindingHandle, + &gEfiComponentNameProtocolGuid, + ComponentName, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + Status = gBS->HandleProtocol ( + gIScsiIp6DriverBinding.DriverBindingHandle, + &gEfiComponentName2ProtocolGuid, + (VOID **) &ComponentName2 + ); + if (!EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + gIScsiIp6DriverBinding.DriverBindingHandle, + &gEfiComponentName2ProtocolGuid, + ComponentName2, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + // + // Uninstall the IScsiInitiatorNameProtocol and all the driver binding protocols. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + gIScsiIp4DriverBinding.DriverBindingHandle, + &gEfiDriverBindingProtocolGuid, + &gIScsiIp4DriverBinding, + &gEfiIScsiInitiatorNameProtocolGuid, + &gIScsiInitiatorName, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->UninstallMultipleProtocolInterfaces ( + gIScsiIp6DriverBinding.DriverBindingHandle, + &gEfiDriverBindingProtocolGuid, + &gIScsiIp6DriverBinding, + NULL + ); + +ON_EXIT: + + if (DeviceHandleBuffer != NULL) { + FreePool (DeviceHandleBuffer); + } + + return Status; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + The entry point for iSCSI driver which initializes the global variables and + installs the driver binding, component name protocol, iSCSI initiator name + protocol and Authentication Info protocol on its image. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +IScsiDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_ISCSI_INITIATOR_NAME_PROTOCOL *IScsiInitiatorName; + EFI_AUTHENTICATION_INFO_PROTOCOL *AuthenticationInfo; + + // + // There should be only one EFI_ISCSI_INITIATOR_NAME_PROTOCOL. + // + Status = gBS->LocateProtocol ( + &gEfiIScsiInitiatorNameProtocolGuid, + NULL, + (VOID **) &IScsiInitiatorName + ); + if (!EFI_ERROR (Status)) { + return EFI_ACCESS_DENIED; + } + + // + // Initialize the EFI Driver Library. + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gIScsiIp4DriverBinding, + ImageHandle, + &gIScsiComponentName, + &gIScsiComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gIScsiIp6DriverBinding, + NULL, + &gIScsiComponentName, + &gIScsiComponentName2 + ); + if (EFI_ERROR (Status)) { + goto Error1; + } + + // + // Install the iSCSI Initiator Name Protocol. + // + Status = gBS->InstallProtocolInterface ( + &ImageHandle, + &gEfiIScsiInitiatorNameProtocolGuid, + EFI_NATIVE_INTERFACE, + &gIScsiInitiatorName + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + + // + // Create the private data structures. + // + mPrivate = AllocateZeroPool (sizeof (ISCSI_PRIVATE_DATA)); + if (mPrivate == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error3; + } + + InitializeListHead (&mPrivate->NicInfoList); + InitializeListHead (&mPrivate->AttemptConfigs); + + // + // Initialize the configuration form of iSCSI. + // + Status = IScsiConfigFormInit (gIScsiIp4DriverBinding.DriverBindingHandle); + if (EFI_ERROR (Status)) { + goto Error4; + } + + // + // There should be only one EFI_AUTHENTICATION_INFO_PROTOCOL. If already exists, + // do not produce the protocol instance. + // + Status = gBS->LocateProtocol ( + &gEfiAuthenticationInfoProtocolGuid, + NULL, + (VOID **) &AuthenticationInfo + ); + if (Status == EFI_NOT_FOUND) { + Status = gBS->InstallProtocolInterface ( + &ImageHandle, + &gEfiAuthenticationInfoProtocolGuid, + EFI_NATIVE_INTERFACE, + &gIScsiAuthenticationInfo + ); + if (EFI_ERROR (Status)) { + goto Error5; + } + } + + return EFI_SUCCESS; + +Error5: + IScsiConfigFormUnload (gIScsiIp4DriverBinding.DriverBindingHandle); + +Error4: + FreePool (mPrivate); + +Error3: + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiIScsiInitiatorNameProtocolGuid, + &gIScsiInitiatorName, + NULL + ); + +Error2: + gBS->UninstallMultipleProtocolInterfaces ( + gIScsiIp6DriverBinding.DriverBindingHandle, + &gEfiDriverBindingProtocolGuid, + &gIScsiIp6DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gIScsiComponentName2, + &gEfiComponentNameProtocolGuid, + &gIScsiComponentName, + NULL + ); + +Error1: + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gIScsiIp4DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gIScsiComponentName2, + &gEfiComponentNameProtocolGuid, + &gIScsiComponentName, + NULL + ); + + return Status; +} + diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDriver.h b/Core/NetworkPkg/IScsiDxe/IScsiDriver.h new file mode 100644 index 0000000000..338e3dcf18 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiDriver.h @@ -0,0 +1,809 @@ +/** @file + The header file of IScsiDriver.c. + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_DRIVER_H_ +#define _ISCSI_DRIVER_H_ + +#define ISCSI_V4_PRIVATE_GUID \ + { \ + 0xfa3cde4c, 0x87c2, 0x427d, { 0xae, 0xde, 0x7d, 0xd0, 0x96, 0xc8, 0x8c, 0x58 } \ + } + +#define ISCSI_V6_PRIVATE_GUID \ + { \ + 0x28be27e5, 0x66cc, 0x4a31, { 0xa3, 0x15, 0xdb, 0x14, 0xc3, 0x74, 0x4d, 0x85 } \ + } + +#define ISCSI_INITIATOR_NAME_VAR_NAME L"I_NAME" + +#define IP_MODE_AUTOCONFIG_IP4 3 +#define IP_MODE_AUTOCONFIG_IP6 4 + +extern EFI_COMPONENT_NAME2_PROTOCOL gIScsiComponentName2; +extern EFI_COMPONENT_NAME_PROTOCOL gIScsiComponentName; +extern EFI_UNICODE_STRING_TABLE *gIScsiControllerNameTable; +extern EFI_ISCSI_INITIATOR_NAME_PROTOCOL gIScsiInitiatorName; +extern EFI_AUTHENTICATION_INFO_PROTOCOL gIScsiAuthenticationInfo; +extern EFI_EXT_SCSI_PASS_THRU_PROTOCOL gIScsiExtScsiPassThruProtocolTemplate; +extern EFI_GUID gIScsiV4PrivateGuid; +extern EFI_GUID gIScsiV6PrivateGuid; + +typedef struct { + CHAR16 PortString[ISCSI_NAME_IFR_MAX_SIZE]; + LIST_ENTRY NicInfoList; + UINT8 NicCount; + UINT8 CurrentNic; + UINT8 MaxNic; + BOOLEAN Ipv6Flag; + BOOLEAN OneSessionEstablished; + BOOLEAN EnableMpio; + UINT8 MpioCount; // The number of attempts in MPIO. + UINT8 Krb5MpioCount; // The number of attempts login with KRB5 in MPIO. + UINT8 SinglePathCount; // The number of single path attempts. + UINT8 ValidSinglePathCount; // The number of valid single path attempts. + UINT8 BootSelectedIndex; + UINT8 AttemptCount; + LIST_ENTRY AttemptConfigs; // User configured Attempt list. + CHAR8 InitiatorName[ISCSI_NAME_MAX_SIZE]; + UINTN InitiatorNameLength; + VOID *NewAttempt; // Attempt is created but not saved. +} ISCSI_PRIVATE_DATA; + +extern ISCSI_PRIVATE_DATA *mPrivate; + +typedef struct { + LIST_ENTRY Link; + UINT32 HwAddressSize; + EFI_MAC_ADDRESS PermanentAddress; + UINT8 NicIndex; + UINT16 VlanId; + UINTN BusNumber; + UINTN DeviceNumber; + UINTN FunctionNumber; +} ISCSI_NIC_INFO; + +typedef struct _ISCSI_PRIVATE_PROTOCOL { + UINT32 Reserved; +} ISCSI_PRIVATE_PROTOCOL; + +// +// EFI Driver Binding Protocol for iSCSI driver. +// + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +IScsiIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error. Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +IScsiIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +IScsiIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +IScsiIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error. Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failed to start the device. + +**/ +EFI_STATUS +EFIAPI +IScsiIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +IScsiIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +// +// EFI Component Name(2) Protocol for iSCSI driver. +// + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IScsiComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is + determined by the driver writer. Language is + specified inRFC 4646 or ISO 639-2 language code + format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IScsiComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI iSCSI Initiator Name Protocol for iSCSI driver. +// + +/** + Retrieves the current set value of iSCSI Initiator Name. + + @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL + instance. + @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer / + Actual size of the variable data buffer. + @param[out] Buffer Pointer to the buffer for data to be read. + + @retval EFI_SUCCESS Data was successfully retrieved into the provided + buffer and the BufferSize was sufficient to handle + the iSCSI initiator name. + @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the result. BufferSize + will be updated with the size required to complete + the request. Buffer will not be affected. + @retval EFI_INVALID_PARAMETER BufferSize is NULL. BufferSize and Buffer will not + be affected. + @retval EFI_INVALID_PARAMETER Buffer is NULL. BufferSize and Buffer will not be + affected. + @retval EFI_DEVICE_ERROR The iSCSI initiator name could not be retrieved + due to a hardware error. + +**/ +EFI_STATUS +EFIAPI +IScsiGetInitiatorName ( + IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + Sets the iSSI Initiator Name. + + @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL + instance. + @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer. + @param[in] Buffer Pointer to the buffer for data to be written. + + @retval EFI_SUCCESS Data was successfully stored by the protocol. + @retval EFI_UNSUPPORTED Platform policies do not allow for data to be + written. + @retval EFI_INVALID_PARAMETER BufferSize exceeds the maximum allowed limit. + BufferSize will be updated with the maximum size + required to complete the request. + @retval EFI_INVALID_PARAMETER Buffersize is NULL. BufferSize and Buffer will not + be affected. + @retval EFI_INVALID_PARAMETER Buffer is NULL. BufferSize and Buffer will not be + affected. + @retval EFI_DEVICE_ERROR The data could not be stored due to a hardware + error. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the data + @retval EFI_PROTOCOL_ERROR Input iSCSI initiator name does not adhere to RFC + 3720 + +**/ +EFI_STATUS +EFIAPI +IScsiSetInitiatorName ( + IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ); + +// +// EFI_AUTHENTICATION_INFO_PROTOCOL for iSCSI driver. +// + +/** + Retrieves the authentication information associated with a particular controller handle. + + @param[in] This Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL. + @param[in] ControllerHandle Handle to the Controller. + @param[out] Buffer Pointer to the authentication information. This function is + responsible for allocating the buffer and it is the caller's + responsibility to free buffer when the caller is finished with buffer. + + @retval EFI_DEVICE_ERROR The authentication information could not be + retrieved due to a hardware error. + +**/ +EFI_STATUS +EFIAPI +IScsiGetAuthenticationInfo ( + IN EFI_AUTHENTICATION_INFO_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + OUT VOID **Buffer + ); + +/** + Set the authentication information for a given controller handle. + + @param[in] This Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL. + @param[in] ControllerHandle Handle to the Controller. + @param[in] Buffer Pointer to the authentication information. + + @retval EFI_UNSUPPORTED If the platform policies do not allow setting of + the authentication information. + +**/ +EFI_STATUS +EFIAPI +IScsiSetAuthenticationInfo ( + IN EFI_AUTHENTICATION_INFO_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN VOID *Buffer + ); + +// +// EFI_EXT_SCSI_PASS_THRU_PROTOCOL for iSCSI driver. +// + +/** + Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. + This function supports both blocking I/O and nonblocking I/O. The blocking I/O + functionality is required, and the nonblocking I/O functionality is optional. + + @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param[in] Target The Target is an array of size TARGET_MAX_BYTES and it + represents the id of the SCSI device to send the SCSI + Request Packet. Each transport driver may choose to + utilize a subset of this size to suit the needs + of transport target representation. For example, a + Fibre Channel driver may use only 8 bytes (WWN) + to represent an FC target. + @param[in] Lun The LUN of the SCSI device to send the SCSI Request Packet. + @param[in, out] Packet A pointer to the SCSI Request Packet to send to the + SCSI device specified by Target and Lun. + @param[in] Event If nonblocking I/O is not supported then Event is ignored, + and blocking I/O is performed. If Event is NULL, then + blocking I/O is performed. If Event is not NULL and non + blocking I/O is supported, then nonblocking I/O is performed, + and Event will be signaled when the SCSI Request Packet + completes. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For + bi-directional commands, InTransferLength bytes + were transferred from InDataBuffer. + For write and bi-directional commands, OutTransferLength + bytes were transferred by OutDataBuffer. + @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. + The number of bytes that could be transferred is + returned in InTransferLength. For write and + bi-directional commands, OutTransferLength bytes + were transferred by OutDataBuffer. + @retval EFI_NOT_READY The SCSI Request Packet could not be sent because + there are too many SCSI Request Packets already + queued. The caller may retry later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send + the SCSI Request Packet. + @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket + are invalid. + @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet + is not supported by the host adapter. + This includes the case of Bi-directional SCSI + commands not supported by the implementation. + The SCSI Request Packet was not sent, + so no additional status information is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI + Request Packet to execute. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruFunction ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on + a SCSI channel. These can either be the list SCSI devices that are actually + present on the SCSI channel, or the list of legal Target Ids and LUNs for the + SCSI channel. Regardless, the caller of this function must probe the Target ID + and LUN returned to see if a SCSI device is actually present at that location + on the SCSI channel. + + @param[in] This The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param[in, out] Target On input, a pointer to the Target ID of a SCSI + device present on the SCSI channel. On output, a + pointer to the Target ID of the next SCSI device + present on a SCSI channel. An input value of + 0xFFFFFFFF retrieves the Target ID of the first + SCSI device present on a SCSI channel. + @param[in, out] Lun On input, a pointer to the LUN of a SCSI device + present on the SCSI channel. On output, a pointer + to the LUN of the next SCSI device present on a + SCSI channel. + + @retval EFI_SUCCESS The Target ID and Lun of the next SCSI device on + the SCSI channel was returned in Target and Lun. + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI + channel. + @retval EFI_INVALID_PARAMETER Target is not 0xFFFFFFFF,and Target and Lun were + not returned on a previous call to + GetNextDevice(). + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ); + +/** + Allocate and build a device path node for a SCSI device on a SCSI channel. + + @param[in] This Protocol instance pointer. + @param[in] Target The Target ID of the SCSI device for which a + device path node is to be allocated and built. + @param[in] Lun The LUN of the SCSI device for which a device + path node is to be allocated and built. + @param[in, out] DevicePath A pointer to a single device path node that + describes the SCSI device specified by Target and + Lun. This function is responsible for allocating + the buffer DevicePath with the boot service + AllocatePool(). It is the caller's + responsibility to free DevicePath when the caller + is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SCSI + device specified by Target and Lun was allocated + and returned in DevicePath. + @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does + not exist on the SCSI channel. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate + DevicePath. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +/** + Translate a device path node to a Target ID and LUN. + + @param[in] This Protocol instance pointer. + @param[in] DevicePath A pointer to the device path node that describes + a SCSI device on the SCSI channel. + @param[out] Target A pointer to the Target ID of a SCSI device on + the SCSI channel. + @param[out] Lun A pointer to the LUN of a SCSI device on the SCSI + channel. + + @retval EFI_SUCCESS DevicePath was successfully translated to a + Target ID and LUN, and they were returned in + Target and Lun. + @retval EFI_INVALID_PARAMETER DevicePath/Target/Lun is NULL. + @retval EFI_UNSUPPORTED This driver does not support the device path node + type in DevicePath. + @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target + ID and LUN does not exist. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ); + +/** + Resets a SCSI channel.This operation resets all the SCSI devices connected to + the SCSI channel. + + @param[in] This Protocol instance pointer. + + @retval EFI_UNSUPPORTED It is not supported. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ); + +/** + Resets a SCSI device that is connected to a SCSI channel. + + @param[in] This Protocol instance pointer. + @param[in] Target The Target ID of the SCSI device to reset. + @param[in] Lun The LUN of the SCSI device to reset. + + @retval EFI_UNSUPPORTED It is not supported. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruResetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ); + +/** + Retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. + + @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL + instance. + @param[in, out] Target (TARGET_MAX_BYTES) of a SCSI device present on + the SCSI channel. On output, a pointer to the + Target ID (an array of TARGET_MAX_BYTES) of the + next SCSI device present on a SCSI channel. + An input value of 0xF(all bytes in the array are 0xF) + in the Target array retrieves the Target ID of the + first SCSI device present on a SCSI channel. + + @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI + channel was returned in Target. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not + returned on a previous call to GetNextTarget(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ); + +#endif diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDxe.inf b/Core/NetworkPkg/IScsiDxe/IScsiDxe.inf new file mode 100644 index 0000000000..3e20828c51 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiDxe.inf @@ -0,0 +1,128 @@ +## @file +# Client-side iSCSI service. +# +# The iSCSI driver provides iSCSI service in the preboot environment and supports +# booting over iSCSI. +# +# Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = IScsiDxe + FILE_GUID = 86CDDF93-4872-4597-8AF9-A35AE4D3725F + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = IScsiDriverEntryPoint + UNLOAD_IMAGE = IScsiUnload + MODULE_UNI_FILE = IScsiDxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# +# DRIVER_BINDING = gIScsiDriverBinding +# COMPONENT_NAME = gIScsiComponentName +# COMPONENT_NAME2 = gIScsiComponentName2 +# + + +[Sources] + ComponentName.c + IScsiAuthenticationInfo.c + IScsiCHAP.h + IScsiCHAP.c + IScsiConfig.c + IScsiConfig.h + IScsiConfigNVDataStruc.h + IScsiConfigStrings.uni + IScsiConfigVfr.vfr + IScsiDhcp.c + IScsiDhcp.h + IScsiDhcp6.c + IScsiDhcp6.h + IScsiDriver.c + IScsiDriver.h + IScsiExtScsiPassThru.c + IScsiIbft.c + IScsiIbft.h + IScsiInitiatorName.c + IScsiImpl.h + IScsiMisc.c + IScsiMisc.h + IScsiProto.c + IScsiProto.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + NetworkPkg/NetworkPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + DevicePathLib + HiiLib + MemoryAllocationLib + NetLib + TcpIoLib + PrintLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + UefiRuntimeServicesTableLib + UefiHiiServicesLib + BaseCryptLib + +[Protocols] + gEfiAcpiTableProtocolGuid ## SOMETIMES_CONSUMES ## SystemTable + gEfiDriverBindingProtocolGuid ## SOMETIMES_PRODUCES + gEfiPciIoProtocolGuid ## SOMETIMES_CONSUMES + gEfiDhcp4ProtocolGuid ## TO_START + gEfiDhcp6ProtocolGuid ## TO_START + gEfiDhcp4ServiceBindingProtocolGuid ## TO_START + gEfiDhcp6ServiceBindingProtocolGuid ## TO_START + gEfiTcp4ProtocolGuid ## TO_START + gEfiTcp6ProtocolGuid ## TO_START + gEfiTcp4ServiceBindingProtocolGuid ## TO_START + gEfiTcp6ServiceBindingProtocolGuid ## TO_START + gEfiExtScsiPassThruProtocolGuid ## BY_START + gEfiHiiConfigAccessProtocolGuid ## PRODUCES + ## TO_START + ## PRODUCES + gEfiDevicePathProtocolGuid + ## PRODUCES + ## UNDEFINED # Variable + gEfiIScsiInitiatorNameProtocolGuid + ## PRODUCES + gEfiAuthenticationInfoProtocolGuid + +[Guids] + gEfiEventExitBootServicesGuid ## SOMETIMES_CONSUMES ## Event + gEfiIfrTianoGuid ## SOMETIMES_PRODUCES ## UNDEFINED + gEfiAcpiTableGuid ## SOMETIMES_CONSUMES ## SystemTable + gEfiAcpi10TableGuid ## SOMETIMES_CONSUMES ## SystemTable + gEfiAcpi20TableGuid ## SOMETIMES_CONSUMES ## SystemTable + + ## SOMETIMES_PRODUCES ## Variable:L"AttemptOrder" + ## SOMETIMES_CONSUMES ## Variable:L"AttemptOrder" + ## SOMETIMES_CONSUMES ## UNDEFINED # HiiIsConfigHdrMatch mVendorStorageName + ## SOMETIMES_PRODUCES ## UNDEFINED # HiiConstructConfigHdr mVendorStorageName + ## SOMETIMES_PRODUCES ## UNDEFINED # HiiGetBrowserData mVendorStorageName + ## SOMETIMES_CONSUMES ## UNDEFINED # HiiSetBrowserData mVendorStorageName + ## SOMETIMES_CONSUMES ## HII + gIScsiConfigGuid + +[UserExtensions.TianoCore."ExtraFiles"] + IScsiDxeExtra.uni diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDxe.uni b/Core/NetworkPkg/IScsiDxe/IScsiDxe.uni new file mode 100644 index 0000000000..89bac6d4e1 Binary files /dev/null and b/Core/NetworkPkg/IScsiDxe/IScsiDxe.uni differ diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni b/Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni new file mode 100644 index 0000000000..fe6988ffdd Binary files /dev/null and b/Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni differ diff --git a/Core/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c b/Core/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c new file mode 100644 index 0000000000..f9712449b5 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c @@ -0,0 +1,425 @@ +/** @file + The implementation of EFI_EXT_SCSI_PASS_THRU_PROTOCOL. + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +EFI_EXT_SCSI_PASS_THRU_PROTOCOL gIScsiExtScsiPassThruProtocolTemplate = { + NULL, + IScsiExtScsiPassThruFunction, + IScsiExtScsiPassThruGetNextTargetLun, + IScsiExtScsiPassThruBuildDevicePath, + IScsiExtScsiPassThruGetTargetLun, + IScsiExtScsiPassThruResetChannel, + IScsiExtScsiPassThruResetTargetLun, + IScsiExtScsiPassThruGetNextTarget +}; + + +/** + Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. + This function supports both blocking I/O and nonblocking I/O. The blocking I/O + functionality is required, and the nonblocking I/O functionality is optional. + + @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param[in] Target The Target is an array of size TARGET_MAX_BYTES and it + represents the id of the SCSI device to send the SCSI + Request Packet. Each transport driver may choose to + utilize a subset of this size to suit the needs + of transport target representation. For example, a + Fibre Channel driver may use only 8 bytes (WWN) + to represent an FC target. + @param[in] Lun The LUN of the SCSI device to send the SCSI Request Packet. + @param[in, out] Packet A pointer to the SCSI Request Packet to send to the + SCSI device specified by Target and Lun. + @param[in] Event If nonblocking I/O is not supported then Event is ignored, + and blocking I/O is performed. If Event is NULL, then + blocking I/O is performed. If Event is not NULL and non + blocking I/O is supported, then nonblocking I/O is performed, + and Event will be signaled when the SCSI Request Packet + completes. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For + bi-directional commands, InTransferLength bytes + were transferred from InDataBuffer. + For write and bi-directional commands, OutTransferLength + bytes were transferred by OutDataBuffer. + @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. + The number of bytes that could be transferred is + returned in InTransferLength. For write and + bi-directional commands, OutTransferLength bytes + were transferred by OutDataBuffer. + @retval EFI_NOT_READY The SCSI Request Packet could not be sent because + there are too many SCSI Request Packets already + queued. The caller may retry later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send + the SCSI Request Packet. + @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket, + are invalid. + @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet + is not supported by the host adapter. + This includes the case of Bi-directional SCSI + commands not supported by the implementation. + The SCSI Request Packet was not sent, + so no additional status information is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI + Request Packet to execute. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruFunction ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + EFI_STATUS Status; + ISCSI_DRIVER_DATA *Private; + + if (Target[0] != 0) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet == NULL) || (Packet->Cdb == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = IScsiExecuteScsiCommand (This, Target, Lun, Packet); + if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) { + // + // Try to reinstate the session and re-execute the Scsi command. + // + Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This); + if (EFI_ERROR (IScsiSessionReinstatement (Private->Session))) { + return EFI_DEVICE_ERROR; + } + + Status = IScsiExecuteScsiCommand (This, Target, Lun, Packet); + } + + return Status; +} + + +/** + Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on + a SCSI channel. These can either be the list SCSI devices that are actually + present on the SCSI channel, or the list of legal Target Ids and LUNs for the + SCSI channel. Regardless, the caller of this function must probe the Target ID + and LUN returned to see if a SCSI device is actually present at that location + on the SCSI channel. + + @param[in] This The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param[in, out] Target On input, a pointer to the Target ID of a SCSI + device present on the SCSI channel. On output, a + pointer to the Target ID of the next SCSI device + present on a SCSI channel. An input value of + 0xFFFFFFFF retrieves the Target ID of the first + SCSI device present on a SCSI channel. + @param[in, out] Lun On input, a pointer to the LUN of a SCSI device + present on the SCSI channel. On output, a pointer + to the LUN of the next SCSI device present on a + SCSI channel. + + @retval EFI_SUCCESS The Target ID and Lun of the next SCSI device on + the SCSI channel was returned in Target and Lun. + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI + channel. + @retval EFI_INVALID_PARAMETER Target is not 0xFFFFFFFF,and Target and Lun were + not returned on a previous call to + GetNextDevice(). + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ) +{ + ISCSI_DRIVER_DATA *Private; + ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData; + UINT8 TargetId[TARGET_MAX_BYTES]; + + Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This); + ConfigNvData = &Private->Session->ConfigData->SessionConfigData; + + if ((*Target)[0] == 0 && (CompareMem (Lun, ConfigNvData->BootLun, sizeof (UINT64)) == 0)) { + // + // Only one pair per iSCSI Driver instance. + // + return EFI_NOT_FOUND; + } + + SetMem (TargetId, TARGET_MAX_BYTES, 0xFF); + if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) { + (*Target)[0] = 0; + CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64)); + + return EFI_SUCCESS; + } + + return EFI_INVALID_PARAMETER; +} + + +/** + Allocate and build a device path node for a SCSI device on a SCSI channel. + + @param[in] This Protocol instance pointer. + @param[in] Target The Target ID of the SCSI device for which a + device path node is to be allocated and built. + @param[in] Lun The LUN of the SCSI device for which a device + path node is to be allocated and built. + @param[in, out] DevicePath A pointer to a single device path node that + describes the SCSI device specified by Target and + Lun. This function is responsible for allocating + the buffer DevicePath with the boot service + AllocatePool(). It is the caller's + responsibility to free DevicePath when the caller + is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SCSI + device specified by Target and Lun was allocated + and returned in DevicePath. + @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does + not exist on the SCSI channel. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate + DevicePath. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + ISCSI_DRIVER_DATA *Private; + ISCSI_SESSION *Session; + ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData; + ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig; + EFI_DEV_PATH *Node; + UINTN DevPathNodeLen; + + if ((DevicePath == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (Target[0] != 0) { + return EFI_NOT_FOUND; + } + + Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This); + Session = Private->Session; + ConfigNvData = &Session->ConfigData->SessionConfigData; + AuthConfig = Session->AuthData.CHAP.AuthConfig; + + if (CompareMem (&Lun, ConfigNvData->BootLun, sizeof (UINT64)) != 0) { + return EFI_NOT_FOUND; + } + + DevPathNodeLen = sizeof (ISCSI_DEVICE_PATH) + AsciiStrLen (ConfigNvData->TargetName) + 1; + Node = AllocateZeroPool (DevPathNodeLen); + if (Node == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_ISCSI_DP; + SetDevicePathNodeLength (&Node->DevPath, DevPathNodeLen); + + // + // 0 for TCP, others are reserved. + // + Node->Iscsi.NetworkProtocol = 0; + + Node->Iscsi.LoginOption = 0; + + switch (Session->AuthType) { + case ISCSI_AUTH_TYPE_NONE: + Node->Iscsi.LoginOption |= 0x0800; + break; + + case ISCSI_AUTH_TYPE_CHAP: + // + // Bit12: 0=CHAP_BI, 1=CHAP_UNI + // + if (AuthConfig->CHAPType == ISCSI_CHAP_UNI) { + Node->Iscsi.LoginOption |= 0x1000; + } + break; + + default: + break; + } + + CopyMem (&Node->Iscsi.Lun, ConfigNvData->BootLun, sizeof (UINT64)); + Node->Iscsi.TargetPortalGroupTag = Session->TargetPortalGroupTag; + AsciiStrCpyS ((CHAR8 *) Node + sizeof (ISCSI_DEVICE_PATH), AsciiStrLen (ConfigNvData->TargetName) + 1, ConfigNvData->TargetName); + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node; + + return EFI_SUCCESS; +} + + +/** + Translate a device path node to a Target ID and LUN. + + @param[in] This Protocol instance pointer. + @param[in] DevicePath A pointer to the device path node that describes + a SCSI device on the SCSI channel. + @param[out] Target A pointer to the Target ID of a SCSI device on + the SCSI channel. + @param[out] Lun A pointer to the LUN of a SCSI device on the SCSI + channel. + + @retval EFI_SUCCESS DevicePath was successfully translated to a + Target ID and LUN, and they were returned in + Target and Lun. + @retval EFI_INVALID_PARAMETER DevicePath/Target/Lun is NULL. + @retval EFI_UNSUPPORTED This driver does not support the device path node + type in DevicePath. + @retval EFI_NOT_FOUND A valid translation does not exist from DevicePath + to a TargetID and LUN. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ) +{ + ISCSI_DRIVER_DATA *Private; + ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData; + + if ((DevicePath == NULL) || (Target == NULL) || (Lun == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || + (DevicePath->SubType != MSG_ISCSI_DP) || + (DevicePathNodeLength (DevicePath) <= sizeof (ISCSI_DEVICE_PATH)) + ) { + return EFI_UNSUPPORTED; + } + + Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This); + ConfigNvData = &Private->Session->ConfigData->SessionConfigData; + + SetMem (*Target, TARGET_MAX_BYTES, 0xFF); + (*Target)[0] = 0; + + if (AsciiStrCmp (ConfigNvData->TargetName, (CHAR8 *) DevicePath + sizeof (ISCSI_DEVICE_PATH)) != 0) { + return EFI_UNSUPPORTED; + } + + CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64)); + + return EFI_SUCCESS; +} + + +/** + Resets a SCSI channel. This operation resets all the SCSI devices connected to + the SCSI channel. + + @param[in] This Protocol instance pointer. + + @retval EFI_UNSUPPORTED It is not supported. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Resets a SCSI device that is connected to a SCSI channel. + + @param[in] This Protocol instance pointer. + @param[in] Target The Target ID of the SCSI device to reset. + @param[in] Lun The LUN of the SCSI device to reset. + + @retval EFI_UNSUPPORTED It is not supported. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruResetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. + + @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL + instance. + @param[in, out] Target (TARGET_MAX_BYTES) of a SCSI device present on + the SCSI channel. On output, a pointer to the + Target ID (an array of TARGET_MAX_BYTES) of the + next SCSI device present on a SCSI channel. + An input value of 0xF(all bytes in the array are 0xF) + in the Target array retrieves the Target ID of the + first SCSI device present on a SCSI channel. + + @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI + channel was returned in Target. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not + returned on a previous call to GetNextTarget(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +EFI_STATUS +EFIAPI +IScsiExtScsiPassThruGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ) +{ + UINT8 TargetId[TARGET_MAX_BYTES]; + + SetMem (TargetId, TARGET_MAX_BYTES, 0xFF); + + if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) { + (*Target)[0] = 0; + return EFI_SUCCESS; + } else if ((*Target)[0] == 0) { + return EFI_NOT_FOUND; + } else { + return EFI_INVALID_PARAMETER; + } +} + diff --git a/Core/NetworkPkg/IScsiDxe/IScsiIbft.c b/Core/NetworkPkg/IScsiDxe/IScsiIbft.c new file mode 100644 index 0000000000..3c179bfc51 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiIbft.c @@ -0,0 +1,546 @@ +/** @file + Implementation for iSCSI Boot Firmware Table publication. + +Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +BOOLEAN mIbftInstalled = FALSE; +UINTN mTableKey; + +/** + Initialize the header of the iSCSI Boot Firmware Table. + + @param[out] Header The header of the iSCSI Boot Firmware Table. + @param[in] OemId The OEM ID. + @param[in] OemTableId The OEM table ID for the iBFT. + +**/ +VOID +IScsiInitIbfTableHeader ( + OUT EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Header, + IN UINT8 *OemId, + IN UINT64 *OemTableId + ) +{ + Header->Signature = EFI_ACPI_3_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE; + Header->Length = IBFT_HEAP_OFFSET; + Header->Revision = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_REVISION; + Header->Checksum = 0; + + CopyMem (Header->OemId, OemId, sizeof (Header->OemId)); + CopyMem (&Header->OemTableId, OemTableId, sizeof (UINT64)); +} + + +/** + Initialize the control section of the iSCSI Boot Firmware Table. + + @param[in] Table The ACPI table. + +**/ +VOID +IScsiInitControlSection ( + IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table + ) +{ + EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control; + UINTN NumOffset; + + Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1); + + Control->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_ID; + Control->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_VERSION; + Control->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE); + + // + // If in multipathing mode, enable the Boot Failover Flag. + // If in single path mode, disable it. Mix-model is not allowed. + // + // BUGBUG: if Boot Failover Flag is set to 1, the OS installer cannot + // find the iSCSI mapped disk. So still keep not set for single path mode. + // + if (mPrivate->EnableMpio) { + Control->Header.Flags = 0; + NumOffset = 2 * (mPrivate->MpioCount - mPrivate->Krb5MpioCount); + } else { + NumOffset = 2 * mPrivate->ValidSinglePathCount; + } + + // + // Each attempt occupies two offsets: one for the NIC section; + // the other for the Target section. + // + if (NumOffset > 4) { + // + // Need expand the control section if more than 2 NIC/Target attempts + // exist. + // + Control->Header.Length = (UINT16) (Control->Header.Length + (NumOffset - 4) * sizeof (UINT16)); + } +} + + +/** + Add one item into the heap. + + @param[in, out] Heap On input, the current address of the heap. On output, the address of + the heap after the item is added. + @param[in] Data The data to add into the heap. + @param[in] Len Length of the Data in byte. + +**/ +VOID +IScsiAddHeapItem ( + IN OUT UINT8 **Heap, + IN VOID *Data, + IN UINTN Len + ) +{ + // + // Add one byte for the NULL delimiter. + // + *Heap -= Len + 1; + + CopyMem (*Heap, Data, Len); + *(*Heap + Len) = 0; +} + + +/** + Fill the Initiator section of the iSCSI Boot Firmware Table. + + @param[in] Table The ACPI table. + @param[in, out] Heap The heap. + +**/ +VOID +IScsiFillInitiatorSection ( + IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table, + IN OUT UINT8 **Heap + ) +{ + EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control; + EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *Initiator; + + Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1); + + // + // Initiator section immediately follows the control section. + // + Initiator = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *) + ((UINT8 *) Control + IBFT_ROUNDUP (Control->Header.Length)); + + Control->InitiatorOffset = (UINT16) ((UINTN) Initiator - (UINTN) Table); + + Initiator->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_ID; + Initiator->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_VERSION; + Initiator->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE); + Initiator->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BLOCK_VALID | + EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BOOT_SELECTED; + + // + // Fill the iSCSI Initiator Name into the heap. + // + IScsiAddHeapItem (Heap, mPrivate->InitiatorName, mPrivate->InitiatorNameLength - 1); + + Initiator->IScsiNameLength = (UINT16) (mPrivate->InitiatorNameLength - 1); + Initiator->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); +} + + +/** + Map the v4 IP address into v6 IP address. + + @param[in] V4 The v4 IP address. + @param[out] V6 The v6 IP address. + +**/ +VOID +IScsiMapV4ToV6Addr ( + IN EFI_IPv4_ADDRESS *V4, + OUT EFI_IPv6_ADDRESS *V6 + ) +{ + UINTN Index; + + ZeroMem (V6, sizeof (EFI_IPv6_ADDRESS)); + + V6->Addr[10] = 0xff; + V6->Addr[11] = 0xff; + + for (Index = 0; Index < 4; Index++) { + V6->Addr[12 + Index] = V4->Addr[Index]; + } +} + + +/** + Fill the NIC and target sections in iSCSI Boot Firmware Table. + + @param[in] Table The buffer of the ACPI table. + @param[in, out] Heap The heap buffer used to store the variable length + parameters such as iSCSI name. + +**/ +VOID +IScsiFillNICAndTargetSections ( + IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table, + IN OUT UINT8 **Heap + ) +{ + EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control; + EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *Nic; + EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *Target; + ISCSI_SESSION_CONFIG_NVDATA *NvData; + ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig; + UINT16 *SectionOffset; + UINTN Index; + UINT16 Length; + LIST_ENTRY *Entry; + ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt; + ISCSI_NIC_INFO *NicInfo; + BOOLEAN Flag; + + // + // Get the offset of the first Nic and Target section. + // + Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1); + Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Table + + Control->InitiatorOffset + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE))); + Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic + + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE))); + + SectionOffset = &Control->NIC0Offset; + + Index = 0; + Flag = TRUE; + + NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) { + if (Index == 0) { + // + // First entry should be boot selected entry. + // + Attempt = IScsiConfigGetAttemptByConfigIndex (mPrivate->BootSelectedIndex); + if (Attempt == NULL) { + // + // First boot selected entry can not be found. + // + break; + } + + ASSERT (Attempt->SessionConfigData.Enabled != ISCSI_DISABLED); + + } else { + if (Index == 1 && Flag) { + Entry = mPrivate->AttemptConfigs.ForwardLink; + Flag = FALSE; + } + + Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + if (Attempt->AttemptConfigIndex == mPrivate->BootSelectedIndex) { + continue; + } + } + + if (Attempt->SessionConfigData.Enabled == ISCSI_DISABLED) { + continue; + } + + // + // Krb5 attempt will not be recorded in iBFT. + // + if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_KRB) { + continue; + } + + // + // If multipath mode is enabled, only the attempts in MPIO will be recorded in iBFT. + // + if (mPrivate->EnableMpio && Attempt->SessionConfigData.Enabled != ISCSI_ENABLED_FOR_MPIO) { + continue; + } + + // + // Only the valid attempts will be recorded. + // + if (!Attempt->ValidiBFTPath) { + continue; + } + + NvData = &Attempt->SessionConfigData; + AuthConfig = &Attempt->AuthConfigData.CHAP; + + // + // Fill the Nic section. + // + + Nic->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_ID; + Nic->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_VERSION; + Nic->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE); + Nic->Header.Index = (UINT8) Index; + Nic->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BLOCK_VALID | + EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_GLOBAL; + + if (Index == 0) { + Nic->Header.Flags |= EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BOOT_SELECTED; + } + + if (NvData->InitiatorInfoFromDhcp) { + Nic->Origin = IpPrefixOriginDhcp; + } else { + Nic->Origin = IpPrefixOriginManual; + } + + if (NvData->IpMode == IP_MODE_IP4 || NvData->IpMode == IP_MODE_AUTOCONFIG) { + // + // Get the subnet mask prefix length. + // + Nic->SubnetMaskPrefixLength = IScsiGetSubnetMaskPrefixLength (&NvData->SubnetMask); + + // + // Map the various v4 addresses into v6 addresses. + // + IScsiMapV4ToV6Addr (&NvData->LocalIp.v4, &Nic->Ip); + IScsiMapV4ToV6Addr (&NvData->Gateway.v4, &Nic->Gateway); + IScsiMapV4ToV6Addr (&Attempt->PrimaryDns.v4, &Nic->PrimaryDns); + IScsiMapV4ToV6Addr (&Attempt->SecondaryDns.v4, &Nic->SecondaryDns); + IScsiMapV4ToV6Addr (&Attempt->DhcpServer.v4, &Nic->DhcpServer); + + } else if (NvData->IpMode == IP_MODE_IP6 || NvData->IpMode == IP_MODE_AUTOCONFIG) { + + Nic->SubnetMaskPrefixLength = NvData->PrefixLength; + CopyMem (&Nic->Ip, &NvData->LocalIp, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Nic->Gateway, &NvData->Gateway, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Nic->PrimaryDns, &Attempt->PrimaryDns, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Nic->SecondaryDns, &Attempt->SecondaryDns, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Nic->DhcpServer, &Attempt->DhcpServer, sizeof (EFI_IPv6_ADDRESS)); + + } else { + ASSERT (FALSE); + } + + // + // Get Nic Info: VLAN tag, Mac address, PCI location. + // + NicInfo = IScsiGetNicInfoByIndex (Attempt->NicIndex); + ASSERT (NicInfo != NULL); + + Nic->VLanTag = NicInfo->VlanId; + CopyMem (Nic->Mac, &NicInfo->PermanentAddress, sizeof (Nic->Mac)); + Nic->PciLocation = (UINT16) ((NicInfo->BusNumber << 8) | + (NicInfo->DeviceNumber << 3) | NicInfo->FunctionNumber); + *SectionOffset = (UINT16) ((UINTN) Nic - (UINTN) Table); + SectionOffset++; + + // + // Fill the Target section. + // + + Target->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_ID; + Target->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_VERSION; + Target->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE); + Target->Header.Index = (UINT8) Index; + Target->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BLOCK_VALID; + + if (Index == 0) { + Target->Header.Flags |= EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BOOT_SELECTED; + } + + Target->Port = NvData->TargetPort; + + if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) { + if (AuthConfig->CHAPType == ISCSI_CHAP_UNI) { + Target->CHAPType = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_CHAP; + } else if (AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) { + Target->CHAPType = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_MUTUAL_CHAP; + } + } else if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_NONE) { + Target->CHAPType = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_NO_CHAP; + } + + Target->NicIndex = (UINT8) Index; + + if (NvData->IpMode == IP_MODE_IP4 || NvData->IpMode == IP_MODE_AUTOCONFIG) { + IScsiMapV4ToV6Addr (&NvData->TargetIp.v4, &Target->Ip); + } else if (NvData->IpMode == IP_MODE_IP6 || NvData->IpMode == IP_MODE_AUTOCONFIG) { + CopyMem (&Target->Ip, &NvData->TargetIp, sizeof (EFI_IPv6_ADDRESS)); + } else { + ASSERT (FALSE); + } + + CopyMem (Target->BootLun, NvData->BootLun, sizeof (Target->BootLun)); + + // + // Target iSCSI Name, CHAP name/secret, reverse CHAP name/secret. + // + Length = (UINT16) AsciiStrLen (NvData->TargetName); + IScsiAddHeapItem (Heap, NvData->TargetName, Length); + + Target->IScsiNameLength = Length; + Target->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); + + if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) { + // + // CHAP Name + // + Length = (UINT16) AsciiStrLen (AuthConfig->CHAPName); + IScsiAddHeapItem (Heap, AuthConfig->CHAPName, Length); + Target->CHAPNameLength = Length; + Target->CHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); + + // + // CHAP Secret + // + Length = (UINT16) AsciiStrLen (AuthConfig->CHAPSecret); + IScsiAddHeapItem (Heap, AuthConfig->CHAPSecret, Length); + Target->CHAPSecretLength = Length; + Target->CHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); + + if (Target->CHAPType == EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_MUTUAL_CHAP) { + // + // Reverse CHAP Name. + // + Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPName); + IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPName, Length); + Target->ReverseCHAPNameLength = Length; + Target->ReverseCHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); + + // + // Reverse CHAP Secret. + // + Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPSecret); + IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPSecret, Length); + Target->ReverseCHAPSecretLength = Length; + Target->ReverseCHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); + } + } + + *SectionOffset = (UINT16) ((UINTN) Target - (UINTN) Table); + SectionOffset++; + + // + // Advance to the next NIC/Target pair. + // + Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Target + + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE))); + Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic + + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE))); + + Index++; + } +} + + +/** + Publish and remove the iSCSI Boot Firmware Table according to the iSCSI + session status. + +**/ +VOID +IScsiPublishIbft ( + IN VOID + ) +{ + EFI_STATUS Status; + EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol; + EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table; + EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp; + EFI_ACPI_DESCRIPTION_HEADER *Rsdt; + UINT8 *Heap; + UINT8 Checksum; + UINTN Index; + + + Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol); + if (EFI_ERROR (Status)) { + return ; + } + + // + // Find ACPI table RSD_PTR from the system table. + // + for (Index = 0, Rsdp = NULL; Index < gST->NumberOfTableEntries; Index++) { + if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi20TableGuid) || + CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi10TableGuid) || + CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpiTableGuid) + ) { + // + // A match was found. + // + Rsdp = (EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *) gST->ConfigurationTable[Index].VendorTable; + break; + } + } + + if (Rsdp == NULL) { + return ; + } else { + Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress; + } + + if (mIbftInstalled) { + Status = AcpiTableProtocol->UninstallAcpiTable ( + AcpiTableProtocol, + mTableKey + ); + if (EFI_ERROR (Status)) { + return ; + } + mIbftInstalled = FALSE; + } + + // + // If there is no valid attempt configuration, just return. + // + if ((!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount == 0) || + (mPrivate->EnableMpio && mPrivate->MpioCount <= mPrivate->Krb5MpioCount)) { + return ; + } + + // + // Allocate 4k bytes to hold the ACPI table. + // + Table = AllocateZeroPool (IBFT_MAX_SIZE); + if (Table == NULL) { + return ; + } + + Heap = (UINT8 *) Table + IBFT_HEAP_OFFSET; + + // + // Fill in the various section of the iSCSI Boot Firmware Table. + // + IScsiInitIbfTableHeader (Table, Rsdt->OemId, &Rsdt->OemTableId); + IScsiInitControlSection (Table); + IScsiFillInitiatorSection (Table, &Heap); + IScsiFillNICAndTargetSections (Table, &Heap); + + Checksum = CalculateCheckSum8((UINT8 *)Table, Table->Length); + Table->Checksum = Checksum; + + // + // Install or update the iBFT table. + // + Status = AcpiTableProtocol->InstallAcpiTable ( + AcpiTableProtocol, + Table, + Table->Length, + &mTableKey + ); + if (EFI_ERROR(Status)) { + return; + } + + mIbftInstalled = TRUE; + FreePool (Table); +} diff --git a/Core/NetworkPkg/IScsiDxe/IScsiIbft.h b/Core/NetworkPkg/IScsiDxe/IScsiIbft.h new file mode 100644 index 0000000000..35d5216419 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiIbft.h @@ -0,0 +1,39 @@ +/** @file + Some extra definitions for iBFT. + +Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_IBFT_H_ +#define _ISCSI_IBFT_H_ + +#include +#include +#include +#include + +#define IBFT_TABLE_VAR_NAME L"iBFT" +#define IBFT_MAX_SIZE 4096 +#define IBFT_HEAP_OFFSET 2048 + +#define IBFT_ROUNDUP(size) NET_ROUNDUP ((size), EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_STRUCTURE_ALIGNMENT) + +/** + Publish and remove the iSCSI Boot Firmware Table according to the iSCSI + session status. + +**/ +VOID +IScsiPublishIbft ( + IN VOID + ); + +#endif diff --git a/Core/NetworkPkg/IScsiDxe/IScsiImpl.h b/Core/NetworkPkg/IScsiDxe/IScsiImpl.h new file mode 100644 index 0000000000..7c8eb37842 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiImpl.h @@ -0,0 +1,198 @@ +/** @file + The shared head file for iSCSI driver. + +Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_IMPL_H_ +#define _ISCSI_IMPL_H_ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "IScsiConfigNVDataStruc.h" +#include "IScsiDriver.h" +#include "IScsiProto.h" +#include "IScsiCHAP.h" +#include "IScsiDhcp.h" +#include "IScsiDhcp6.h" +#include "IScsiIbft.h" +#include "IScsiMisc.h" +#include "IScsiConfig.h" + +#define ISCSI_AUTH_INITIAL 0 + +#define ISCSI_SESSION_SIGNATURE SIGNATURE_32 ('I', 'S', 'S', 'N') +/// +/// 10 seconds +/// +#define ISCSI_GET_MAPPING_TIMEOUT 100000000U +/// +/// 3 seconds +/// +#define ISCSI_WAIT_IPSEC_TIMEOUT 30000000U + +struct _ISCSI_SESSION { + UINT32 Signature; + + ISCSI_DRIVER_DATA *Private; + ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData; + + UINT8 AuthType; + union { + ISCSI_CHAP_AUTH_DATA CHAP; + } AuthData; + + UINT8 State; + + UINT8 Isid[6]; + UINT16 Tsih; + + UINT32 CmdSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + + UINT32 InitiatorTaskTag; + UINT16 NextCid; + + LIST_ENTRY Conns; + UINT32 NumConns; + + LIST_ENTRY TcbList; + + // + // Session-wide parameters + // + UINT16 TargetPortalGroupTag; + UINT32 MaxConnections; + BOOLEAN InitialR2T; + BOOLEAN ImmediateData; + UINT32 MaxBurstLength; + UINT32 FirstBurstLength; + UINT32 DefaultTime2Wait; + UINT32 DefaultTime2Retain; + UINT16 MaxOutstandingR2T; + BOOLEAN DataPDUInOrder; + BOOLEAN DataSequenceInOrder; + UINT8 ErrorRecoveryLevel; +}; + +#define ISCSI_CONNECTION_SIGNATURE SIGNATURE_32 ('I', 'S', 'C', 'N') + +struct _ISCSI_CONNECTION { + UINT32 Signature; + LIST_ENTRY Link; + + EFI_EVENT TimeoutEvent; + + ISCSI_SESSION *Session; + + UINT8 State; + UINT8 CurrentStage; + UINT8 NextStage; + + UINT8 AuthStep; + + BOOLEAN PartialReqSent; + BOOLEAN PartialRspRcvd; + + BOOLEAN TransitInitiated; + BOOLEAN ParamNegotiated; + + UINT16 Cid; + UINT32 ExpStatSN; + + // + // Queues... + // + NET_BUF_QUEUE RspQue; + + BOOLEAN Ipv6Flag; + TCP_IO TcpIo; + + // + // Connection-only parameters. + // + UINT32 MaxRecvDataSegmentLength; + ISCSI_DIGEST_TYPE HeaderDigest; + ISCSI_DIGEST_TYPE DataDigest; +}; + +#define ISCSI_DRIVER_DATA_SIGNATURE SIGNATURE_32 ('I', 'S', 'D', 'A') + +#define ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU(PassThru) \ + CR ( \ + PassThru, \ + ISCSI_DRIVER_DATA, \ + IScsiExtScsiPassThru, \ + ISCSI_DRIVER_DATA_SIGNATURE \ + ) + +#define ISCSI_DRIVER_DATA_FROM_IDENTIFIER(Identifier) \ + CR ( \ + Identifier, \ + ISCSI_DRIVER_DATA, \ + IScsiIdentifier, \ + ISCSI_DRIVER_DATA_SIGNATURE \ + ) + +struct _ISCSI_DRIVER_DATA { + UINT32 Signature; + EFI_HANDLE Image; + EFI_HANDLE Controller; + ISCSI_PRIVATE_PROTOCOL IScsiIdentifier; + + EFI_EVENT ExitBootServiceEvent; + + EFI_EXT_SCSI_PASS_THRU_PROTOCOL IScsiExtScsiPassThru; + EFI_EXT_SCSI_PASS_THRU_MODE ExtScsiPassThruMode; + EFI_HANDLE ExtScsiPassThruHandle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_HANDLE ChildHandle; + ISCSI_SESSION *Session; +}; + +#endif diff --git a/Core/NetworkPkg/IScsiDxe/IScsiInitiatorName.c b/Core/NetworkPkg/IScsiDxe/IScsiInitiatorName.c new file mode 100644 index 0000000000..7a3934c5c9 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiInitiatorName.c @@ -0,0 +1,136 @@ +/** @file + Implementation for EFI iSCSI Initiator Name Protocol. + +Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +EFI_ISCSI_INITIATOR_NAME_PROTOCOL gIScsiInitiatorName = { + IScsiGetInitiatorName, + IScsiSetInitiatorName +}; + + +/** + Retrieves the current set value of iSCSI Initiator Name. + + @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL + instance. + @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer / + Actual size of the variable data buffer. + @param[out] Buffer Pointer to the buffer for data to be read. + The data is a null-terminated UTF-8 encoded string. + The maximum length is 223 characters, including the null-terminator. + + @retval EFI_SUCCESS Data was successfully retrieved into the provided + buffer and the BufferSize was sufficient to handle + the iSCSI initiator name. + @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the result. BufferSize + will be updated with the size required to complete + the request. Buffer will not be affected. + @retval EFI_INVALID_PARAMETER BufferSize is NULL. BufferSize and Buffer will not + be affected. + @retval EFI_INVALID_PARAMETER Buffer is NULL. BufferSize and Buffer will not be + affected. + @retval EFI_DEVICE_ERROR The iSCSI initiator name could not be retrieved + due to a hardware error. + +**/ +EFI_STATUS +EFIAPI +IScsiGetInitiatorName ( + IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + + if ((BufferSize == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = gRT->GetVariable ( + ISCSI_INITIATOR_NAME_VAR_NAME, + &gEfiIScsiInitiatorNameProtocolGuid, + NULL, + BufferSize, + Buffer + ); + + return Status; +} + + +/** + Sets the iSSI Initiator Name. + + @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL + instance. + @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer. + @param[in] Buffer Pointer to the buffer for data to be written. + The data is a null-terminated UTF-8 encoded string. + The maximum length is 223 characters, including the null-terminator. + + @retval EFI_SUCCESS Data was successfully stored by the protocol. + @retval EFI_UNSUPPORTED Platform policies do not allow for data to be + written. + @retval EFI_INVALID_PARAMETER BufferSize exceeds the maximum allowed limit. + BufferSize will be updated with the maximum size + required to complete the request. + @retval EFI_INVALID_PARAMETER Buffersize is NULL. BufferSize and Buffer will not + be affected. + @retval EFI_INVALID_PARAMETER Buffer is NULL. BufferSize and Buffer will not be + affected. + @retval EFI_DEVICE_ERROR The data could not be stored due to a hardware + error. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the data + @retval EFI_PROTOCOL_ERROR Input iSCSI initiator name does not adhere to RFC + 3720 + +**/ +EFI_STATUS +EFIAPI +IScsiSetInitiatorName ( + IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + + if ((BufferSize == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (*BufferSize > ISCSI_NAME_MAX_SIZE) { + *BufferSize = ISCSI_NAME_MAX_SIZE; + return EFI_INVALID_PARAMETER; + } + // + // Only support iqn iSCSI names. + // + Status = IScsiNormalizeName ((CHAR8 *) Buffer, *BufferSize - 1); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gRT->SetVariable ( + ISCSI_INITIATOR_NAME_VAR_NAME, + &gEfiIScsiInitiatorNameProtocolGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + *BufferSize, + Buffer + ); + + return Status; +} diff --git a/Core/NetworkPkg/IScsiDxe/IScsiMisc.c b/Core/NetworkPkg/IScsiDxe/IScsiMisc.c new file mode 100644 index 0000000000..be8f1532bf --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiMisc.c @@ -0,0 +1,1547 @@ +/** @file + Miscellaneous routines for iSCSI driver. + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 IScsiHexString[] = "0123456789ABCDEFabcdef"; + +/** + Removes (trims) specified leading and trailing characters from a string. + + @param[in, out] Str Pointer to the null-terminated string to be trimmed. + On return, Str will hold the trimmed string. + + @param[in] CharC Character will be trimmed from str. + +**/ +VOID +IScsiStrTrim ( + IN OUT CHAR16 *Str, + IN CHAR16 CharC + ) +{ + CHAR16 *Pointer1; + CHAR16 *Pointer2; + + if (*Str == 0) { + return ; + } + + // + // Trim off the leading and trailing characters c + // + for (Pointer1 = Str; (*Pointer1 != 0) && (*Pointer1 == CharC); Pointer1++) { + ; + } + + Pointer2 = Str; + if (Pointer2 == Pointer1) { + while (*Pointer1 != 0) { + Pointer2++; + Pointer1++; + } + } else { + while (*Pointer1 != 0) { + *Pointer2 = *Pointer1; + Pointer1++; + Pointer2++; + } + *Pointer2 = 0; + } + + + for (Pointer1 = Str + StrLen(Str) - 1; Pointer1 >= Str && *Pointer1 == CharC; Pointer1--) { + ; + } + if (Pointer1 != Str + StrLen(Str) - 1) { + *(Pointer1 + 1) = 0; + } +} + +/** + Calculate the prefix length of the IPv4 subnet mask. + + @param[in] SubnetMask The IPv4 subnet mask. + + @return The prefix length of the subnet mask. + @retval 0 Other errors as indicated. + +**/ +UINT8 +IScsiGetSubnetMaskPrefixLength ( + IN EFI_IPv4_ADDRESS *SubnetMask + ) +{ + UINT8 Len; + UINT32 ReverseMask; + + // + // The SubnetMask is in network byte order. + // + ReverseMask = (SubnetMask->Addr[0] << 24) | (SubnetMask->Addr[1] << 16) | (SubnetMask->Addr[2] << 8) | (SubnetMask->Addr[3]); + + // + // Reverse it. + // + ReverseMask = ~ReverseMask; + + if ((ReverseMask & (ReverseMask + 1)) != 0) { + return 0; + } + + Len = 0; + + while (ReverseMask != 0) { + ReverseMask = ReverseMask >> 1; + Len++; + } + + return (UINT8) (32 - Len); +} + + +/** + Convert the hexadecimal encoded LUN string into the 64-bit LUN. + + @param[in] Str The hexadecimal encoded LUN string. + @param[out] Lun Storage to return the 64-bit LUN. + + @retval EFI_SUCCESS The 64-bit LUN is stored in Lun. + @retval EFI_INVALID_PARAMETER The string is malformatted. + +**/ +EFI_STATUS +IScsiAsciiStrToLun ( + IN CHAR8 *Str, + OUT UINT8 *Lun + ) +{ + UINTN Index, IndexValue, IndexNum, SizeStr; + CHAR8 TemStr[2]; + UINT8 TemValue; + UINT16 Value[4]; + + ZeroMem (Lun, 8); + ZeroMem (TemStr, 2); + ZeroMem ((UINT8 *) Value, sizeof (Value)); + SizeStr = AsciiStrLen (Str); + IndexValue = 0; + IndexNum = 0; + + for (Index = 0; Index < SizeStr; Index ++) { + TemStr[0] = Str[Index]; + TemValue = (UINT8) AsciiStrHexToUint64 (TemStr); + if (TemValue == 0 && TemStr[0] != '0') { + if ((TemStr[0] != '-') || (IndexNum == 0)) { + // + // Invalid Lun Char. + // + return EFI_INVALID_PARAMETER; + } + } + + if ((TemValue == 0) && (TemStr[0] == '-')) { + // + // Next Lun value. + // + if (++IndexValue >= 4) { + // + // Max 4 Lun value. + // + return EFI_INVALID_PARAMETER; + } + // + // Restart str index for the next lun value. + // + IndexNum = 0; + continue; + } + + if (++IndexNum > 4) { + // + // Each Lun Str can't exceed size 4, because it will be as UINT16 value. + // + return EFI_INVALID_PARAMETER; + } + + // + // Combine UINT16 value. + // + Value[IndexValue] = (UINT16) ((Value[IndexValue] << 4) + TemValue); + } + + for (Index = 0; Index <= IndexValue; Index ++) { + *((UINT16 *) &Lun[Index * 2]) = HTONS (Value[Index]); + } + + return EFI_SUCCESS; +} + +/** + Convert the 64-bit LUN into the hexadecimal encoded LUN string. + + @param[in] Lun The 64-bit LUN. + @param[out] Str The storage to return the hexadecimal encoded LUN string. + +**/ +VOID +IScsiLunToUnicodeStr ( + IN UINT8 *Lun, + OUT CHAR16 *Str + ) +{ + UINTN Index; + CHAR16 *TempStr; + + TempStr = Str; + + for (Index = 0; Index < 4; Index++) { + + if ((Lun[2 * Index] | Lun[2 * Index + 1]) == 0) { + CopyMem (TempStr, L"0-", sizeof (L"0-")); + } else { + TempStr[0] = (CHAR16) IScsiHexString[Lun[2 * Index] >> 4]; + TempStr[1] = (CHAR16) IScsiHexString[Lun[2 * Index] & 0x0F]; + TempStr[2] = (CHAR16) IScsiHexString[Lun[2 * Index + 1] >> 4]; + TempStr[3] = (CHAR16) IScsiHexString[Lun[2 * Index + 1] & 0x0F]; + TempStr[4] = L'-'; + TempStr[5] = 0; + + IScsiStrTrim (TempStr, L'0'); + } + + TempStr += StrLen (TempStr); + } + // + // Remove the last '-' + // + ASSERT (StrLen(Str) >= 1); + Str[StrLen (Str) - 1] = 0; + + for (Index = StrLen (Str) - 1; Index > 1; Index = Index - 2) { + if ((Str[Index] == L'0') && (Str[Index - 1] == L'-')) { + Str[Index - 1] = 0; + } else { + break; + } + } +} + +/** + Convert the formatted IP address into the binary IP address. + + @param[in] Str The UNICODE string. + @param[in] IpMode Indicates whether the IP address is v4 or v6. + @param[out] Ip The storage to return the ASCII string. + + @retval EFI_SUCCESS The binary IP address is returned in Ip. + @retval EFI_INVALID_PARAMETER The IP string is malformatted or IpMode is + invalid. + +**/ +EFI_STATUS +IScsiAsciiStrToIp ( + IN CHAR8 *Str, + IN UINT8 IpMode, + OUT EFI_IP_ADDRESS *Ip + ) +{ + EFI_STATUS Status; + + if (IpMode == IP_MODE_IP4 || IpMode == IP_MODE_AUTOCONFIG_IP4) { + return NetLibAsciiStrToIp4 (Str, &Ip->v4); + + } else if (IpMode == IP_MODE_IP6 || IpMode == IP_MODE_AUTOCONFIG_IP6) { + return NetLibAsciiStrToIp6 (Str, &Ip->v6); + + } else if (IpMode == IP_MODE_AUTOCONFIG) { + Status = NetLibAsciiStrToIp4 (Str, &Ip->v4); + if (!EFI_ERROR (Status)) { + return Status; + } + return NetLibAsciiStrToIp6 (Str, &Ip->v6); + + } + + return EFI_INVALID_PARAMETER; +} + +/** + Convert the mac address into a hexadecimal encoded "-" seperated string. + + @param[in] Mac The mac address. + @param[in] Len Length in bytes of the mac address. + @param[in] VlanId VLAN ID of the network device. + @param[out] Str The storage to return the mac string. + +**/ +VOID +IScsiMacAddrToStr ( + IN EFI_MAC_ADDRESS *Mac, + IN UINT32 Len, + IN UINT16 VlanId, + OUT CHAR16 *Str + ) +{ + UINT32 Index; + CHAR16 *String; + + for (Index = 0; Index < Len; Index++) { + Str[3 * Index] = (CHAR16) IScsiHexString[(Mac->Addr[Index] >> 4) & 0x0F]; + Str[3 * Index + 1] = (CHAR16) IScsiHexString[Mac->Addr[Index] & 0x0F]; + Str[3 * Index + 2] = L':'; + } + + String = &Str[3 * Index - 1] ; + if (VlanId != 0) { + String += UnicodeSPrint (String, 6 * sizeof (CHAR16), L"\\%04x", (UINTN) VlanId); + } + + *String = L'\0'; +} + +/** + Convert the binary encoded buffer into a hexadecimal encoded string. + + @param[in] BinBuffer The buffer containing the binary data. + @param[in] BinLength Length of the binary buffer. + @param[in, out] HexStr Pointer to the string. + @param[in, out] HexLength The length of the string. + + @retval EFI_SUCCESS The binary data is converted to the hexadecimal string + and the length of the string is updated. + @retval EFI_BUFFER_TOO_SMALL The string is too small. + @retval EFI_INVALID_PARAMETER The IP string is malformatted. + +**/ +EFI_STATUS +IScsiBinToHex ( + IN UINT8 *BinBuffer, + IN UINT32 BinLength, + IN OUT CHAR8 *HexStr, + IN OUT UINT32 *HexLength + ) +{ + UINTN Index; + + if ((HexStr == NULL) || (BinBuffer == NULL) || (BinLength == 0)) { + return EFI_INVALID_PARAMETER; + } + + if (((*HexLength) - 3) < BinLength * 2) { + *HexLength = BinLength * 2 + 3; + return EFI_BUFFER_TOO_SMALL; + } + + *HexLength = BinLength * 2 + 3; + // + // Prefix for Hex String. + // + HexStr[0] = '0'; + HexStr[1] = 'x'; + + for (Index = 0; Index < BinLength; Index++) { + HexStr[Index * 2 + 2] = IScsiHexString[BinBuffer[Index] >> 4]; + HexStr[Index * 2 + 3] = IScsiHexString[BinBuffer[Index] & 0xf]; + } + + HexStr[Index * 2 + 2] = '\0'; + + return EFI_SUCCESS; +} + + +/** + Convert the hexadecimal string into a binary encoded buffer. + + @param[in, out] BinBuffer The binary buffer. + @param[in, out] BinLength Length of the binary buffer. + @param[in] HexStr The hexadecimal string. + + @retval EFI_SUCCESS The hexadecimal string is converted into a binary + encoded buffer. + @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data. + +**/ +EFI_STATUS +IScsiHexToBin ( + IN OUT UINT8 *BinBuffer, + IN OUT UINT32 *BinLength, + IN CHAR8 *HexStr + ) +{ + UINTN Index; + UINTN Length; + UINT8 Digit; + CHAR8 TemStr[2]; + + ZeroMem (TemStr, sizeof (TemStr)); + + // + // Find out how many hex characters the string has. + // + if ((HexStr[0] == '0') && ((HexStr[1] == 'x') || (HexStr[1] == 'X'))) { + HexStr += 2; + } + + Length = AsciiStrLen (HexStr); + + for (Index = 0; Index < Length; Index ++) { + TemStr[0] = HexStr[Index]; + Digit = (UINT8) AsciiStrHexToUint64 (TemStr); + if (Digit == 0 && TemStr[0] != '0') { + // + // Invalid Lun Char. + // + break; + } + if ((Index & 1) == 0) { + BinBuffer [Index/2] = Digit; + } else { + BinBuffer [Index/2] = (UINT8) ((BinBuffer [Index/2] << 4) + Digit); + } + } + + *BinLength = (UINT32) ((Index + 1)/2); + + return EFI_SUCCESS; +} + + +/** + Convert the decimal-constant string or hex-constant string into a numerical value. + + @param[in] Str String in decimal or hex. + + @return The numerical value. + +**/ +UINTN +IScsiNetNtoi ( + IN CHAR8 *Str + ) +{ + if ((Str[0] == '0') && ((Str[1] == 'x') || (Str[1] == 'X'))) { + Str += 2; + + return AsciiStrHexToUintn (Str); + } + + return AsciiStrDecimalToUintn (Str); +} + + +/** + Generate random numbers. + + @param[in, out] Rand The buffer to contain random numbers. + @param[in] RandLength The length of the Rand buffer. + +**/ +VOID +IScsiGenRandom ( + IN OUT UINT8 *Rand, + IN UINTN RandLength + ) +{ + UINT32 Random; + + while (RandLength > 0) { + Random = NET_RANDOM (NetRandomInitSeed ()); + *Rand++ = (UINT8) (Random); + RandLength--; + } +} + + +/** + Record the NIC info in global structure. + + @param[in] Controller The handle of the controller. + + @retval EFI_SUCCESS The operation is completed. + @retval EFI_OUT_OF_RESOURCES Do not have sufficient resources to finish this + operation. + +**/ +EFI_STATUS +IScsiAddNic ( + IN EFI_HANDLE Controller + ) +{ + EFI_STATUS Status; + ISCSI_NIC_INFO *NicInfo; + LIST_ENTRY *Entry; + EFI_MAC_ADDRESS MacAddr; + UINTN HwAddressSize; + UINT16 VlanId; + + // + // Get MAC address of this network device. + // + Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get VLAN ID of this network device. + // + VlanId = NetLibGetVlanId (Controller); + + // + // Check whether the NIC info already exists. Return directly if so. + // + NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) { + NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link); + if (NicInfo->HwAddressSize == HwAddressSize && + CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 && + NicInfo->VlanId == VlanId) { + mPrivate->CurrentNic = NicInfo->NicIndex; + return EFI_SUCCESS; + } + + if (mPrivate->MaxNic < NicInfo->NicIndex) { + mPrivate->MaxNic = NicInfo->NicIndex; + } + } + + // + // Record the NIC info in private structure. + // + NicInfo = AllocateZeroPool (sizeof (ISCSI_NIC_INFO)); + if (NicInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize); + NicInfo->HwAddressSize = (UINT32) HwAddressSize; + NicInfo->VlanId = VlanId; + NicInfo->NicIndex = (UINT8) (mPrivate->MaxNic + 1); + mPrivate->MaxNic = NicInfo->NicIndex; + + // + // Get the PCI location. + // + IScsiGetNICPciLocation ( + Controller, + &NicInfo->BusNumber, + &NicInfo->DeviceNumber, + &NicInfo->FunctionNumber + ); + + InsertTailList (&mPrivate->NicInfoList, &NicInfo->Link); + mPrivate->NicCount++; + + mPrivate->CurrentNic = NicInfo->NicIndex; + return EFI_SUCCESS; +} + + +/** + Delete the recorded NIC info from global structure. Also delete corresponding + attempts. + + @param[in] Controller The handle of the controller. + + @retval EFI_SUCCESS The operation is completed. + @retval EFI_NOT_FOUND The NIC info to be deleted is not recorded. + +**/ +EFI_STATUS +IScsiRemoveNic ( + IN EFI_HANDLE Controller + ) +{ + EFI_STATUS Status; + ISCSI_NIC_INFO *NicInfo; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + ISCSI_NIC_INFO *ThisNic; + EFI_MAC_ADDRESS MacAddr; + UINTN HwAddressSize; + UINT16 VlanId; + + // + // Get MAC address of this network device. + // + Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get VLAN ID of this network device. + // + VlanId = NetLibGetVlanId (Controller); + + // + // Check whether the NIC information exists. + // + ThisNic = NULL; + + NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) { + NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link); + if (NicInfo->HwAddressSize == HwAddressSize && + CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 && + NicInfo->VlanId == VlanId) { + + ThisNic = NicInfo; + break; + } + } + + if (ThisNic == NULL) { + return EFI_NOT_FOUND; + } + + mPrivate->CurrentNic = ThisNic->NicIndex; + + RemoveEntryList (&ThisNic->Link); + FreePool (ThisNic); + mPrivate->NicCount--; + + // + // Remove all attempts related to this NIC. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) { + AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); + if (AttemptConfigData->NicIndex == mPrivate->CurrentNic) { + RemoveEntryList (&AttemptConfigData->Link); + mPrivate->AttemptCount--; + + if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO && mPrivate->MpioCount > 0) { + if (--mPrivate->MpioCount == 0) { + mPrivate->EnableMpio = FALSE; + } + + if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB && mPrivate->Krb5MpioCount > 0) { + mPrivate->Krb5MpioCount--; + } + + } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED && mPrivate->SinglePathCount > 0) { + mPrivate->SinglePathCount--; + + if (mPrivate->ValidSinglePathCount > 0) { + mPrivate->ValidSinglePathCount--; + } + } + + FreePool (AttemptConfigData); + } + } + + // + // Free attempt is created but not saved to system. + // + if (mPrivate->NewAttempt != NULL) { + FreePool (mPrivate->NewAttempt); + mPrivate->NewAttempt = NULL; + } + + return EFI_SUCCESS; +} + + +/** + Get the recorded NIC info from global structure by the Index. + + @param[in] NicIndex The index indicates the position of NIC info. + + @return Pointer to the NIC info, or NULL if not found. + +**/ +ISCSI_NIC_INFO * +IScsiGetNicInfoByIndex ( + IN UINT8 NicIndex + ) +{ + LIST_ENTRY *Entry; + ISCSI_NIC_INFO *NicInfo; + + NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) { + NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link); + if (NicInfo->NicIndex == NicIndex) { + return NicInfo; + } + } + + return NULL; +} + + +/** + Get the NIC's PCI location and return it accroding to the composited + format defined in iSCSI Boot Firmware Table. + + @param[in] Controller The handle of the controller. + @param[out] Bus The bus number. + @param[out] Device The device number. + @param[out] Function The function number. + + @return The composited representation of the NIC PCI location. + +**/ +UINT16 +IScsiGetNICPciLocation ( + IN EFI_HANDLE Controller, + OUT UINTN *Bus, + OUT UINTN *Device, + OUT UINTN *Function + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_HANDLE PciIoHandle; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN Segment; + + Status = gBS->HandleProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath + ); + if (EFI_ERROR (Status)) { + return 0; + } + + Status = gBS->LocateDevicePath ( + &gEfiPciIoProtocolGuid, + &DevicePath, + &PciIoHandle + ); + if (EFI_ERROR (Status)) { + return 0; + } + + Status = gBS->HandleProtocol (PciIoHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo); + if (EFI_ERROR (Status)) { + return 0; + } + + Status = PciIo->GetLocation (PciIo, &Segment, Bus, Device, Function); + if (EFI_ERROR (Status)) { + return 0; + } + + return (UINT16) ((*Bus << 8) | (*Device << 3) | *Function); +} + + +/** + Read the EFI variable (VendorGuid/Name) and return a dynamically allocated + buffer, and the size of the buffer. If failure, return NULL. + + @param[in] Name String part of EFI variable name. + @param[in] VendorGuid GUID part of EFI variable name. + @param[out] VariableSize Returns the size of the EFI variable that was read. + + @return Dynamically allocated memory that contains a copy of the EFI variable. + @return Caller is responsible freeing the buffer. + @retval NULL Variable was not read. + +**/ +VOID * +IScsiGetVariableAndSize ( + IN CHAR16 *Name, + IN EFI_GUID *VendorGuid, + OUT UINTN *VariableSize + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + VOID *Buffer; + + Buffer = NULL; + + // + // Pass in a zero size buffer to find the required buffer size. + // + BufferSize = 0; + Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer); + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Allocate the buffer to return + // + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + return NULL; + } + // + // Read variable into the allocated buffer. + // + Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer); + if (EFI_ERROR (Status)) { + BufferSize = 0; + } + } + + *VariableSize = BufferSize; + return Buffer; +} + + +/** + Create the iSCSI driver data. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller. + + @return The iSCSI driver data created. + @retval NULL Other errors as indicated. + +**/ +ISCSI_DRIVER_DATA * +IScsiCreateDriverData ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller + ) +{ + ISCSI_DRIVER_DATA *Private; + EFI_STATUS Status; + + Private = AllocateZeroPool (sizeof (ISCSI_DRIVER_DATA)); + if (Private == NULL) { + return NULL; + } + + Private->Signature = ISCSI_DRIVER_DATA_SIGNATURE; + Private->Image = Image; + Private->Controller = Controller; + Private->Session = NULL; + + // + // Create an event to be signaled when the BS to RT transition is triggerd so + // as to abort the iSCSI session. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + IScsiOnExitBootService, + Private, + &gEfiEventExitBootServicesGuid, + &Private->ExitBootServiceEvent + ); + if (EFI_ERROR (Status)) { + FreePool (Private); + return NULL; + } + + Private->ExtScsiPassThruHandle = NULL; + CopyMem(&Private->IScsiExtScsiPassThru, &gIScsiExtScsiPassThruProtocolTemplate, sizeof(EFI_EXT_SCSI_PASS_THRU_PROTOCOL)); + + // + // 0 is designated to the TargetId, so use another value for the AdapterId. + // + Private->ExtScsiPassThruMode.AdapterId = 2; + Private->ExtScsiPassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL; + Private->ExtScsiPassThruMode.IoAlign = 4; + Private->IScsiExtScsiPassThru.Mode = &Private->ExtScsiPassThruMode; + + return Private; +} + + +/** + Clean the iSCSI driver data. + + @param[in] Private The iSCSI driver data. + +**/ +VOID +IScsiCleanDriverData ( + IN ISCSI_DRIVER_DATA *Private + ) +{ + EFI_STATUS Status; + + if (Private->DevicePath != NULL) { + gBS->UninstallProtocolInterface ( + Private->ExtScsiPassThruHandle, + &gEfiDevicePathProtocolGuid, + Private->DevicePath + ); + + FreePool (Private->DevicePath); + } + + if (Private->ExtScsiPassThruHandle != NULL) { + Status = gBS->UninstallProtocolInterface ( + Private->ExtScsiPassThruHandle, + &gEfiExtScsiPassThruProtocolGuid, + &Private->IScsiExtScsiPassThru + ); + if (!EFI_ERROR (Status)) { + mPrivate->OneSessionEstablished = FALSE; + } + } + + gBS->CloseEvent (Private->ExitBootServiceEvent); + + FreePool (Private); +} + +/** + Check wheather the Controller handle is configured to use DHCP protocol. + + @param[in] Controller The handle of the controller. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval TRUE The handle of the controller need the Dhcp protocol. + @retval FALSE The handle of the controller does not need the Dhcp protocol. + +**/ +BOOLEAN +IScsiDhcpIsConfigured ( + IN EFI_HANDLE Controller, + IN UINT8 IpVersion + ) +{ + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptTmp; + UINT8 *AttemptConfigOrder; + UINTN AttemptConfigOrderSize; + UINTN Index; + EFI_STATUS Status; + EFI_MAC_ADDRESS MacAddr; + UINTN HwAddressSize; + UINT16 VlanId; + CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN]; + CHAR16 AttemptName[ISCSI_NAME_IFR_MAX_SIZE]; + + AttemptConfigOrder = IScsiGetVariableAndSize ( + L"AttemptOrder", + &gIScsiConfigGuid, + &AttemptConfigOrderSize + ); + if (AttemptConfigOrder == NULL || AttemptConfigOrderSize == 0) { + return FALSE; + } + + // + // Get MAC address of this network device. + // + Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize); + if(EFI_ERROR (Status)) { + return FALSE; + } + // + // Get VLAN ID of this network device. + // + VlanId = NetLibGetVlanId (Controller); + IScsiMacAddrToStr (&MacAddr, (UINT32) HwAddressSize, VlanId, MacString); + + for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) { + UnicodeSPrint ( + AttemptName, + (UINTN) 128, + L"%s%d", + MacString, + (UINTN) AttemptConfigOrder[Index] + ); + Status = GetVariable2 ( + AttemptName, + &gEfiIScsiInitiatorNameProtocolGuid, + (VOID**)&AttemptTmp, + NULL + ); + if(AttemptTmp == NULL || EFI_ERROR (Status)) { + continue; + } + + ASSERT (AttemptConfigOrder[Index] == AttemptTmp->AttemptConfigIndex); + + if (AttemptTmp->SessionConfigData.Enabled == ISCSI_DISABLED) { + FreePool (AttemptTmp); + continue; + } + + if (AttemptTmp->SessionConfigData.IpMode != IP_MODE_AUTOCONFIG && + AttemptTmp->SessionConfigData.IpMode != ((IpVersion == IP_VERSION_4) ? IP_MODE_IP4 : IP_MODE_IP6)) { + FreePool (AttemptTmp); + continue; + } + + if(AttemptTmp->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG || + AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp == TRUE || + AttemptTmp->SessionConfigData.TargetInfoFromDhcp == TRUE) { + FreePool (AttemptTmp); + FreePool (AttemptConfigOrder); + return TRUE; + } + + FreePool (AttemptTmp); + } + + FreePool (AttemptConfigOrder); + return FALSE; +} + +/** + Get the various configuration data. + + @param[in] Private The iSCSI driver data. + + @retval EFI_SUCCESS The configuration data is retrieved. + @retval EFI_NOT_FOUND This iSCSI driver is not configured yet. + +**/ +EFI_STATUS +IScsiGetConfigData ( + IN ISCSI_DRIVER_DATA *Private + ) +{ + EFI_STATUS Status; + CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN]; + UINTN Index; + ISCSI_NIC_INFO *NicInfo; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData; + ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptTmp; + UINT8 *AttemptConfigOrder; + UINTN AttemptConfigOrderSize; + CHAR16 IScsiMode[64]; + CHAR16 IpMode[64]; + + // + // There should be at least one attempt configured. + // + AttemptConfigOrder = IScsiGetVariableAndSize ( + L"AttemptOrder", + &gIScsiConfigGuid, + &AttemptConfigOrderSize + ); + if (AttemptConfigOrder == NULL || AttemptConfigOrderSize == 0) { + return EFI_NOT_FOUND; + } + + // + // Get the iSCSI Initiator Name. + // + mPrivate->InitiatorNameLength = ISCSI_NAME_MAX_SIZE; + Status = gIScsiInitiatorName.Get ( + &gIScsiInitiatorName, + &mPrivate->InitiatorNameLength, + mPrivate->InitiatorName + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the normal configuration. + // + for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) { + + // + // Check whether the attempt exists in AttemptConfig. + // + AttemptTmp = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]); + if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled == ISCSI_DISABLED) { + continue; + } else if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled != ISCSI_DISABLED) { + // + // Check the autoconfig path to see whether it should be retried. + // + if (AttemptTmp->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG && + !AttemptTmp->AutoConfigureSuccess) { + if (mPrivate->Ipv6Flag && + AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) { + // + // Autoconfigure for IP6 already attempted but failed. Do not try again. + // + continue; + } else if (!mPrivate->Ipv6Flag && + AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) { + // + // Autoconfigure for IP4 already attempted but failed. Do not try again. + // + continue; + } else { + // + // Try another approach for this autoconfigure path. + // + AttemptTmp->AutoConfigureMode = + (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4); + AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp = TRUE; + AttemptTmp->SessionConfigData.TargetInfoFromDhcp = TRUE; + AttemptTmp->DhcpSuccess = FALSE; + + // + // Get some information from the dhcp server. + // + if (!mPrivate->Ipv6Flag) { + Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp); + if (!EFI_ERROR (Status)) { + AttemptTmp->DhcpSuccess = TRUE; + } + } else { + Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp); + if (!EFI_ERROR (Status)) { + AttemptTmp->DhcpSuccess = TRUE; + } + } + + // + // Refresh the state of this attempt to NVR. + // + AsciiStrToUnicodeStrS (AttemptTmp->MacString, MacString, sizeof (MacString) / sizeof (MacString[0])); + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"%s%d", + MacString, + (UINTN) AttemptTmp->AttemptConfigIndex + ); + + gRT->SetVariable ( + mPrivate->PortString, + &gEfiIScsiInitiatorNameProtocolGuid, + ISCSI_CONFIG_VAR_ATTR, + sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA), + AttemptTmp + ); + + continue; + } + } else if (AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp && !AttemptTmp->ValidPath) { + // + // Get DHCP information for already added, but failed, attempt. + // + AttemptTmp->DhcpSuccess = FALSE; + if (!mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP4)) { + Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp); + if (!EFI_ERROR (Status)) { + AttemptTmp->DhcpSuccess = TRUE; + } + } else if (mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP6)) { + Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp); + if (!EFI_ERROR (Status)) { + AttemptTmp->DhcpSuccess = TRUE; + } + } + + // + // Refresh the state of this attempt to NVR. + // + AsciiStrToUnicodeStrS (AttemptTmp->MacString, MacString, sizeof (MacString) / sizeof (MacString[0])); + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"%s%d", + MacString, + (UINTN) AttemptTmp->AttemptConfigIndex + ); + + gRT->SetVariable ( + mPrivate->PortString, + &gEfiIScsiInitiatorNameProtocolGuid, + ISCSI_CONFIG_VAR_ATTR, + sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA), + AttemptTmp + ); + + continue; + + } else { + continue; + } + } + + // + // This attempt does not exist in AttemptConfig. Try to add a new one. + // + + NicInfo = IScsiGetNicInfoByIndex (mPrivate->CurrentNic); + ASSERT (NicInfo != NULL); + IScsiMacAddrToStr (&NicInfo->PermanentAddress, NicInfo->HwAddressSize, NicInfo->VlanId, MacString); + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) 128, + L"%s%d", + MacString, + (UINTN) AttemptConfigOrder[Index] + ); + + GetVariable2 ( + mPrivate->PortString, + &gEfiIScsiInitiatorNameProtocolGuid, + (VOID**)&AttemptConfigData, + NULL + ); + + if (AttemptConfigData == NULL) { + continue; + } + + ASSERT (AttemptConfigOrder[Index] == AttemptConfigData->AttemptConfigIndex); + + AttemptConfigData->NicIndex = NicInfo->NicIndex; + AttemptConfigData->DhcpSuccess = FALSE; + AttemptConfigData->ValidiBFTPath = (BOOLEAN) (mPrivate->EnableMpio ? TRUE : FALSE); + AttemptConfigData->ValidPath = FALSE; + + if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) { + AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp = TRUE; + AttemptConfigData->SessionConfigData.TargetInfoFromDhcp = TRUE; + + AttemptConfigData->AutoConfigureMode = + (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4); + AttemptConfigData->AutoConfigureSuccess = FALSE; + } + + // + // Get some information from dhcp server. + // + if (AttemptConfigData->SessionConfigData.Enabled != ISCSI_DISABLED && + AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp) { + + if (!mPrivate->Ipv6Flag && + (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4 || + AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4)) { + Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptConfigData); + if (!EFI_ERROR (Status)) { + AttemptConfigData->DhcpSuccess = TRUE; + } + } else if (mPrivate->Ipv6Flag && + (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6 || + AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6)) { + Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptConfigData); + if (!EFI_ERROR (Status)) { + AttemptConfigData->DhcpSuccess = TRUE; + } + } + + // + // Refresh the state of this attempt to NVR. + // + AsciiStrToUnicodeStrS (AttemptConfigData->MacString, MacString, sizeof (MacString) / sizeof (MacString[0])); + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"%s%d", + MacString, + (UINTN) AttemptConfigData->AttemptConfigIndex + ); + + gRT->SetVariable ( + mPrivate->PortString, + &gEfiIScsiInitiatorNameProtocolGuid, + ISCSI_CONFIG_VAR_ATTR, + sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA), + AttemptConfigData + ); + } + + // + // Update Attempt Help Info. + // + + if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED) { + UnicodeSPrint (IScsiMode, 64, L"Disabled"); + } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) { + UnicodeSPrint (IScsiMode, 64, L"Enabled"); + } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) { + UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO"); + } + + if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) { + UnicodeSPrint (IpMode, 64, L"IP4"); + } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) { + UnicodeSPrint (IpMode, 64, L"IP6"); + } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) { + UnicodeSPrint (IpMode, 64, L"Autoconfigure"); + } + + UnicodeSPrint ( + mPrivate->PortString, + (UINTN) ISCSI_NAME_IFR_MAX_SIZE, + L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s", + MacString, + NicInfo->BusNumber, + NicInfo->DeviceNumber, + NicInfo->FunctionNumber, + IScsiMode, + IpMode + ); + + AttemptConfigData->AttemptTitleHelpToken = HiiSetString ( + mCallbackInfo->RegisteredHandle, + 0, + mPrivate->PortString, + NULL + ); + ASSERT (AttemptConfigData->AttemptTitleHelpToken != 0); + + // + // Record the attempt in global link list. + // + InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link); + mPrivate->AttemptCount++; + + if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) { + mPrivate->MpioCount++; + mPrivate->EnableMpio = TRUE; + + if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB) { + mPrivate->Krb5MpioCount++; + } + } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) { + mPrivate->SinglePathCount++; + } + } + + // + // Reorder the AttemptConfig by the configured order. + // + for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) { + AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]); + if (AttemptConfigData == NULL) { + continue; + } + + RemoveEntryList (&AttemptConfigData->Link); + InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link); + } + + // + // Update the Main Form. + // + IScsiConfigUpdateAttempt (); + + FreePool (AttemptConfigOrder); + + // + // There should be at least one attempt configuration. + // + if (!mPrivate->EnableMpio) { + if (mPrivate->SinglePathCount == 0) { + return EFI_NOT_FOUND; + } + mPrivate->ValidSinglePathCount = mPrivate->SinglePathCount; + } + + return EFI_SUCCESS; +} + + +/** + Get the device path of the iSCSI tcp connection and update it. + + @param Session The iSCSI session. + + @return The updated device path. + @retval NULL Other errors as indicated. + +**/ +EFI_DEVICE_PATH_PROTOCOL * +IScsiGetTcpConnDevicePath ( + IN ISCSI_SESSION *Session + ) +{ + ISCSI_CONNECTION *Conn; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_STATUS Status; + EFI_DEV_PATH *DPathNode; + UINTN PathLen; + + if (Session->State != SESSION_STATE_LOGGED_IN) { + return NULL; + } + + Conn = NET_LIST_USER_STRUCT_S ( + Session->Conns.ForwardLink, + ISCSI_CONNECTION, + Link, + ISCSI_CONNECTION_SIGNATURE + ); + + Status = gBS->HandleProtocol ( + Conn->TcpIo.Handle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath + ); + if (EFI_ERROR (Status)) { + return NULL; + } + // + // Duplicate it. + // + DevicePath = DuplicateDevicePath (DevicePath); + if (DevicePath == NULL) { + return NULL; + } + + DPathNode = (EFI_DEV_PATH *) DevicePath; + + while (!IsDevicePathEnd (&DPathNode->DevPath)) { + if (DevicePathType (&DPathNode->DevPath) == MESSAGING_DEVICE_PATH) { + if (!Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv4_DP) { + DPathNode->Ipv4.LocalPort = 0; + + DPathNode->Ipv4.StaticIpAddress = + (BOOLEAN) (!Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp); + + // + // Add a judgement here to support previous versions of IPv4_DEVICE_PATH. + // In previous versions of IPv4_DEVICE_PATH, GatewayIpAddress and SubnetMask + // do not exist. + // In new version of IPv4_DEVICE_PATH, structcure length is 27. + // + + PathLen = DevicePathNodeLength (&DPathNode->Ipv4); + + if (PathLen == IP4_NODE_LEN_NEW_VERSIONS) { + + IP4_COPY_ADDRESS ( + &DPathNode->Ipv4.GatewayIpAddress, + &Session->ConfigData->SessionConfigData.Gateway + ); + + IP4_COPY_ADDRESS ( + &DPathNode->Ipv4.SubnetMask, + &Session->ConfigData->SessionConfigData.SubnetMask + ); + } + + break; + } else if (Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv6_DP) { + DPathNode->Ipv6.LocalPort = 0; + + // + // Add a judgement here to support previous versions of IPv6_DEVICE_PATH. + // In previous versions of IPv6_DEVICE_PATH, IpAddressOrigin, PrefixLength + // and GatewayIpAddress do not exist. + // In new version of IPv6_DEVICE_PATH, structure length is 60, while in + // old versions, the length is 43. + // + + PathLen = DevicePathNodeLength (&DPathNode->Ipv6); + + if (PathLen == IP6_NODE_LEN_NEW_VERSIONS ) { + + DPathNode->Ipv6.IpAddressOrigin = 0; + DPathNode->Ipv6.PrefixLength = IP6_PREFIX_LENGTH; + ZeroMem (&DPathNode->Ipv6.GatewayIpAddress, sizeof (EFI_IPv6_ADDRESS)); + } + else if (PathLen == IP6_NODE_LEN_OLD_VERSIONS) { + + // + // StaticIPAddress is a field in old versions of IPv6_DEVICE_PATH, while ignored in new + // version. Set StaticIPAddress through its' offset in old IPv6_DEVICE_PATH. + // + *((UINT8 *)(&DPathNode->Ipv6) + IP6_OLD_IPADDRESS_OFFSET) = + (BOOLEAN) (!Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp); + } + + break; + } + } + + DPathNode = (EFI_DEV_PATH *) NextDevicePathNode (&DPathNode->DevPath); + } + + return DevicePath; +} + + +/** + Abort the session when the transition from BS to RT is initiated. + + @param[in] Event The event signaled. + @param[in] Context The iSCSI driver data. + +**/ +VOID +EFIAPI +IScsiOnExitBootService ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + ISCSI_DRIVER_DATA *Private; + + Private = (ISCSI_DRIVER_DATA *) Context; + gBS->CloseEvent (Private->ExitBootServiceEvent); + + if (Private->Session != NULL) { + IScsiSessionAbort (Private->Session); + } +} + +/** + Tests whether a controller handle is being managed by IScsi driver. + + This function tests whether the driver specified by DriverBindingHandle is + currently managing the controller specified by ControllerHandle. This test + is performed by evaluating if the the protocol specified by ProtocolGuid is + present on ControllerHandle and is was opened by DriverBindingHandle and Nic + Device handle with an attribute of EFI_OPEN_PROTOCOL_BY_DRIVER. + If ProtocolGuid is NULL, then ASSERT(). + + @param ControllerHandle A handle for a controller to test. + @param DriverBindingHandle Specifies the driver binding handle for the + driver. + @param ProtocolGuid Specifies the protocol that the driver specified + by DriverBindingHandle opens in its Start() + function. + + @retval EFI_SUCCESS ControllerHandle is managed by the driver + specified by DriverBindingHandle. + @retval EFI_UNSUPPORTED ControllerHandle is not managed by the driver + specified by DriverBindingHandle. + +**/ +EFI_STATUS +EFIAPI +IScsiTestManagedDevice ( + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE DriverBindingHandle, + IN EFI_GUID *ProtocolGuid + ) +{ + EFI_STATUS Status; + VOID *ManagedInterface; + EFI_HANDLE NicControllerHandle; + + ASSERT (ProtocolGuid != NULL); + + NicControllerHandle = NetLibGetNicHandle (ControllerHandle, ProtocolGuid); + if (NicControllerHandle == NULL) { + return EFI_UNSUPPORTED; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + (EFI_GUID *) ProtocolGuid, + &ManagedInterface, + DriverBindingHandle, + NicControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (!EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + (EFI_GUID *) ProtocolGuid, + DriverBindingHandle, + NicControllerHandle + ); + return EFI_UNSUPPORTED; + } + + if (Status != EFI_ALREADY_STARTED) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/IScsiDxe/IScsiMisc.h b/Core/NetworkPkg/IScsiDxe/IScsiMisc.h new file mode 100644 index 0000000000..6a762e3596 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiMisc.h @@ -0,0 +1,406 @@ +/** @file + Miscellaneous definitions for iSCSI driver. + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_MISC_H_ +#define _ISCSI_MISC_H_ + +typedef struct _ISCSI_DRIVER_DATA ISCSI_DRIVER_DATA; + +/// +/// IPv4 Device Path Node Length +/// +#define IP4_NODE_LEN_NEW_VERSIONS 27 + +/// +/// IPv6 Device Path Node Length +/// +#define IP6_NODE_LEN_OLD_VERSIONS 43 +#define IP6_NODE_LEN_NEW_VERSIONS 60 + +/// +/// The ignored field StaticIpAddress's offset in old IPv6 Device Path +/// +#define IP6_OLD_IPADDRESS_OFFSET 42 + +#pragma pack(1) +typedef struct _ISCSI_SESSION_CONFIG_NVDATA { + UINT16 TargetPort; + UINT8 Enabled; + UINT8 IpMode; + + EFI_IP_ADDRESS LocalIp; + EFI_IPv4_ADDRESS SubnetMask; + EFI_IP_ADDRESS Gateway; + + BOOLEAN InitiatorInfoFromDhcp; + BOOLEAN TargetInfoFromDhcp; + CHAR8 TargetName[ISCSI_NAME_MAX_SIZE]; + EFI_IP_ADDRESS TargetIp; + UINT8 PrefixLength; + UINT8 BootLun[8]; + + UINT16 ConnectTimeout; ///< timout value in milliseconds + UINT8 ConnectRetryCount; + UINT8 IsId[6]; +} ISCSI_SESSION_CONFIG_NVDATA; +#pragma pack() + +/** + Calculate the prefix length of the IPv4 subnet mask. + + @param[in] SubnetMask The IPv4 subnet mask. + + @return The prefix length of the subnet mask. + @retval 0 Other errors as indicated. + +**/ +UINT8 +IScsiGetSubnetMaskPrefixLength ( + IN EFI_IPv4_ADDRESS *SubnetMask + ); + +/** + Convert the hexadecimal encoded LUN string into the 64-bit LUN. + + @param[in] Str The hexadecimal encoded LUN string. + @param[out] Lun Storage to return the 64-bit LUN. + + @retval EFI_SUCCESS The 64-bit LUN is stored in Lun. + @retval EFI_INVALID_PARAMETER The string is malformatted. + +**/ +EFI_STATUS +IScsiAsciiStrToLun ( + IN CHAR8 *Str, + OUT UINT8 *Lun + ); + +/** + Convert the 64-bit LUN into the hexadecimal encoded LUN string. + + @param[in] Lun The 64-bit LUN. + @param[out] String The storage to return the hexadecimal encoded LUN string. + +**/ +VOID +IScsiLunToUnicodeStr ( + IN UINT8 *Lun, + OUT CHAR16 *String + ); + +/** + Convert the mac address into a hexadecimal encoded "-" seperated string. + + @param[in] Mac The mac address. + @param[in] Len Length in bytes of the mac address. + @param[in] VlanId VLAN ID of the network device. + @param[out] Str The storage to return the mac string. + +**/ +VOID +IScsiMacAddrToStr ( + IN EFI_MAC_ADDRESS *Mac, + IN UINT32 Len, + IN UINT16 VlanId, + OUT CHAR16 *Str + ); + +/** + Convert the formatted IP address into the binary IP address. + + @param[in] Str The UNICODE string. + @param[in] IpMode Indicates whether the IP address is v4 or v6. + @param[out] Ip The storage to return the ASCII string. + + @retval EFI_SUCCESS The binary IP address is returned in Ip. + @retval EFI_INVALID_PARAMETER The IP string is malformatted or IpMode is + invalid. + +**/ +EFI_STATUS +IScsiAsciiStrToIp ( + IN CHAR8 *Str, + IN UINT8 IpMode, + OUT EFI_IP_ADDRESS *Ip + ); + +/** + Convert the binary encoded buffer into a hexadecimal encoded string. + + @param[in] BinBuffer The buffer containing the binary data. + @param[in] BinLength Length of the binary buffer. + @param[in, out] HexStr Pointer to the string. + @param[in, out] HexLength The length of the string. + + @retval EFI_SUCCESS The binary data is converted to the hexadecimal string + and the length of the string is updated. + @retval EFI_BUFFER_TOO_SMALL The string is too small. + @retval EFI_INVALID_PARAMETER The IP string is malformatted. + +**/ +EFI_STATUS +IScsiBinToHex ( + IN UINT8 *BinBuffer, + IN UINT32 BinLength, + IN OUT CHAR8 *HexStr, + IN OUT UINT32 *HexLength + ); + +/** + Convert the hexadecimal string into a binary encoded buffer. + + @param[in, out] BinBuffer The binary buffer. + @param[in, out] BinLength Length of the binary buffer. + @param[in] HexStr The hexadecimal string. + + @retval EFI_SUCCESS The hexadecimal string is converted into a binary + encoded buffer. + @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data. + +**/ +EFI_STATUS +IScsiHexToBin ( + IN OUT UINT8 *BinBuffer, + IN OUT UINT32 *BinLength, + IN CHAR8 *HexStr + ); + + +/** + Convert the decimal-constant string or hex-constant string into a numerical value. + + @param[in] Str String in decimal or hex. + + @return The numerical value. + +**/ +UINTN +IScsiNetNtoi ( + IN CHAR8 *Str + ); + +/** + Generate random numbers. + + @param[in, out] Rand The buffer to contain random numbers. + @param[in] RandLength The length of the Rand buffer. + +**/ +VOID +IScsiGenRandom ( + IN OUT UINT8 *Rand, + IN UINTN RandLength + ); + +/** + Record the NIC information in a global structure. + + @param[in] Controller The handle of the controller. + + @retval EFI_SUCCESS The operation is completed. + @retval EFI_OUT_OF_RESOURCES Do not have sufficient resource to finish this + operation. + +**/ +EFI_STATUS +IScsiAddNic ( + IN EFI_HANDLE Controller + ); + +/** + Delete the recorded NIC information from a global structure. Also delete corresponding + attempts. + + @param[in] Controller The handle of the controller. + + @retval EFI_SUCCESS The operation completed. + @retval EFI_NOT_FOUND The NIC information to be deleted is not recorded. + +**/ +EFI_STATUS +IScsiRemoveNic ( + IN EFI_HANDLE Controller + ); + +/** + Get the recorded NIC information from a global structure by the Index. + + @param[in] NicIndex The index indicates the position of NIC info. + + @return Pointer to the NIC info or NULL if not found. + +**/ +ISCSI_NIC_INFO * +IScsiGetNicInfoByIndex ( + IN UINT8 NicIndex + ); + + +/** + Get the NIC's PCI location and return it accroding to the composited + format defined in iSCSI Boot Firmware Table. + + @param[in] Controller The handle of the controller. + @param[out] Bus The bus number. + @param[out] Device The device number. + @param[out] Function The function number. + + @return The composited representation of the NIC PCI location. + +**/ +UINT16 +IScsiGetNICPciLocation ( + IN EFI_HANDLE Controller, + OUT UINTN *Bus, + OUT UINTN *Device, + OUT UINTN *Function + ); + +/** + Read the EFI variable (VendorGuid/Name) and return a dynamically allocated + buffer, and the size of the buffer. If failure, return NULL. + + @param[in] Name String part of EFI variable name. + @param[in] VendorGuid GUID part of EFI variable name. + @param[out] VariableSize Returns the size of the EFI variable that was read. + + @return Dynamically allocated memory that contains a copy of the EFI variable. + @return Caller is responsible freeing the buffer. + @retval NULL Variable was not read. + +**/ +VOID * +IScsiGetVariableAndSize ( + IN CHAR16 *Name, + IN EFI_GUID *VendorGuid, + OUT UINTN *VariableSize + ); + +/** + Create the iSCSI driver data. + + @param[in] Image The handle of the driver image. + @param[in] Controller The handle of the controller. + + @return The iSCSI driver data created. + @retval NULL Other errors as indicated. + +**/ +ISCSI_DRIVER_DATA * +IScsiCreateDriverData ( + IN EFI_HANDLE Image, + IN EFI_HANDLE Controller + ); + +/** + Clean the iSCSI driver data. + + @param[in] Private The iSCSI driver data. + +**/ +VOID +IScsiCleanDriverData ( + IN ISCSI_DRIVER_DATA *Private + ); + +/** + Check wheather the Controller handle is configured to use DHCP protocol. + + @param[in] Controller The handle of the controller. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval TRUE The handle of the controller need the Dhcp protocol. + @retval FALSE The handle of the controller does not need the Dhcp protocol. + +**/ +BOOLEAN +IScsiDhcpIsConfigured ( + IN EFI_HANDLE Controller, + IN UINT8 IpVersion + ); + +/** + Get the various configuration data of this iSCSI instance. + + @param[in] Private The iSCSI driver data. + + @retval EFI_SUCCESS Obtained the configuration of this instance. + @retval EFI_ABORTED The operation was aborted. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiGetConfigData ( + IN ISCSI_DRIVER_DATA *Private + ); + +/** + Get the device path of the iSCSI tcp connection and update it. + + @param[in] Session The iSCSI session data. + + @return The updated device path. + @retval NULL Other errors as indicated. + +**/ +EFI_DEVICE_PATH_PROTOCOL * +IScsiGetTcpConnDevicePath ( + IN ISCSI_SESSION *Session + ); + +/** + Abort the session when the transition from BS to RT is initiated. + + @param[in] Event The event signaled. + @param[in] Context The iSCSI driver data. + +**/ +VOID +EFIAPI +IScsiOnExitBootService ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Tests whether a controller handle is being managed by IScsi driver. + + This function tests whether the driver specified by DriverBindingHandle is + currently managing the controller specified by ControllerHandle. This test + is performed by evaluating if the the protocol specified by ProtocolGuid is + present on ControllerHandle and is was opened by DriverBindingHandle and Nic + Device handle with an attribute of EFI_OPEN_PROTOCOL_BY_DRIVER. + If ProtocolGuid is NULL, then ASSERT(). + + @param ControllerHandle A handle for a controller to test. + @param DriverBindingHandle Specifies the driver binding handle for the + driver. + @param ProtocolGuid Specifies the protocol that the driver specified + by DriverBindingHandle opens in its Start() + function. + + @retval EFI_SUCCESS ControllerHandle is managed by the driver + specified by DriverBindingHandle. + @retval EFI_UNSUPPORTED ControllerHandle is not managed by the driver + specified by DriverBindingHandle. + +**/ +EFI_STATUS +EFIAPI +IScsiTestManagedDevice ( + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE DriverBindingHandle, + IN EFI_GUID *ProtocolGuid + ); +#endif diff --git a/Core/NetworkPkg/IScsiDxe/IScsiProto.c b/Core/NetworkPkg/IScsiDxe/IScsiProto.c new file mode 100644 index 0000000000..4c4e3c28e7 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiProto.c @@ -0,0 +1,3133 @@ +/** @file + The implementation of iSCSI protocol based on RFC3720. + +Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IScsiImpl.h" + +UINT32 mDataSegPad = 0; + +/** + Attach the iSCSI connection to the iSCSI session. + + @param[in, out] Session The iSCSI session. + @param[in, out] Conn The iSCSI connection. + +**/ +VOID +IScsiAttatchConnection ( + IN OUT ISCSI_SESSION *Session, + IN OUT ISCSI_CONNECTION *Conn + ) +{ + InsertTailList (&Session->Conns, &Conn->Link); + Conn->Session = Session; + Session->NumConns++; +} + +/** + Detach the iSCSI connection from the session it belongs to. + + @param[in, out] Conn The iSCSI connection. + +**/ +VOID +IScsiDetatchConnection ( + IN OUT ISCSI_CONNECTION *Conn + ) +{ + RemoveEntryList (&Conn->Link); + Conn->Session->NumConns--; + Conn->Session = NULL; +} + + +/** + Check the sequence number according to RFC3720. + + @param[in, out] ExpSN The currently expected sequence number. + @param[in] NewSN The sequence number to check. + + @retval EFI_SUCCESS The check passed and the ExpSN is increased. + @retval EFI_NOT_READY Response was sent due to a retransmission request. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred. + +**/ +EFI_STATUS +IScsiCheckSN ( + IN OUT UINT32 *ExpSN, + IN UINT32 NewSN + ) +{ + if (!ISCSI_SEQ_EQ (NewSN, *ExpSN)) { + if (ISCSI_SEQ_LT (NewSN, *ExpSN)) { + // + // Duplicate + // + return EFI_NOT_READY; + } else { + return EFI_PROTOCOL_ERROR; + } + } else { + // + // Advance the ExpSN + // + (*ExpSN)++; + return EFI_SUCCESS; + } +} + + +/** + Update the sequence numbers for the iSCSI command. + + @param[in, out] Session The iSCSI session. + @param[in] MaxCmdSN Maximum CmdSN from the target. + @param[in] ExpCmdSN Next expected CmdSN from the target. + +**/ +VOID +IScsiUpdateCmdSN ( + IN OUT ISCSI_SESSION *Session, + IN UINT32 MaxCmdSN, + IN UINT32 ExpCmdSN + ) +{ + if (ISCSI_SEQ_LT (MaxCmdSN, ExpCmdSN - 1)) { + return ; + } + + if (ISCSI_SEQ_GT (MaxCmdSN, Session->MaxCmdSN)) { + Session->MaxCmdSN = MaxCmdSN; + } + + if (ISCSI_SEQ_GT (ExpCmdSN, Session->ExpCmdSN)) { + Session->ExpCmdSN = ExpCmdSN; + } +} + + +/** + This function does the iSCSI connection login. + + @param[in, out] Conn The iSCSI connection to login. + @param Timeout The timeout value in millisecond. + + @retval EFI_SUCCESS The iSCSI connection is logged into the iSCSI target. + @retval EFI_TIMEOUT Timeout occurred during the login procedure. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiConnLogin ( + IN OUT ISCSI_CONNECTION *Conn, + IN UINT16 Timeout + ) +{ + EFI_STATUS Status; + + // + // Start the timer, and wait Timeout seconds to establish the TCP connection. + // + Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout * TICKS_PER_MS); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Try to establish the tcp connection. + // + Status = TcpIoConnect (&Conn->TcpIo, Conn->TimeoutEvent); + gBS->SetTimer (Conn->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + return Status; + } + + Conn->State = CONN_STATE_IN_LOGIN; + + // + // Connection is established, start the iSCSI Login. + // + do { + Status = IScsiSendLoginReq (Conn); + if (EFI_ERROR (Status)) { + break; + } + + Status = IScsiReceiveLoginRsp (Conn); + if (EFI_ERROR (Status)) { + break; + } + } while (Conn->CurrentStage != ISCSI_FULL_FEATURE_PHASE); + + return Status; +} + + +/** + Reset the iSCSI connection. + + @param[in, out] Conn The iSCSI connection to reset. + +**/ +VOID +IScsiConnReset ( + IN OUT ISCSI_CONNECTION *Conn + ) +{ + TcpIoReset (&Conn->TcpIo); +} + + +/** + Create a TCP connection for the iSCSI session. + + @param[in] Session Points to the iSCSI session. + + @return The newly created iSCSI connection. + +**/ +ISCSI_CONNECTION * +IScsiCreateConnection ( + IN ISCSI_SESSION *Session + ) +{ + ISCSI_DRIVER_DATA *Private; + ISCSI_SESSION_CONFIG_NVDATA *NvData; + ISCSI_CONNECTION *Conn; + TCP_IO_CONFIG_DATA TcpIoConfig; + TCP4_IO_CONFIG_DATA *Tcp4IoConfig; + TCP6_IO_CONFIG_DATA *Tcp6IoConfig; + EFI_STATUS Status; + + Private = Session->Private; + NvData = &Session->ConfigData->SessionConfigData; + + Conn = AllocateZeroPool (sizeof (ISCSI_CONNECTION)); + if (Conn == NULL) { + return NULL; + } + + Conn->Signature = ISCSI_CONNECTION_SIGNATURE; + Conn->State = CONN_STATE_FREE; + Conn->CurrentStage = ISCSI_SECURITY_NEGOTIATION; + Conn->NextStage = ISCSI_LOGIN_OPERATIONAL_NEGOTIATION; + Conn->AuthStep = ISCSI_AUTH_INITIAL; + Conn->ExpStatSN = 0; + Conn->PartialReqSent = FALSE; + Conn->PartialRspRcvd = FALSE; + Conn->ParamNegotiated = FALSE; + Conn->Cid = Session->NextCid++; + Conn->Ipv6Flag = NvData->IpMode == IP_MODE_IP6 || Session->ConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6; + + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Conn->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + FreePool (Conn); + return NULL; + } + + NetbufQueInit (&Conn->RspQue); + + // + // Set the default connection-only parameters. + // + Conn->MaxRecvDataSegmentLength = DEFAULT_MAX_RECV_DATA_SEG_LEN; + Conn->HeaderDigest = IScsiDigestNone; + Conn->DataDigest = IScsiDigestNone; + + if (!Conn->Ipv6Flag) { + Tcp4IoConfig = &TcpIoConfig.Tcp4IoConfigData; + + CopyMem (&Tcp4IoConfig->LocalIp, &NvData->LocalIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Tcp4IoConfig->SubnetMask, &NvData->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Tcp4IoConfig->Gateway, &NvData->Gateway, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Tcp4IoConfig->RemoteIp, &NvData->TargetIp, sizeof (EFI_IPv4_ADDRESS)); + + Tcp4IoConfig->RemotePort = NvData->TargetPort; + Tcp4IoConfig->ActiveFlag = TRUE; + Tcp4IoConfig->StationPort = 0; + } else { + Tcp6IoConfig = &TcpIoConfig.Tcp6IoConfigData; + + CopyMem (&Tcp6IoConfig->RemoteIp, &NvData->TargetIp, sizeof (EFI_IPv6_ADDRESS)); + Tcp6IoConfig->RemotePort = NvData->TargetPort; + Tcp6IoConfig->ActiveFlag = TRUE; + Tcp6IoConfig->StationPort = 0; + } + + // + // Create the TCP IO for this connection. + // + Status = TcpIoCreateSocket ( + Private->Image, + Private->Controller, + (UINT8) (!Conn->Ipv6Flag ? TCP_VERSION_4: TCP_VERSION_6), + &TcpIoConfig, + &Conn->TcpIo + ); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Conn->TimeoutEvent); + FreePool (Conn); + Conn = NULL; + } + + return Conn; +} + + +/** + Destroy an iSCSI connection. + + @param[in] Conn The connection to destroy. + +**/ +VOID +IScsiDestroyConnection ( + IN ISCSI_CONNECTION *Conn + ) +{ + TcpIoDestroySocket (&Conn->TcpIo); + + NetbufQueFlush (&Conn->RspQue); + gBS->CloseEvent (Conn->TimeoutEvent); + FreePool (Conn); +} + +/** + Retrieve the IPv6 Address/Prefix/Gateway from the established TCP connection, these informations + will be filled in the iSCSI Boot Firmware Table. + + @param[in] Conn The connection used in the iSCSI login phase. + + @retval EFI_SUCCESS Get the NIC information successfully. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiGetIp6NicInfo ( + IN ISCSI_CONNECTION *Conn + ) +{ + ISCSI_SESSION_CONFIG_NVDATA *NvData; + EFI_TCP6_PROTOCOL *Tcp6; + EFI_IP6_MODE_DATA Ip6ModeData; + EFI_STATUS Status; + EFI_IPv6_ADDRESS *TargetIp; + UINTN Index; + UINT8 SubnetPrefixLength; + UINTN RouteEntry; + + NvData = &Conn->Session->ConfigData->SessionConfigData; + TargetIp = &NvData->TargetIp.v6; + Tcp6 = Conn->TcpIo.Tcp.Tcp6; + + ZeroMem (&Ip6ModeData, sizeof (EFI_IP6_MODE_DATA)); + Status = Tcp6->GetModeData ( + Tcp6, + NULL, + NULL, + &Ip6ModeData, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!Ip6ModeData.IsConfigured) { + Status = EFI_ABORTED; + goto ON_EXIT; + } + + IP6_COPY_ADDRESS (&NvData->LocalIp, &Ip6ModeData.ConfigData.StationAddress); + + NvData->PrefixLength = 0; + for (Index = 0; Index < Ip6ModeData.AddressCount; Index++) { + if (EFI_IP6_EQUAL (&NvData->LocalIp.v6, &Ip6ModeData.AddressList[Index].Address)) { + NvData->PrefixLength = Ip6ModeData.AddressList[Index].PrefixLength; + break; + } + } + + SubnetPrefixLength = 0; + RouteEntry = Ip6ModeData.RouteCount; + for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) { + if (NetIp6IsNetEqual (TargetIp, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) { + if (SubnetPrefixLength < Ip6ModeData.RouteTable[Index].PrefixLength) { + SubnetPrefixLength = Ip6ModeData.RouteTable[Index].PrefixLength; + RouteEntry = Index; + } + } + } + if (RouteEntry != Ip6ModeData.RouteCount) { + IP6_COPY_ADDRESS (&NvData->Gateway, &Ip6ModeData.RouteTable[RouteEntry].Gateway); + } + +ON_EXIT: + if (Ip6ModeData.AddressList != NULL) { + FreePool (Ip6ModeData.AddressList); + } + if (Ip6ModeData.GroupTable!= NULL) { + FreePool (Ip6ModeData.GroupTable); + } + if (Ip6ModeData.RouteTable!= NULL) { + FreePool (Ip6ModeData.RouteTable); + } + if (Ip6ModeData.NeighborCache!= NULL) { + FreePool (Ip6ModeData.NeighborCache); + } + if (Ip6ModeData.PrefixTable!= NULL) { + FreePool (Ip6ModeData.PrefixTable); + } + if (Ip6ModeData.IcmpTypeList!= NULL) { + FreePool (Ip6ModeData.IcmpTypeList); + } + + return Status; +} + +/** + Login the iSCSI session. + + @param[in] Session The iSCSI session. + + @retval EFI_SUCCESS The iSCSI session login procedure finished. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NO_MEDIA There was a media error. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiSessionLogin ( + IN ISCSI_SESSION *Session + ) +{ + EFI_STATUS Status; + ISCSI_CONNECTION *Conn; + VOID *Tcp; + EFI_GUID *ProtocolGuid; + UINT8 RetryCount; + BOOLEAN MediaPresent; + + // + // Check media status before session login. + // + MediaPresent = TRUE; + NetLibDetectMedia (Session->Private->Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Set session identifier + // + CopyMem (Session->Isid, Session->ConfigData->SessionConfigData.IsId, 6); + + RetryCount = 0; + + do { + // + // Create a connection for the session. + // + Conn = IScsiCreateConnection (Session); + if (Conn == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IScsiAttatchConnection (Session, Conn); + + // + // Login througth the newly created connection. + // + Status = IScsiConnLogin (Conn, Session->ConfigData->SessionConfigData.ConnectTimeout); + if (EFI_ERROR (Status)) { + IScsiConnReset (Conn); + IScsiDetatchConnection (Conn); + IScsiDestroyConnection (Conn); + } + + if (Status != EFI_TIMEOUT) { + break; + } + + RetryCount++; + } while (RetryCount <= Session->ConfigData->SessionConfigData.ConnectRetryCount); + + if (!EFI_ERROR (Status)) { + Session->State = SESSION_STATE_LOGGED_IN; + + if (!Conn->Ipv6Flag) { + ProtocolGuid = &gEfiTcp4ProtocolGuid; + } else { + ProtocolGuid = &gEfiTcp6ProtocolGuid; + } + + Status = gBS->OpenProtocol ( + Conn->TcpIo.Handle, + ProtocolGuid, + (VOID **) &Tcp, + Session->Private->Image, + Session->Private->ExtScsiPassThruHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + ASSERT_EFI_ERROR (Status); + + if (Conn->Ipv6Flag) { + Status = IScsiGetIp6NicInfo (Conn); + } + } + + return Status; +} + + +/** + Wait for IPsec negotiation, then try to login the iSCSI session again. + + @param[in] Session The iSCSI session. + + @retval EFI_SUCCESS The iSCSI session login procedure finished. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred. + +**/ +EFI_STATUS +IScsiSessionReLogin ( + IN ISCSI_SESSION *Session + ) +{ + + EFI_STATUS Status; + EFI_STATUS TimerStatus; + EFI_EVENT Timer; + + Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer ( + Timer, + TimerRelative, + ISCSI_WAIT_IPSEC_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Timer); + return Status; + } + + do { + + TimerStatus = gBS->CheckEvent (Timer); + + if (!EFI_ERROR (TimerStatus)) { + Status = IScsiSessionLogin (Session); + } + + } while (TimerStatus == EFI_NOT_READY); + + gBS->CloseEvent (Timer); + return Status; +} + + +/** + Build and send the iSCSI login request to the iSCSI target according to + the current login stage. + + @param[in] Conn The connection in the iSCSI login phase. + + @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this + connection. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR Some kind of device error occurred. + +**/ +EFI_STATUS +IScsiSendLoginReq ( + IN ISCSI_CONNECTION *Conn + ) +{ + NET_BUF *Pdu; + EFI_STATUS Status; + + // + // Build the Login Request PDU. + // + Pdu = IScsiPrepareLoginReq (Conn); + if (Pdu == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Send it to the iSCSI target. + // + Status = TcpIoTransmit (&Conn->TcpIo, Pdu); + + NetbufFree (Pdu); + + return Status; +} + + +/** + Receive and process the iSCSI login response. + + @param[in] Conn The connection in the iSCSI login phase. + + @retval EFI_SUCCESS The iSCSI login response PDU is received and processed. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiReceiveLoginRsp ( + IN ISCSI_CONNECTION *Conn + ) +{ + EFI_STATUS Status; + NET_BUF *Pdu; + + Pdu = NULL; + + // + // Receive the iSCSI login response. + // + Status = IScsiReceivePdu (Conn, &Pdu, NULL, FALSE, FALSE, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Pdu != NULL); + + // + // A Login Response is received; process it. + // + Status = IScsiProcessLoginRsp (Conn, Pdu); + + NetbufFree (Pdu); + + return Status; +} + + +/** + Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU. + The DataSegmentLength and the actual size of the net buffer containing this PDU will be + updated. + + @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will + be added to. + @param[in] Key The key name string. + @param[in] Value The value string. + + @retval EFI_SUCCESS The key-value pair is added to the PDU's data segment and + the correspondence length fields are updated. + @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value + pair. + @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer. +**/ +EFI_STATUS +IScsiAddKeyValuePair ( + IN OUT NET_BUF *Pdu, + IN CHAR8 *Key, + IN CHAR8 *Value + ) +{ + UINT32 DataSegLen; + UINT32 KeyLen; + UINT32 ValueLen; + UINT32 TotalLen; + ISCSI_LOGIN_REQUEST *LoginReq; + CHAR8 *Data; + + LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, NULL); + if (LoginReq == NULL) { + return EFI_PROTOCOL_ERROR; + } + DataSegLen = NTOH24 (LoginReq->DataSegmentLength); + + KeyLen = (UINT32) AsciiStrLen (Key); + ValueLen = (UINT32) AsciiStrLen (Value); + + // + // 1 byte for the key value separator '=' and 1 byte for the null + // delimiter after the value. + // + TotalLen = KeyLen + 1 + ValueLen + 1; + + // + // Allocate the space for the key-value pair. + // + Data = (CHAR8 *) NetbufAllocSpace (Pdu, TotalLen, NET_BUF_TAIL); + if (Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Add the key. + // + CopyMem (Data, Key, KeyLen); + Data += KeyLen; + + *Data = '='; + Data++; + + // + // Add the value. + // + CopyMem (Data, Value, ValueLen); + Data += ValueLen; + + *Data = '\0'; + + // + // Update the DataSegmentLength + // + ISCSI_SET_DATASEG_LEN (LoginReq, DataSegLen + TotalLen); + + return EFI_SUCCESS; +} + + +/** + Prepare the iSCSI login request to be sent according to the current login status. + + @param[in, out] Conn The connection in the iSCSI login phase. + + @return The pointer to the net buffer containing the iSCSI login request built. + @retval NULL Other errors as indicated. + +**/ +NET_BUF * +IScsiPrepareLoginReq ( + IN OUT ISCSI_CONNECTION *Conn + ) +{ + ISCSI_SESSION *Session; + NET_BUF *Nbuf; + ISCSI_LOGIN_REQUEST *LoginReq; + EFI_STATUS Status; + + Session = Conn->Session; + + Nbuf = NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST) + DEFAULT_MAX_RECV_DATA_SEG_LEN); + if (Nbuf == NULL) { + return NULL; + } + + LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufAllocSpace (Nbuf, sizeof (ISCSI_LOGIN_REQUEST), NET_BUF_TAIL); + ASSERT (LoginReq != NULL); + ZeroMem (LoginReq, sizeof (ISCSI_LOGIN_REQUEST)); + + // + // Init the login request pdu + // + ISCSI_SET_OPCODE (LoginReq, ISCSI_OPCODE_LOGIN_REQ, ISCSI_REQ_IMMEDIATE); + ISCSI_SET_STAGES (LoginReq, Conn->CurrentStage, Conn->NextStage); + LoginReq->VersionMax = ISCSI_VERSION_MAX; + LoginReq->VersionMin = ISCSI_VERSION_MIN; + LoginReq->Tsih = HTONS (Session->Tsih); + LoginReq->InitiatorTaskTag = HTONL (Session->InitiatorTaskTag); + LoginReq->Cid = HTONS (Conn->Cid); + LoginReq->CmdSN = HTONL (Session->CmdSN); + + // + // For the first Login Request on a coonection this is ExpStatSN for the + // old connection, and this field is only valid if the Login Request restarts + // a connection. + // For subsequent Login Requests it is used to acknowledge the Login Responses + // with their increasing StatSN values. + // + LoginReq->ExpStatSN = HTONL (Conn->ExpStatSN); + CopyMem (LoginReq->Isid, Session->Isid, sizeof (LoginReq->Isid)); + + if (Conn->PartialRspRcvd) { + // + // A partial response. The initiator must send an empty Login Request. + // + return Nbuf; + } + + Status = EFI_SUCCESS; + + switch (Conn->CurrentStage) { + case ISCSI_SECURITY_NEGOTIATION: + // + // Both none authentication and CHAP authentication share the CHAP path. + // + // + if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) { + Status = IScsiCHAPToSendReq (Conn, Nbuf); + } + + break; + + case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION: + // + // Only negotiate the paramter once. + // + if (!Conn->ParamNegotiated) { + IScsiFillOpParams (Conn, Nbuf); + } + + ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT); + break; + + default: + // + // An error occurs... + // + Status = EFI_DEVICE_ERROR; + break; + } + + if (EFI_ERROR (Status)) { + NetbufFree (Nbuf); + Nbuf = NULL; + } else { + // + // Pad the data segment if needed. + // + IScsiPadSegment (Nbuf, ISCSI_GET_DATASEG_LEN (LoginReq)); + // + // Check whether we will issue the stage transition signal? + // + Conn->TransitInitiated = ISCSI_FLAG_ON (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT); + } + + return Nbuf; +} + + +/** + Process the iSCSI Login Response. + + @param[in, out] Conn The connection on which the iSCSI login response is received. + @param[in, out] Pdu The iSCSI login response PDU. + + @retval EFI_SUCCESS The iSCSI login response PDU is processed, and all checks are passed. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred. + @retval EFI_MEDIA_CHANGED Target is redirected. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiProcessLoginRsp ( + IN OUT ISCSI_CONNECTION *Conn, + IN OUT NET_BUF *Pdu + ) +{ + EFI_STATUS Status; + ISCSI_SESSION *Session; + ISCSI_LOGIN_RESPONSE *LoginRsp; + BOOLEAN Transit; + BOOLEAN Continue; + UINT8 CurrentStage; + UINT8 NextStage; + UINT8 *DataSeg; + UINT32 DataSegLen; + + Status = EFI_SUCCESS; + Session = Conn->Session; + + LoginRsp = (ISCSI_LOGIN_RESPONSE *) NetbufGetByte (Pdu, 0, NULL); + if (LoginRsp == NULL) { + return EFI_PROTOCOL_ERROR; + } + if (!ISCSI_CHECK_OPCODE (LoginRsp, ISCSI_OPCODE_LOGIN_RSP)) { + // + // It is not a Login Response. + // + return EFI_PROTOCOL_ERROR; + } + // + // Get the data segment, if any. + // + DataSegLen = ISCSI_GET_DATASEG_LEN (LoginRsp); + if (DataSegLen != 0) { + DataSeg = NetbufGetByte (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NULL); + } else { + DataSeg = NULL; + } + // + // Check the status class in the login response PDU. + // + switch (LoginRsp->StatusClass) { + case ISCSI_LOGIN_STATUS_SUCCESS: + // + // Just break here; the response and the data segment will be processed later. + // + break; + + case ISCSI_LOGIN_STATUS_REDIRECTION: + // + // The target may be moved to a different address. + // + if (DataSeg == NULL) { + return EFI_PROTOCOL_ERROR; + } + // + // Process the TargetAddress key-value strings in the data segment to update the + // target address info. + // + Status = IScsiUpdateTargetAddress (Session, (CHAR8 *) DataSeg, DataSegLen); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Session will be restarted on this error status because the Target is + // redirected by this Login Response. + // + return EFI_MEDIA_CHANGED; + + default: + // + // Initiator Error, Target Error, or any other undefined error code. + // + return EFI_PROTOCOL_ERROR; + } + // + // The status is success; extract the wanted fields from the header segment. + // + Transit = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT); + Continue = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE); + + CurrentStage = ISCSI_GET_CURRENT_STAGE (LoginRsp); + NextStage = ISCSI_GET_NEXT_STAGE (LoginRsp); + + LoginRsp->InitiatorTaskTag = NTOHL (LoginRsp->InitiatorTaskTag); + + if ((Transit && Continue) || + (CurrentStage != Conn->CurrentStage) || + (!Conn->TransitInitiated && Transit) || + (Transit && (NextStage != Conn->NextStage)) || + (CompareMem (Session->Isid, LoginRsp->Isid, sizeof (LoginRsp->Isid)) != 0) || + (LoginRsp->InitiatorTaskTag != Session->InitiatorTaskTag) + ) { + // + // A Login Response with the C bit set to 1 MUST have the T bit set to 0. + // The CSG in the Login Response MUST be the same with the I-end of this connection. + // The T bit can't be 1 if the last Login Response sent by the initiator doesn't + // initiate the transistion. + // The NSG MUST be the same with the I-end of this connection if Transit is required. + // The ISID in the Login Response MUST be the same with this session. + // + return EFI_PROTOCOL_ERROR; + } + + LoginRsp->StatSN = NTOHL (LoginRsp->StatSN); + LoginRsp->ExpCmdSN = NTOHL (LoginRsp->ExpCmdSN); + LoginRsp->MaxCmdSN = NTOHL (LoginRsp->MaxCmdSN); + + if ((Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION) && (Conn->AuthStep == ISCSI_AUTH_INITIAL)) { + // + // If the Login Request is a leading Login Request, the target MUST use + // the value presented in CmdSN as the target value for ExpCmdSN. + // + if ((Session->State == SESSION_STATE_FREE) && (Session->CmdSN != LoginRsp->ExpCmdSN)) { + return EFI_PROTOCOL_ERROR; + } + + // + // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN + // and ExpCmdSN. + // + Conn->ExpStatSN = LoginRsp->StatSN + 1; + Session->MaxCmdSN = LoginRsp->MaxCmdSN; + Session->ExpCmdSN = LoginRsp->ExpCmdSN; + } else { + // + // Check the StatSN of this PDU. + // + Status = IScsiCheckSN (&Conn->ExpStatSN, LoginRsp->StatSN); + if (!EFI_ERROR (Status)) { + // + // Update the MaxCmdSN and ExpCmdSN. + // + IScsiUpdateCmdSN (Session, LoginRsp->MaxCmdSN, LoginRsp->ExpCmdSN); + } else { + return Status; + } + } + // + // Trim off the header segment. + // + NetbufTrim (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NET_BUF_HEAD); + + // + // Queue this login response first in case it's a partial response so that + // later when the full response list is received we can combine these scattered + // responses' data segment and then process it. + // + NET_GET_REF (Pdu); + NetbufQueAppend (&Conn->RspQue, Pdu); + + Conn->PartialRspRcvd = Continue; + if (Continue) { + // + // It is a partial response; must wait for another or more Request/Response + // conversations to get the full response. + // + return EFI_SUCCESS; + } + + switch (CurrentStage) { + case ISCSI_SECURITY_NEGOTIATION: + // + // In security negotiation stage, let CHAP module handle it. + // + if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) { + Status = IScsiCHAPOnRspReceived (Conn); + } + break; + + case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION: + // + // Response received with negotiation response on iSCSI parameters: check them. + // + Status = IScsiCheckOpParams (Conn); + if (!EFI_ERROR (Status)) { + Conn->ParamNegotiated = TRUE; + } + + break; + + default: + // + // Should never get here. + // + Status = EFI_PROTOCOL_ERROR; + break; + } + + if (Transit && (Status == EFI_SUCCESS)) { + // + // Do the state transition. + // + Conn->CurrentStage = Conn->NextStage; + + if (Conn->CurrentStage == ISCSI_LOGIN_OPERATIONAL_NEGOTIATION) { + Conn->NextStage = ISCSI_FULL_FEATURE_PHASE; + } else { + // + // CurrentStage is iSCSI Full Feature. It is the Login-Final Response; + // get the TSIH from the Login Response. + // + Session->Tsih = NTOHS (LoginRsp->Tsih); + } + } + // + // Flush the response(s) received. + // + NetbufQueFlush (&Conn->RspQue); + + return Status; +} + + +/** + Updated the target information according the data received in the iSCSI + login response with an target redirection status. + + @param[in, out] Session The iSCSI session. + @param[in] Data The data segment that should contain the + TargetAddress key-value list. + @param[in] Len Length of the data. + + @retval EFI_SUCCESS The target address is updated. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NOT_FOUND The TargetAddress key is not found. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiUpdateTargetAddress ( + IN OUT ISCSI_SESSION *Session, + IN CHAR8 *Data, + IN UINT32 Len + ) +{ + LIST_ENTRY *KeyValueList; + CHAR8 *TargetAddress; + CHAR8 *IpStr; + EFI_STATUS Status; + UINTN Number; + UINT8 IpMode; + + KeyValueList = IScsiBuildKeyValueList (Data, Len); + if (KeyValueList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = EFI_NOT_FOUND; + + while (TRUE) { + TargetAddress = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ADDRESS); + if (TargetAddress == NULL) { + break; + } + + if (!NET_IS_DIGIT (TargetAddress[0])) { + // + // The domainname of the target may be presented in three formats: a DNS host name, + // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted + // IPv4 address. + // + continue; + } + + IpStr = TargetAddress; + + while ((*TargetAddress != 0) && (*TargetAddress != ':') && (*TargetAddress != ',')) { + // + // NULL, ':', or ',' ends the IPv4 string. + // + TargetAddress++; + } + + if (*TargetAddress == ',') { + // + // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent + // as the result of a redirection. + // + continue; + } else if (*TargetAddress == ':') { + *TargetAddress = '\0'; + + TargetAddress++; + + Number = AsciiStrDecimalToUintn (TargetAddress); + if (Number > 0xFFFF) { + continue; + } else { + Session->ConfigData->SessionConfigData.TargetPort = (UINT16) Number; + } + } else { + // + // The string only contains the IPv4 address. Use the well-known port. + // + Session->ConfigData->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT; + } + // + // Update the target IP address. + // + if (Session->ConfigData->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) { + IpMode = Session->ConfigData->SessionConfigData.IpMode; + } else { + IpMode = Session->ConfigData->AutoConfigureMode; + } + + Status = IScsiAsciiStrToIp ( + IpStr, + IpMode, + &Session->ConfigData->SessionConfigData.TargetIp + ); + + if (EFI_ERROR (Status)) { + continue; + } else { + break; + } + } + + IScsiFreeKeyValueList (KeyValueList); + + return Status; +} + + +/** + The callback function to free the net buffer list. + + @param[in] Arg The opaque parameter. + +**/ +VOID +EFIAPI +IScsiFreeNbufList ( + VOID *Arg + ) +{ + ASSERT (Arg != NULL); + + NetbufFreeList ((LIST_ENTRY *) Arg); + FreePool (Arg); +} + + +/** + The callback function called in NetBufFree; it does nothing. + + @param[in] Arg The opaque parameter. + +**/ +VOID +EFIAPI +IScsiNbufExtFree ( + VOID *Arg + ) +{ +} + + +/** + Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and + an optional data segment. The two parts will be put into two blocks of buffers in the + net buffer. The digest check will be conducted in this function if needed and the digests + will be trimmed from the PDU buffer. + + @param[in] Conn The iSCSI connection to receive data from. + @param[out] Pdu The received iSCSI pdu. + @param[in] Context The context used to describe information on the caller provided + buffer to receive data segment of the iSCSI pdu. It is optional. + @param[in] HeaderDigest Whether there will be header digest received. + @param[in] DataDigest Whether there will be data digest. + @param[in] TimeoutEvent The timeout event. It is optional. + + @retval EFI_SUCCESS An iSCSI pdu is received. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiReceivePdu ( + IN ISCSI_CONNECTION *Conn, + OUT NET_BUF **Pdu, + IN ISCSI_IN_BUFFER_CONTEXT *Context, OPTIONAL + IN BOOLEAN HeaderDigest, + IN BOOLEAN DataDigest, + IN EFI_EVENT TimeoutEvent OPTIONAL + ) +{ + LIST_ENTRY *NbufList; + UINT32 Len; + NET_BUF *PduHdr; + UINT8 *Header; + EFI_STATUS Status; + UINT32 PadLen; + UINT32 InDataOffset; + NET_FRAGMENT Fragment[2]; + UINT32 FragmentCount; + NET_BUF *DataSeg; + UINT32 PadAndCRC32[2]; + + NbufList = AllocatePool (sizeof (LIST_ENTRY)); + if (NbufList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (NbufList); + + // + // The header digest will be received together with the PDU header, if exists. + // + Len = sizeof (ISCSI_BASIC_HEADER) + (HeaderDigest ? sizeof (UINT32) : 0); + PduHdr = NetbufAlloc (Len); + if (PduHdr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL); + ASSERT (Header != NULL); + InsertTailList (NbufList, &PduHdr->List); + + // + // First step, receive the BHS of the PDU. + // + Status = TcpIoReceive (&Conn->TcpIo, PduHdr, FALSE, TimeoutEvent); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (HeaderDigest) { + // + // TODO: check the header-digest. + // + // + // Trim off the digest. + // + NetbufTrim (PduHdr, sizeof (UINT32), NET_BUF_TAIL); + } + + Len = ISCSI_GET_DATASEG_LEN (Header); + if (Len == 0) { + // + // No data segment. + // + goto FORM_PDU; + } + // + // Get the length of the padding bytes of the data segment. + // + PadLen = ISCSI_GET_PAD_LEN (Len); + + switch (ISCSI_GET_OPCODE (Header)) { + case ISCSI_OPCODE_SCSI_DATA_IN: + // + // To reduce memory copy overhead, try to use the buffer described by Context + // if the PDU is an iSCSI SCSI data. + // + InDataOffset = ISCSI_GET_BUFFER_OFFSET (Header); + if ((Context == NULL) || ((InDataOffset + Len) > Context->InDataLen)) { + Status = EFI_PROTOCOL_ERROR; + goto ON_EXIT; + } + + Fragment[0].Len = Len; + Fragment[0].Bulk = Context->InData + InDataOffset; + + if (DataDigest || (PadLen != 0)) { + // + // The data segment is padded. Use two fragments to receive it: + // the first to receive the useful data; the second to receive the padding. + // + Fragment[1].Len = PadLen + (DataDigest ? sizeof (UINT32) : 0); + Fragment[1].Bulk = (UINT8 *)PadAndCRC32 + (4 - PadLen); + + FragmentCount = 2; + } else { + FragmentCount = 1; + } + + DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL); + if (DataSeg == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + break; + + case ISCSI_OPCODE_SCSI_RSP: + case ISCSI_OPCODE_NOP_IN: + case ISCSI_OPCODE_LOGIN_RSP: + case ISCSI_OPCODE_TEXT_RSP: + case ISCSI_OPCODE_ASYNC_MSG: + case ISCSI_OPCODE_REJECT: + case ISCSI_OPCODE_VENDOR_T0: + case ISCSI_OPCODE_VENDOR_T1: + case ISCSI_OPCODE_VENDOR_T2: + // + // Allocate buffer to receive the data segment. + // + Len += PadLen + (DataDigest ? sizeof (UINT32) : 0); + DataSeg = NetbufAlloc (Len); + if (DataSeg == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL); + break; + + default: + Status = EFI_PROTOCOL_ERROR; + goto ON_EXIT; + } + + InsertTailList (NbufList, &DataSeg->List); + + // + // Receive the data segment with the data digest, if any. + // + Status = TcpIoReceive (&Conn->TcpIo, DataSeg, FALSE, TimeoutEvent); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (DataDigest) { + // + // TODO: Check the data digest. + // + NetbufTrim (DataSeg, sizeof (UINT32), NET_BUF_TAIL); + } + + if (PadLen != 0) { + // + // Trim off the padding bytes in the data segment. + // + NetbufTrim (DataSeg, PadLen, NET_BUF_TAIL); + } + +FORM_PDU: + // + // Form the pdu from a list of pdu segments. + // + *Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList); + if (*Pdu == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } + +ON_EXIT: + + if (EFI_ERROR (Status)) { + // + // Free the Nbufs in this NbufList and the NbufList itself. + // + IScsiFreeNbufList (NbufList); + } + + return Status; +} + + +/** + Check and get the result of the parameter negotiation. + + @param[in, out] Conn The connection in iSCSI login. + + @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +IScsiCheckOpParams ( + IN OUT ISCSI_CONNECTION *Conn + ) +{ + EFI_STATUS Status; + LIST_ENTRY *KeyValueList; + CHAR8 *Data; + UINT32 Len; + ISCSI_SESSION *Session; + CHAR8 *Value; + UINTN NumericValue; + + ASSERT (Conn->RspQue.BufNum != 0); + + Session = Conn->Session; + + Len = Conn->RspQue.BufSize; + Data = AllocatePool (Len); + if (Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NetbufQueCopy (&Conn->RspQue, 0, Len, (UINT8 *) Data); + + Status = EFI_PROTOCOL_ERROR; + + // + // Extract the Key-Value pairs into a list. + // + KeyValueList = IScsiBuildKeyValueList (Data, Len); + if (KeyValueList == NULL) { + FreePool (Data); + return Status; + } + // + // HeaderDigest + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_HEADER_DIGEST); + if (Value == NULL) { + goto ON_ERROR; + } + + if (AsciiStrCmp (Value, "CRC32") == 0) { + if (Conn->HeaderDigest != IScsiDigestCRC32) { + goto ON_ERROR; + } + } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) { + Conn->HeaderDigest = IScsiDigestNone; + } else { + goto ON_ERROR; + } + // + // DataDigest + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_DIGEST); + if (Value == NULL) { + goto ON_ERROR; + } + + if (AsciiStrCmp (Value, "CRC32") == 0) { + if (Conn->DataDigest != IScsiDigestCRC32) { + goto ON_ERROR; + } + } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) { + Conn->DataDigest = IScsiDigestNone; + } else { + goto ON_ERROR; + } + // + // ErrorRecoveryLevel: result fuction is Minimum. + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_ERROR_RECOVERY_LEVEL); + if (Value == NULL) { + goto ON_ERROR; + } + + NumericValue = IScsiNetNtoi (Value); + if (NumericValue > 2) { + goto ON_ERROR; + } + + Session->ErrorRecoveryLevel = (UINT8) MIN (Session->ErrorRecoveryLevel, NumericValue); + + // + // InitialR2T: result function is OR. + // + if (!Session->InitialR2T) { + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T); + if (Value == NULL) { + goto ON_ERROR; + } + + Session->InitialR2T = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0); + } + + // + // ImmediateData: result function is AND. + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_IMMEDIATE_DATA); + if (Value == NULL) { + goto ON_ERROR; + } + + Session->ImmediateData = (BOOLEAN) (Session->ImmediateData && (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0)); + + // + // MaxRecvDataSegmentLength is declarative. + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH); + if (Value != NULL) { + Conn->MaxRecvDataSegmentLength = (UINT32) IScsiNetNtoi (Value); + } + // + // MaxBurstLength: result funtion is Mininum. + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_BURST_LENGTH); + if (Value == NULL) { + goto ON_ERROR; + } + + NumericValue = IScsiNetNtoi (Value); + Session->MaxBurstLength = (UINT32) MIN (Session->MaxBurstLength, NumericValue); + + // + // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and + // ImmediateData=No. + // + if (!(Session->InitialR2T && !Session->ImmediateData)) { + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH); + if (Value == NULL) { + goto ON_ERROR; + } + + NumericValue = IScsiNetNtoi (Value); + Session->FirstBurstLength = (UINT32) MIN (Session->FirstBurstLength, NumericValue); + } + + // + // MaxConnections: result function is Minimum. + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_CONNECTIONS); + if (Value == NULL) { + goto ON_ERROR; + } + + NumericValue = IScsiNetNtoi (Value); + if ((NumericValue == 0) || (NumericValue > 65535)) { + goto ON_ERROR; + } + + Session->MaxConnections = (UINT32) MIN (Session->MaxConnections, NumericValue); + + // + // DataPDUInOrder: result function is OR. + // + if (!Session->DataPDUInOrder) { + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER); + if (Value == NULL) { + goto ON_ERROR; + } + + Session->DataPDUInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0); + } + + // + // DataSequenceInorder: result function is OR. + // + if (!Session->DataSequenceInOrder) { + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER); + if (Value == NULL) { + goto ON_ERROR; + } + + Session->DataSequenceInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0); + } + + // + // DefaultTime2Wait: result function is Maximum. + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2WAIT); + if (Value == NULL) { + goto ON_ERROR; + } + + NumericValue = IScsiNetNtoi (Value); + if (NumericValue == 0) { + Session->DefaultTime2Wait = 0; + } else if (NumericValue > 3600) { + goto ON_ERROR; + } else { + Session->DefaultTime2Wait = (UINT32) MAX (Session->DefaultTime2Wait, NumericValue); + } + // + // DefaultTime2Retain: result function is Minimum. + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2RETAIN); + if (Value == NULL) { + goto ON_ERROR; + } + + NumericValue = IScsiNetNtoi (Value); + if (NumericValue == 0) { + Session->DefaultTime2Retain = 0; + } else if (NumericValue > 3600) { + goto ON_ERROR; + } else { + Session->DefaultTime2Retain = (UINT32) MIN (Session->DefaultTime2Retain, NumericValue); + } + // + // MaxOutstandingR2T: result function is Minimum. + // + Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_OUTSTANDING_R2T); + if (Value == NULL) { + goto ON_ERROR; + } + + NumericValue = IScsiNetNtoi (Value); + if ((NumericValue == 0) || (NumericValue > 65535)) { + goto ON_ERROR; + } + + Session->MaxOutstandingR2T = (UINT16) MIN (Session->MaxOutstandingR2T, NumericValue); + + // + // Remove declarative key-value pairs, if any. + // + IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_SESSION_TYPE); + IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ALIAS); + IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG); + + + // + // Remove the key-value that may not needed for result function is OR. + // + IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T); + IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER); + IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER); + + // + // Remove irrelevant parameter, if any. + // + if (Session->InitialR2T && !Session->ImmediateData) { + IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH); + } + + if (IsListEmpty (KeyValueList)) { + // + // Succeed if no more keys in the list. + // + Status = EFI_SUCCESS; + } + +ON_ERROR: + + IScsiFreeKeyValueList (KeyValueList); + + FreePool (Data); + + return Status; +} + + +/** + Fill the operational parameters. + + @param[in] Conn The connection in iSCSI login. + @param[in, out] Pdu The iSCSI login request PDU to fill the parameters. + +**/ +VOID +IScsiFillOpParams ( + IN ISCSI_CONNECTION *Conn, + IN OUT NET_BUF *Pdu + ) +{ + ISCSI_SESSION *Session; + CHAR8 Value[256]; + + Session = Conn->Session; + + AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == IScsiDigestCRC32) ? "None,CRC32" : "None"); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value); + + AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == IScsiDigestCRC32) ? "None,CRC32" : "None"); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value); + + AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value); + + AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No"); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value); + + AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No"); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value); + + AsciiSPrint (Value, sizeof (Value), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value); + + AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value); + + AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value); + + AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value); + + AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No"); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value); + + AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No"); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value); + + AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value); + + AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value); + + AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T); + IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value); +} + + +/** + Pad the iSCSI AHS or data segment to an integer number of 4 byte words. + + @param[in, out] Pdu The iSCSI pdu which contains segments to pad. + @param[in] Len The length of the last segment in the PDU. + + @retval EFI_SUCCESS The segment is padded or there is no need to pad it. + @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the + padding bytes. +**/ +EFI_STATUS +IScsiPadSegment ( + IN OUT NET_BUF *Pdu, + IN UINT32 Len + ) +{ + UINT32 PadLen; + UINT8 *Data; + + PadLen = ISCSI_GET_PAD_LEN (Len); + + if (PadLen != 0) { + Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL); + if (Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (Data, PadLen); + } + + return EFI_SUCCESS; +} + + +/** + Build a key-value list from the data segment. + + @param[in] Data The data segment containing the key-value pairs. + @param[in] Len Length of the data segment. + + @return The key-value list. + @retval NULL Other errors as indicated. + +**/ +LIST_ENTRY * +IScsiBuildKeyValueList ( + IN CHAR8 *Data, + IN UINT32 Len + ) +{ + LIST_ENTRY *ListHead; + ISCSI_KEY_VALUE_PAIR *KeyValuePair; + + ListHead = AllocatePool (sizeof (LIST_ENTRY)); + if (ListHead == NULL) { + return NULL; + } + + InitializeListHead (ListHead); + + while (Len > 0) { + KeyValuePair = AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR)); + if (KeyValuePair == NULL) { + goto ON_ERROR; + } + + InitializeListHead (&KeyValuePair->List); + + KeyValuePair->Key = Data; + + while ((Len > 0) && (*Data != '=')) { + Len--; + Data++; + } + + if (*Data == '=') { + *Data = '\0'; + + Data++; + Len--; + } else { + FreePool (KeyValuePair); + goto ON_ERROR; + } + + KeyValuePair->Value = Data; + + InsertTailList (ListHead, &KeyValuePair->List);; + + Data += AsciiStrLen (KeyValuePair->Value) + 1; + Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1; + } + + return ListHead; + +ON_ERROR: + + IScsiFreeKeyValueList (ListHead); + + return NULL; +} + + +/** + Get the value string by the key name from the key-value list. If found, + the key-value entry will be removed from the list. + + @param[in, out] KeyValueList The key-value list. + @param[in] Key The key name to find. + + @return The value string. + @retval NULL The key value pair cannot be found. + +**/ +CHAR8 * +IScsiGetValueByKeyFromList ( + IN OUT LIST_ENTRY *KeyValueList, + IN CHAR8 *Key + ) +{ + LIST_ENTRY *Entry; + ISCSI_KEY_VALUE_PAIR *KeyValuePair; + CHAR8 *Value; + + Value = NULL; + + NET_LIST_FOR_EACH (Entry, KeyValueList) { + KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List); + + if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) { + Value = KeyValuePair->Value; + + RemoveEntryList (&KeyValuePair->List); + FreePool (KeyValuePair); + break; + } + } + + return Value; +} + + +/** + Free the key-value list. + + @param[in] KeyValueList The key-value list. + +**/ +VOID +IScsiFreeKeyValueList ( + IN LIST_ENTRY *KeyValueList + ) +{ + LIST_ENTRY *Entry; + ISCSI_KEY_VALUE_PAIR *KeyValuePair; + + while (!IsListEmpty (KeyValueList)) { + Entry = NetListRemoveHead (KeyValueList); + KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List); + + FreePool (KeyValuePair); + } + + FreePool (KeyValueList); +} + + +/** + Normalize the iSCSI name according to RFC. + + @param[in, out] Name The iSCSI name. + @param[in] Len Length of the iSCSI name. + + @retval EFI_SUCCESS The iSCSI name is valid and normalized. + @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format. + +**/ +EFI_STATUS +IScsiNormalizeName ( + IN OUT CHAR8 *Name, + IN UINTN Len + ) +{ + UINTN Index; + + for (Index = 0; Index < Len; Index++) { + if (NET_IS_UPPER_CASE_CHAR (Name[Index])) { + // + // Convert the upper-case characters to lower-case ones. + // + Name[Index] = (CHAR8) (Name[Index] - 'A' + 'a'); + } + + if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) && + !NET_IS_DIGIT (Name[Index]) && + (Name[Index] != '-') && + (Name[Index] != '.') && + (Name[Index] != ':') + ) { + // + // ASCII dash, dot, colon lower-case characters and digit characters + // are allowed. + // + return EFI_PROTOCOL_ERROR; + } + } + + if ((Len < 4) || (CompareMem (Name, "iqn.", 4) != 0)) { + // + // Only IQN format is accepted now. + // + return EFI_PROTOCOL_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Create an iSCSI task control block. + + @param[in] Conn The connection on which the task control block will be created. + @param[out] Tcb The newly created task control block. + + @retval EFI_SUCCESS The task control block is created. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NOT_READY The target cannot accept new commands. + +**/ +EFI_STATUS +IScsiNewTcb ( + IN ISCSI_CONNECTION *Conn, + OUT ISCSI_TCB **Tcb + ) +{ + ISCSI_SESSION *Session; + ISCSI_TCB *NewTcb; + + ASSERT (Tcb != NULL); + + Session = Conn->Session; + + if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) { + return EFI_NOT_READY; + } + + NewTcb = AllocateZeroPool (sizeof (ISCSI_TCB)); + if (NewTcb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (&NewTcb->Link); + + NewTcb->SoFarInOrder = TRUE; + NewTcb->InitiatorTaskTag = Session->InitiatorTaskTag; + NewTcb->CmdSN = Session->CmdSN; + NewTcb->Conn = Conn; + + InsertTailList (&Session->TcbList, &NewTcb->Link); + + // + // Advance the initiator task tag. + // + Session->InitiatorTaskTag++; + Session->CmdSN++; + + *Tcb = NewTcb; + + return EFI_SUCCESS; +} + + +/** + Delete the tcb from the connection and destroy it. + + @param[in] Tcb The tcb to delete. + +**/ +VOID +IScsiDelTcb ( + IN ISCSI_TCB *Tcb + ) +{ + RemoveEntryList (&Tcb->Link); + + FreePool (Tcb); +} + + +/** + Find the task control block by the initator task tag. + + @param[in] TcbList The tcb list. + @param[in] InitiatorTaskTag The initiator task tag. + + @return The task control block found. + @retval NULL The task control block cannot be found. + +**/ +ISCSI_TCB * +IScsiFindTcbByITT ( + IN LIST_ENTRY *TcbList, + IN UINT32 InitiatorTaskTag + ) +{ + ISCSI_TCB *Tcb; + LIST_ENTRY *Entry; + + Tcb = NULL; + + NET_LIST_FOR_EACH (Entry, TcbList) { + Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link); + + if (Tcb->InitiatorTaskTag == InitiatorTaskTag) { + break; + } + } + + return Tcb; +} + + +/** + Create a data segment, pad it, and calculate the CRC if needed. + + @param[in] Data The data to fill into the data segment. + @param[in] Len Length of the data. + @param[in] DataDigest Whether to calculate CRC for this data segment. + + @return The net buffer wrapping the data segment. + +**/ +NET_BUF * +IScsiNewDataSegment ( + IN UINT8 *Data, + IN UINT32 Len, + IN BOOLEAN DataDigest + ) +{ + NET_FRAGMENT Fragment[2]; + UINT32 FragmentCount; + UINT32 PadLen; + NET_BUF *DataSeg; + + Fragment[0].Len = Len; + Fragment[0].Bulk = Data; + + PadLen = ISCSI_GET_PAD_LEN (Len); + if (PadLen != 0) { + Fragment[1].Len = PadLen; + Fragment[1].Bulk = (UINT8 *) &mDataSegPad; + + FragmentCount = 2; + } else { + FragmentCount = 1; + } + + DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL); + + return DataSeg; +} + + +/** + Create a iSCSI SCSI command PDU to encapsulate the command issued + by SCSI through the EXT SCSI PASS THRU Protocol. + + @param[in] Packet The EXT SCSI PASS THRU request packet containing the SCSI command. + @param[in] Lun The LUN. + @param[in] Tcb The tcb assocated with this SCSI command. + + @return The created iSCSI SCSI command PDU. + @retval NULL Other errors as indicated. + +**/ +NET_BUF * +IScsiNewScsiCmdPdu ( + IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN UINT64 Lun, + IN ISCSI_TCB *Tcb + ) +{ + LIST_ENTRY *NbufList; + NET_BUF *Pdu; + NET_BUF *PduHeader; + NET_BUF *DataSeg; + SCSI_COMMAND *ScsiCmd; + UINT8 AHSLength; + UINT32 Length; + ISCSI_ADDITIONAL_HEADER *Header; + ISCSI_BI_EXP_READ_DATA_LEN_AHS *BiExpReadDataLenAHS; + ISCSI_SESSION *Session; + UINT32 ImmediateDataLen; + + AHSLength = 0; + + if (Packet->DataDirection == DataBi) { + // + // Bidirectional Read/Write command, the bidirectional expected + // read data length AHS is required. + // + AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS); + } + + if (Packet->CdbLength > 16) { + // + // The CDB exceeds 16 bytes. An extended CDB AHS is required. + // + AHSLength = (UINT8) (AHSLength + ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER)); + } + + Length = sizeof (SCSI_COMMAND) + AHSLength; + PduHeader = NetbufAlloc (Length); + if (PduHeader == NULL) { + return NULL; + } + + ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL); + if (ScsiCmd == NULL) { + NetbufFree (PduHeader); + return NULL; + } + Header = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1); + + ZeroMem (ScsiCmd, Length); + + ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0); + ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE); + + // + // Set the READ/WRITE flags according to the IO type of this request. + // + switch (Packet->DataDirection) { + case DataIn: + ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ); + ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength); + break; + + case DataOut: + ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE); + ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength); + break; + + case DataBi: + ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE); + ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength); + + // + // Fill the bidirectional expected read data length AHS. + // + BiExpReadDataLenAHS = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header; + Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1); + + BiExpReadDataLenAHS->Length = NTOHS (5); + BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN; + BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength); + + break; + } + + ScsiCmd->TotalAHSLength = AHSLength; + CopyMem (ScsiCmd->Lun, &Lun, sizeof (ScsiCmd->Lun)); + ScsiCmd->InitiatorTaskTag = NTOHL (Tcb->InitiatorTaskTag); + ScsiCmd->CmdSN = NTOHL (Tcb->CmdSN); + ScsiCmd->ExpStatSN = NTOHL (Tcb->Conn->ExpStatSN); + + CopyMem (ScsiCmd->Cdb, Packet->Cdb, sizeof (ScsiCmd->Cdb)); + + if (Packet->CdbLength > 16) { + Header->Length = NTOHS ((UINT16) (Packet->CdbLength - 15)); + Header->Type = ISCSI_AHS_TYPE_EXT_CDB; + + CopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16); + } + + Pdu = PduHeader; + Session = Tcb->Conn->Session; + ImmediateDataLen = 0; + + if (Session->ImmediateData && (Packet->OutTransferLength != 0)) { + // + // Send immediate data in this SCSI Command PDU. The length of the immeidate + // data is the minimum of FirstBurstLength, the data length to be xfered, and + // the MaxRecvdataSegmentLength on this connection. + // + ImmediateDataLen = MIN (Session->FirstBurstLength, Packet->OutTransferLength); + ImmediateDataLen = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength); + + // + // Update the data segment length in the PDU header. + // + ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen); + + // + // Create the data segment. + // + DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE); + if (DataSeg == NULL) { + NetbufFree (PduHeader); + Pdu = NULL; + goto ON_EXIT; + } + + NbufList = AllocatePool (sizeof (LIST_ENTRY)); + if (NbufList == NULL) { + NetbufFree (PduHeader); + NetbufFree (DataSeg); + + Pdu = NULL; + goto ON_EXIT; + } + + InitializeListHead (NbufList); + InsertTailList (NbufList, &PduHeader->List); + InsertTailList (NbufList, &DataSeg->List); + + Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList); + if (Pdu == NULL) { + IScsiFreeNbufList (NbufList); + } + } + + if (Session->InitialR2T || + (ImmediateDataLen == Session->FirstBurstLength) || + (ImmediateDataLen == Packet->OutTransferLength) + ) { + // + // Unsolicited data out sequence is not allowed, + // or FirstBustLength data is already sent out by immediate data, + // or all the OUT data accompany this SCSI packet are sent as + // immediate data. The final flag should be set on this SCSI Command + // PDU. + // + ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL); + } + +ON_EXIT: + + return Pdu; +} + + +/** + Create a new iSCSI SCSI Data Out PDU. + + @param[in] Data The data to put into the Data Out PDU. + @param[in] Len Length of the data. + @param[in] DataSN The DataSN of the Data Out PDU. + @param[in] Tcb The task control block of this Data Out PDU. + @param[in] Lun The LUN. + + @return The net buffer wrapping the Data Out PDU. + @retval NULL Other errors as indicated. + +**/ +NET_BUF * +IScsiNewDataOutPdu ( + IN UINT8 *Data, + IN UINT32 Len, + IN UINT32 DataSN, + IN ISCSI_TCB *Tcb, + IN UINT64 Lun + ) +{ + LIST_ENTRY *NbufList; + NET_BUF *PduHdr; + NET_BUF *DataSeg; + NET_BUF *Pdu; + ISCSI_SCSI_DATA_OUT *DataOutHdr; + ISCSI_XFER_CONTEXT *XferContext; + + NbufList = AllocatePool (sizeof (LIST_ENTRY)); + if (NbufList == NULL) { + return NULL; + } + + InitializeListHead (NbufList); + + // + // Allocate memory for the BHS. + // + PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT)); + if (PduHdr == NULL) { + FreePool (NbufList); + return NULL; + } + // + // Insert the BHS into the buffer list. + // + InsertTailList (NbufList, &PduHdr->List); + + DataOutHdr = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL); + ASSERT (DataOutHdr != NULL); + XferContext = &Tcb->XferContext; + + ZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT)); + + // + // Set the flags and fields of the Data Out PDU BHS. + // + ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0); + ISCSI_SET_DATASEG_LEN (DataOutHdr, Len); + + DataOutHdr->InitiatorTaskTag = HTONL (Tcb->InitiatorTaskTag); + DataOutHdr->TargetTransferTag = HTONL (XferContext->TargetTransferTag); + DataOutHdr->ExpStatSN = HTONL (Tcb->Conn->ExpStatSN); + DataOutHdr->DataSN = HTONL (DataSN); + DataOutHdr->BufferOffset = HTONL (XferContext->Offset); + + if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) { + CopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun)); + } + // + // Build the data segment for this Data Out PDU. + // + DataSeg = IScsiNewDataSegment (Data, Len, FALSE); + if (DataSeg == NULL) { + IScsiFreeNbufList (NbufList); + return NULL; + } + // + // Put the data segment into the buffer list and combine it with the BHS + // into a full Data Out PDU. + // + InsertTailList (NbufList, &DataSeg->List); + Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList); + if (Pdu == NULL) { + IScsiFreeNbufList (NbufList); + } + + return Pdu; +} + + +/** + Generate a consecutive sequence of iSCSI SCSI Data Out PDUs. + + @param[in] Data The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs. + @param[in] Tcb The task control block of the data to send out. + @param[in] Lun The LUN the data will be sent to. + + @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU. + @retval NULL Other errors as indicated. + +**/ +LIST_ENTRY * +IScsiGenerateDataOutPduSequence ( + IN UINT8 *Data, + IN ISCSI_TCB *Tcb, + IN UINT64 Lun + ) +{ + LIST_ENTRY *PduList; + UINT32 DataSN; + UINT32 DataLen; + NET_BUF *DataOutPdu; + ISCSI_CONNECTION *Conn; + ISCSI_XFER_CONTEXT *XferContext; + UINT8 *DataOutPacket; + + PduList = AllocatePool (sizeof (LIST_ENTRY)); + if (PduList == NULL) { + return NULL; + } + + InitializeListHead (PduList); + + DataSN = 0; + Conn = Tcb->Conn; + DataOutPdu = NULL; + XferContext = &Tcb->XferContext; + + while (XferContext->DesiredLength > 0) { + // + // Determine the length of data this Data Out PDU can carry. + // + DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength); + + // + // Create a Data Out PDU. + // + DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun); + if (DataOutPdu == NULL) { + IScsiFreeNbufList (PduList); + PduList = NULL; + + goto ON_EXIT; + } + + InsertTailList (PduList, &DataOutPdu->List); + + // + // Update the context and DataSN. + // + Data += DataLen; + XferContext->Offset += DataLen; + XferContext->DesiredLength -= DataLen; + DataSN++; + } + // + // Set the F bit for the last data out PDU in this sequence. + // + DataOutPacket = NetbufGetByte (DataOutPdu, 0, NULL); + if (DataOutPacket == NULL) { + IScsiFreeNbufList (PduList); + PduList = NULL; + goto ON_EXIT; + } + + ISCSI_SET_FLAG (DataOutPacket, ISCSI_BHS_FLAG_FINAL); + +ON_EXIT: + + return PduList; +} + +/** + Send the Data in a sequence of Data Out PDUs one by one. + + @param[in] Data The data to carry by Data Out PDUs. + @param[in] Lun The LUN the data will be sent to. + @param[in] Tcb The task control block. + + @retval EFI_SUCCES The data is sent out to the LUN. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiSendDataOutPduSequence ( + IN UINT8 *Data, + IN UINT64 Lun, + IN ISCSI_TCB *Tcb + ) +{ + LIST_ENTRY *DataOutPduList; + LIST_ENTRY *Entry; + NET_BUF *Pdu; + EFI_STATUS Status; + + // + // Generate the Data Out PDU sequence. + // + DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun); + if (DataOutPduList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = EFI_SUCCESS; + + // + // Send the Data Out PDU's one by one. + // + NET_LIST_FOR_EACH (Entry, DataOutPduList) { + Pdu = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + Status = TcpIoTransmit (&Tcb->Conn->TcpIo, Pdu); + + if (EFI_ERROR (Status)) { + break; + } + } + + IScsiFreeNbufList (DataOutPduList); + + return Status; +} + + +/** + Process the received iSCSI SCSI Data In PDU. + + @param[in] Pdu The Data In PDU received. + @param[in] Tcb The task control block. + @param[in, out] Packet The EXT SCSI PASS THRU request packet. + + @retval EFI_SUCCES The check on the Data IN PDU is passed and some update + actions are taken. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred. + @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiOnDataInRcvd ( + IN NET_BUF *Pdu, + IN ISCSI_TCB *Tcb, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + ISCSI_SCSI_DATA_IN *DataInHdr; + EFI_STATUS Status; + + DataInHdr = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL); + if (DataInHdr == NULL) { + return EFI_PROTOCOL_ERROR; + } + + DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag); + DataInHdr->ExpCmdSN = NTOHL (DataInHdr->ExpCmdSN); + DataInHdr->MaxCmdSN = NTOHL (DataInHdr->MaxCmdSN); + DataInHdr->DataSN = NTOHL (DataInHdr->DataSN); + + // + // Check the DataSN. + // + Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN); + if (EFI_ERROR (Status)) { + return Status; + } + + if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) { + return EFI_PROTOCOL_ERROR; + } + // + // Update the command related sequence numbers. + // + IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN); + + if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) { + if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) { + // + // The S bit is on but the F bit is off. + // + return EFI_PROTOCOL_ERROR; + } + + Tcb->StatusXferd = TRUE; + + if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) { + // + // Underflow and Overflow are mutual flags. + // + return EFI_PROTOCOL_ERROR; + } + // + // S bit is on, the StatSN is valid. + // + Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN)); + if (EFI_ERROR (Status)) { + return Status; + } + + Packet->HostAdapterStatus = 0; + Packet->TargetStatus = DataInHdr->Status; + + if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) { + Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount); + Status = EFI_BAD_BUFFER_SIZE; + } + + if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) { + Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount); + } + } + + return Status; +} + + +/** + Process the received iSCSI R2T PDU. + + @param[in] Pdu The R2T PDU received. + @param[in] Tcb The task control block. + @param[in] Lun The Lun. + @param[in, out] Packet The EXT SCSI PASS THRU request packet. + + @retval EFI_SUCCES The R2T PDU is valid and the solicited data is sent out. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiOnR2TRcvd ( + IN NET_BUF *Pdu, + IN ISCSI_TCB *Tcb, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + ISCSI_READY_TO_TRANSFER *R2THdr; + EFI_STATUS Status; + ISCSI_XFER_CONTEXT *XferContext; + UINT8 *Data; + + R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL); + if (R2THdr == NULL) { + return EFI_PROTOCOL_ERROR; + } + + R2THdr->InitiatorTaskTag = NTOHL (R2THdr->InitiatorTaskTag); + R2THdr->TargetTransferTag = NTOHL (R2THdr->TargetTransferTag); + R2THdr->StatSN = NTOHL (R2THdr->StatSN); + R2THdr->R2TSeqNum = NTOHL (R2THdr->R2TSeqNum); + R2THdr->BufferOffset = NTOHL (R2THdr->BufferOffset); + R2THdr->DesiredDataTransferLength = NTOHL (R2THdr->DesiredDataTransferLength); + + if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) { + return EFI_PROTOCOL_ERROR;; + } + // + // Check the sequence number. + // + Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSeqNum); + if (EFI_ERROR (Status)) { + return Status; + } + + XferContext = &Tcb->XferContext; + XferContext->TargetTransferTag = R2THdr->TargetTransferTag; + XferContext->Offset = R2THdr->BufferOffset; + XferContext->DesiredLength = R2THdr->DesiredDataTransferLength; + + if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) || + (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength) + ) { + return EFI_PROTOCOL_ERROR; + } + // + // Send the data solicited by this R2T. + // + Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset; + Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb); + + return Status; +} + + +/** + Process the received iSCSI SCSI Response PDU. + + @param[in] Pdu The Response PDU received. + @param[in] Tcb The task control block. + @param[in, out] Packet The EXT SCSI PASS THRU request packet. + + @retval EFI_SUCCES The Response PDU is processed. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred. + @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiOnScsiRspRcvd ( + IN NET_BUF *Pdu, + IN ISCSI_TCB *Tcb, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + SCSI_RESPONSE *ScsiRspHdr; + ISCSI_SENSE_DATA *SenseData; + EFI_STATUS Status; + UINT32 DataSegLen; + + ScsiRspHdr = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL); + if (ScsiRspHdr == NULL) { + return EFI_PROTOCOL_ERROR; + } + + ScsiRspHdr->InitiatorTaskTag = NTOHL (ScsiRspHdr->InitiatorTaskTag); + if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) { + return EFI_PROTOCOL_ERROR; + } + + ScsiRspHdr->StatSN = NTOHL (ScsiRspHdr->StatSN); + + Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN); + if (EFI_ERROR (Status)) { + return Status; + } + + ScsiRspHdr->MaxCmdSN = NTOHL (ScsiRspHdr->MaxCmdSN); + ScsiRspHdr->ExpCmdSN = NTOHL (ScsiRspHdr->ExpCmdSN); + IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN); + + Tcb->StatusXferd = TRUE; + + Packet->HostAdapterStatus = ScsiRspHdr->Response; + if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) { + return EFI_SUCCESS; + } + + Packet->TargetStatus = ScsiRspHdr->Status; + + if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW | SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW) || + ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW | SCSI_RSP_PDU_FLAG_UNDERFLOW) + ) { + return EFI_PROTOCOL_ERROR; + } + + if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW)) { + Packet->InTransferLength += NTOHL (ScsiRspHdr->BiReadResidualCount); + Status = EFI_BAD_BUFFER_SIZE; + } + + if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) { + Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount); + } + + if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) { + if (Packet->DataDirection == DataIn) { + Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount); + } else { + Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount); + } + + Status = EFI_BAD_BUFFER_SIZE; + } + + if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) { + if (Packet->DataDirection == DataIn) { + Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount); + } else { + Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount); + } + } + + DataSegLen = ISCSI_GET_DATASEG_LEN (ScsiRspHdr); + if (DataSegLen != 0) { + SenseData = (ISCSI_SENSE_DATA *) NetbufGetByte (Pdu, sizeof (SCSI_RESPONSE), NULL); + if (SenseData == NULL) { + return EFI_PROTOCOL_ERROR; + } + + SenseData->Length = NTOHS (SenseData->Length); + + Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength); + if (Packet->SenseDataLength != 0) { + CopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength); + } + } else { + Packet->SenseDataLength = 0; + } + + return Status; +} + + +/** + Process the received NOP In PDU. + + @param[in] Pdu The NOP In PDU received. + @param[in] Tcb The task control block. + + @retval EFI_SUCCES The NOP In PDU is processed and the related sequence + numbers are updated. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred. + +**/ +EFI_STATUS +IScsiOnNopInRcvd ( + IN NET_BUF *Pdu, + IN ISCSI_TCB *Tcb + ) +{ + ISCSI_NOP_IN *NopInHdr; + EFI_STATUS Status; + + NopInHdr = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL); + if (NopInHdr == NULL) { + return EFI_PROTOCOL_ERROR; + } + + NopInHdr->StatSN = NTOHL (NopInHdr->StatSN); + NopInHdr->ExpCmdSN = NTOHL (NopInHdr->ExpCmdSN); + NopInHdr->MaxCmdSN = NTOHL (NopInHdr->MaxCmdSN); + + if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) { + if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) { + return EFI_PROTOCOL_ERROR; + } + } else { + Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN); + if (EFI_ERROR (Status)) { + return Status; + } + } + + IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN); + + return EFI_SUCCESS; +} + + +/** + Execute the SCSI command issued through the EXT SCSI PASS THRU protocol. + + @param[in] PassThru The EXT SCSI PASS THRU protocol. + @param[in] Target The target ID. + @param[in] Lun The LUN. + @param[in, out] Packet The request packet containing IO request, SCSI command + buffer and buffers to read/write. + + @retval EFI_SUCCES The SCSI command is executed and the result is updated to + the Packet. + @retval EFI_DEVICE_ERROR Session state was not as required. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer. + @retval EFI_NOT_READY The target can not accept new commands. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiExecuteScsiCommand ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + EFI_STATUS Status; + ISCSI_DRIVER_DATA *Private; + ISCSI_SESSION *Session; + EFI_EVENT TimeoutEvent; + ISCSI_CONNECTION *Conn; + ISCSI_TCB *Tcb; + NET_BUF *Pdu; + ISCSI_XFER_CONTEXT *XferContext; + UINT8 *Data; + ISCSI_IN_BUFFER_CONTEXT InBufferContext; + UINT64 Timeout; + UINT8 *PduHdr; + + Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru); + Session = Private->Session; + Status = EFI_SUCCESS; + Tcb = NULL; + TimeoutEvent = NULL; + Timeout = 0; + + if (Session->State != SESSION_STATE_LOGGED_IN) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Conn = NET_LIST_USER_STRUCT_S ( + Session->Conns.ForwardLink, + ISCSI_CONNECTION, + Link, + ISCSI_CONNECTION_SIGNATURE + ); + + if (Packet->Timeout != 0) { + Timeout = MultU64x32 (Packet->Timeout, 4); + } + + Status = IScsiNewTcb (Conn, &Tcb); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU. + // + Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb); + if (Pdu == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + XferContext = &Tcb->XferContext; + PduHdr = NetbufGetByte (Pdu, 0, NULL); + if (PduHdr == NULL) { + Status = EFI_PROTOCOL_ERROR; + NetbufFree (Pdu); + goto ON_EXIT; + } + XferContext->Offset = ISCSI_GET_DATASEG_LEN (PduHdr); + + // + // Transmit the SCSI Command PDU. + // + Status = TcpIoTransmit (&Conn->TcpIo, Pdu); + + NetbufFree (Pdu); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (!Session->InitialR2T && + (XferContext->Offset < Session->FirstBurstLength) && + (XferContext->Offset < Packet->OutTransferLength) + ) { + // + // Unsolicited Data-Out sequence is allowed. There is remaining SCSI + // OUT data, and the limit of FirstBurstLength is not reached. + // + XferContext->TargetTransferTag = ISCSI_RESERVED_TAG; + XferContext->DesiredLength = MIN ( + Session->FirstBurstLength, + Packet->OutTransferLength - XferContext->Offset + ); + + Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset; + Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + InBufferContext.InData = (UINT8 *) Packet->InDataBuffer; + InBufferContext.InDataLen = Packet->InTransferLength; + + while (!Tcb->StatusXferd) { + // + // Start the timeout timer. + // + if (Timeout != 0) { + Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + TimeoutEvent = Conn->TimeoutEvent; + } + + // + // Try to receive PDU from target. + // + Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + PduHdr = NetbufGetByte (Pdu, 0, NULL); + if (PduHdr == NULL) { + Status = EFI_PROTOCOL_ERROR; + NetbufFree (Pdu); + goto ON_EXIT; + } + switch (ISCSI_GET_OPCODE (PduHdr)) { + case ISCSI_OPCODE_SCSI_DATA_IN: + Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet); + break; + + case ISCSI_OPCODE_R2T: + Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet); + break; + + case ISCSI_OPCODE_SCSI_RSP: + Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet); + break; + + case ISCSI_OPCODE_NOP_IN: + Status = IScsiOnNopInRcvd (Pdu, Tcb); + break; + + case ISCSI_OPCODE_VENDOR_T0: + case ISCSI_OPCODE_VENDOR_T1: + case ISCSI_OPCODE_VENDOR_T2: + // + // These messages are vendor specific. Skip them. + // + break; + + default: + Status = EFI_PROTOCOL_ERROR; + break; + } + + NetbufFree (Pdu); + + if (EFI_ERROR (Status)) { + break; + } + } + +ON_EXIT: + + if (TimeoutEvent != NULL) { + gBS->SetTimer (TimeoutEvent, TimerCancel, 0); + } + + if (Tcb != NULL) { + IScsiDelTcb (Tcb); + } + + return Status; +} + + +/** + Reinstate the session on some error. + + @param[in] Session The iSCSI session + + @retval EFI_SUCCESS The session is reinstated from some error. + @retval Other Reinstatement failed. + +**/ +EFI_STATUS +IScsiSessionReinstatement ( + IN ISCSI_SESSION *Session + ) +{ + EFI_STATUS Status; + + ASSERT (Session->State != SESSION_STATE_FREE); + + // + // Abort the session and re-init it. + // + IScsiSessionAbort (Session); + IScsiSessionInit (Session, TRUE); + + // + // Login again. + // + Status = IScsiSessionLogin (Session); + + return Status; +} + + +/** + Initialize some session parameters before login. + + @param[in, out] Session The iSCSI session. + @param[in] Recovery Whether the request is from a fresh new start or recovery. + +**/ +VOID +IScsiSessionInit ( + IN OUT ISCSI_SESSION *Session, + IN BOOLEAN Recovery + ) +{ + if (!Recovery) { + Session->Signature = ISCSI_SESSION_SIGNATURE; + Session->State = SESSION_STATE_FREE; + + InitializeListHead (&Session->Conns); + InitializeListHead (&Session->TcbList); + } + + Session->Tsih = 0; + + Session->CmdSN = 1; + Session->InitiatorTaskTag = 1; + Session->NextCid = 1; + + Session->TargetPortalGroupTag = 0; + Session->MaxConnections = ISCSI_MAX_CONNS_PER_SESSION; + Session->InitialR2T = FALSE; + Session->ImmediateData = TRUE; + Session->MaxBurstLength = 262144; + Session->FirstBurstLength = MAX_RECV_DATA_SEG_LEN_IN_FFP; + Session->DefaultTime2Wait = 2; + Session->DefaultTime2Retain = 20; + Session->MaxOutstandingR2T = DEFAULT_MAX_OUTSTANDING_R2T; + Session->DataPDUInOrder = TRUE; + Session->DataSequenceInOrder = TRUE; + Session->ErrorRecoveryLevel = 0; +} + + +/** + Abort the iSCSI session. That is, reset all the connection(s), and free the + resources. + + @param[in, out] Session The iSCSI session. + +**/ +VOID +IScsiSessionAbort ( + IN OUT ISCSI_SESSION *Session + ) +{ + ISCSI_CONNECTION *Conn; + EFI_GUID *ProtocolGuid; + + if (Session->State != SESSION_STATE_LOGGED_IN) { + return ; + } + + ASSERT (!IsListEmpty (&Session->Conns)); + + while (!IsListEmpty (&Session->Conns)) { + Conn = NET_LIST_USER_STRUCT_S ( + Session->Conns.ForwardLink, + ISCSI_CONNECTION, + Link, + ISCSI_CONNECTION_SIGNATURE + ); + if (!Conn->Ipv6Flag) { + ProtocolGuid = &gEfiTcp4ProtocolGuid; + } else { + ProtocolGuid = &gEfiTcp6ProtocolGuid; + } + + gBS->CloseProtocol ( + Conn->TcpIo.Handle, + ProtocolGuid, + Session->Private->Image, + Session->Private->ExtScsiPassThruHandle + ); + + IScsiConnReset (Conn); + + IScsiDetatchConnection (Conn); + IScsiDestroyConnection (Conn); + } + + Session->State = SESSION_STATE_FAILED; + + return ; +} diff --git a/Core/NetworkPkg/IScsiDxe/IScsiProto.h b/Core/NetworkPkg/IScsiDxe/IScsiProto.h new file mode 100644 index 0000000000..8099f34596 --- /dev/null +++ b/Core/NetworkPkg/IScsiDxe/IScsiProto.h @@ -0,0 +1,1036 @@ +/** @file + The header file of iSCSI Protocol that defines many specific data structures. + +Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _ISCSI_PROTO_H_ +#define _ISCSI_PROTO_H_ + +// +// RFC 1982 Serial Number Arithmetic, SERIAL_BITS = 32 +// +#define ISCSI_SEQ_EQ(s1, s2) ((s1) == (s2)) +#define ISCSI_SEQ_LT(s1, s2) \ + ( \ + (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) < ((UINT32) 1 << 31)) || \ + (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) > ((UINT32) 1 << 31)) \ + ) +#define ISCSI_SEQ_GT(s1, s2) \ + ( \ + (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) > ((UINT32) 1 << 31)) || \ + (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) < ((UINT32) 1 << 31)) \ + ) + +#define ISCSI_WELL_KNOWN_PORT 3260 +#define ISCSI_MAX_CONNS_PER_SESSION 1 + +#define DEFAULT_MAX_RECV_DATA_SEG_LEN 8192 +#define MAX_RECV_DATA_SEG_LEN_IN_FFP 65536 +#define DEFAULT_MAX_OUTSTANDING_R2T 1 + +#define ISCSI_VERSION_MAX 0x00 +#define ISCSI_VERSION_MIN 0x00 + +#define ISCSI_KEY_AUTH_METHOD "AuthMethod" +#define ISCSI_KEY_HEADER_DIGEST "HeaderDigest" +#define ISCSI_KEY_DATA_DIGEST "DataDigest" +#define ISCSI_KEY_MAX_CONNECTIONS "MaxConnections" +#define ISCSI_KEY_TARGET_NAME "TargetName" +#define ISCSI_KEY_INITIATOR_NAME "InitiatorName" +#define ISCSI_KEY_TARGET_ALIAS "TargetAlias" +#define ISCSI_KEY_INITIATOR_ALIAS "InitiatorAlias" +#define ISCSI_KEY_TARGET_ADDRESS "TargetAddress" +#define ISCSI_KEY_INITIAL_R2T "InitialR2T" +#define ISCSI_KEY_IMMEDIATE_DATA "ImmediateData" +#define ISCSI_KEY_TARGET_PORTAL_GROUP_TAG "TargetPortalGroupTag" +#define ISCSI_KEY_MAX_BURST_LENGTH "MaxBurstLength" +#define ISCSI_KEY_FIRST_BURST_LENGTH "FirstBurstLength" +#define ISCSI_KEY_DEFAULT_TIME2WAIT "DefaultTime2Wait" +#define ISCSI_KEY_DEFAULT_TIME2RETAIN "DefaultTime2Retain" +#define ISCSI_KEY_MAX_OUTSTANDING_R2T "MaxOutstandingR2T" +#define ISCSI_KEY_DATA_PDU_IN_ORDER "DataPDUInOrder" +#define ISCSI_KEY_DATA_SEQUENCE_IN_ORDER "DataSequenceInOrder" +#define ISCSI_KEY_ERROR_RECOVERY_LEVEL "ErrorRecoveryLevel" +#define ISCSI_KEY_SESSION_TYPE "SessionType" +#define ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH "MaxRecvDataSegmentLength" + +#define ISCSI_KEY_VALUE_NONE "None" + +/// +/// connection state for initiator +/// + +#define CONN_STATE_FREE 0 +#define CONN_STATE_XPT_WAIT 1 +#define CONN_STATE_IN_LOGIN 2 +#define CONN_STATE_LOGGED_IN 3 +#define CONN_STATE_IN_LOGOUT 4 +#define CONN_STATE_LOGOUT_REQUESTED 5 +#define CONN_STATE_CLEANUP_WAIT 6 +#define CONN_STATE_IN_CLEANUP 7 + +/// +/// session state for initiator +/// +#define SESSION_STATE_FREE 0 +#define SESSION_STATE_LOGGED_IN 1 +#define SESSION_STATE_FAILED 2 + +#define ISCSI_RESERVED_TAG 0xffffffff + +#define ISCSI_REQ_IMMEDIATE 0x40 +#define ISCSI_OPCODE_MASK 0x3F + +#define ISCSI_SET_OPCODE(PduHdr, Op, Flgs) ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) = ((Op) | (Flgs))) +#define ISCSI_GET_OPCODE(PduHdr) ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) & ISCSI_OPCODE_MASK) +#define ISCSI_CHECK_OPCODE(PduHdr, Op) ((((PduHdr)->OpCode) & ISCSI_OPCODE_MASK) == (Op)) +#define ISCSI_IMMEDIATE_ON(PduHdr) ((PduHdr)->OpCode & ISCSI_REQ_IMMEDIATE) +#define ISCSI_SET_FLAG(PduHdr, Flag) (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags |= (BOOLEAN)(Flag)) +#define ISCSI_CLEAR_FLAG(PduHdr, Flag) (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags &= ~(Flag)) +#define ISCSI_FLAG_ON(PduHdr, Flag) ((BOOLEAN) ((((ISCSI_BASIC_HEADER *) (PduHdr))->Flags & (Flag)) == (Flag))) +#define ISCSI_SET_STAGES(PduHdr, Cur, Nxt) ((PduHdr)->Flags = (UINT8) ((PduHdr)->Flags | ((Cur) << 2 | (Nxt)))) +#define ISCSI_GET_CURRENT_STAGE(PduHdr) ((UINT8) (((PduHdr)->Flags >> 2) & 0x3)) +#define ISCSI_GET_NEXT_STAGE(PduHdr) ((UINT8) (((PduHdr)->Flags) & 0x3)) + +#define ISCSI_GET_PAD_LEN(DataLen) ((~(DataLen) + 1) & 0x3) +#define ISCSI_ROUNDUP(DataLen) (((DataLen) + 3) &~(0x3)) + +#define HTON24(Dst, Src) \ + do { \ + (Dst)[0] = (UINT8) ((UINT8) ((Src) >> 16) & 0xFF); \ + (Dst)[1] = (UINT8) ((UINT8) ((Src) >> 8) & 0xFF); \ + (Dst)[2] = (UINT8) ((UINT8) (Src) & 0xFF); \ + } while (0); + +#define NTOH24(src) (((src)[0] << 16) | ((src)[1] << 8) | ((src)[2])) + +#define ISCSI_GET_DATASEG_LEN(PduHdr) NTOH24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength) +#define ISCSI_SET_DATASEG_LEN(PduHdr, Len) HTON24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength, (Len)) +#define ISCSI_GET_BUFFER_OFFSET(PduHdr) NTOHL (((ISCSI_SCSI_DATA_IN *) (PduHdr))->BufferOffset) + +// +// Initiator opcodes. +// +#define ISCSI_OPCODE_NOP_OUT 0x00 +#define ISCSI_OPCODE_SCSI_CMD 0x01 +#define ISCSI_OPCODE_SCSI_TMF_REQ 0x02 +#define ISCSI_OPCODE_LOGIN_REQ 0x03 +#define ISCSI_OPCODE_TEXT_REQ 0x04 +#define ISCSI_OPCODE_SCSI_DATA_OUT 0x05 +#define ISCSI_OPCODE_LOGOUT_REQ 0x06 +#define ISCSI_OPCODE_SNACK_REQ 0x10 +#define ISCSI_OPCODE_VENDOR_I0 0x1c +#define ISCSI_OPCODE_VENDOR_I1 0x1d +#define ISCSI_OPCODE_VENDOR_I2 0x1e + +// +// Target opcodes. +// +#define ISCSI_OPCODE_NOP_IN 0x20 +#define ISCSI_OPCODE_SCSI_RSP 0x21 +#define ISCSI_OPCODE_SCSI_TMF_RSP 0x22 +#define ISCSI_OPCODE_LOGIN_RSP 0x23 +#define ISCSI_OPCODE_TEXT_RSP 0x24 +#define ISCSI_OPCODE_SCSI_DATA_IN 0x25 +#define ISCSI_OPCODE_LOGOUT_RSP 0x26 +#define ISCSI_OPCODE_R2T 0x31 +#define ISCSI_OPCODE_ASYNC_MSG 0x32 +#define ISCSI_OPCODE_VENDOR_T0 0x3c +#define ISCSI_OPCODE_VENDOR_T1 0x3d +#define ISCSI_OPCODE_VENDOR_T2 0x3e +#define ISCSI_OPCODE_REJECT 0x3f + +#define ISCSI_BHS_FLAG_FINAL 0x80 + +// +// Defined AHS types, others are reserved. +// +#define ISCSI_AHS_TYPE_EXT_CDB 0x1 +#define ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN 0x2 + +#define SCSI_CMD_PDU_FLAG_READ 0x40 +#define SCSI_CMD_PDU_FLAG_WRITE 0x20 + +#define ISCSI_CMD_PDU_TASK_ATTR_MASK 0x07 + +// +// task attributes +// +#define ISCSI_TASK_ATTR_UNTAGGED 0x00 +#define ISCSI_TASK_ATTR_SIMPLE 0x01 +#define ISCSI_TASK_ATTR_ORDERD 0x02 +#define ISCSI_TASK_ATTR_HOQ 0x03 +#define ISCSI_TASK_ATTR_ACA 0x04 + +// +// Flag bit definitions in SCSI response. +// +#define SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW 0x10 +#define SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW 0x08 +#define SCSI_RSP_PDU_FLAG_OVERFLOW 0x04 +#define SCSI_RSP_PDU_FLAG_UNDERFLOW 0x02 + +// +// iSCSI service response codes. +// +#define ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET 0x00 +#define ISCSI_SERVICE_RSP_TARGET_FAILURE 0x01 + +#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_COMPLETE 0 +#define ISCSI_TMF_RSP_PDU_RSP_TASK_NOT_EXIST 1 +#define ISCSI_TMF_RSP_PDU_RSP_LUN_NOT_EXIST 2 +#define ISCSI_TMF_RSP_PDU_RSP_TASK_STILL_ALLEGIANT 3 +#define ISCSI_TMF_RSP_PDU_RSP_TASK_REASSGIN_NOT_SUPPORTED 4 +#define ISCSI_TMF_RSP_PDU_RSP_NOT_SUPPORTED 5 +#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_AHTH_FAILED 6 +#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_REJECTED 255 + +#define SCSI_DATA_IN_PDU_FLAG_ACKKNOWLEDGE 0x40 +#define SCSI_DATA_IN_PDU_FLAG_OVERFLOW SCSI_RSP_PDU_FLAG_OVERFLOW +#define SCSI_DATA_IN_PDU_FLAG_UNDERFLOW SCSI_RSP_PDU_FLAG_UNDERFLOW +#define SCSI_DATA_IN_PDU_FLAG_STATUS_VALID 0x01 + +#define ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT 0x80 +#define ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE 0x40 + +#define ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT +#define ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE + +#define ISCSI_LOGIN_STATUS_SUCCESS 0 +#define ISCSI_LOGIN_STATUS_REDIRECTION 1 +#define ISCSI_LOGIN_STATUS_INITIATOR_ERROR 2 +#define ISCSI_LOGIN_STATUS_TARGET_ERROR 3 + +#define ISCSI_LOGOUT_REASON_CLOSE_SESSION 0 +#define ISCSI_LOGOUT_REASON_CLOSE_CONNECTION 1 +#define ISCSI_LOGOUT_REASON_REMOVE_CONNECTION_FOR_RECOVERY 2 + +#define ISCSI_LOGOUT_RESPONSE_SESSION_CLOSED_SUCCESS 0 +#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND 1 +#define ISCSI_LOGOUT_RESPONSE_RECOVERY_NOT_SUPPORTED 2 +#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED 3 + +#define ISCSI_SNACK_REQUEST_TYPE_DATA_OR_R2T 0 +#define ISCSI_SNACK_REQUEST_TYPE_STATUS 1 +#define ISCSI_SNACK_REQUEST_TYPE_DATA_ACK 2 +#define ISCSI_SNACK_REQUEST_TYPE_RDATA 3 + +#define ISCSI_SECURITY_NEGOTIATION 0 +#define ISCSI_LOGIN_OPERATIONAL_NEGOTIATION 1 +#define ISCSI_FULL_FEATURE_PHASE 3 + +typedef struct _ISCSI_SESSION ISCSI_SESSION; +typedef struct _ISCSI_CONNECTION ISCSI_CONNECTION; + +typedef enum { + DataIn = 0, + DataOut = 1, + DataBi = 2 +} DATA_DIRECTION; + +/// +/// iSCSI Basic Header Segment +/// +typedef struct _ISCSI_BASIC_HEADER { + UINT8 OpCode; + UINT8 Flags; + UINT16 OpCodeSpecific1; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 OpCodeSpecific2[7]; +} ISCSI_BASIC_HEADER; + +typedef struct _ISCSI_ADDTIONAL_HEADER { + UINT16 Length; + UINT8 Type; + UINT8 TypeSpecific[1]; +} ISCSI_ADDITIONAL_HEADER; + +typedef struct _ISCSI_BI_EXP_READ_DATA_LEN_AHS { + UINT16 Length; + UINT8 Type; + UINT8 Reserved; + UINT32 ExpReadDataLength; +} ISCSI_BI_EXP_READ_DATA_LEN_AHS; + +/// +/// SCSI Command +/// +typedef struct _SCSI_COMMAND { + UINT8 OpCode; + UINT8 Flags; + UINT16 Reserved; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 ExpDataXferLength; + UINT32 CmdSN; + UINT32 ExpStatSN; + UINT8 Cdb[16]; +} SCSI_COMMAND; + +/// +/// SCSI Response +/// +typedef struct _SCSI_RESPONSE { + UINT8 OpCode; + UINT8 Flags; + UINT8 Response; + UINT8 Status; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Reserved[8]; + UINT32 InitiatorTaskTag; + UINT32 SNACKTag; + UINT32 StatSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + UINT32 ExpDataSN; + UINT32 BiReadResidualCount; + UINT32 ResidualCount; +} SCSI_RESPONSE; + +typedef struct _ISCSI_SENSE_DATA { + UINT16 Length; + UINT8 Data[2]; +} ISCSI_SENSE_DATA; + +/// +/// iSCSI Task Managment Function Request. +/// +typedef struct _ISCSI_TMF_REQUEST { + UINT8 OpCode; + UINT8 Fuction; + UINT16 Reserved1; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 ReferencedTaskTag; + UINT32 CmdSN; + UINT32 ExpStatSN; + UINT32 RefCmdSN; + UINT32 ExpDataSN; + UINT32 Reserved2[2]; +} ISCSI_TMF_REQUEST; + +/// +/// iSCSI Task Management Function Response. +/// +typedef struct _ISCSI_TMF_RESPONSE { + UINT8 OpCode; + UINT8 Reserved1; + UINT8 Response; + UINT8 Reserved2; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT32 Reserver3[2]; + UINT32 InitiatorTaskTag; + UINT32 Reserved4; + UINT32 StatSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + UINT32 Reserved[3]; +} ISCSI_TMF_RESPONSE; + +/// +/// SCSI Data-Out +/// +typedef struct _ISCSI_SCSI_DATA_OUT { + UINT8 OpCode; + UINT8 Reserved1[3]; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 TargetTransferTag; + UINT32 Reserved2; + UINT32 ExpStatSN; + UINT32 Reserved3; + UINT32 DataSN; + UINT32 BufferOffset; + UINT32 Reserved4; +} ISCSI_SCSI_DATA_OUT; + +/// +/// SCSI Data-In +/// +typedef struct _ISCSI_SCSI_DATA_IN { + UINT8 OpCode; + UINT8 Flags; + UINT8 Reserved1; + UINT8 Status; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 TargetTransferTag; + UINT32 StatSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + UINT32 DataSN; + UINT32 BufferOffset; + UINT32 ResidualCount; +} ISCSI_SCSI_DATA_IN; + +/// +/// Ready To Transfer. +/// +typedef struct _ISCSI_READY_TO_TRANSFER { + UINT8 OpCode; + UINT8 Reserved1[3]; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 TargetTransferTag; + UINT32 StatSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + UINT32 R2TSeqNum; + UINT32 BufferOffset; + UINT32 DesiredDataTransferLength; +} ISCSI_READY_TO_TRANSFER; + +typedef struct _ISCSI_ASYNC_MESSAGE { + UINT8 OpCode; + UINT8 Reserved1[8]; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 Reserved2; + UINT32 StatSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + UINT8 AsyncEvent; + UINT8 AsyncVCode; + UINT16 Parameter1; + UINT16 Parameter2; + UINT16 Parameter3; + UINT32 Reserved3; +} ISCSI_ASYNC_MESSAGE; + +/// +/// Login Request. +/// +typedef struct _ISCSI_LOGIN_REQUEST { + UINT8 OpCode; + UINT8 Flags; + UINT8 VersionMax; + UINT8 VersionMin; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Isid[6]; + UINT16 Tsih; + UINT32 InitiatorTaskTag; + UINT16 Cid; + UINT16 Reserved1; + UINT32 CmdSN; + UINT32 ExpStatSN; + UINT32 Reserved2[4]; +} ISCSI_LOGIN_REQUEST; + +/// +/// Login Response. +/// +typedef struct _ISCSI_LOGIN_RESPONSE { + UINT8 OpCode; + UINT8 Flags; + UINT8 VersionMax; + UINT8 VersionActive; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Isid[6]; + UINT16 Tsih; + UINT32 InitiatorTaskTag; + UINT32 Reserved1; + UINT32 StatSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + UINT8 StatusClass; + UINT8 StatusDetail; + UINT8 Reserved2[10]; +} ISCSI_LOGIN_RESPONSE; + +/// +/// Logout Request. +/// +typedef struct _ISCSI_LOGOUT_REQUEST { + UINT8 OpCode; + UINT8 ReasonCode; + UINT16 Reserved1; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT32 Reserved2[2]; + UINT32 InitiatorTaskTag; + UINT16 Cid; + UINT16 Reserved3; + UINT32 CmdSN; + UINT32 ExpStatSN; + UINT32 Reserved4[4]; +} ISCSI_LOGOUT_REQUEST; + +/// +/// Logout Response. +/// +typedef struct _ISCSI_LOGOUT_RESPONSE { + UINT8 OpCode; + UINT8 Reserved1; + UINT8 Response; + UINT8 Reserved2; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT32 Reserved3[2]; + UINT32 InitiatorTaskTag; + UINT32 Reserved4; + UINT32 StatSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + UINT32 Reserved5; + UINT16 Time2Wait; + UINT16 Time2Retain; + UINT32 Reserved6; +} ISCSI_LOGOUT_RESPONSE; + +/// +/// SNACK Request. +/// +typedef struct _ISCSI_SNACK_REQUEST { + UINT8 OpCode; + UINT8 Type; + UINT16 Reserved1; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 TargetTransferTag; + UINT32 Reserved2; + UINT32 ExpStatSN; + UINT32 Reserved[2]; + UINT32 BegRun; + UINT32 RunLength; +} ISCSI_SNACK_REQUEST; + +/// +/// Reject. +/// +typedef struct _ISCSI_REJECT { + UINT8 OpCode; + UINT8 Reserved1; + UINT8 Reason; + UINT8 Reserved2; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT32 Reserved3[2]; + UINT32 InitiatorTaskTag; + UINT32 Reserved4; + UINT32 StatSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + UINT32 DataSN; + UINT32 Reserved5[2]; +} ISCSI_REJECT; + +/// +/// NOP-Out. +/// +typedef struct _ISCSI_NOP_OUT { + UINT8 OpCode; + UINT8 Reserved1[3]; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 TargetTransferTag; + UINT32 CmdSN; + UINT32 ExpStatSN; + UINT32 Reserved2[4]; +} ISCSI_NOP_OUT; + +/// +/// NOP-In. +/// +typedef struct _ISCSI_NOP_IN { + UINT8 OpCode; + UINT8 Reserved1[3]; + UINT8 TotalAHSLength; + UINT8 DataSegmentLength[3]; + UINT8 Lun[8]; + UINT32 InitiatorTaskTag; + UINT32 TargetTransferTag; + UINT32 StatSN; + UINT32 ExpCmdSN; + UINT32 MaxCmdSN; + UINT32 Reserved2[3]; +} ISCSI_NOP_IN; + +typedef enum { + IScsiDigestNone, + IScsiDigestCRC32 +} ISCSI_DIGEST_TYPE; + +typedef struct _ISCSI_XFER_CONTEXT { + UINT32 TargetTransferTag; + UINT32 Offset; + UINT32 DesiredLength; + UINT32 ExpDataSN; +} ISCSI_XFER_CONTEXT; + +typedef struct _ISCSI_IN_BUFFER_CONTEXT { + UINT8 *InData; + UINT32 InDataLen; +} ISCSI_IN_BUFFER_CONTEXT; + +typedef struct _ISCSI_TCB { + LIST_ENTRY Link; + + BOOLEAN SoFarInOrder; + UINT32 ExpDataSN; + BOOLEAN FbitReceived; + BOOLEAN StatusXferd; + UINT32 ActiveR2Ts; + UINT32 Response; + CHAR8 *Reason; + UINT32 InitiatorTaskTag; + UINT32 CmdSN; + UINT32 SNACKTag; + + ISCSI_XFER_CONTEXT XferContext; + + ISCSI_CONNECTION *Conn; +} ISCSI_TCB; + +typedef struct _ISCSI_KEY_VALUE_PAIR { + LIST_ENTRY List; + + CHAR8 *Key; + CHAR8 *Value; +} ISCSI_KEY_VALUE_PAIR; + +/** + Attach the iSCSI connection to the iSCSI session. + + @param[in, out] Session The iSCSI session. + @param[in, out] Conn The iSCSI connection. + +**/ +VOID +IScsiAttatchConnection ( + IN OUT ISCSI_SESSION *Session, + IN OUT ISCSI_CONNECTION *Conn + ); + +/** + Detach the iSCSI connection from the session it belongs to. + + @param[in, out] Conn The iSCSI connection. + +**/ +VOID +IScsiDetatchConnection ( + IN OUT ISCSI_CONNECTION *Conn + ); + +/** + This function performs the iSCSI connection login. + + @param[in, out] Conn The iSCSI connection to login. + @param Timeout The timeout value in milliseconds. + + @retval EFI_SUCCESS The iSCSI connection is logged into the iSCSI target. + @retval EFI_TIMEOUT Timeout occurred during the login procedure. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiConnLogin ( + IN OUT ISCSI_CONNECTION *Conn, + IN UINT16 Timeout + ); + +/** + Create a TCP connection for the iSCSI session. + + @param[in] Session Points to the iSCSI session. + + @return The newly created iSCSI connection. + +**/ +ISCSI_CONNECTION * +IScsiCreateConnection ( + IN ISCSI_SESSION *Session + ); + +/** + Destroy an iSCSI connection. + + @param[in] Conn The connection to destroy. + +**/ +VOID +IScsiDestroyConnection ( + IN ISCSI_CONNECTION *Conn + ); + +/** + Login the iSCSI session. + + @param[in] Session The iSCSI session + + @retval EFI_SUCCESS The iSCSI session login procedure finished. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NO_MEDIA There was a media error. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiSessionLogin ( + IN ISCSI_SESSION *Session + ); + +/** + Wait for IPsec negotiation, then try to login the iSCSI session again. + + @param[in] Session The iSCSI session + + @retval EFI_SUCCESS The iSCSI session login procedure finished. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened. + +**/ +EFI_STATUS +IScsiSessionReLogin ( + IN ISCSI_SESSION *Session + ); + +/** + Build and send the iSCSI login request to the iSCSI target according to + the current login stage. + + @param[in] Conn The connection in the iSCSI login phase. + + @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this + connection. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_DEVICE_ERROR Some kind of device error happened. + +**/ +EFI_STATUS +IScsiSendLoginReq ( + IN ISCSI_CONNECTION *Conn + ); + +/** + Receive and process the iSCSI login response. + + @param[in] Conn The connection in the iSCSI login phase. + + @retval EFI_SUCCESS The iSCSI login response PDU is received and processed. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiReceiveLoginRsp ( + IN ISCSI_CONNECTION *Conn + ); + +/** + Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU. + The DataSegmentLength and the actual size of the net buffer containing this PDU will be + updated. + + @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will + be added to. + @param[in] Key The key name string. + @param[in] Value The value string. + + @retval EFI_SUCCESS The key-valu pair is added to the PDU's datasegment and + the correspondence length fields are updated. + @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value + pair. +**/ +EFI_STATUS +IScsiAddKeyValuePair ( + IN OUT NET_BUF *Pdu, + IN CHAR8 *Key, + IN CHAR8 *Value + ); + +/** + Prepare the iSCSI login request to be sent according to the current login status. + + @param[in, out] Conn The connection in the iSCSI login phase. + + @return The pointer to the net buffer containing the iSCSI login request built. + @retval NULL Other errors as indicated. + +**/ +NET_BUF * +IScsiPrepareLoginReq ( + IN OUT ISCSI_CONNECTION *Conn + ); + +/** + Process the iSCSI Login Response. + + @param[in, out] Conn The connection on which the iSCSI login response is received. + @param[in, out] Pdu The iSCSI login response PDU. + + @retval EFI_SUCCESS The iSCSI login response PDU is processed and all check are passed. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened. + @retval EFI_MEDIA_CHANGED Target is redirected. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiProcessLoginRsp ( + IN OUT ISCSI_CONNECTION *Conn, + IN OUT NET_BUF *Pdu + ); + +/** + Updated the target information according the data received in the iSCSI + login response with an target redirection status. + + @param[in, out] Session The iSCSI session. + @param[in] Data The data segment which should contain the + TargetAddress key-value list. + @param[in] Len Length of the data. + + @retval EFI_SUCCESS The target address is updated. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NOT_FOUND The TargetAddress key is not found. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiUpdateTargetAddress ( + IN OUT ISCSI_SESSION *Session, + IN CHAR8 *Data, + IN UINT32 Len + ); + +/** + The callback function to free the net buffer list. + + @param[in] Arg The opaque parameter. + +**/ +VOID +EFIAPI +IScsiFreeNbufList ( + VOID *Arg + ); + +/** + Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and + an optional data segment. The two parts will be put into two blocks of buffers in the + net buffer. The digest check will be conducted in this function if needed and the digests + will be trimmed from the PDU buffer. + + @param[in] Conn The iSCSI connection to receive data from. + @param[out] Pdu The received iSCSI pdu. + @param[in] Context The context used to describe information on the caller provided + buffer to receive data segment of the iSCSI pdu, it's optional. + @param[in] HeaderDigest Whether there will be header digest received. + @param[in] DataDigest Whether there will be data digest. + @param[in] TimeoutEvent The timeout event, it's optional. + + @retval EFI_SUCCESS An iSCSI pdu is received. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiReceivePdu ( + IN ISCSI_CONNECTION *Conn, + OUT NET_BUF **Pdu, + IN ISCSI_IN_BUFFER_CONTEXT *Context, OPTIONAL + IN BOOLEAN HeaderDigest, + IN BOOLEAN DataDigest, + IN EFI_EVENT TimeoutEvent OPTIONAL + ); + +/** + Check and get the result of the parameter negotiation. + + @param[in, out] Conn The connection in iSCSI login. + + @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished. + @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +EFI_STATUS +IScsiCheckOpParams ( + IN OUT ISCSI_CONNECTION *Conn + ); + +/** + Fill the operational parameters. + + @param[in] Conn The connection in iSCSI login. + @param[in, out] Pdu The iSCSI login request PDU to fill the parameters. + +**/ +VOID +IScsiFillOpParams ( + IN ISCSI_CONNECTION *Conn, + IN OUT NET_BUF *Pdu + ); + +/** + Pad the iSCSI AHS or data segment to an integer number of 4 byte words. + + @param[in, out] Pdu The iSCSI pdu which contains segments to pad. + @param[in] Len The length of the last semgnet in the PDU. + + @retval EFI_SUCCESS The segment is padded or no need to pad it. + @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the + padding bytes. +**/ +EFI_STATUS +IScsiPadSegment ( + IN OUT NET_BUF *Pdu, + IN UINT32 Len + ); + +/** + Build a key-value list from the data segment. + + @param[in] Data The data segment containing the key-value pairs. + @param[in] Len Length of the data segment. + + @return The key-value list. + @retval NULL Other errors as indicated. + +**/ +LIST_ENTRY * +IScsiBuildKeyValueList ( + IN CHAR8 *Data, + IN UINT32 Len + ); + +/** + Get the value string by the key name from the key-value list. If found, + the key-value entry will be removed from the list. + + @param[in, out] KeyValueList The key-value list. + @param[in] Key The key name to find. + + @return The value string. + @retval NULL The key value pair can not be found. + +**/ +CHAR8 * +IScsiGetValueByKeyFromList ( + IN OUT LIST_ENTRY *KeyValueList, + IN CHAR8 *Key + ); + +/** + Free the key-value list. + + @param[in] KeyValueList The key-value list. + +**/ +VOID +IScsiFreeKeyValueList ( + IN LIST_ENTRY *KeyValueList + ); + +/** + Normalize the iSCSI name according to RFC. + + @param[in, out] Name The iSCSI name. + @param[in] Len length of the iSCSI name. + + @retval EFI_SUCCESS The iSCSI name is valid and normalized. + @retval EFI_PROTOCOL_ERROR The iSCSI name is mal-formatted or not in the IQN format. + +**/ +EFI_STATUS +IScsiNormalizeName ( + IN OUT CHAR8 *Name, + IN UINTN Len + ); + +/** + Execute the SCSI command issued through the EXT SCSI PASS THRU protocol. + + @param[in] PassThru The EXT SCSI PASS THRU protocol. + @param[in] Target The target ID. + @param[in] Lun The LUN. + @param[in, out] Packet The request packet containing IO request, SCSI command + buffer and buffers to read/write. + + @retval EFI_SUCCES The SCSI command is executed and the result is updated to + the Packet. + @retval EFI_DEVICE_ERROR Session state was not as required. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval EFI_NOT_READY The target can not accept new commands. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +IScsiExecuteScsiCommand ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ); + +/** + Reinstate the session on some error. + + @param[in] Session The iSCSI session + + @retval EFI_SUCCES The session is reinstated from some error. + @retval Other Reinstatement failed. + +**/ +EFI_STATUS +IScsiSessionReinstatement ( + IN ISCSI_SESSION *Session + ); + +/** + Initialize some session parameters before login. + + @param[in, out] Session The iSCSI session. + @param[in] Recovery Whether the request is from a fresh new start or recovery. + +**/ +VOID +IScsiSessionInit ( + IN OUT ISCSI_SESSION *Session, + IN BOOLEAN Recovery + ); + +/** + Abort the iSCSI session, that is, reset all the connection and free the + resources. + + @param[in, out] Session The iSCSI session. + +**/ +VOID +IScsiSessionAbort ( + IN OUT ISCSI_SESSION *Session + ); + +#endif diff --git a/Core/NetworkPkg/Include/Guid/IScsiConfigHii.h b/Core/NetworkPkg/Include/Guid/IScsiConfigHii.h new file mode 100644 index 0000000000..5077022558 --- /dev/null +++ b/Core/NetworkPkg/Include/Guid/IScsiConfigHii.h @@ -0,0 +1,26 @@ +/** @file + GUIDs used as HII FormSet and HII Package list GUID in IScsiConfig driver + that supports IP4 and IP6 both. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __ISCSI_CONFIG_HII_GUID_H__ +#define __ISCSI_CONFIG_HII_GUID_H__ + +#define ISCSI_CONFIG_GUID \ + { \ + 0x4b47d616, 0xa8d6, 0x4552, { 0x9d, 0x44, 0xcc, 0xad, 0x2e, 0xf, 0x4c, 0xf9 } \ + } + +extern EFI_GUID gIScsiConfigGuid; + +#endif diff --git a/Core/NetworkPkg/Include/Guid/Ip6ConfigHii.h b/Core/NetworkPkg/Include/Guid/Ip6ConfigHii.h new file mode 100644 index 0000000000..f7a3889596 --- /dev/null +++ b/Core/NetworkPkg/Include/Guid/Ip6ConfigHii.h @@ -0,0 +1,25 @@ +/** @file + GUIDs used as HII FormSet and HII Package list GUID in Ip6Config driver. + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __IP6_CONFIG_HII_GUID_H__ +#define __IP6_CONFIG_HII_GUID_H__ + +#define IP6_CONFIG_NVDATA_GUID \ + { \ + 0x2eea107, 0x98db, 0x400e, { 0x98, 0x30, 0x46, 0xa, 0x15, 0x42, 0xd7, 0x99 } \ + } + +extern EFI_GUID gIp6ConfigNvDataGuid; + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/ComponentName.c b/Core/NetworkPkg/Ip6Dxe/ComponentName.c new file mode 100644 index 0000000000..75a1562ca0 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/ComponentName.c @@ -0,0 +1,448 @@ +/** @file + Implementation of EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL protocol. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +// +// EFI Component Name Functions +// + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIp6ComponentName = { + Ip6ComponentNameGetDriverName, + Ip6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ip6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ip6ComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIp6DriverNameTable[] = { + { + "eng;en", + L"IP6 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gIp6ControllerNameTable = NULL; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mIp6DriverNameTable, + DriverName, + (BOOLEAN) (This == &gIp6ComponentName) + ); + +} + +/** + Update the component name for the IP6 child handle. + + @param Ip6[in] A pointer to the EFI_IP6_PROTOCOL. + + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + +**/ +EFI_STATUS +UpdateName ( + IN EFI_IP6_PROTOCOL *Ip6 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[128]; + EFI_IP6_MODE_DATA Ip6ModeData; + UINTN Offset; + CHAR16 Address[sizeof"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"]; + + if (Ip6 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer. + // + Offset = 0; + Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL); + if (!EFI_ERROR (Status) && Ip6ModeData.IsStarted) { + Status = NetLibIp6ToStr (&Ip6ModeData.ConfigData.StationAddress, Address, sizeof(Address)); + if (EFI_ERROR (Status)) { + return Status; + } + Offset += UnicodeSPrint ( + HandleName, + sizeof(HandleName), + L"IPv6(StationAddress=%s, ", + Address + ); + Status = NetLibIp6ToStr (&Ip6ModeData.ConfigData.DestinationAddress, Address, sizeof(Address)); + if (EFI_ERROR (Status)) { + return Status; + } + UnicodeSPrint ( + HandleName + Offset, + sizeof(HandleName) - Offset * sizeof (CHAR16), + L"DestinationAddress=%s)", + Address + ); + } else if (!Ip6ModeData.IsStarted) { + UnicodeSPrint (HandleName, sizeof(HandleName), L"IPv6(Not started)"); + } else { + UnicodeSPrint (HandleName, sizeof(HandleName), L"IPv6(%r)", Status); + } + + if (gIp6ControllerNameTable != NULL) { + FreeUnicodeStringTable (gIp6ControllerNameTable); + gIp6ControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gIp6ComponentName.SupportedLanguages, + &gIp6ControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gIp6ComponentName2.SupportedLanguages, + &gIp6ControllerNameTable, + HandleName, + FALSE + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6; + + // + // Only provide names for child handles. + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiManagedNetworkProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiIp6ProtocolGuid, + (VOID **)&Ip6, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateName (Ip6); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gIp6ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gIp6ComponentName) + ); +} diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Common.c b/Core/NetworkPkg/Ip6Dxe/Ip6Common.c new file mode 100644 index 0000000000..4f71d052bf --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Common.c @@ -0,0 +1,673 @@ +/** @file + The implementation of common functions shared by IP6 driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL, + only the address count is returned. + + @param[in] IpSb The IP6 service binding instance. + @param[out] AddressCount The number of returned addresses. + @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO. + This is an optional parameter. + + + @retval EFI_SUCCESS The address array successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6BuildEfiAddressList ( + IN IP6_SERVICE *IpSb, + OUT UINT32 *AddressCount, + OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL + ) +{ + UINT32 Count; + LIST_ENTRY *Entry; + EFI_IP6_ADDRESS_INFO *EfiAddrInfo; + IP6_ADDRESS_INFO *AddrInfo; + + if (AddressCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IpSb->LinkLocalOk) { + Count = 1 + IpSb->DefaultInterface->AddressCount; + } else { + Count = 0; + } + + *AddressCount = Count; + + if ((AddressList == NULL) || (Count == 0)) { + return EFI_SUCCESS; + } + + if (*AddressList == NULL) { + *AddressList = AllocatePool (sizeof (EFI_IP6_ADDRESS_INFO) * Count); + if (*AddressList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + EfiAddrInfo = *AddressList; + + IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &IpSb->LinkLocalAddr); + EfiAddrInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + + EfiAddrInfo++; + Count = 1; + + NET_LIST_FOR_EACH (Entry, &IpSb->DefaultInterface->AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &AddrInfo->Address); + EfiAddrInfo->PrefixLength = AddrInfo->PrefixLength; + + EfiAddrInfo++; + Count++; + } + + ASSERT (Count == *AddressCount); + + return EFI_SUCCESS; +} + +/** + Generate the multicast addresses identify the group of all IPv6 nodes or IPv6 + routers defined in RFC4291. + + All Nodes Addresses: FF01::1, FF02::1. + All Router Addresses: FF01::2, FF02::2, FF05::2. + + @param[in] Router If TRUE, generate all routers addresses, + else generate all node addresses. + @param[in] Scope interface-local(1), link-local(2), or site-local(5) + @param[out] Ip6Addr The generated multicast address. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The address is generated. + +**/ +EFI_STATUS +Ip6SetToAllNodeMulticast ( + IN BOOLEAN Router, + IN UINT8 Scope, + OUT EFI_IPv6_ADDRESS *Ip6Addr + ) +{ + if (Ip6Addr == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Router && Scope == IP6_SITE_LOCAL_SCOPE) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (Ip6Addr, sizeof (EFI_IPv6_ADDRESS)); + Ip6Addr->Addr[0] = 0xFF; + Ip6Addr->Addr[1] = Scope; + + if (!Router) { + Ip6Addr->Addr[15] = 0x1; + } else { + Ip6Addr->Addr[15] = 0x2; + } + + return EFI_SUCCESS; +} + +/** + This function converts MAC address to 64 bits interface ID according to RFC4291 + and returns the interface ID. Currently only 48-bit MAC address is supported by + this function. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL The operation fails. + @return Pointer to the generated interface ID. + +**/ +UINT8 * +Ip6CreateInterfaceID ( + IN OUT IP6_SERVICE *IpSb + ) +{ + UINT8 InterfaceId[8]; + UINT8 Byte; + EFI_MAC_ADDRESS *MacAddr; + UINT32 AddrLen; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + AddrLen = IpSb->SnpMode.HwAddressSize; + + // + // Currently only IEEE 802 48-bit MACs are supported to create link local address. + // + if (AddrLen != IP6_MAC_LEN || IpSb->InterfaceIdLen != IP6_IF_ID_LEN) { + return NULL; + } + + MacAddr = &IpSb->SnpMode.CurrentAddress; + + // + // Convert MAC address to 64 bits interface ID according to Appendix A of RFC4291: + // 1. Insert 0xFFFE to the middle + // 2. Invert the universal/local bit - bit 6 in network order + // + CopyMem (InterfaceId, MacAddr, 3); + InterfaceId[3] = 0xFF; + InterfaceId[4] = 0xFE; + CopyMem (&InterfaceId[5], &MacAddr->Addr[3], 3); + + Byte = (UINT8) (InterfaceId[0] & IP6_U_BIT); + if (Byte == IP6_U_BIT) { + InterfaceId[0] &= ~IP6_U_BIT; + } else { + InterfaceId[0] |= IP6_U_BIT; + } + + // + // Return the interface ID. + // + return AllocateCopyPool (IpSb->InterfaceIdLen, InterfaceId); +} + +/** + This function creates link-local address from interface identifier. The + interface identifier is normally created from MAC address. It might be manually + configured by administrator if the link-local address created from MAC address + is a duplicate address. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL If the operation fails. + @return The generated Link Local address, in network order. + +**/ +EFI_IPv6_ADDRESS * +Ip6CreateLinkLocalAddr ( + IN OUT IP6_SERVICE *IpSb + ) +{ + EFI_IPv6_ADDRESS *Ip6Addr; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + UINTN DataSize; + EFI_IP6_CONFIG_INTERFACE_ID InterfaceId; + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + if (IpSb->InterfaceId != NULL) { + FreePool (IpSb->InterfaceId); + } + + // + // Get the interface id if it is manully configured. + // + Ip6Config = &IpSb->Ip6ConfigInstance.Ip6Config; + DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID); + ZeroMem (&InterfaceId, DataSize); + + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + &DataSize, + &InterfaceId + ); + if (Status == EFI_NOT_FOUND) { + // + // Since the interface id is not configured, generate the interface id from + // MAC address. + // + IpSb->InterfaceId = Ip6CreateInterfaceID (IpSb); + if (IpSb->InterfaceId == NULL) { + return NULL; + } + + CopyMem (&InterfaceId, IpSb->InterfaceId, IpSb->InterfaceIdLen); + // + // Record the interface id. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + DataSize, + &InterfaceId + ); + if (EFI_ERROR (Status)) { + FreePool (IpSb->InterfaceId); + IpSb->InterfaceId = NULL; + return NULL; + } + } else if (!EFI_ERROR (Status)) { + IpSb->InterfaceId = AllocateCopyPool (DataSize, &InterfaceId); + if (IpSb->InterfaceId == NULL) { + return NULL; + } + } else { + return NULL; + } + + // + // Append FE80::/64 to the left of IPv6 address then return. + // + Ip6Addr = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS)); + if (Ip6Addr == NULL) { + FreePool (IpSb->InterfaceId); + IpSb->InterfaceId = NULL; + return NULL; + } + + CopyMem (&Ip6Addr->Addr[8], IpSb->InterfaceId, IpSb->InterfaceIdLen); + Ip6Addr->Addr[1] = 0x80; + Ip6Addr->Addr[0] = 0xFE; + + return Ip6Addr; +} + +/** + Compute the solicited-node multicast address for an unicast or anycast address, + by taking the low-order 24 bits of this address, and appending those bits to + the prefix FF02:0:0:0:0:1:FF00::/104. + + @param[in] Ip6Addr The unicast or anycast address, in network order. + @param[out] MulticastAddr The generated solicited-node multicast address, + in network order. + +**/ +VOID +Ip6CreateSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6Addr, + OUT EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + ASSERT (Ip6Addr != NULL && MulticastAddr != NULL); + + ZeroMem (MulticastAddr, sizeof (EFI_IPv6_ADDRESS)); + + MulticastAddr->Addr[0] = 0xFF; + MulticastAddr->Addr[1] = 0x02; + MulticastAddr->Addr[11] = 0x1; + MulticastAddr->Addr[12] = 0xFF; + + CopyMem (&MulticastAddr->Addr[13], &Ip6Addr->Addr[13], 3); +} + +/** + Insert a node IP6_ADDRESS_INFO to an IP6 interface. + + @param[in, out] IpIf Points to an IP6 interface. + @param[in] AddrInfo Points to IP6_ADDRESS_INFO + +**/ +VOID +Ip6AddAddr ( + IN OUT IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddrInfo + ) +{ + InsertHeadList (&IpIf->AddressList, &AddrInfo->Link); + IpIf->AddressCount++; +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +Ip6DestroyChildEntryByAddr ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + IP6_PROTOCOL *Instance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + EFI_IPv6_ADDRESS *Address; + + Instance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + ServiceBinding = ((IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT*) Context)->ServiceBinding; + Address = ((IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT*) Context)->Address; + + if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, Address)) { + return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); + } + + return EFI_SUCCESS; +} + +/** + Destroy the IP instance if its StationAddress is removed. It is the help function + for Ip6RemoveAddr(). + + @param[in, out] IpSb Points to an IP6 service binding instance. + @param[in] Address The to be removed address + +**/ +VOID +Ip6DestroyInstanceByAddress ( + IN OUT IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address + ) +{ + LIST_ENTRY *List; + IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT Context; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + List = &IpSb->Children; + Context.ServiceBinding = &IpSb->ServiceBinding; + Context.Address = Address; + NetDestroyLinkList ( + List, + Ip6DestroyChildEntryByAddr, + &Context, + NULL + ); +} + +/** + Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO. + + This function removes the matching IPv6 addresses from the address list and + adjusts the address count of the address list. If IpSb is not NULL, this function + calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the + its solicited-node multicast MAC address from the filter list and sends out + a Multicast Listener Done. If Prefix is NULL, all address in the address list + will be removed. If Prefix is not NULL, the address that matching the Prefix + with PrefixLength in the address list will be removed. + + @param[in] IpSb NULL or points to IP6 service binding instance. + @param[in, out] AddressList Address list array. + @param[in, out] AddressCount The count of addresses in address list array. + @param[in] Prefix NULL or an IPv6 address prefix. + @param[in] PrefixLength The length of Prefix. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength + cannot be found in the address list. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6RemoveAddr ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN OUT LIST_ENTRY *AddressList, + IN OUT UINT32 *AddressCount, + IN EFI_IPv6_ADDRESS *Prefix OPTIONAL, + IN UINT8 PrefixLength + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ADDRESS_INFO *AddrInfo; + EFI_IPv6_ADDRESS SnMCastAddr; + + if (IsListEmpty (AddressList) || *AddressCount < 1 || PrefixLength > IP6_PREFIX_NUM) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_NOT_FOUND; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + if (Prefix == NULL || + (PrefixLength == 128 && EFI_IP6_EQUAL (Prefix, &AddrInfo->Address)) || + (PrefixLength == AddrInfo->PrefixLength && NetIp6IsNetEqual (Prefix, &AddrInfo->Address, PrefixLength)) + ) { + if (IpSb != NULL) { + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + Ip6CreateSNMulticastAddr (&AddrInfo->Address, &SnMCastAddr); + Ip6LeaveGroup (IpSb, &SnMCastAddr); + + // + // Destroy any instance who is using the dying address as the source address. + // + Ip6DestroyInstanceByAddress (IpSb, &AddrInfo->Address); + } + + RemoveEntryList (Entry); + FreePool (AddrInfo); + (*AddressCount)--; + + Status = EFI_SUCCESS; + } + } + + return Status; +} + +/** + Check whether the incoming Ipv6 address is a solicited-node multicast address. + + @param[in] Ip6 Ip6 address, in network order. + + @retval TRUE Yes, solicited-node multicast address + @retval FALSE No + +**/ +BOOLEAN +Ip6IsSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6 + ) +{ + EFI_IPv6_ADDRESS Sn; + BOOLEAN Flag; + + Ip6CreateSNMulticastAddr (Ip6, &Sn); + Flag = FALSE; + + if (CompareMem (Sn.Addr, Ip6->Addr, 13) == 0) { + Flag = TRUE; + } + + return Flag; +} + +/** + Check whether the incoming IPv6 address is one of the maintained addresses in + the IP6 service binding instance. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] Address The IP6 address to be checked. + @param[out] Interface If not NULL, output the IP6 interface which + maintains the Address. + @param[out] AddressInfo If not NULL, output the IP6 address information + of the Address. + + @retval TRUE Yes, it is one of the maintained address. + @retval FALSE No, it is not one of the maintained address. + +**/ +BOOLEAN +Ip6IsOneOfSetAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address, + OUT IP6_INTERFACE **Interface OPTIONAL, + OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_ADDRESS_INFO *TmpAddressInfo; + + // + // Check link-local address first + // + if (IpSb->LinkLocalOk && EFI_IP6_EQUAL (&IpSb->LinkLocalAddr, Address)) { + if (Interface != NULL) { + *Interface = IpSb->DefaultInterface; + } + + if (AddressInfo != NULL) { + *AddressInfo = NULL; + } + + return TRUE; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { + TmpAddressInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + if (EFI_IP6_EQUAL (&TmpAddressInfo->Address, Address)) { + if (Interface != NULL) { + *Interface = IpIf; + } + + if (AddressInfo != NULL) { + *AddressInfo = TmpAddressInfo; + } + + return TRUE; + } + } + } + + return FALSE; +} + +/** + Check whether the incoming MAC address is valid. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] LinkAddress The MAC address. + + @retval TRUE Yes, it is valid. + @retval FALSE No, it is not valid. + +**/ +BOOLEAN +Ip6IsValidLinkAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_MAC_ADDRESS *LinkAddress + ) +{ + UINT32 Index; + + // + // TODO: might be updated later to be more acceptable. + // + for (Index = IpSb->SnpMode.HwAddressSize; Index < sizeof (EFI_MAC_ADDRESS); Index++) { + if (LinkAddress->Addr[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Copy the PrefixLength bits from Src to Dest. + + @param[out] Dest A pointer to the buffer to copy to. + @param[in] Src A pointer to the buffer to copy from. + @param[in] PrefixLength The number of bits to copy. + +**/ +VOID +Ip6CopyAddressByPrefix ( + OUT EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src, + IN UINT8 PrefixLength + ) +{ + UINT8 Byte; + UINT8 Bit; + UINT8 Mask; + + ASSERT (Dest != NULL && Src != NULL); + ASSERT (PrefixLength < IP6_PREFIX_NUM); + + Byte = (UINT8) (PrefixLength / 8); + Bit = (UINT8) (PrefixLength % 8); + + ZeroMem (Dest, sizeof (EFI_IPv6_ADDRESS)); + + CopyMem (Dest, Src, Byte); + + if (Bit > 0) { + Mask = (UINT8) (0xFF << (8 - Bit)); + ASSERT (Byte < 16); + Dest->Addr[Byte] = (UINT8) (Src->Addr[Byte] & Mask); + } +} + +/** + Get the MAC address for a multicast IP address. Call + Mnp's McastIpToMac to find the MAC address instead of + hard-coding the NIC to be Ethernet. + + @param[in] Mnp The Mnp instance to get the MAC address. + @param[in] Multicast The multicast IP address to translate. + @param[out] Mac The buffer to hold the translated address. + + @retval EFI_SUCCESS The multicast IP successfully + translated to a multicast MAC address. + @retval Other The address is not converted because an error occurred. + +**/ +EFI_STATUS +Ip6GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_IPv6_ADDRESS *Multicast, + OUT EFI_MAC_ADDRESS *Mac + ) +{ + EFI_IP_ADDRESS EfiIp; + + IP6_COPY_ADDRESS (&EfiIp.v6, Multicast); + + return Mnp->McastIpToMac (Mnp, TRUE, &EfiIp, Mac); +} + +/** + Convert the multibyte field in IP header's byter order. + In spite of its name, it can also be used to convert from + host to network byte order. + + @param[in, out] Head The IP head to convert. + + @return Point to the converted IP head. + +**/ +EFI_IP6_HEADER * +Ip6NtohHead ( + IN OUT EFI_IP6_HEADER *Head + ) +{ + Head->FlowLabelL = NTOHS (Head->FlowLabelL); + Head->PayloadLength = NTOHS (Head->PayloadLength); + + return Head; +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Common.h b/Core/NetworkPkg/Ip6Dxe/Ip6Common.h new file mode 100644 index 0000000000..488c5b23b7 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Common.h @@ -0,0 +1,318 @@ +/** @file + Common definition and functions for IP6 driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_COMMON_H__ +#define __EFI_IP6_COMMON_H__ + +#define IP6_LINK_EQUAL(Mac1, Mac2) (CompareMem ((Mac1), (Mac2), sizeof (EFI_MAC_ADDRESS)) == 0) + +// +// Convert the Microsecond to second. IP transmit/receive time is +// in the unit of microsecond. IP ticks once per second. +// +#define IP6_US_TO_SEC(Us) (((Us) + 999999) / 1000000) + +#define IP6_ETHER_PROTO 0x86DD + +#define IP6_MAC_LEN 6 +#define IP6_IF_ID_LEN 8 + +#define IP6_INTERFACE_LOCAL_SCOPE 1 +#define IP6_LINK_LOCAL_SCOPE 2 +#define IP6_SITE_LOCAL_SCOPE 5 + +#define IP6_INFINIT_LIFETIME 0xFFFFFFFF + +#define IP6_HOP_LIMIT 255 +// +// Make it to 64 since all 54 bits are zero. +// +#define IP6_LINK_LOCAL_PREFIX_LENGTH 64 + +#define IP6_TIMER_INTERVAL_IN_MS 100 +#define IP6_ONE_SECOND_IN_MS 1000 + +// +// The packet is received as link level broadcast/multicast/promiscuous. +// +#define IP6_LINK_BROADCAST 0x00000001 +#define IP6_LINK_MULTICAST 0x00000002 +#define IP6_LINK_PROMISC 0x00000004 + +#define IP6_U_BIT 0x02 + +typedef enum { + Ip6Promiscuous = 1, + Ip6Unicast, + Ip6Multicast, + Ip6AnyCast +} IP6_ADDRESS_TYPE; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + EFI_IPv6_ADDRESS *Address; +} IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT; + +typedef struct _IP6_INTERFACE IP6_INTERFACE; +typedef struct _IP6_PROTOCOL IP6_PROTOCOL; +typedef struct _IP6_SERVICE IP6_SERVICE; +typedef struct _IP6_ADDRESS_INFO IP6_ADDRESS_INFO; + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL, + only the address count is returned. + + @param[in] IpSb The IP6 service binding instance. + @param[out] AddressCount The number of returned addresses. + @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO. + This is an optional parameter. + + + @retval EFI_SUCCESS The address array is successfully build + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6BuildEfiAddressList ( + IN IP6_SERVICE *IpSb, + OUT UINT32 *AddressCount, + OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL + ); + +/** + Generate the multicast addresses identify the group of all IPv6 nodes or IPv6 + routers defined in RFC4291. + + All Nodes Addresses: FF01::1, FF02::1. + All Router Addresses: FF01::2, FF02::2, FF05::2. + + @param[in] Router If TRUE, generate all routers addresses, + else generate all node addresses. + @param[in] Scope interface-local(1), link-local(2), or site-local(5) + @param[out] Ip6Addr The generated multicast address. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The address is generated. + +**/ +EFI_STATUS +Ip6SetToAllNodeMulticast ( + IN BOOLEAN Router, + IN UINT8 Scope, + OUT EFI_IPv6_ADDRESS *Ip6Addr + ); + +/** + This function converts MAC address to 64 bits interface ID according to RFC4291 + and returns the interface ID. Currently only 48-bit MAC address is supported by + this function. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL The operation fails. + @return Pointer to the generated interface ID. + +**/ +UINT8 * +Ip6CreateInterfaceID ( + IN OUT IP6_SERVICE *IpSb + ); + +/** + This function creates link-local address from interface identifier. The + interface identifier is normally created from MAC address. It might be manually + configured by administrator if the link-local address created from MAC address + is a duplicate address. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL If the operation fails. + @return The generated Link Local address, in network order. + +**/ +EFI_IPv6_ADDRESS * +Ip6CreateLinkLocalAddr ( + IN OUT IP6_SERVICE *IpSb + ); + +/** + Compute the solicited-node multicast address for an unicast or anycast address, + by taking the low-order 24 bits of this address, and appending those bits to + the prefix FF02:0:0:0:0:1:FF00::/104. + + @param Ip6Addr The unicast or anycast address, in network order. + @param MulticastAddr The generated solicited-node multicast address, + in network order. + +**/ +VOID +Ip6CreateSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6Addr, + OUT EFI_IPv6_ADDRESS *MulticastAddr + ); + +/** + Check whether the incoming Ipv6 address is a solicited-node multicast address. + + @param[in] Ip6 Ip6 address, in network order. + + @retval TRUE Yes, solicited-node multicast address + @retval FALSE No + +**/ +BOOLEAN +Ip6IsSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6 + ); + +/** + Check whether the incoming IPv6 address is one of the maintained address in + the IP6 service binding instance. + + @param[in] IpSb Points to a IP6 service binding instance + @param[in] Address The IP6 address to be checked. + @param[out] Interface If not NULL, output the IP6 interface which + maintains the Address. + @param[out] AddressInfo If not NULL, output the IP6 address information + of the Address. + + @retval TRUE Yes, it is one of the maintained addresses. + @retval FALSE No, it is not one of the maintained addresses. + +**/ +BOOLEAN +Ip6IsOneOfSetAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address, + OUT IP6_INTERFACE **Interface OPTIONAL, + OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL + ); + +/** + Check whether the incoming MAC address is valid. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] LinkAddress The MAC address. + + @retval TRUE Yes, it is valid. + @retval FALSE No, it is not valid. + +**/ +BOOLEAN +Ip6IsValidLinkAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_MAC_ADDRESS *LinkAddress + ); + + +/** + Copy the PrefixLength bits from Src to Dest. + + @param[out] Dest A pointer to the buffer to copy to. + @param[in] Src A pointer to the buffer to copy from. + @param[in] PrefixLength The number of bits to copy. + +**/ +VOID +Ip6CopyAddressByPrefix ( + OUT EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src, + IN UINT8 PrefixLength + ); + +/** + Insert a node IP6_ADDRESS_INFO to an IP6 interface. + + @param[in, out] IpIf Points to an IP6 interface. + @param[in] AddrInfo Points to an IP6_ADDRESS_INFO. + +**/ +VOID +Ip6AddAddr ( + IN OUT IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddrInfo + ); + +/** + Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO. + + This function removes the matching IPv6 addresses from the address list and + adjusts the address count of the address list. If IpSb is not NULL, this function + calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the + its solicited-node multicast MAC address from the filter list and sends out + a Multicast Listener Done. If Prefix is NULL, all address in the address list + will be removed. If Prefix is not NULL, the address that matching the Prefix + with PrefixLength in the address list will be removed. + + @param[in] IpSb NULL or points to IP6 service binding instance. + @param[in, out] AddressList address list array + @param[in, out] AddressCount the count of addresses in address list array + @param[in] Prefix NULL or an IPv6 address prefix + @param[in] PrefixLength the length of Prefix + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength + cannot be found in address list. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6RemoveAddr ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN OUT LIST_ENTRY *AddressList, + IN OUT UINT32 *AddressCount, + IN EFI_IPv6_ADDRESS *Prefix OPTIONAL, + IN UINT8 PrefixLength + ); + +/** + Get the MAC address for a multicast IP address. Call + Mnp's McastIpToMac to find the MAC address instead of + hard-coding the NIC to be Ethernet. + + @param[in] Mnp The Mnp instance to get the MAC address. + @param[in] Multicast The multicast IP address to translate. + @param[out] Mac The buffer to hold the translated address. + + @retval EFI_SUCCESS The multicast IP is successfully + translated to a multicast MAC address. + @retval Other The address is not converted because an error occurred. + +**/ +EFI_STATUS +Ip6GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_IPv6_ADDRESS *Multicast, + OUT EFI_MAC_ADDRESS *Mac + ); + +/** + Convert the multibyte field in IP header's byter order. + In spite of its name, it can also be used to convert from + host to network byte order. + + @param[in, out] Head The IP head to convert. + + @return Point to the converted IP head. + +**/ +EFI_IP6_HEADER * +Ip6NtohHead ( + IN OUT EFI_IP6_HEADER *Head + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Config.vfr b/Core/NetworkPkg/Ip6Dxe/Ip6Config.vfr new file mode 100644 index 0000000000..67fd386bca --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Config.vfr @@ -0,0 +1,178 @@ +/** @file + VFR file used by the IP6 configuration component. + + Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6NvData.h" + +#define EFI_NETWORK_DEVICE_CLASS 0x04 + +formset + guid = IP6_CONFIG_NVDATA_GUID, + title = STRING_TOKEN(STR_IP6_CONFIG_FORM_TITLE), + help = STRING_TOKEN(STR_IP6_CONFIG_FORM_HELP), + + varstore IP6_CONFIG_IFR_NVDATA, + name = IP6_CONFIG_IFR_NVDATA, + guid = IP6_CONFIG_NVDATA_GUID; + + form formid = FORMID_HEAD_FORM, + title = STRING_TOKEN(STR_IP6_DEVICE_FORM_TITLE); + + goto FORMID_MAIN_FORM, + prompt = STRING_TOKEN (STR_GET_CURRENT_SETTING), + help = STRING_TOKEN (STR_GET_CURRENT_SETTING_HELP), + flags = INTERACTIVE, + key = KEY_GET_CURRENT_SETTING; + + endform; + + form formid = FORMID_MAIN_FORM, + title = STRING_TOKEN(STR_IP6_DEVICE_FORM_TITLE); + + text + help = STRING_TOKEN(STR_IP6_INTERFACE_NAME_HELP), + text = STRING_TOKEN(STR_IP6_INTERFACE_NAME), + text = STRING_TOKEN(STR_IP6_INTERFACE_NAME_CONTENT); + + text + help = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_HELP), + text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE), + text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_CONTENT); + + text + help = STRING_TOKEN(STR_IP6_MAC_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_MAC_ADDRESS), + text = STRING_TOKEN(STR_IP6_MAC_ADDRESS_CONTENT); + + text + help = STRING_TOKEN(STR_IP6_HOST_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_HOST_ADDRESS), + text = STRING_TOKEN(STR_NULL); + + label HOST_ADDRESS_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN(STR_IP6_ROUTE_TABLE_HELP), + text = STRING_TOKEN(STR_IP6_ROUTE_TABLE), + text = STRING_TOKEN(STR_NULL); + + label ROUTE_TABLE_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS), + text = STRING_TOKEN(STR_NULL); + + label GATEWAY_ADDRESS_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN(STR_IP6_DNS_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_DNS_ADDRESS), + text = STRING_TOKEN(STR_NULL); + + label DNS_ADDRESS_LABEL; + label LABEL_END; + + string varid = IP6_CONFIG_IFR_NVDATA.InterfaceId, + prompt = STRING_TOKEN(STR_IP6_INTERFACE_ID), + help = STRING_TOKEN(STR_IP6_INTERFACE_ID_HELP), + flags = INTERACTIVE, + key = KEY_INTERFACE_ID, + minsize = INTERFACE_ID_STR_MIN_SIZE, + maxsize = INTERFACE_ID_STR_MAX_SIZE, + endstring; + + numeric varid = IP6_CONFIG_IFR_NVDATA.DadTransmitCount, + prompt = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT), + help = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT_HELP), + flags = 0, + minimum = 0, + maximum = DAD_MAX_TRANSMIT_COUNT, + step = 0, + endnumeric; + + oneof varid = IP6_CONFIG_IFR_NVDATA.Policy, + prompt = STRING_TOKEN(STR_POLICY_TYPE_PROMPT), + help = STRING_TOKEN(STR_POLICY_TYPE_HELP), + option text = STRING_TOKEN(STR_POLICY_TYPE_AUTO), value = IP6_POLICY_AUTO, flags = DEFAULT; + option text = STRING_TOKEN(STR_POLICY_TYPE_MANUAL), value = IP6_POLICY_MANUAL, flags = 0; + endoneof; + + subtitle text = STRING_TOKEN(STR_NULL); + + suppressif ideqval IP6_CONFIG_IFR_NVDATA.Policy == IP6_POLICY_AUTO; + goto FORMID_MANUAL_CONFIG_FORM, + prompt = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM), + help = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM_HELP), + flags = 0; + subtitle text = STRING_TOKEN(STR_NULL); + endif; + + text + help = STRING_TOKEN (STR_SAVE_CHANGES_HELP), + text = STRING_TOKEN (STR_SAVE_CHANGES), + flags = INTERACTIVE, + key = KEY_SAVE_CHANGES; + + endform; + + form formid = FORMID_MANUAL_CONFIG_FORM, + title = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM); + + string varid = IP6_CONFIG_IFR_NVDATA.ManualAddress, + prompt = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS), + help = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_MANUAL_ADDRESS, + minsize = ADDRESS_STR_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + string varid = IP6_CONFIG_IFR_NVDATA.GatewayAddress, + prompt = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDRESS), + help = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDR_HELP), + flags = INTERACTIVE, + key = KEY_GATEWAY_ADDRESS, + minsize = ADDRESS_STR_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + string varid = IP6_CONFIG_IFR_NVDATA.DnsAddress, + prompt = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS), + help = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_DNS_ADDRESS, + minsize = ADDRESS_STR_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + text + help = STRING_TOKEN (STR_SAVE_AND_EXIT), + text = STRING_TOKEN (STR_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_SAVE_CONFIG_CHANGES; + + text + help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + text = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_IGNORE_CONFIG_CHANGES; + + endform; + +endformset; + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c new file mode 100644 index 0000000000..75d4f23fb0 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c @@ -0,0 +1,2388 @@ +/** @file + The implementation of EFI IPv6 Configuration Protocol. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +LIST_ENTRY mIp6ConfigInstanceList = {&mIp6ConfigInstanceList, &mIp6ConfigInstanceList}; + +/** + The event process routine when the DHCPv6 service binding protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context Pointer to the IP6 config instance data. + +**/ +VOID +EFIAPI +Ip6ConfigOnDhcp6SbInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Update the current policy to NewPolicy. During the transition + period, the default router list, on-link prefix list, autonomous prefix list + and address list in all interfaces will be released. + + @param[in] IpSb The IP6 service binding instance. + @param[in] NewPolicy The new policy to be updated to. + +**/ +VOID +Ip6ConfigOnPolicyChanged ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_CONFIG_POLICY NewPolicy + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + LIST_ENTRY *Next; + IP6_INTERFACE *IpIf; + IP6_DAD_ENTRY *DadEntry; + IP6_DELAY_JOIN_LIST *DelayNode; + + // + // Currently there are only two policies: Manual and Automatic. Regardless of + // what transition is going on, i.e., Manual -> Automatic and Automatic -> + // Manual, we have to free default router list, on-link prefix list, autonomous + // prefix list, address list in all the interfaces and destroy any IPv6 child + // instance whose local IP is neither 0 nor the link-local address. + // + Ip6CleanDefaultRouterList (IpSb); + Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix); + Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix); + + // + // It's tricky... If the LinkLocal address is O.K., add back the link-local + // prefix to the on-link prefix table. + // + if (IpSb->LinkLocalOk) { + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + IP6_LINK_LOCAL_PREFIX_LENGTH, + &IpSb->LinkLocalAddr + ); + } + + // + // All IPv6 children that use global unicast address as it's source address + // should be destryoed now. The survivers are those use the link-local address + // or the unspecified address as the source address. + // TODO: Conduct a check here. + Ip6RemoveAddr ( + IpSb, + &IpSb->DefaultInterface->AddressList, + &IpSb->DefaultInterface->AddressCount, + NULL, + 0 + ); + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + // + // remove all pending delay node and DAD entries for the global addresses. + // + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) { + DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link); + if (!NetIp6IsLinkLocalAddr (&DelayNode->AddressInfo->Address)) { + RemoveEntryList (&DelayNode->Link); + FreePool (DelayNode); + } + } + + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) { + DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE); + + if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) { + // + // Fail this DAD entry if the address is not link-local. + // + Ip6OnDADFinished (FALSE, IpIf, DadEntry); + } + } + } + + if (NewPolicy == Ip6ConfigPolicyAutomatic) { + // + // Set paramters to trigger router solicitation sending in timer handler. + // + IpSb->RouterAdvertiseReceived = FALSE; + IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS; + // + // delay 1 second + // + IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_ONE_SECOND_IN_MS); + } + +} + +/** + The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in] OtherInfoOnly If FALSE, get stateful address and other information + via DHCPv6. Otherwise, only get the other information. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_UNSUPPORTED The DHCP6 driver is not available. + +**/ +EFI_STATUS +Ip6ConfigStartStatefulAutoConfig ( + IN IP6_CONFIG_INSTANCE *Instance, + IN BOOLEAN OtherInfoOnly + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + EFI_DHCP6_CONFIG_DATA Dhcp6CfgData; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_DHCP6_PACKET_OPTION *OptList[1]; + UINT16 OptBuf[4]; + EFI_DHCP6_PACKET_OPTION *Oro; + EFI_DHCP6_RETRANSMISSION InfoReqReXmit; + + // + // A host must not invoke stateful address configuration if it is already + // participating in the statuful protocol as a result of an earlier advertisement. + // + if (Instance->Dhcp6Handle != NULL) { + return EFI_SUCCESS; + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + Instance->OtherInfoOnly = OtherInfoOnly; + + Status = NetLibCreateServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Instance->Dhcp6Handle + ); + + if (Status == EFI_UNSUPPORTED) { + // + // No DHCPv6 Service Binding protocol, register a notify. + // + if (Instance->Dhcp6SbNotifyEvent == NULL) { + Instance->Dhcp6SbNotifyEvent = EfiCreateProtocolNotifyEvent ( + &gEfiDhcp6ServiceBindingProtocolGuid, + TPL_CALLBACK, + Ip6ConfigOnDhcp6SbInstalled, + (VOID *) Instance, + &Instance->Registration + ); + } + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Instance->Dhcp6SbNotifyEvent != NULL) { + gBS->CloseEvent (Instance->Dhcp6SbNotifyEvent); + } + + Status = gBS->OpenProtocol ( + Instance->Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Instance->Dhcp6, + IpSb->Image, + IpSb->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + ASSERT_EFI_ERROR (Status); + + Dhcp6 = Instance->Dhcp6; + Dhcp6->Configure (Dhcp6, NULL); + + // + // Set the exta options to send. Here we only want the option request option + // with DNS SERVERS. + // + Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf; + Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO); + Oro->OpLen = HTONS (2); + *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS); + OptList[0] = Oro; + + Status = EFI_SUCCESS; + + if (!OtherInfoOnly) { + // + // Get stateful address and other information via DHCPv6. + // + Dhcp6CfgData.Dhcp6Callback = NULL; + Dhcp6CfgData.CallbackContext = NULL; + Dhcp6CfgData.OptionCount = 1; + Dhcp6CfgData.OptionList = &OptList[0]; + Dhcp6CfgData.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; + Dhcp6CfgData.IaDescriptor.IaId = Instance->IaId; + Dhcp6CfgData.IaInfoEvent = Instance->Dhcp6Event; + Dhcp6CfgData.ReconfigureAccept = FALSE; + Dhcp6CfgData.RapidCommit = FALSE; + Dhcp6CfgData.SolicitRetransmission = NULL; + + Status = Dhcp6->Configure (Dhcp6, &Dhcp6CfgData); + + if (!EFI_ERROR (Status)) { + + if (IpSb->LinkLocalOk) { + Status = Dhcp6->Start (Dhcp6); + } else { + IpSb->Dhcp6NeedStart = TRUE; + } + + } + } else { + // + // Only get other information via DHCPv6, this doesn't require a config + // action. + // + InfoReqReXmit.Irt = 4; + InfoReqReXmit.Mrc = 64; + InfoReqReXmit.Mrt = 60; + InfoReqReXmit.Mrd = 0; + + if (IpSb->LinkLocalOk) { + Status = Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + Instance->Dhcp6Event, + Ip6ConfigOnDhcp6Reply, + Instance + ); + } else { + IpSb->Dhcp6NeedInfoRequest = TRUE; + } + + } + + return Status; +} + +/** + Signal the registered event. It is the callback routine for NetMapIterate. + + @param[in] Map Points to the list of registered event. + @param[in] Item The registered event. + @param[in] Arg Not used. + +**/ +EFI_STATUS +EFIAPI +Ip6ConfigSignalEvent ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg + ) +{ + gBS->SignalEvent ((EFI_EVENT) Item->Key); + + return EFI_SUCCESS; +} + +/** + Read the configuration data from variable storage according to the VarName and + gEfiIp6ConfigProtocolGuid. It checks the integrity of variable data. If the + data is corrupted, it clears the variable data to ZERO. Othewise, it outputs the + configuration data to IP6_CONFIG_INSTANCE. + + @param[in] VarName The pointer to the variable name + @param[in, out] Instance The pointer to the IP6 config instance data. + + @retval EFI_NOT_FOUND The variable can not be found or already corrupted. + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_SUCCESS The configuration data was retrieved successfully. + +**/ +EFI_STATUS +Ip6ConfigReadConfigData ( + IN CHAR16 *VarName, + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + UINTN VarSize; + IP6_CONFIG_VARIABLE *Variable; + IP6_CONFIG_DATA_ITEM *DataItem; + UINTN Index; + IP6_CONFIG_DATA_RECORD DataRecord; + CHAR8 *Data; + + // + // Try to read the configuration variable. + // + VarSize = 0; + Status = gRT->GetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + NULL, + &VarSize, + NULL + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Allocate buffer and read the config variable. + // + Variable = AllocatePool (VarSize); + if (Variable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gRT->GetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + NULL, + &VarSize, + Variable + ); + if (EFI_ERROR (Status) || (UINT16) (~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize)) != 0) { + // + // GetVariable still error or the variable is corrupted. + // Fall back to the default value. + // + FreePool (Variable); + + // + // Remove the problematic variable and return EFI_NOT_FOUND, a new + // variable will be set again. + // + gRT->SetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + IP6_CONFIG_VARIABLE_ATTRIBUTE, + 0, + NULL + ); + + return EFI_NOT_FOUND; + } + + // + // Get the IAID we use. + // + Instance->IaId = Variable->IaId; + + for (Index = 0; Index < Variable->DataRecordCount; Index++) { + + CopyMem (&DataRecord, &Variable->DataRecord[Index], sizeof (DataRecord)); + + DataItem = &Instance->DataItem[DataRecord.DataType]; + if (DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED) && + (DataItem->DataSize != DataRecord.DataSize) + ) { + // + // Perhaps a corrupted data record... + // + continue; + } + + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) { + // + // This data item has variable length data. + // + DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize); + if (DataItem->Data.Ptr == NULL) { + // + // no memory resource + // + continue; + } + } + + Data = (CHAR8 *) Variable + DataRecord.Offset; + CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize); + + DataItem->DataSize = DataRecord.DataSize; + DataItem->Status = EFI_SUCCESS; + } + + FreePool (Variable); + return EFI_SUCCESS; + } + + return Status; +} + +/** + Write the configuration data from IP6_CONFIG_INSTANCE to variable storage. + + @param[in] VarName The pointer to the variable name. + @param[in] Instance The pointer to the IP6 configuration instance data. + + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_SUCCESS The configuration data is written successfully. + +**/ +EFI_STATUS +Ip6ConfigWriteConfigData ( + IN CHAR16 *VarName, + IN IP6_CONFIG_INSTANCE *Instance + ) +{ + UINTN Index; + UINTN VarSize; + IP6_CONFIG_DATA_ITEM *DataItem; + IP6_CONFIG_VARIABLE *Variable; + IP6_CONFIG_DATA_RECORD *DataRecord; + CHAR8 *Heap; + EFI_STATUS Status; + + VarSize = sizeof (IP6_CONFIG_VARIABLE) - sizeof (IP6_CONFIG_DATA_RECORD); + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) { + + VarSize += sizeof (IP6_CONFIG_DATA_RECORD) + DataItem->DataSize; + } + } + + Variable = AllocatePool (VarSize); + if (Variable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Variable->IaId = Instance->IaId; + Heap = (CHAR8 *) Variable + VarSize; + Variable->DataRecordCount = 0; + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) { + + Heap -= DataItem->DataSize; + CopyMem (Heap, DataItem->Data.Ptr, DataItem->DataSize); + + DataRecord = &Variable->DataRecord[Variable->DataRecordCount]; + DataRecord->DataType = (EFI_IP6_CONFIG_DATA_TYPE) Index; + DataRecord->DataSize = (UINT32) DataItem->DataSize; + DataRecord->Offset = (UINT16) (Heap - (CHAR8 *) Variable); + + Variable->DataRecordCount++; + } + } + + Variable->Checksum = 0; + Variable->Checksum = (UINT16) ~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize); + + Status = gRT->SetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + IP6_CONFIG_VARIABLE_ATTRIBUTE, + VarSize, + Variable + ); + + FreePool (Variable); + + return Status; +} + +/** + The work function for EfiIp6ConfigGetData() to get the interface information + of the communication device this IP6Config instance manages. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in, out] DataSize On input, in bytes, the size of Data. On output, in + bytes, the size of buffer required to store the specified + configuration data. + @param[in] Data The data buffer in which the configuration data is returned. + Ignored if DataSize is ZERO. + + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified + configuration data, and the required size is + returned in DataSize. + @retval EFI_SUCCESS The specified configuration data was obtained. + +**/ +EFI_STATUS +Ip6ConfigGetIfInfo ( + IN IP6_CONFIG_INSTANCE *Instance, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ) +{ + IP6_SERVICE *IpSb; + UINTN Length; + IP6_CONFIG_DATA_ITEM *Item; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + UINT32 AddressCount; + UINT32 RouteCount; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Length = sizeof (EFI_IP6_CONFIG_INTERFACE_INFO); + + // + // Calculate the required length, add the buffer size for AddressInfo and + // RouteTable + // + Ip6BuildEfiAddressList (IpSb, &AddressCount, NULL); + Ip6BuildEfiRouteTable (IpSb->RouteTable, &RouteCount, NULL); + + Length += AddressCount * sizeof (EFI_IP6_ADDRESS_INFO) + RouteCount * sizeof (EFI_IP6_ROUTE_TABLE); + + if (*DataSize < Length) { + *DataSize = Length; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Copy the fixed size part of the interface info. + // + Item = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo]; + IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data; + CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP6_CONFIG_INTERFACE_INFO)); + + // + // AddressInfo + // + IfInfo->AddressInfo = (EFI_IP6_ADDRESS_INFO *) (IfInfo + 1); + Ip6BuildEfiAddressList (IpSb, &IfInfo->AddressInfoCount, &IfInfo->AddressInfo); + + // + // RouteTable + // + IfInfo->RouteTable = (EFI_IP6_ROUTE_TABLE *) (IfInfo->AddressInfo + IfInfo->AddressInfoCount); + Ip6BuildEfiRouteTable (IpSb->RouteTable, &IfInfo->RouteCount, &IfInfo->RouteTable); + + if (IfInfo->AddressInfoCount == 0) { + IfInfo->AddressInfo = NULL; + } + + if (IfInfo->RouteCount == 0) { + IfInfo->RouteTable = NULL; + } + + return EFI_SUCCESS; +} + +/** + The work function for EfiIp6ConfigSetData() to set the alternative inteface ID + for the communication device managed by this IP6Config instance, if the link local + IPv6 addresses generated from the interface ID based on the default source the + EFI IPv6 Protocol uses is a duplicate address. + + @param[in] Instance Pointer to the IP6 configuration instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type, + 8 bytes. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetAltIfId ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_INTERFACE_ID *OldIfId; + EFI_IP6_CONFIG_INTERFACE_ID *NewIfId; + IP6_CONFIG_DATA_ITEM *DataItem; + + if (DataSize != sizeof (EFI_IP6_CONFIG_INTERFACE_ID)) { + return EFI_BAD_BUFFER_SIZE; + } + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId]; + OldIfId = DataItem->Data.AltIfId; + NewIfId = (EFI_IP6_CONFIG_INTERFACE_ID *) Data; + + CopyMem (OldIfId, NewIfId, DataSize); + DataItem->Status = EFI_SUCCESS; + + return EFI_SUCCESS; +} + +/** + The work function for EfiIp6ConfigSetData() to set the general configuration + policy for the EFI IPv6 network stack that is running on the communication device + managed by this IP6Config instance. The policy will affect other configuration settings. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_INVALID_PARAMETER The to be set policy is invalid. + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_ABORTED The new policy equals the current policy. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetPolicy ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_POLICY NewPolicy; + IP6_CONFIG_DATA_ITEM *DataItem; + IP6_SERVICE *IpSb; + + if (DataSize != sizeof (EFI_IP6_CONFIG_POLICY)) { + return EFI_BAD_BUFFER_SIZE; + } + + NewPolicy = *((EFI_IP6_CONFIG_POLICY *) Data); + + if (NewPolicy > Ip6ConfigPolicyAutomatic) { + return EFI_INVALID_PARAMETER; + } + + if (NewPolicy == Instance->Policy) { + + return EFI_ABORTED; + } else { + + if (NewPolicy == Ip6ConfigPolicyAutomatic) { + // + // Clean the ManualAddress, Gateway and DnsServers, shrink the variable + // data size, and fire up all the related events. + // + DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); + } else { + // + // The policy is changed from automatic to manual. Stop the DHCPv6 process + // and destroy the DHCPv6 child. + // + if (Instance->Dhcp6Handle != NULL) { + Ip6ConfigDestroyDhcp6 (Instance); + } + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Ip6ConfigOnPolicyChanged (IpSb, NewPolicy); + + Instance->Policy = NewPolicy; + + return EFI_SUCCESS; + } +} + +/** + The work function for EfiIp6ConfigSetData() to set the number of consecutive + Neighbor Solicitation messages sent while performing Duplicate Address Detection + on a tentative address. A value of ZERO indicates that Duplicate Address Detection + will not be performed on a tentative address. + + @param[in] Instance The Instance Pointer to the IP6 config instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_ABORTED The new transmit count equals the current configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetDadXmits ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *OldDadXmits; + + if (DataSize != sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS)) { + return EFI_BAD_BUFFER_SIZE; + } + + OldDadXmits = Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits].Data.DadXmits; + + if ((*(UINT32 *) Data) == OldDadXmits->DupAddrDetectTransmits) { + + return EFI_ABORTED; + } else { + + OldDadXmits->DupAddrDetectTransmits = *((UINT32 *) Data); + return EFI_SUCCESS; + } +} + +/** + The callback function for Ip6SetAddr. The prototype is defined + as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed + for the manual address set by Ip6ConfigSetMaunualAddress. + + @param[in] IsDadPassed If TRUE, Duplicate Address Detection passed. + @param[in] TargetAddress The tentative IPv6 address to be checked. + @param[in] Context Pointer to the IP6 configuration instance data. + +**/ +VOID +Ip6ManualAddrDadCallback ( + IN BOOLEAN IsDadPassed, + IN EFI_IPv6_ADDRESS *TargetAddress, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + UINTN Index; + IP6_CONFIG_DATA_ITEM *Item; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddr; + EFI_IP6_CONFIG_MANUAL_ADDRESS *PassedAddr; + UINTN DadPassCount; + UINTN DadFailCount; + IP6_SERVICE *IpSb; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + Item = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + ManualAddr = NULL; + + for (Index = 0; Index < Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); Index++) { + // + // Find the original tag used to place into the NET_MAP. + // + ManualAddr = Item->Data.ManualAddress + Index; + if (EFI_IP6_EQUAL (TargetAddress, &ManualAddr->Address)) { + break; + } + } + + ASSERT (Index != Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + + if (IsDadPassed) { + NetMapInsertTail (&Instance->DadPassedMap, ManualAddr, NULL); + } else { + NetMapInsertTail (&Instance->DadFailedMap, ManualAddr, NULL); + } + + DadPassCount = NetMapGetCount (&Instance->DadPassedMap); + DadFailCount = NetMapGetCount (&Instance->DadFailedMap); + + if ((DadPassCount + DadFailCount) == (Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS))) { + // + // All addresses have finished the configuration process. + // + if (DadFailCount != 0) { + // + // There is at least one duplicate address. + // + FreePool (Item->Data.Ptr); + + Item->DataSize = DadPassCount * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + if (Item->DataSize == 0) { + // + // All failed, bad luck. + // + Item->Data.Ptr = NULL; + Item->Status = EFI_NOT_FOUND; + } else { + // + // Part of addresses are detected to be duplicates, so update the + // data with those passed. + // + PassedAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AllocatePool (Item->DataSize); + ASSERT (PassedAddr != NULL); + + Item->Data.Ptr = PassedAddr; + Item->Status = EFI_SUCCESS; + + while (!NetMapIsEmpty (&Instance->DadPassedMap)) { + ManualAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) NetMapRemoveHead (&Instance->DadPassedMap, NULL); + CopyMem (PassedAddr, ManualAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + + PassedAddr++; + } + + ASSERT ((UINTN) PassedAddr - (UINTN) Item->Data.Ptr == Item->DataSize); + } + } else { + // + // All addresses are valid. + // + Item->Status = EFI_SUCCESS; + } + + // + // Remove the tags we put in the NET_MAPs. + // + while (!NetMapIsEmpty (&Instance->DadFailedMap)) { + NetMapRemoveHead (&Instance->DadFailedMap, NULL); + } + + while (!NetMapIsEmpty (&Instance->DadPassedMap)) { + NetMapRemoveHead (&Instance->DadPassedMap, NULL); + } + + // + // Signal the waiting events. + // + NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL); + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Ip6ConfigWriteConfigData (IpSb->MacString, Instance); + } +} + +/** + The work function for EfiIp6ConfigSetData() to set the station addresses manually + for the EFI IPv6 network stack. It is only configurable when the policy is + Ip6ConfigPolicyManual. + + @param[in] Instance Pointer to the IP6 configuration instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set + under the current policy. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_NOT_READY An asynchrous process is invoked to set the specified + configuration data, and the process is not finished. + @retval EFI_ABORTED The manual addresses to be set equal current + configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetMaunualAddress ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_MANUAL_ADDRESS *NewAddress; + EFI_IP6_CONFIG_MANUAL_ADDRESS *TmpAddress; + IP6_CONFIG_DATA_ITEM *DataItem; + UINTN NewAddressCount; + UINTN Index1; + UINTN Index2; + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *CurrentAddrInfo; + IP6_ADDRESS_INFO *Copy; + LIST_ENTRY CurrentSourceList; + UINT32 CurrentSourceCount; + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + EFI_STATUS Status; + BOOLEAN IsUpdated; + + ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY); + + if (((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0) || (DataSize == 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip6ConfigPolicyManual) { + return EFI_WRITE_PROTECTED; + } + + NewAddressCount = DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + NewAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) Data; + + for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) { + + if (NetIp6IsLinkLocalAddr (&NewAddress->Address) || + !NetIp6IsValidUnicast (&NewAddress->Address) || + (NewAddress->PrefixLength > 128) + ) { + // + // make sure the IPv6 address is unicast and not link-local address && + // the prefix length is valid. + // + return EFI_INVALID_PARAMETER; + } + + TmpAddress = NewAddress + 1; + for (Index2 = Index1 + 1; Index2 < NewAddressCount; Index2++, TmpAddress++) { + // + // Any two addresses in the array can't be equal. + // + if (EFI_IP6_EQUAL (&TmpAddress->Address, &NewAddress->Address)) { + + return EFI_INVALID_PARAMETER; + } + } + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + // + // Build the current source address list. + // + InitializeListHead (&CurrentSourceList); + CurrentSourceCount = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { + CurrentAddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), CurrentAddrInfo); + if (Copy == NULL) { + break; + } + + InsertTailList (&CurrentSourceList, &Copy->Link); + CurrentSourceCount++; + } + } + + // + // Update the value... a long journey starts + // + NewAddress = AllocateCopyPool (DataSize, Data); + if (NewAddress == NULL) { + Ip6RemoveAddr (NULL, &CurrentSourceList, &CurrentSourceCount, NULL, 0); + + return EFI_OUT_OF_RESOURCES; + } + + // + // Store the new data, and init the DataItem status to EFI_NOT_READY because + // we may have an asynchronous configuration process. + // + DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NewAddress; + DataItem->DataSize = DataSize; + DataItem->Status = EFI_NOT_READY; + + // + // Trigger DAD, it's an asynchronous process. + // + IsUpdated = FALSE; + + for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) { + if (Ip6IsOneOfSetAddress (IpSb, &NewAddress->Address, NULL, &CurrentAddrInfo)) { + ASSERT (CurrentAddrInfo != NULL); + // + // Remove this already existing source address from the CurrentSourceList + // built before. + // + Ip6RemoveAddr ( + NULL, + &CurrentSourceList, + &CurrentSourceCount, + &CurrentAddrInfo->Address, + 128 + ); + + // + // If the new address's prefix length is not specified, just use the previous configured + // prefix length for this address. + // + if (NewAddress->PrefixLength == 0) { + NewAddress->PrefixLength = CurrentAddrInfo->PrefixLength; + } + + // + // This manual address is already in use, see whether prefix length is changed. + // + if (NewAddress->PrefixLength != CurrentAddrInfo->PrefixLength) { + // + // Remove the on-link prefix table, the route entry will be removed + // implicitly. + // + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + CurrentAddrInfo->PrefixLength, + &CurrentAddrInfo->Address + ); + if (PrefixEntry != NULL) { + Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE); + } + + // + // Save the prefix length. + // + CurrentAddrInfo->PrefixLength = NewAddress->PrefixLength; + IsUpdated = TRUE; + } + + // + // create a new on-link prefix entry. + // + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + NewAddress->PrefixLength, + &NewAddress->Address + ); + if (PrefixEntry == NULL) { + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + NewAddress->PrefixLength, + &NewAddress->Address + ); + } + + CurrentAddrInfo->IsAnycast = NewAddress->IsAnycast; + // + // Artificially mark this address passed DAD be'coz it is already in use. + // + Ip6ManualAddrDadCallback (TRUE, &NewAddress->Address, Instance); + } else { + // + // A new address. + // + IsUpdated = TRUE; + + // + // Set the new address, this will trigger DAD and activate the address if + // DAD succeeds. + // + Ip6SetAddress ( + IpSb->DefaultInterface, + &NewAddress->Address, + NewAddress->IsAnycast, + NewAddress->PrefixLength, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + Ip6ManualAddrDadCallback, + Instance + ); + } + } + + // + // Check the CurrentSourceList, it now contains those addresses currently in + // use and will be removed. + // + IpIf = IpSb->DefaultInterface; + + while (!IsListEmpty (&CurrentSourceList)) { + IsUpdated = TRUE; + + CurrentAddrInfo = NET_LIST_HEAD (&CurrentSourceList, IP6_ADDRESS_INFO, Link); + + // + // This local address is going to be removed, the IP instances that are + // currently using it will be destroyed. + // + Ip6RemoveAddr ( + IpSb, + &IpIf->AddressList, + &IpIf->AddressCount, + &CurrentAddrInfo->Address, + 128 + ); + + // + // Remove the on-link prefix table, the route entry will be removed + // implicitly. + // + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + CurrentAddrInfo->PrefixLength, + &CurrentAddrInfo->Address + ); + if (PrefixEntry != NULL) { + Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE); + } + + RemoveEntryList (&CurrentAddrInfo->Link); + FreePool (CurrentAddrInfo); + } + + if (IsUpdated) { + if (DataItem->Status == EFI_NOT_READY) { + // + // If DAD is disabled on this interface, the configuration process is + // actually synchronous, and the data item's status will be changed to + // the final status before we reach here, just check it. + // + Status = EFI_NOT_READY; + } else { + Status = EFI_SUCCESS; + } + } else { + // + // No update is taken, reset the status to success and return EFI_ABORTED. + // + DataItem->Status = EFI_SUCCESS; + Status = EFI_ABORTED; + } + + return Status; +} + +/** + The work function for EfiIp6ConfigSetData() to set the gateway addresses manually + for the EFI IPv6 network stack that is running on the communication device that + this EFI IPv6 Configuration Protocol manages. It is not configurable when the policy is + Ip6ConfigPolicyAutomatic. The gateway addresses must be unicast IPv6 addresses. + + @param[in] Instance The pointer to the IP6 config instance data. + @param[in] DataSize The size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. This points to an array of + EFI_IPv6_ADDRESS instances. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set + under the current policy. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to complete the operation. + @retval EFI_ABORTED The manual gateway addresses to be set equal the + current configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetGateway ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + UINTN Index1; + UINTN Index2; + EFI_IPv6_ADDRESS *OldGateway; + EFI_IPv6_ADDRESS *NewGateway; + UINTN OldGatewayCount; + UINTN NewGatewayCount; + IP6_CONFIG_DATA_ITEM *Item; + BOOLEAN OneRemoved; + BOOLEAN OneAdded; + IP6_SERVICE *IpSb; + IP6_DEFAULT_ROUTER *DefaultRouter; + VOID *Tmp; + + if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip6ConfigPolicyManual) { + return EFI_WRITE_PROTECTED; + } + + NewGateway = (EFI_IPv6_ADDRESS *) Data; + NewGatewayCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + for (Index1 = 0; Index1 < NewGatewayCount; Index1++) { + + if (!NetIp6IsValidUnicast (NewGateway + Index1)) { + + return EFI_INVALID_PARAMETER; + } + + for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) { + if (EFI_IP6_EQUAL (NewGateway + Index1, NewGateway + Index2)) { + return EFI_INVALID_PARAMETER; + } + } + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Item = &Instance->DataItem[Ip6ConfigDataTypeGateway]; + OldGateway = Item->Data.Gateway; + OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS); + OneRemoved = FALSE; + OneAdded = FALSE; + + if (NewGatewayCount != OldGatewayCount) { + Tmp = AllocatePool (DataSize); + if (Tmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + Tmp = NULL; + } + + for (Index1 = 0; Index1 < OldGatewayCount; Index1++) { + // + // Find the gateways that are no long in the new setting and remove them. + // + for (Index2 = 0; Index2 < NewGatewayCount; Index2++) { + if (EFI_IP6_EQUAL (OldGateway + Index1, NewGateway + Index2)) { + OneRemoved = TRUE; + break; + } + } + + if (Index2 == NewGatewayCount) { + // + // Remove this default router. + // + DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1); + if (DefaultRouter != NULL) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + } + + for (Index1 = 0; Index1 < NewGatewayCount; Index1++) { + + DefaultRouter = Ip6FindDefaultRouter (IpSb, NewGateway + Index1); + if (DefaultRouter == NULL) { + Ip6CreateDefaultRouter (IpSb, NewGateway + Index1, IP6_INF_ROUTER_LIFETIME); + OneAdded = TRUE; + } + } + + if (!OneRemoved && !OneAdded) { + Item->Status = EFI_SUCCESS; + return EFI_ABORTED; + } else { + + if (Tmp != NULL) { + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + Item->Data.Ptr = Tmp; + } + + CopyMem (Item->Data.Ptr, Data, DataSize); + Item->DataSize = DataSize; + Item->Status = EFI_SUCCESS; + return EFI_SUCCESS; + } +} + +/** + The work function for EfiIp6ConfigSetData() to set the DNS server list for the + EFI IPv6 network stack running on the communication device that this EFI IPv6 + Configuration Protocol manages. It is not configurable when the policy is + Ip6ConfigPolicyAutomatic. The DNS server addresses must be unicast IPv6 addresses. + + @param[in] Instance The pointer to the IP6 config instance data. + @param[in] DataSize The size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set, points to an array of + EFI_IPv6_ADDRESS instances. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set + under the current policy. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_ABORTED The DNS server addresses to be set equal the current + configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetDnsServer ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + UINTN OldIndex; + UINTN NewIndex; + UINTN Index1; + EFI_IPv6_ADDRESS *OldDns; + EFI_IPv6_ADDRESS *NewDns; + UINTN OldDnsCount; + UINTN NewDnsCount; + IP6_CONFIG_DATA_ITEM *Item; + BOOLEAN OneAdded; + VOID *Tmp; + + if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip6ConfigPolicyManual) { + return EFI_WRITE_PROTECTED; + } + + Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + NewDns = (EFI_IPv6_ADDRESS *) Data; + OldDns = Item->Data.DnsServers; + NewDnsCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + OldDnsCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS); + OneAdded = FALSE; + + if (NewDnsCount != OldDnsCount) { + Tmp = AllocatePool (DataSize); + if (Tmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + Tmp = NULL; + } + + for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) { + + if (!NetIp6IsValidUnicast (NewDns + NewIndex)) { + // + // The dns server address must be unicast. + // + FreePool (Tmp); + return EFI_INVALID_PARAMETER; + } + + for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) { + if (EFI_IP6_EQUAL (NewDns + NewIndex, NewDns + Index1)) { + FreePool (Tmp); + return EFI_INVALID_PARAMETER; + } + } + + if (OneAdded) { + // + // If any address in the new setting is not in the old settings, skip the + // comparision below. + // + continue; + } + + for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) { + if (EFI_IP6_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) { + // + // If found break out. + // + break; + } + } + + if (OldIndex == OldDnsCount) { + OneAdded = TRUE; + } + } + + if (!OneAdded && (DataSize == Item->DataSize)) { + // + // No new item is added and the size is the same. + // + Item->Status = EFI_SUCCESS; + return EFI_ABORTED; + } else { + if (Tmp != NULL) { + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + Item->Data.Ptr = Tmp; + } + + CopyMem (Item->Data.Ptr, Data, DataSize); + Item->DataSize = DataSize; + Item->Status = EFI_SUCCESS; + return EFI_SUCCESS; + } +} + +/** + Generate the operational state of the interface this IP6 config instance manages + and output in EFI_IP6_CONFIG_INTERFACE_INFO. + + @param[in] IpSb The pointer to the IP6 service binding instance. + @param[out] IfInfo The pointer to the IP6 configuration interface information structure. + +**/ +VOID +Ip6ConfigInitIfInfo ( + IN IP6_SERVICE *IpSb, + OUT EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo + ) +{ + IfInfo->Name[0] = L'e'; + IfInfo->Name[1] = L't'; + IfInfo->Name[2] = L'h'; + IfInfo->Name[3] = (CHAR16) (L'0' + IpSb->Ip6ConfigInstance.IfIndex); + IfInfo->Name[4] = 0; + + IfInfo->IfType = IpSb->SnpMode.IfType; + IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize; + CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize); +} + +/** + Parse DHCPv6 reply packet to get the DNS server list. + It is the work function for Ip6ConfigOnDhcp6Reply and Ip6ConfigOnDhcp6Event. + + @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL instance. + @param[in, out] Instance The pointer to the IP6 configuration instance data. + @param[in] Reply The pointer to the DHCPv6 reply packet. + + @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. + @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or + the DNS server address is not valid. + +**/ +EFI_STATUS +Ip6ConfigParseDhcpReply ( + IN EFI_DHCP6_PROTOCOL *Dhcp6, + IN OUT IP6_CONFIG_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Reply + ) +{ + EFI_STATUS Status; + UINT32 OptCount; + EFI_DHCP6_PACKET_OPTION **OptList; + UINT16 OpCode; + UINT16 Length; + UINTN Index; + UINTN Index2; + EFI_IPv6_ADDRESS *DnsServer; + IP6_CONFIG_DATA_ITEM *Item; + + // + // A DHCPv6 reply packet is received as the response to our InfoRequest + // packet. + // + OptCount = 0; + Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_READY; + } + + OptList = AllocatePool (OptCount * sizeof (EFI_DHCP6_PACKET_OPTION *)); + if (OptList == NULL) { + return EFI_NOT_READY; + } + + Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, OptList); + if (EFI_ERROR (Status)) { + Status = EFI_NOT_READY; + goto ON_EXIT; + } + + Status = EFI_SUCCESS; + + for (Index = 0; Index < OptCount; Index++) { + // + // Go through all the options to check the ones we are interested in. + // The OpCode and Length are in network byte-order and may not be naturally + // aligned. + // + CopyMem (&OpCode, &OptList[Index]->OpCode, sizeof (OpCode)); + OpCode = NTOHS (OpCode); + + if (OpCode == IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS) { + CopyMem (&Length, &OptList[Index]->OpLen, sizeof (Length)); + Length = NTOHS (Length); + + if ((Length == 0) || ((Length % sizeof (EFI_IPv6_ADDRESS)) != 0)) { + // + // The length should be a multiple of 16 bytes. + // + Status = EFI_NOT_READY; + break; + } + + // + // Validate the DnsServers: whether they are unicast addresses. + // + DnsServer = (EFI_IPv6_ADDRESS *) OptList[Index]->Data; + for (Index2 = 0; Index2 < Length / sizeof (EFI_IPv6_ADDRESS); Index2++) { + if (!NetIp6IsValidUnicast (DnsServer)) { + Status = EFI_NOT_READY; + goto ON_EXIT; + } + + DnsServer++; + } + + Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + + if (Item->DataSize != Length) { + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + + Item->Data.Ptr = AllocatePool (Length); + ASSERT (Item->Data.Ptr != NULL); + } + + CopyMem (Item->Data.Ptr, OptList[Index]->Data, Length); + Item->DataSize = Length; + Item->Status = EFI_SUCCESS; + + // + // Signal the waiting events. + // + NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL); + + break; + } + } + +ON_EXIT: + + FreePool (OptList); + return Status; +} + +/** + The callback function for Ip6SetAddr. The prototype is defined + as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed + on the tentative address by DHCPv6 in Ip6ConfigOnDhcp6Event(). + + @param[in] IsDadPassed If TRUE, Duplicate Address Detection passes. + @param[in] TargetAddress The tentative IPv6 address to be checked. + @param[in] Context Pointer to the IP6 configuration instance data. + +**/ +VOID +Ip6ConfigSetStatefulAddrCallback ( + IN BOOLEAN IsDadPassed, + IN EFI_IPv6_ADDRESS *TargetAddress, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + + // + // We should record the addresses that fail the DAD, and DECLINE them. + // + if (IsDadPassed) { + // + // Decrease the count, no interests in those passed DAD. + // + if (Instance->FailedIaAddressCount > 0 ) { + Instance->FailedIaAddressCount--; + } + } else { + // + // Record it. + // + IP6_COPY_ADDRESS (Instance->DeclineAddress + Instance->DeclineAddressCount, TargetAddress); + Instance->DeclineAddressCount++; + } + + if (Instance->FailedIaAddressCount == Instance->DeclineAddressCount) { + // + // The checking on all addresses are finished. + // + if (Instance->DeclineAddressCount != 0) { + // + // Decline those duplicates. + // + if (Instance->Dhcp6 != NULL) { + Instance->Dhcp6->Decline ( + Instance->Dhcp6, + Instance->DeclineAddressCount, + Instance->DeclineAddress + ); + } + } + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + Instance->DeclineAddress = NULL; + Instance->DeclineAddressCount = 0; + } +} + +/** + The event handle routine when DHCPv6 process is finished or is updated. + + @param[in] Event Not used. + @param[in] Context The pointer to the IP6 configuration instance data. + +**/ +VOID +EFIAPI +Ip6ConfigOnDhcp6Event ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_STATUS Status; + EFI_DHCP6_MODE_DATA Dhcp6ModeData; + EFI_DHCP6_IA *Ia; + EFI_DHCP6_IA_ADDRESS *IaAddr; + UINT32 Index; + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *AddrInfo; + IP6_INTERFACE *IpIf; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + + if ((Instance->Policy != Ip6ConfigPolicyAutomatic) || Instance->OtherInfoOnly) { + // + // IPv6 is not operating in the automatic policy now or + // the DHCPv6 information request message exchange is aborted. + // + return ; + } + + // + // The stateful address autoconfiguration is done or updated. + // + Dhcp6 = Instance->Dhcp6; + + Status = Dhcp6->GetModeData (Dhcp6, &Dhcp6ModeData, NULL); + if (EFI_ERROR (Status)) { + return ; + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + IpIf = IpSb->DefaultInterface; + Ia = Dhcp6ModeData.Ia; + IaAddr = Ia->IaAddress; + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + + Instance->DeclineAddress = (EFI_IPv6_ADDRESS *) AllocatePool (Ia->IaAddressCount * sizeof (EFI_IPv6_ADDRESS)); + if (Instance->DeclineAddress == NULL) { + goto ON_EXIT; + } + + Instance->FailedIaAddressCount = Ia->IaAddressCount; + Instance->DeclineAddressCount = 0; + + for (Index = 0; Index < Ia->IaAddressCount; Index++, IaAddr++) { + if (Ia->IaAddress[Index].ValidLifetime != 0 && Ia->State == Dhcp6Bound) { + // + // Set this address, either it's a new address or with updated lifetimes. + // An appropriate prefix length will be set. + // + Ip6SetAddress ( + IpIf, + &IaAddr->IpAddress, + FALSE, + 0, + IaAddr->ValidLifetime, + IaAddr->PreferredLifetime, + Ip6ConfigSetStatefulAddrCallback, + Instance + ); + } else { + // + // discard this address, artificially decrease the count as if this address + // passed DAD. + // + if (Ip6IsOneOfSetAddress (IpSb, &IaAddr->IpAddress, NULL, &AddrInfo)) { + ASSERT (AddrInfo != NULL); + Ip6RemoveAddr ( + IpSb, + &IpIf->AddressList, + &IpIf->AddressCount, + &AddrInfo->Address, + AddrInfo->PrefixLength + ); + } + + if (Instance->FailedIaAddressCount > 0) { + Instance->FailedIaAddressCount--; + } + } + } + + // + // Parse the Reply packet to get the options we need. + // + if (Dhcp6ModeData.Ia->ReplyPacket != NULL) { + Ip6ConfigParseDhcpReply (Dhcp6, Instance, Dhcp6ModeData.Ia->ReplyPacket); + } + +ON_EXIT: + + FreePool (Dhcp6ModeData.ClientId); + FreePool (Dhcp6ModeData.Ia); +} + +/** + The event process routine when the DHCPv6 server is answered with a reply packet + for an information request. + + @param[in] This Points to the EFI_DHCP6_PROTOCOL. + @param[in] Context The pointer to the IP6 configuration instance data. + @param[in] Packet The DHCPv6 reply packet. + + @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. + @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or + the DNS server address is not valid. + +**/ +EFI_STATUS +EFIAPI +Ip6ConfigOnDhcp6Reply ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ) +{ + return Ip6ConfigParseDhcpReply (This, (IP6_CONFIG_INSTANCE *) Context, Packet); +} + +/** + The event process routine when the DHCPv6 service binding protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context The pointer to the IP6 config instance data. + +**/ +VOID +EFIAPI +Ip6ConfigOnDhcp6SbInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + + if ((Instance->Dhcp6Handle != NULL) || (Instance->Policy != Ip6ConfigPolicyAutomatic)) { + // + // The DHCP6 child is already created or the policy is no longer AUTOMATIC. + // + return ; + } + + Ip6ConfigStartStatefulAutoConfig (Instance, Instance->OtherInfoOnly); +} + +/** + Set the configuration for the EFI IPv6 network stack running on the communication + device this EFI IPv6 Configuration Protocol instance manages. + + This function is used to set the configuration data of type DataType for the EFI + IPv6 network stack that is running on the communication device that this EFI IPv6 + Configuration Protocol instance manages. + + DataSize is used to calculate the count of structure instances in the Data for + a DataType in which multiple structure instances are allowed. + + This function is always non-blocking. When setting some type of configuration data, + an asynchronous process is invoked to check the correctness of the data, such as + performing Duplicate Address Detection on the manually set local IPv6 addresses. + EFI_NOT_READY is returned immediately to indicate that such an asynchronous process + is invoked, and the process is not finished yet. The caller wanting to get the result + of the asynchronous process is required to call RegisterDataNotify() to register an + event on the specified configuration data. Once the event is signaled, the caller + can call GetData() to obtain the configuration data and know the result. + For other types of configuration data that do not require an asynchronous configuration + process, the result of the operation is immediately returned. + + @param[in] This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to set. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. The type of the data buffer is + associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set successfully. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - Data is NULL. + - One or more fields in Data do not match the requirement of the + data type indicated by DataType. + @retval EFI_WRITE_PROTECTED The specified configuration data is read-only or the specified + configuration data cannot be set under the current policy. + @retval EFI_ACCESS_DENIED Another set operation on the specified configuration + data is already in process. + @retval EFI_NOT_READY An asynchronous process was invoked to set the specified + configuration data, and the process is not finished yet. + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type + indicated by DataType. + @retval EFI_UNSUPPORTED This DataType is not supported. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6ConfigSetData ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + IP6_SERVICE *IpSb; + + if ((This == NULL) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = Instance->DataItem[DataType].Status; + if (Status != EFI_NOT_READY) { + + if (Instance->DataItem[DataType].SetData == NULL) { + // + // This type of data is readonly. + // + Status = EFI_WRITE_PROTECTED; + } else { + + Status = Instance->DataItem[DataType].SetData (Instance, DataSize, Data); + if (!EFI_ERROR (Status)) { + // + // Fire up the events registered with this type of data. + // + NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL); + Ip6ConfigWriteConfigData (IpSb->MacString, Instance); + } else if (Status == EFI_ABORTED) { + // + // The SetData is aborted because the data to set is the same with + // the one maintained. + // + Status = EFI_SUCCESS; + NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip6ConfigSignalEvent, NULL); + } + } + } else { + // + // Another asynchornous process is on the way. + // + Status = EFI_ACCESS_DENIED; + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Get the configuration data for the EFI IPv6 network stack running on the communication + device that this EFI IPv6 Configuration Protocol instance manages. + + This function returns the configuration data of type DataType for the EFI IPv6 network + stack running on the communication device that this EFI IPv6 Configuration Protocol instance + manages. + + The caller is responsible for allocating the buffer used to return the specified + configuration data. The required size will be returned to the caller if the size of + the buffer is too small. + + EFI_NOT_READY is returned if the specified configuration data is not ready due to an + asynchronous configuration process already in progress. The caller can call RegisterDataNotify() + to register an event on the specified configuration data. Once the asynchronous configuration + process is finished, the event will be signaled, and a subsequent GetData() call will return + the specified configuration data. + + @param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to get. + @param[in, out] DataSize On input, in bytes, the size of Data. On output, in bytes, the + size of buffer required to store the specified configuration data. + @param[in] Data The data buffer in which the configuration data is returned. The + type of the data buffer is associated with the DataType. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - DataSize is NULL. + - Data is NULL if *DataSize is not zero. + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified configuration data, + and the required size is returned in DataSize. + @retval EFI_NOT_READY The specified configuration data is not ready due to an + asynchronous configuration process already in progress. + @retval EFI_NOT_FOUND The specified configuration data is not found. + +**/ +EFI_STATUS +EFIAPI +EfiIp6ConfigGetData ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + IP6_CONFIG_DATA_ITEM *DataItem; + + if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_NOT_FOUND; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); + DataItem = &Instance->DataItem[DataType]; + + Status = Instance->DataItem[DataType].Status; + if (!EFI_ERROR (Status)) { + + if (DataItem->GetData != NULL) { + + Status = DataItem->GetData (Instance, DataSize, Data); + } else if (*DataSize < Instance->DataItem[DataType].DataSize) { + // + // Update the buffer length. + // + *DataSize = Instance->DataItem[DataType].DataSize; + Status = EFI_BUFFER_TOO_SMALL; + } else { + + *DataSize = Instance->DataItem[DataType].DataSize; + CopyMem (Data, Instance->DataItem[DataType].Data.Ptr, *DataSize); + } + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Register an event that is signaled whenever a configuration process on the specified + configuration data is done. + + This function registers an event that is to be signaled whenever a configuration + process on the specified configuration data is performed. An event can be registered + for a different DataType simultaneously. The caller is responsible for determining + which type of configuration data causes the signaling of the event in such an event. + + @param[in] This Pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to unregister the event for. + @param[in] Event The event to register. + + @retval EFI_SUCCESS The notification event for the specified configuration data is + registered. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_UNSUPPORTED The configuration data type specified by DataType is not + supported. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The Event is already registered for the DataType. + +**/ +EFI_STATUS +EFIAPI +EfiIp6ConfigRegisterDataNotify ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + NET_MAP *EventMap; + NET_MAP_ITEM *Item; + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); + EventMap = &Instance->DataItem[DataType].EventMap; + + // + // Check whether this event is already registered for this DataType. + // + Item = NetMapFindKey (EventMap, Event); + if (Item == NULL) { + + Status = NetMapInsertTail (EventMap, Event, NULL); + + if (EFI_ERROR (Status)) { + + Status = EFI_OUT_OF_RESOURCES; + } + + } else { + + Status = EFI_ACCESS_DENIED; + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Remove a previously registered event for the specified configuration data. + + @param This The pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param DataType The type of data to remove from the previously + registered event. + @param Event The event to be unregistered. + + @retval EFI_SUCCESS The event registered for the specified + configuration data was removed. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_NOT_FOUND The Event has not been registered for the + specified DataType. + +**/ +EFI_STATUS +EFIAPI +EfiIp6ConfigUnregisterDataNotify ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + NET_MAP_ITEM *Item; + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_NOT_FOUND; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); + + Item = NetMapFindKey (&Instance->DataItem[DataType].EventMap, Event); + if (Item != NULL) { + + NetMapRemoveItem (&Instance->DataItem[DataType].EventMap, Item, NULL); + Status = EFI_SUCCESS; + } else { + + Status = EFI_NOT_FOUND; + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Initialize an IP6_CONFIG_INSTANCE. + + @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully. + +**/ +EFI_STATUS +Ip6ConfigInitInstance ( + OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_SERVICE *IpSb; + IP6_CONFIG_INSTANCE *TmpInstance; + LIST_ENTRY *Entry; + EFI_STATUS Status; + UINTN Index; + UINT16 IfIndex; + IP6_CONFIG_DATA_ITEM *DataItem; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + Instance->Signature = IP6_CONFIG_INSTANCE_SIGNATURE; + + // + // Determine the index of this interface. + // + IfIndex = 0; + NET_LIST_FOR_EACH (Entry, &mIp6ConfigInstanceList) { + TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_CONFIG_INSTANCE, Link, IP6_CONFIG_INSTANCE_SIGNATURE); + + if (TmpInstance->IfIndex > IfIndex) { + // + // There is a sequence hole because some interface is down. + // + break; + } + + IfIndex++; + } + + Instance->IfIndex = IfIndex; + NetListInsertBefore (Entry, &Instance->Link); + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { + // + // Initialize the event map for each data item. + // + NetMapInit (&Instance->DataItem[Index].EventMap); + } + + // + // Initialize the NET_MAPs used for DAD on manually configured source addresses. + // + NetMapInit (&Instance->DadFailedMap); + NetMapInit (&Instance->DadPassedMap); + + // + // Initialize each data type: associate storage and set data size for the + // fixed size data types, hook the SetData function, set the data attribute. + // + DataItem = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo]; + DataItem->GetData = Ip6ConfigGetIfInfo; + DataItem->Data.Ptr = &Instance->InterfaceInfo; + DataItem->DataSize = sizeof (Instance->InterfaceInfo); + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE); + Ip6ConfigInitIfInfo (IpSb, &Instance->InterfaceInfo); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId]; + DataItem->SetData = Ip6ConfigSetAltIfId; + DataItem->Data.Ptr = &Instance->AltIfId; + DataItem->DataSize = sizeof (Instance->AltIfId); + DataItem->Status = EFI_NOT_FOUND; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypePolicy]; + DataItem->SetData = Ip6ConfigSetPolicy; + DataItem->Data.Ptr = &Instance->Policy; + DataItem->DataSize = sizeof (Instance->Policy); + Instance->Policy = Ip6ConfigPolicyAutomatic; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits]; + DataItem->SetData = Ip6ConfigSetDadXmits; + DataItem->Data.Ptr = &Instance->DadXmits; + DataItem->DataSize = sizeof (Instance->DadXmits); + Instance->DadXmits.DupAddrDetectTransmits = IP6_CONFIG_DEFAULT_DAD_XMITS; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + DataItem->SetData = Ip6ConfigSetMaunualAddress; + DataItem->Status = EFI_NOT_FOUND; + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway]; + DataItem->SetData = Ip6ConfigSetGateway; + DataItem->Status = EFI_NOT_FOUND; + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + DataItem->SetData = Ip6ConfigSetDnsServer; + DataItem->Status = EFI_NOT_FOUND; + + // + // Create the event used for DHCP. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ip6ConfigOnDhcp6Event, + Instance, + &Instance->Dhcp6Event + ); + ASSERT_EFI_ERROR (Status); + + Instance->Configured = TRUE; + + // + // Try to read the config data from NV variable. + // + Status = Ip6ConfigReadConfigData (IpSb->MacString, Instance); + if (Status == EFI_NOT_FOUND) { + // + // The NV variable is not set, so generate a random IAID, and write down the + // fresh new configuration as the NV variable now. + // + Instance->IaId = NET_RANDOM (NetRandomInitSeed ()); + + for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) { + Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31)); + } + + Ip6ConfigWriteConfigData (IpSb->MacString, Instance); + } else if (EFI_ERROR (Status)) { + return Status; + } + + Instance->Ip6Config.SetData = EfiIp6ConfigSetData; + Instance->Ip6Config.GetData = EfiIp6ConfigGetData; + Instance->Ip6Config.RegisterDataNotify = EfiIp6ConfigRegisterDataNotify; + Instance->Ip6Config.UnregisterDataNotify = EfiIp6ConfigUnregisterDataNotify; + + + // + // Publish the IP6 configuration form + // + return Ip6ConfigFormInit (Instance); +} + +/** + Release an IP6_CONFIG_INSTANCE. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + +**/ +VOID +Ip6ConfigCleanInstance ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + UINTN Index; + IP6_CONFIG_DATA_ITEM *DataItem; + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + + if (!Instance->Configured) { + return ; + } + + if (Instance->Dhcp6Handle != NULL) { + + Ip6ConfigDestroyDhcp6 (Instance); + } + + // + // Close the event. + // + if (Instance->Dhcp6Event != NULL) { + gBS->CloseEvent (Instance->Dhcp6Event); + } + + NetMapClean (&Instance->DadPassedMap); + NetMapClean (&Instance->DadFailedMap); + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) { + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + } + + NetMapClean (&Instance->DataItem[Index].EventMap); + } + + Ip6ConfigFormUnload (Instance); + + RemoveEntryList (&Instance->Link); +} + +/** + Destroy the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + + @retval EFI_SUCCESS The child was successfully destroyed. + @retval Others Failed to destroy the child. + +**/ +EFI_STATUS +Ip6ConfigDestroyDhcp6 ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_SERVICE *IpSb; + EFI_STATUS Status; + EFI_DHCP6_PROTOCOL *Dhcp6; + + Dhcp6 = Instance->Dhcp6; + ASSERT (Dhcp6 != NULL); + + Dhcp6->Stop (Dhcp6); + Dhcp6->Configure (Dhcp6, NULL); + Instance->Dhcp6 = NULL; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + // + // Close DHCPv6 protocol and destroy the child. + // + Status = gBS->CloseProtocol ( + Instance->Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + Instance->Dhcp6Handle + ); + + Instance->Dhcp6Handle = NULL; + + return Status; +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h new file mode 100644 index 0000000000..581978bd5e --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h @@ -0,0 +1,295 @@ +/** @file + Definitions for EFI IPv6 Configuartion Protocol implementation. + + Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __IP6_CONFIG_IMPL_H__ +#define __IP6_CONFIG_IMPL_H__ + +#define IP6_CONFIG_INSTANCE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'C') +#define IP6_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'F', 'C', 'I') +#define IP6_CONFIG_VARIABLE_ATTRIBUTE (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) + +#define IP6_CONFIG_DEFAULT_DAD_XMITS 1 +#define IP6_CONFIG_DHCP6_OPTION_ORO 6 +#define IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS 23 + +#define DATA_ATTRIB_SIZE_FIXED 0x1 +#define DATA_ATTRIB_VOLATILE 0x2 + +#define DATA_ATTRIB_SET(Attrib, Bits) (BOOLEAN)((Attrib) & (Bits)) +#define SET_DATA_ATTRIB(Attrib, Bits) ((Attrib) |= (Bits)) + +typedef struct _IP6_CONFIG_INSTANCE IP6_CONFIG_INSTANCE; + +#define IP6_CONFIG_INSTANCE_FROM_PROTOCOL(Proto) \ + CR ((Proto), \ + IP6_CONFIG_INSTANCE, \ + Ip6Config, \ + IP6_CONFIG_INSTANCE_SIGNATURE \ + ) + + +#define IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK(Callback) \ + CR ((Callback), \ + IP6_CONFIG_INSTANCE, \ + CallbackInfo, \ + IP6_CONFIG_INSTANCE_SIGNATURE \ + ) + +#define IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE(Instance) \ + CR ((Instance), \ + IP6_SERVICE, \ + Ip6ConfigInstance, \ + IP6_SERVICE_SIGNATURE \ + ) + +#define IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(ConfigAccess) \ + CR ((ConfigAccess), \ + IP6_FORM_CALLBACK_INFO, \ + HiiConfigAccess, \ + IP6_FORM_CALLBACK_INFO_SIGNATURE \ + ) + +/** + The prototype of work function for EfiIp6ConfigSetData(). + + @param[in] Instance The pointer to the IP6 config instance data. + @param[in] DataSize In bytes, the size of the buffer pointed to by Data. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type, + 8 bytes. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set successfully. + +**/ +typedef +EFI_STATUS +(*IP6_CONFIG_SET_DATA) ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ); + +/** + The prototype of work function for EfiIp6ConfigGetData(). + + @param[in] Instance The pointer to the IP6 config instance data. + @param[in, out] DataSize On input, in bytes, the size of Data. On output, in + bytes, the size of buffer required to store the specified + configuration data. + @param[in] Data The data buffer in which the configuration data is returned. + Ignored if DataSize is ZERO. + + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified + configuration data, and the required size is + returned in DataSize. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +typedef +EFI_STATUS +(*IP6_CONFIG_GET_DATA) ( + IN IP6_CONFIG_INSTANCE *Instance, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ); + +typedef union { + VOID *Ptr; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + EFI_IP6_CONFIG_INTERFACE_ID *AltIfId; + EFI_IP6_CONFIG_POLICY *Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress; + EFI_IPv6_ADDRESS *Gateway; + EFI_IPv6_ADDRESS *DnsServers; +} IP6_CONFIG_DATA; + +typedef struct { + IP6_CONFIG_SET_DATA SetData; + IP6_CONFIG_GET_DATA GetData; + EFI_STATUS Status; + UINT8 Attribute; + NET_MAP EventMap; + IP6_CONFIG_DATA Data; + UINTN DataSize; +} IP6_CONFIG_DATA_ITEM; + +typedef struct { + UINT16 Offset; + UINT32 DataSize; + EFI_IP6_CONFIG_DATA_TYPE DataType; +} IP6_CONFIG_DATA_RECORD; + +#pragma pack(1) + +// +// heap data that contains the data for each data record. +// +// BOOLEAN IsAltIfIdSet; +// EFI_IP6_CONFIG_POLICY Policy; +// EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; +// UINT32 ManualaddressCount; +// UINT32 GatewayCount; +// UINT32 DnsServersCount; +// EFI_IP6_CONFIG_INTERFACE_ID AltIfId; +// EFI_IP6_CONFIG_MANUAL_ADDRESS ManualAddress[]; +// EFI_IPv6_ADDRESS Gateway[]; +// EFI_IPv6_ADDRESS DnsServers[]; +// +typedef struct { + UINT32 IaId; + UINT16 Checksum; + UINT16 DataRecordCount; + IP6_CONFIG_DATA_RECORD DataRecord[1]; +} IP6_CONFIG_VARIABLE; + +#pragma pack() + +typedef struct { + LIST_ENTRY Link; + EFI_IP6_ADDRESS_INFO AddrInfo; +} IP6_ADDRESS_INFO_ENTRY; + +typedef struct { + EFI_IP6_CONFIG_POLICY Policy; ///< manual or automatic + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadTransmitCount; ///< dad transmits count + EFI_IP6_CONFIG_INTERFACE_ID InterfaceId; ///< alternative interface id + LIST_ENTRY ManualAddress; ///< IP addresses + UINT32 ManualAddressCount; ///< IP addresses count + LIST_ENTRY GatewayAddress; ///< Gateway address + UINT32 GatewayAddressCount; ///< Gateway address count + LIST_ENTRY DnsAddress; ///< DNS server address + UINT32 DnsAddressCount; ///< DNS server address count +} IP6_CONFIG_NVDATA; + +typedef struct _IP6_FORM_CALLBACK_INFO { + UINT32 Signature; + EFI_HANDLE ChildHandle; + EFI_HII_CONFIG_ACCESS_PROTOCOL HiiConfigAccess; + EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath; + EFI_HII_HANDLE RegisteredHandle; +} IP6_FORM_CALLBACK_INFO; + +struct _IP6_CONFIG_INSTANCE { + UINT32 Signature; + BOOLEAN Configured; + LIST_ENTRY Link; + UINT16 IfIndex; + + EFI_IP6_CONFIG_INTERFACE_INFO InterfaceInfo; + EFI_IP6_CONFIG_INTERFACE_ID AltIfId; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + + IP6_CONFIG_DATA_ITEM DataItem[Ip6ConfigDataTypeMaximum]; + NET_MAP DadFailedMap; + NET_MAP DadPassedMap; + + EFI_IP6_CONFIG_PROTOCOL Ip6Config; + + EFI_EVENT Dhcp6SbNotifyEvent; + VOID *Registration; + EFI_HANDLE Dhcp6Handle; + EFI_DHCP6_PROTOCOL *Dhcp6; + BOOLEAN OtherInfoOnly; + UINT32 IaId; + EFI_EVENT Dhcp6Event; + UINT32 FailedIaAddressCount; + EFI_IPv6_ADDRESS *DeclineAddress; + UINT32 DeclineAddressCount; + + IP6_FORM_CALLBACK_INFO CallbackInfo; + IP6_CONFIG_NVDATA Ip6NvData; +}; + +/** + The event process routine when the DHCPv6 server is answered with a reply packet + for an information request. + + @param[in] This Points to the EFI_DHCP6_PROTOCOL. + @param[in] Context The pointer to the IP6 configuration instance data. + @param[in] Packet The DHCPv6 reply packet. + + @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. + @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or + the DNS server address is not valid. + +**/ +EFI_STATUS +EFIAPI +Ip6ConfigOnDhcp6Reply ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ); + +/** + The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in] OtherInfoOnly If FALSE, get stateful address and other information + via DHCPv6. Otherwise, only get the other information. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_UNSUPPORTED The DHCP6 driver is not available. + +**/ +EFI_STATUS +Ip6ConfigStartStatefulAutoConfig ( + IN IP6_CONFIG_INSTANCE *Instance, + IN BOOLEAN OtherInfoOnly + ); + +/** + Initialize an IP6_CONFIG_INSTANCE. + + @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully. + +**/ +EFI_STATUS +Ip6ConfigInitInstance ( + OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + Release an IP6_CONFIG_INSTANCE. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + +**/ +VOID +Ip6ConfigCleanInstance ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + Destroy the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + + @retval EFI_SUCCESS The child was successfully destroyed. + @retval Others Failed to destroy the child. + +**/ +EFI_STATUS +Ip6ConfigDestroyDhcp6 ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c new file mode 100644 index 0000000000..1b878a56a3 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c @@ -0,0 +1,2095 @@ +/** @file + Helper functions for configuring or obtaining the parameters relating to IP6. + + Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +CHAR16 mIp6ConfigStorageName[] = L"IP6_CONFIG_IFR_NVDATA"; + +/** + The notify function of create event when performing a manual configuration. + + @param[in] Event The pointer of Event. + @param[in] Context The pointer of Context. + +**/ +VOID +EFIAPI +Ip6ConfigManualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Get the configuration data for the EFI IPv6 network stack running on the + communication. It is a help function to the call EfiIp6ConfigGetData(). + + @param[in] Ip6Config The pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to get. + @param[out] DataSize The size of buffer required in bytes. + @param[out] Data The data buffer in which the configuration data is returned. The + type of the data buffer associated with the DataType. + It is the caller's responsibility to free the resource. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - Ip6Config is NULL or invalid. + - DataSize is NULL. + - Data is NULL. + @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to lack of resources. + @retval EFI_NOT_READY The specified configuration data is not ready due to an + asynchronous configuration process already in progress. + @retval EFI_NOT_FOUND The specified configuration data was not found. + +**/ +EFI_STATUS +Ip6ConfigNvGetData ( + IN EFI_IP6_CONFIG_PROTOCOL *Ip6Config, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + OUT UINTN *DataSize, + OUT VOID **Data + ) +{ + UINTN BufferSize; + VOID *Buffer; + EFI_STATUS Status; + + if ((Ip6Config == NULL) || (Data == NULL) || (DataSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + BufferSize = 0; + Status = Ip6Config->GetData ( + Ip6Config, + DataType, + &BufferSize, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6Config->GetData ( + Ip6Config, + DataType, + &BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + *DataSize = BufferSize; + *Data = Buffer; + + return EFI_SUCCESS; +} + +/** + Free all nodes in IP6_ADDRESS_INFO_ENTRY in the list array specified + with ListHead. + + @param[in] ListHead The head of the list array in IP6_ADDRESS_INFO_ENTRY. + +**/ +VOID +Ip6FreeAddressInfoList ( + IN LIST_ENTRY *ListHead + ) +{ + IP6_ADDRESS_INFO_ENTRY *Node; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, ListHead) { + Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link); + RemoveEntryList (&Node->Link); + FreePool (Node); + } +} + +/** + Convert the IPv6 address into a formatted string. + + @param[in] Ip6 The IPv6 address. + @param[out] Str The formatted IP string. + +**/ +VOID +Ip6ToStr ( + IN EFI_IPv6_ADDRESS *Ip6, + OUT CHAR16 *Str + ) +{ + UINTN Index; + BOOLEAN Short; + UINTN Number; + CHAR16 FormatString[8]; + + Short = FALSE; + + for (Index = 0; Index < 15; Index = Index + 2) { + if (!Short && + Index % 2 == 0 && + Ip6->Addr[Index] == 0 && + Ip6->Addr[Index + 1] == 0 + ) { + // + // Deal with the case of ::. + // + if (Index == 0) { + *Str = L':'; + *(Str + 1) = L':'; + Str = Str + 2; + } else { + *Str = L':'; + Str = Str + 1; + } + + while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) { + Index = Index + 2; + } + + Short = TRUE; + + if (Index == 16) { + // + // :: is at the end of the address. + // + *Str = L'\0'; + break; + } + } + + ASSERT (Index < 15); + + if (Ip6->Addr[Index] == 0) { + Number = UnicodeSPrint (Str, 2 * IP6_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]); + } else { + if (Ip6->Addr[Index + 1] < 0x10) { + CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:")); + } else { + CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:")); + } + + Number = UnicodeSPrint ( + Str, + 2 * IP6_STR_MAX_SIZE, + (CONST CHAR16 *) FormatString, + (UINTN) Ip6->Addr[Index], + (UINTN) Ip6->Addr[Index + 1] + ); + } + + Str = Str + Number; + + if (Index + 2 == 16) { + *Str = L'\0'; + if (*(Str - 1) == L':') { + *(Str - 1) = L'\0'; + } + } + } +} + +/** + Convert EFI_IP6_CONFIG_INTERFACE_ID to string format. + + @param[out] String The buffer to store the converted string. + @param[in] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID. + + @retval EFI_SUCCESS The string converted successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6ConvertInterfaceIdToString ( + OUT CHAR16 *String, + IN EFI_IP6_CONFIG_INTERFACE_ID *IfId + ) +{ + UINT8 Index; + UINTN Number; + + if ((String == NULL) || (IfId == NULL)) { + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < 8; Index++) { + Number = UnicodeSPrint ( + String, + 2 * INTERFACE_ID_STR_STORAGE, + L"%x:", + (UINTN) IfId->Id[Index] + ); + String = String + Number; + } + + *(String - 1) = '\0'; + + return EFI_SUCCESS; +} + +/** + Parse InterfaceId in string format and convert it to EFI_IP6_CONFIG_INTERFACE_ID. + + @param[in] String The buffer of the string to be parsed. + @param[out] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6ParseInterfaceIdFromString ( + IN CONST CHAR16 *String, + OUT EFI_IP6_CONFIG_INTERFACE_ID *IfId + ) +{ + UINT8 Index; + CHAR16 *IfIdStr; + CHAR16 *TempStr; + UINTN NodeVal; + + if ((String == NULL) || (IfId == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IfIdStr = (CHAR16 *) String; + + ZeroMem (IfId, sizeof (EFI_IP6_CONFIG_INTERFACE_ID)); + + for (Index = 0; Index < 8; Index++) { + TempStr = IfIdStr; + + while ((*IfIdStr != L'\0') && (*IfIdStr != L':')) { + IfIdStr++; + } + + // + // The InterfaceId format is X:X:X:X, the number of X should not exceed 8. + // If the number of X is less than 8, zero is appended to the InterfaceId. + // + if ((*IfIdStr == ':') && (Index == 7)) { + return EFI_INVALID_PARAMETER; + } + + // + // Convert the string to interface id. AsciiStrHexToUintn stops at the + // first character that is not a valid hex character, ':' or '\0' here. + // + NodeVal = StrHexToUintn (TempStr); + if (NodeVal > 0xFF) { + return EFI_INVALID_PARAMETER; + } + + IfId->Id[Index] = (UINT8) NodeVal; + + IfIdStr++; + } + + return EFI_SUCCESS; +} + +/** + Create Hii Extend Label OpCode as the start opcode and end opcode. It is + a help function. + + @param[in] StartLabelNumber The number of start label. + @param[out] StartOpCodeHandle Points to the start opcode handle. + @param[out] StartLabel Points to the created start opcode. + @param[out] EndOpCodeHandle Points to the end opcode handle. + @param[out] EndLabel Points to the created end opcode. + + @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this + operation. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +Ip6CreateOpCode ( + IN UINT16 StartLabelNumber, + OUT VOID **StartOpCodeHandle, + OUT EFI_IFR_GUID_LABEL **StartLabel, + OUT VOID **EndOpCodeHandle, + OUT EFI_IFR_GUID_LABEL **EndLabel + ) +{ + EFI_STATUS Status; + EFI_IFR_GUID_LABEL *InternalStartLabel; + EFI_IFR_GUID_LABEL *InternalEndLabel; + + if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) { + return EFI_INVALID_PARAMETER; + } + + *StartOpCodeHandle = NULL; + *EndOpCodeHandle = NULL; + Status = EFI_OUT_OF_RESOURCES; + + // + // Initialize the container for dynamic opcodes. + // + *StartOpCodeHandle = HiiAllocateOpCodeHandle (); + if (*StartOpCodeHandle == NULL) { + return Status; + } + + *EndOpCodeHandle = HiiAllocateOpCodeHandle (); + if (*EndOpCodeHandle == NULL) { + goto Exit; + } + + // + // Create Hii Extend Label OpCode as the start opcode. + // + InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + *StartOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + if (InternalStartLabel == NULL) { + goto Exit; + } + + InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + InternalStartLabel->Number = StartLabelNumber; + + // + // Create Hii Extend Label OpCode as the end opcode. + // + InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + *EndOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + if (InternalEndLabel == NULL) { + goto Exit; + } + + InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + InternalEndLabel->Number = LABEL_END; + + *StartLabel = InternalStartLabel; + *EndLabel = InternalEndLabel; + + return EFI_SUCCESS; + +Exit: + + if (*StartOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (*StartOpCodeHandle); + } + + if (*EndOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (*EndOpCodeHandle); + } + + return Status; +} + +/** + This function converts the different format of address list to string format and + then generates the corresponding text opcode to illustarate the address info in + IP6 configuration page. Currently, the following formats are supported: + EFI_IP6_ADDRESS_INFO AddressType: Ip6ConfigNvHostAddress; + EFI_IPv6_ADDRESS AddressType: Ip6ConfigNvGatewayAddress and Ip6ConfigNvDnsAddress; + EFI_IP6_ROUTE_TABLE AddressType: Ip6ConfigNvRouteTable. + + @param[in, out] String The pointer to the buffer to store the converted + string. + @param[in] HiiHandle A handle that was previously registered in the + HII Database. + @param[in] AddressType The address type. + @param[in] AddressInfo Pointer to the address list. + @param[in] AddressCount The address count of the address list. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_UNSUPPORTED The AddressType is not supported. + + +**/ +EFI_STATUS +Ip6ConvertAddressListToString ( + IN OUT CHAR16 *String, + IN EFI_HII_HANDLE HiiHandle, + IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType, + IN VOID *AddressInfo, + IN UINTN AddressCount + ) +{ + UINTN Index; + UINTN Number; + CHAR16 *TempStr; + EFI_STATUS Status; + VOID *StartOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *EndLabel; + UINT16 StartLabelNumber; + EFI_STRING_ID TextTwo; + UINT8 *AddressHead; + UINT8 PrefixLength; + EFI_IPv6_ADDRESS *Address; + + if ((String == NULL) || (HiiHandle == NULL) || (AddressInfo == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (AddressType == Ip6ConfigNvHostAddress) { + StartLabelNumber = HOST_ADDRESS_LABEL; + } else if (AddressType == Ip6ConfigNvGatewayAddress) { + StartLabelNumber = GATEWAY_ADDRESS_LABEL; + } else if (AddressType == Ip6ConfigNvDnsAddress) { + StartLabelNumber = DNS_ADDRESS_LABEL; + } else if (AddressType == Ip6ConfigNvRouteTable) { + StartLabelNumber = ROUTE_TABLE_LABEL; + } else { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + Status = Ip6CreateOpCode ( + StartLabelNumber, + &StartOpCodeHandle, + &StartLabel, + &EndOpCodeHandle, + &EndLabel + ); + if (EFI_ERROR (Status)) { + return Status; + } + + AddressHead = (UINT8 *) AddressInfo; + + for (Index = 0; Index < AddressCount; Index++) { + if (AddressType == Ip6ConfigNvHostAddress) { + AddressInfo = AddressHead + sizeof (EFI_IP6_ADDRESS_INFO) * Index; + Address = &((EFI_IP6_ADDRESS_INFO *) AddressInfo)->Address; + } else if (AddressType == Ip6ConfigNvRouteTable) { + AddressInfo = AddressHead + sizeof (EFI_IP6_ROUTE_TABLE) * Index; + Address = &((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Destination; + } else { + AddressInfo = AddressHead + sizeof (EFI_IPv6_ADDRESS) * Index; + Address = AddressInfo; + } + + // + // Convert the IP address info to string. + // + Ip6ToStr (Address, String); + TempStr = String + StrLen (String); + + if ((AddressType == Ip6ConfigNvHostAddress) || (AddressType == Ip6ConfigNvRouteTable)) { + if (AddressType == Ip6ConfigNvHostAddress) { + PrefixLength = ((EFI_IP6_ADDRESS_INFO *) AddressInfo)->PrefixLength; + } else { + PrefixLength = ((EFI_IP6_ROUTE_TABLE *) AddressInfo)->PrefixLength; + } + + // + // Append the prefix length to the string. + // + *TempStr = L'/'; + TempStr++; + Number = UnicodeSPrint (TempStr, 6, L"%d", PrefixLength); + TempStr = TempStr + Number; + } + + if (AddressType == Ip6ConfigNvRouteTable) { + // + // Append " >> " to the string. + // + Number = UnicodeSPrint (TempStr, 8, L" >> "); + TempStr = TempStr + Number; + + // + // Append the gateway address to the string. + // + Ip6ToStr (&((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Gateway, TempStr); + TempStr = TempStr + StrLen (TempStr); + } + + // + // Generate a text opcode and update the UI. + // + TextTwo = HiiSetString (HiiHandle, 0, String, NULL); + if (TextTwo == 0) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + HiiCreateTextOpCode (StartOpCodeHandle, STR_NULL, STR_NULL, TextTwo); + + String = TempStr; + *String = IP6_ADDRESS_DELIMITER; + String++; + } + + *(String - 1) = '\0'; + + Status = HiiUpdateForm ( + HiiHandle, // HII handle + &gIp6ConfigNvDataGuid, // Formset GUID + FORMID_MAIN_FORM, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + EndOpCodeHandle // Replace data + ); + +Exit: + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); + + return Status; +} + +/** + Parse address list in string format and convert it to a list array of node in + IP6_ADDRESS_INFO_ENTRY. + + @param[in] String The buffer to string to be parsed. + @param[out] ListHead The list head of array. + @param[out] AddressCount The number of list nodes in the array. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of resource. + +**/ +EFI_STATUS +Ip6ParseAddressListFromString ( + IN CONST CHAR16 *String, + OUT LIST_ENTRY *ListHead, + OUT UINT32 *AddressCount + ) +{ + EFI_STATUS Status; + CHAR16 *LocalString; + CHAR16 *Temp; + CHAR16 *TempStr; + EFI_IP6_ADDRESS_INFO AddressInfo; + IP6_ADDRESS_INFO_ENTRY *Node; + BOOLEAN Last; + UINT32 Count; + + if ((String == NULL) || (ListHead == NULL) || (AddressCount == NULL)) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&AddressInfo, sizeof (EFI_IP6_ADDRESS_INFO)); + LocalString = (CHAR16 *) AllocateCopyPool (StrSize (String), String); + if (LocalString == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Clean the original address list. + // + Ip6FreeAddressInfoList (ListHead); + + Temp = LocalString; + Last = FALSE; + Count = 0; + + while (*LocalString != L'\0') { + TempStr = LocalString; + while ((*LocalString != L'\0') && (*LocalString != IP6_ADDRESS_DELIMITER)) { + LocalString++; + } + + if (*LocalString == L'\0') { + Last = TRUE; + } + + *LocalString = L'\0'; + + Status = NetLibStrToIp6andPrefix (TempStr, &AddressInfo.Address, &AddressInfo.PrefixLength); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (AddressInfo.PrefixLength == 0xFF) { + AddressInfo.PrefixLength = 0; + } + + if (!NetIp6IsValidUnicast (&AddressInfo.Address)) { + Status = EFI_INVALID_PARAMETER; + goto Error; + } + + Node = AllocatePool (sizeof (IP6_ADDRESS_INFO_ENTRY)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CopyMem (&Node->AddrInfo, &AddressInfo, sizeof (EFI_IP6_ADDRESS_INFO)); + InsertTailList (ListHead, &Node->Link); + Count++; + + if (Last) { + break; + } + + LocalString++; + } + + FreePool (Temp); + *AddressCount = Count; + return EFI_SUCCESS; + +Error: + Ip6FreeAddressInfoList (ListHead); + FreePool (Temp); + return Status; +} + +/** + This function converts the interface info to string and draws it to the IP6 UI. + The interface information includes interface name, interface type, hardware + address and route table information. + + @param[in] IfInfo The pointer of EFI_IP6_CONFIG_INTERFACE_INFO. + @param[in] HiiHandle The handle that was previously registered in the + HII Database. + @param[in, out] IfrNvData Points to IP6_CONFIG_IFR_NVDATA. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources. + +**/ +EFI_STATUS +Ip6ConvertInterfaceInfoToString ( + IN EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo, + IN EFI_HII_HANDLE HiiHandle, + IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData + ) +{ + UINT32 Index; + UINTN Number; + CHAR16 *String; + CHAR16 PortString[ADDRESS_STR_MAX_SIZE]; + CHAR16 FormatString[8]; + EFI_STRING_ID StringId; + + if ((IfInfo == NULL) || (HiiHandle == NULL) || (IfrNvData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Print the interface name. + // + StringId = HiiSetString ( + HiiHandle, + STRING_TOKEN (STR_IP6_INTERFACE_NAME_CONTENT), + IfInfo->Name, + NULL + ); + if (StringId == 0) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Print the interface type. + // + if (IfInfo->IfType == Ip6InterfaceTypeEthernet) { + CopyMem (PortString, IP6_ETHERNET, sizeof (IP6_ETHERNET)); + } else if (IfInfo->IfType == Ip6InterfaceTypeExperimentalEthernet) { + CopyMem (PortString, IP6_EXPERIMENTAL_ETHERNET, sizeof (IP6_EXPERIMENTAL_ETHERNET)); + } else { + // + // Refer to RFC1700, chapter Number Hardware Type. + // + UnicodeSPrint (PortString, 6, L"%d", IfInfo->IfType); + } + + StringId = HiiSetString ( + HiiHandle, + STRING_TOKEN (STR_IP6_INTERFACE_TYPE_CONTENT), + PortString, + NULL + ); + if (StringId == 0) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Convert the hardware address. + // + String = PortString; + ASSERT (IfInfo->HwAddressSize <= 32); + + for (Index = 0; Index < IfInfo->HwAddressSize; Index++) { + + if (IfInfo->HwAddress.Addr[Index] < 0x10) { + CopyMem (FormatString, L"0%x-", sizeof (L"0%x-")); + } else { + CopyMem (FormatString, L"%x-", sizeof (L"%x-")); + } + + Number = UnicodeSPrint ( + String, + 8, + (CONST CHAR16 *) FormatString, + (UINTN) IfInfo->HwAddress.Addr[Index] + ); + String = String + Number; + } + + if (Index != 0) { + ASSERT (String > PortString); + String--; + *String = '\0'; + } + + // + // Print the hardware address. + // + StringId = HiiSetString ( + HiiHandle, + STRING_TOKEN (STR_IP6_MAC_ADDRESS_CONTENT), + PortString, + NULL + ); + if (StringId == 0) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + Build the address info list from list array of node in IP6_ADDRESS_INFO_ENTRY. + + @param[in] Instance Points to IP6 config instance data. + @param[in] AddressType The address type. + @param[out] AddressInfo The pointer to the buffer to store the address list. + @param[out] AddressSize The address size of the address list. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_UNSUPPORTED The AddressType is not supported. + +**/ +EFI_STATUS +Ip6BuildNvAddressInfo ( + IN IP6_CONFIG_INSTANCE *Instance, + IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType, + OUT VOID **AddressInfo, + OUT UINTN *AddressSize + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + LIST_ENTRY *Entry; + LIST_ENTRY *ListHead; + IP6_ADDRESS_INFO_ENTRY *Node; + VOID *AddressList; + VOID *TmpStr; + UINTN DataSize; + EFI_IPv6_ADDRESS *Ip6Address; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress; + + if ((Instance == NULL) || (AddressInfo == NULL) || (AddressSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + + Ip6NvData = &Instance->Ip6NvData; + + if (AddressType == Ip6ConfigNvHostAddress) { + ListHead = &Ip6NvData->ManualAddress; + DataSize = sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS) * Ip6NvData->ManualAddressCount; + } else if (AddressType == Ip6ConfigNvGatewayAddress) { + ListHead = &Ip6NvData->GatewayAddress; + DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->GatewayAddressCount; + } else if (AddressType == Ip6ConfigNvDnsAddress) { + ListHead = &Ip6NvData->DnsAddress; + DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->DnsAddressCount; + } else { + return EFI_UNSUPPORTED; + } + + AddressList = AllocateZeroPool (DataSize); + if (AddressList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TmpStr = AddressList; + + NET_LIST_FOR_EACH (Entry, ListHead) { + Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link); + if (AddressType == Ip6ConfigNvHostAddress) { + ManualAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AddressList; + IP6_COPY_ADDRESS (&ManualAddress->Address, &Node->AddrInfo.Address); + ManualAddress->PrefixLength = Node->AddrInfo.PrefixLength; + AddressList = (UINT8 *) AddressList + sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + } else { + Ip6Address = (EFI_IPv6_ADDRESS *) AddressList; + IP6_COPY_ADDRESS (Ip6Address, &Node->AddrInfo.Address); + AddressList = (UINT8 *) AddressList + sizeof (EFI_IPv6_ADDRESS); + } + } + + *AddressInfo = TmpStr; + *AddressSize = DataSize; + return EFI_SUCCESS; +} + +/** + Convert the IP6 configuration data into the IFR data. + + @param[in, out] IfrNvData The IFR NV data. + @param[in] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_UNSUPPORTED The policy is not supported in the current implementation. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConvertConfigNvDataToIfrNvData ( + IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData, + IN IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + UINTN DataSize; + VOID *Data; + EFI_STATUS Status; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + EFI_HII_HANDLE HiiHandle; + + if ((IfrNvData == NULL) || (Instance == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + + Ip6Config = &Instance->Ip6Config; + Ip6NvData = &Instance->Ip6NvData; + Data = NULL; + DataSize = 0; + HiiHandle = Instance->CallbackInfo.RegisteredHandle; + + // + // Get the current interface info. + // + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Convert the interface info to string and print. + // + Status = Ip6ConvertInterfaceInfoToString ( + (EFI_IP6_CONFIG_INTERFACE_INFO *) Data, + HiiHandle, + IfrNvData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get the interface id. + // + DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID); + ZeroMem (&Ip6NvData->InterfaceId, DataSize); + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + &DataSize, + &Ip6NvData->InterfaceId + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Ip6ConvertInterfaceIdToString (IfrNvData->InterfaceId, &Ip6NvData->InterfaceId); + + // + // Get current policy. + // + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + &DataSize, + &Policy + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (Policy == Ip6ConfigPolicyManual) { + IfrNvData->Policy = IP6_POLICY_MANUAL; + } else if (Policy == Ip6ConfigPolicyAutomatic) { + IfrNvData->Policy = IP6_POLICY_AUTO; + } else { + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // Get Duplicate Address Detection Transmits count. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &DadXmits + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + IfrNvData->DadTransmitCount = DadXmits.DupAddrDetectTransmits; + +Exit: + if (Data != NULL) { + FreePool (Data); + } + + return Status; +} + +/** + Convert IFR data into IP6 configuration data. The policy, alternative interface + ID, and DAD transmit counts, and will be saved. + + @param[in] IfrNvData The IFR NV data. + @param[in, out] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConvertIfrNvDataToConfigNvDataGeneral ( + IN IP6_CONFIG_IFR_NVDATA *IfrNvData, + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_STATUS Status; + + if ((IfrNvData == NULL) || (Instance == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + Ip6NvData = &Instance->Ip6NvData; + Ip6Config = &Instance->Ip6Config; + + // + // Update those fields which don't have INTERACTIVE attribute. + // + if (IfrNvData->Policy == IP6_POLICY_AUTO) { + Ip6NvData->Policy = Ip6ConfigPolicyAutomatic; + } else if (IfrNvData->Policy == IP6_POLICY_MANUAL) { + Ip6NvData->Policy = Ip6ConfigPolicyManual; + } + + Ip6NvData->DadTransmitCount.DupAddrDetectTransmits = IfrNvData->DadTransmitCount; + + // + // Set the configured policy. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Ip6NvData->Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set the duplicate address detection transmits count. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeDupAddrDetectTransmits, + sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS), + &Ip6NvData->DadTransmitCount + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set the alternative interface ID + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + sizeof (EFI_IP6_CONFIG_INTERFACE_ID), + &Ip6NvData->InterfaceId + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Convert IFR data into IP6 configuration data. The policy, configured + manual address, gateway address, and DNS server address will be saved. + + @param[in] IfrNvData The IFR NV data. + @param[in, out] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConvertIfrNvDataToConfigNvDataAdvanced ( + IN IP6_CONFIG_IFR_NVDATA *IfrNvData, + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_STATUS Status; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress; + EFI_IPv6_ADDRESS *Address; + BOOLEAN IsAddressOk; + EFI_EVENT SetAddressEvent; + EFI_EVENT TimeoutEvent; + UINTN DataSize; + + if ((IfrNvData == NULL) || (Instance == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (IfrNvData->Policy == IP6_POLICY_AUTO) { + return EFI_SUCCESS; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + Ip6NvData = &Instance->Ip6NvData; + Ip6Config = &Instance->Ip6Config; + + // + // Update those fields which don't have INTERACTIVE attribute. + // + Ip6NvData->Policy = Ip6ConfigPolicyManual; + + // + // Set the configured policy. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Ip6NvData->Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create events & timers for asynchronous settings. + // + SetAddressEvent = NULL; + TimeoutEvent = NULL; + ManualAddress = NULL; + Address = NULL; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6ConfigManualAddressNotify, + &IsAddressOk, + &SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Set the manual address list. This is an asynchronous process. + // + if (!IsListEmpty (&Ip6NvData->ManualAddress) && (Ip6NvData->ManualAddressCount != 0)) { + Status = Ip6BuildNvAddressInfo ( + Instance, + Ip6ConfigNvHostAddress, + (VOID **) &ManualAddress, + &DataSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + IsAddressOk = FALSE; + + Status = Ip6Config->RegisterDataNotify ( + Ip6Config, + Ip6ConfigDataTypeManualAddress, + SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeManualAddress, + DataSize, + (VOID *) ManualAddress + ); + if (Status == EFI_NOT_READY) { + gBS->SetTimer (TimeoutEvent, TimerRelative, 50000000); + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + if (IsAddressOk) { + Status = EFI_SUCCESS; + } + break; + } + } + + Status = Ip6Config->UnregisterDataNotify ( + Ip6Config, + Ip6ConfigDataTypeManualAddress, + SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + // + // Set gateway address list. + // + if (!IsListEmpty (&Ip6NvData->GatewayAddress) && (Ip6NvData->GatewayAddressCount != 0)) { + Status = Ip6BuildNvAddressInfo ( + Instance, + Ip6ConfigNvGatewayAddress, + (VOID **) &Address, + &DataSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeGateway, + DataSize, + (VOID *) Address + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + FreePool (Address); + Address = NULL; + } + + // + // Set DNS server address list. + // + if (!IsListEmpty (&Ip6NvData->DnsAddress) && (Ip6NvData->DnsAddressCount != 0)) { + Status = Ip6BuildNvAddressInfo ( + Instance, + Ip6ConfigNvDnsAddress, + (VOID **) &Address, + &DataSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeDnsServer, + DataSize, + (VOID *) Address + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + Status = EFI_SUCCESS; + +Exit: + if (SetAddressEvent != NULL) { + gBS->CloseEvent (SetAddressEvent); + } + + if (TimeoutEvent != NULL) { + gBS->CloseEvent (TimeoutEvent); + } + + if (ManualAddress != NULL) { + FreePool (ManualAddress); + } + + if (Address != NULL) { + FreePool (Address); + } + + return Status; +} + + +/** + This function allows the caller to request the current + configuration for one or more named elements. The resulting + string is in format. Any and all alternative + configuration strings shall also be appended to the end of the + current configuration string. If they are, they must appear + after the current configuration. They must contain the same + routing (GUID, NAME, PATH) as the current configuration string. + They must have an additional description indicating the type of + alternative configuration the string represents, + "ALTCFG=". That (when + converted from Hex UNICODE to binary) is a reference to a + string in the associated string pack. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Request A null-terminated Unicode string in + format. Note that this + includes the routing information as well as + the configurable name / value pairs. It is + invalid for this string to be in + format. + @param[out] Progress On return, points to a character in the + Request string. Points to the string's null + terminator if request was successful. Points + to the most recent "&" before the first + failing name / value pair (or the beginning + of the string if the failure is in the first + name / value pair) if the request was not + successful. + @param[out] Results A null-terminated Unicode string in + format which has all values + filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_SUCCESS The Results string is filled with the + values corresponding to all requested + names. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + @retval EFI_INVALID_PARAMETER For example, passing in a NULL + for the Request parameter + would result in this type of + error. In this case, the + Progress parameter would be + set to NULL. + @retval EFI_NOT_FOUND Routing data doesn't match any + known driver. Progress set to the + first character in the routing header. + Note: There is no requirement that the + driver validate the routing data. It + must skip the in order to + process the names. + @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set + to most recent & before the + error or the beginning of the + string. + @retval EFI_INVALID_PARAMETER Unknown name. Progress points + to the & before the name in + question. Currently not implemented. +**/ +EFI_STATUS +EFIAPI +Ip6FormExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + + EFI_STATUS Status; + IP6_FORM_CALLBACK_INFO *Private; + IP6_CONFIG_INSTANCE *Ip6ConfigInstance; + IP6_CONFIG_IFR_NVDATA *IfrNvData; + EFI_STRING ConfigRequestHdr; + EFI_STRING ConfigRequest; + BOOLEAN AllocatedRequest; + UINTN Size; + UINTN BufferSize; + + if (This == NULL || Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Request; + if ((Request != NULL) && + !HiiIsConfigHdrMatch (Request, &gIp6ConfigNvDataGuid, mIp6ConfigStorageName)) { + return EFI_NOT_FOUND; + } + + ConfigRequestHdr = NULL; + ConfigRequest = NULL; + AllocatedRequest = FALSE; + Size = 0; + + Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); + Ip6ConfigInstance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private); + BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA); + + IfrNvData = (IP6_CONFIG_IFR_NVDATA *) AllocateZeroPool (BufferSize); + if (IfrNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6ConvertConfigNvDataToIfrNvData (IfrNvData, Ip6ConfigInstance); + if (EFI_ERROR (Status)) { + goto Exit; + } + + ConfigRequest = Request; + if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { + // + // Request has no request element, construct full request string. + // Allocate and fill a buffer large enough to hold the template + // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator. + // + ConfigRequestHdr = HiiConstructConfigHdr ( + &gIp6ConfigNvDataGuid, + mIp6ConfigStorageName, + Private->ChildHandle + ); + Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + ASSERT (ConfigRequest != NULL); + AllocatedRequest = TRUE; + UnicodeSPrint ( + ConfigRequest, + Size, + L"%s&OFFSET=0&WIDTH=%016LX", + ConfigRequestHdr, + (UINT64) BufferSize + ); + FreePool (ConfigRequestHdr); + } + + // + // Convert buffer data to by helper function BlockToConfig() + // + Status = gHiiConfigRouting->BlockToConfig ( + gHiiConfigRouting, + ConfigRequest, + (UINT8 *) IfrNvData, + BufferSize, + Results, + Progress + ); + +Exit: + FreePool (IfrNvData); + // + // Free the allocated config request string. + // + if (AllocatedRequest) { + FreePool (ConfigRequest); + ConfigRequest = NULL; + } + // + // Set Progress string to the original request string. + // + if (Request == NULL) { + *Progress = NULL; + } else if (StrStr (Request, L"OFFSET") == NULL) { + *Progress = Request + StrLen (Request); + } + + return Status; +} + +/** + This function applies changes in a driver's configuration. + Input is a Configuration, which has the routing data for this + driver followed by name / value configuration pairs. The driver + must apply those pairs to its configurable storage. If the + driver's configuration is stored in a linear block of data + and the driver's name / value pairs are in + format, it may use the ConfigToBlock helper function (above) to + simplify the job. Currently not implemented. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Configuration A null-terminated Unicode string in + format. + @param[out] Progress A pointer to a string filled in with the + offset of the most recent '&' before the + first failing name / value pair (or the + beginn ing of the string if the failure + is in the first name / value pair) or + the terminating NULL if all was + successful. + + @retval EFI_SUCCESS The results have been distributed or are + awaiting distribution. + @retval EFI_OUT_OF_MEMORY Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + @retval EFI_INVALID_PARAMETERS Passing in a NULL for the + Results parameter would result + in this type of error. + @retval EFI_NOT_FOUND Target for the specified routing data + was not found. +**/ +EFI_STATUS +EFIAPI +Ip6FormRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + if (This == NULL || Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check routing data in . + // Note: if only one Storage is used, then this checking could be skipped. + // + if (!HiiIsConfigHdrMatch (Configuration, &gIp6ConfigNvDataGuid, mIp6ConfigStorageName)) { + *Progress = Configuration; + return EFI_NOT_FOUND; + } + + *Progress = Configuration + StrLen (Configuration); + + return EFI_SUCCESS; +} + +/** + Display host addresses, route table, DNS addresses and gateway addresses in + "IPv6 Current Setting" page. + + @param[in] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6GetCurrentSetting ( + IN IP6_CONFIG_INSTANCE *Instance + ) +{ + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_HII_HANDLE HiiHandle; + EFI_IP6_CONFIG_INTERFACE_INFO *Data; + UINTN DataSize; + EFI_STATUS Status; + CHAR16 PortString[ADDRESS_STR_MAX_SIZE]; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + + + Ip6Config = &Instance->Ip6Config; + HiiHandle = Instance->CallbackInfo.RegisteredHandle; + Data = NULL; + + // + // Get current interface info. + // + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Generate dynamic text opcode for host address and draw it. + // + IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data; + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvHostAddress, + IfInfo->AddressInfo, + IfInfo->AddressInfoCount + ); + if (EFI_ERROR (Status)) { + FreePool (Data); + return Status; + } + + // + // Generate the dynamic text opcode for route table and draw it. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvRouteTable, + IfInfo->RouteTable, + IfInfo->RouteCount + ); + if (EFI_ERROR (Status)) { + FreePool (Data); + return Status; + } + + // + // Get DNS server list. + // + FreePool (Data); + DataSize = 0; + Data = NULL; + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeDnsServer, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + if (Data != NULL) { + FreePool (Data); + } + return Status; + } + + if (DataSize > 0) { + // + // Generate the dynamic text opcode for DNS server and draw it. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvDnsAddress, + Data, + DataSize / sizeof (EFI_IPv6_ADDRESS) + ); + if (EFI_ERROR (Status)) { + FreePool (Data); + return Status; + } + } + + // + // Get gateway adderss list. + // + if (Data != NULL) { + FreePool (Data); + } + + DataSize = 0; + Data = NULL; + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeGateway, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + if (Data != NULL) { + FreePool (Data); + } + return Status; + } + + if (DataSize > 0) { + // + // Generate the dynamic text opcode for gateway and draw it. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvGatewayAddress, + Data, + DataSize / sizeof (EFI_IPv6_ADDRESS) + ); + if (EFI_ERROR (Status)) { + FreePool (Data); + return Status; + } + } + + if (Data != NULL) { + FreePool (Data); + } + + return EFI_SUCCESS; +} + +/** + This function is called to provide results data to the driver. + This data consists of a unique key that is used to identify + which data is either being passed back or being asked for. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Action Specifies the type of action taken by the browser. + @param[in] QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. The format of the data tends to + vary based on the opcode that generated the callback. + @param[in] Type The type of value for the question. + @param[in] Value A pointer to the data being sent to the original + exporting driver. + @param[out] ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. Currently not implemented. + @retval EFI_INVALID_PARAMETER Passed in the wrong parameter. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +Ip6FormCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + IP6_FORM_CALLBACK_INFO *Private; + UINTN BufferSize; + IP6_CONFIG_IFR_NVDATA *IfrNvData; + EFI_STATUS Status; + EFI_INPUT_KEY Key; + IP6_CONFIG_INSTANCE *Instance; + IP6_CONFIG_NVDATA *Ip6NvData; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); + Instance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private); + Ip6NvData = &Instance->Ip6NvData; + + if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) || (Action == EFI_BROWSER_ACTION_FORM_CLOSE)){ + return EFI_SUCCESS; + } + + if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) { + return EFI_UNSUPPORTED; + } + + if ((Value == NULL) || (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve uncommitted data from Browser + // + + BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA); + IfrNvData = AllocateZeroPool (BufferSize); + if (IfrNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = EFI_SUCCESS; + + HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData); + + if (Action == EFI_BROWSER_ACTION_CHANGING) { + switch (QuestionId) { + case KEY_GET_CURRENT_SETTING: + Status = Ip6GetCurrentSetting (Instance); + break; + + default: + break; + } + } else if (Action == EFI_BROWSER_ACTION_CHANGED) { + switch (QuestionId) { + case KEY_SAVE_CONFIG_CHANGES: + Status = Ip6ConvertIfrNvDataToConfigNvDataAdvanced (IfrNvData, Instance); + if (EFI_ERROR (Status)) { + break; + } + + Status = Ip6GetCurrentSetting (Instance); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT; + break; + + case KEY_IGNORE_CONFIG_CHANGES: + Ip6FreeAddressInfoList (&Ip6NvData->ManualAddress); + Ip6FreeAddressInfoList (&Ip6NvData->GatewayAddress); + Ip6FreeAddressInfoList (&Ip6NvData->DnsAddress); + + Ip6NvData->ManualAddressCount = 0; + Ip6NvData->GatewayAddressCount = 0; + Ip6NvData->DnsAddressCount = 0; + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; + break; + + case KEY_SAVE_CHANGES: + Status = Ip6ConvertIfrNvDataToConfigNvDataGeneral (IfrNvData, Instance); + if (EFI_ERROR (Status)) { + break; + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT; + break; + + case KEY_INTERFACE_ID: + Status = Ip6ParseInterfaceIdFromString (IfrNvData->InterfaceId, &Ip6NvData->InterfaceId); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Interface ID!", + NULL + ); + } + + break; + + case KEY_MANUAL_ADDRESS: + Status = Ip6ParseAddressListFromString ( + IfrNvData->ManualAddress, + &Ip6NvData->ManualAddress, + &Ip6NvData->ManualAddressCount + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Host Addresses!", + NULL + ); + } + + break; + + case KEY_GATEWAY_ADDRESS: + Status = Ip6ParseAddressListFromString ( + IfrNvData->GatewayAddress, + &Ip6NvData->GatewayAddress, + &Ip6NvData->GatewayAddressCount + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Gateway Addresses!", + NULL + ); + } + + break; + + case KEY_DNS_ADDRESS: + Status = Ip6ParseAddressListFromString ( + IfrNvData->DnsAddress, + &Ip6NvData->DnsAddress, + &Ip6NvData->DnsAddressCount + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid DNS Addresses!", + NULL + ); + } + + break; + + default: + break; + } + } + + if (!EFI_ERROR (Status)) { + // + // Pass changed uncommitted data back to Form Browser. + // + BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA); + HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL); + } + + FreePool (IfrNvData); + return Status; +} + +/** + Install HII Config Access protocol for network device and allocate resources. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form. + + @retval EFI_SUCCESS The HII Config Access protocol is installed. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConfigFormInit ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + IP6_FORM_CALLBACK_INFO *CallbackInfo; + EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; + VENDOR_DEVICE_PATH VendorDeviceNode; + EFI_SERVICE_BINDING_PROTOCOL *MnpSb; + CHAR16 *MacString; + CHAR16 MenuString[128]; + CHAR16 PortString[128]; + CHAR16 *OldMenuString; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + ASSERT (IpSb != NULL); + + Status = gBS->HandleProtocol ( + IpSb->Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CallbackInfo = &Instance->CallbackInfo; + CallbackInfo->Signature = IP6_FORM_CALLBACK_INFO_SIGNATURE; + + // + // Construct device path node for EFI HII Config Access protocol, + // which consists of controller physical device path and one hardware + // vendor guid node. + // + ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH)); + VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH; + VendorDeviceNode.Header.SubType = HW_VENDOR_DP; + + CopyGuid (&VendorDeviceNode.Guid, &gEfiCallerIdGuid); + + SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH)); + CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode + ); + if (CallbackInfo->HiiVendorDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + ConfigAccess = &CallbackInfo->HiiConfigAccess; + ConfigAccess->ExtractConfig = Ip6FormExtractConfig; + ConfigAccess->RouteConfig = Ip6FormRouteConfig; + ConfigAccess->Callback = Ip6FormCallback; + + // + // Install Device Path Protocol and Config Access protocol on new handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &CallbackInfo->ChildHandle, + &gEfiDevicePathProtocolGuid, + CallbackInfo->HiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + ConfigAccess, + NULL + ); + if (!EFI_ERROR (Status)) { + // + // Open the Parent Handle for the child + // + Status = gBS->OpenProtocol ( + IpSb->Controller, + &gEfiManagedNetworkServiceBindingProtocolGuid, + (VOID **) &MnpSb, + IpSb->Image, + CallbackInfo->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } + + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Publish our HII data + // + CallbackInfo->RegisteredHandle = HiiAddPackages ( + &gIp6ConfigNvDataGuid, + CallbackInfo->ChildHandle, + Ip6DxeStrings, + Ip6ConfigBin, + NULL + ); + if (CallbackInfo->RegisteredHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Append MAC string in the menu help string and tile help string + // + Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &MacString); + if (!EFI_ERROR (Status)) { + OldMenuString = HiiGetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP6_CONFIG_FORM_HELP), + NULL) + ; + UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString); + HiiSetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP6_CONFIG_FORM_HELP), + MenuString, + NULL + ); + UnicodeSPrint (PortString, 128, L"MAC:%s", MacString); + HiiSetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP6_DEVICE_FORM_HELP), + PortString, + NULL + ); + + FreePool (MacString); + FreePool (OldMenuString); + + InitializeListHead (&Instance->Ip6NvData.ManualAddress); + InitializeListHead (&Instance->Ip6NvData.GatewayAddress); + InitializeListHead (&Instance->Ip6NvData.DnsAddress); + + return EFI_SUCCESS; + } + +Error: + Ip6ConfigFormUnload (Instance); + return Status; +} + +/** + Uninstall the HII Config Access protocol for network devices and free up the resources. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form. + +**/ +VOID +Ip6ConfigFormUnload ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_SERVICE *IpSb; + IP6_FORM_CALLBACK_INFO *CallbackInfo; + IP6_CONFIG_NVDATA *Ip6NvData; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + ASSERT (IpSb != NULL); + + CallbackInfo = &Instance->CallbackInfo; + + if (CallbackInfo->ChildHandle != NULL) { + + // + // Close the child handle + // + gBS->CloseProtocol ( + IpSb->Controller, + &gEfiManagedNetworkServiceBindingProtocolGuid, + IpSb->Image, + CallbackInfo->ChildHandle + ); + // + // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL + // + gBS->UninstallMultipleProtocolInterfaces ( + CallbackInfo->ChildHandle, + &gEfiDevicePathProtocolGuid, + CallbackInfo->HiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &CallbackInfo->HiiConfigAccess, + NULL + ); + } + + if (CallbackInfo->HiiVendorDevicePath != NULL) { + FreePool (CallbackInfo->HiiVendorDevicePath); + } + + if (CallbackInfo->RegisteredHandle != NULL) { + // + // Remove HII package list + // + HiiRemovePackages (CallbackInfo->RegisteredHandle); + } + + Ip6NvData = &Instance->Ip6NvData; + + Ip6FreeAddressInfoList (&Ip6NvData->ManualAddress); + Ip6FreeAddressInfoList (&Ip6NvData->GatewayAddress); + Ip6FreeAddressInfoList (&Ip6NvData->DnsAddress); + + Ip6NvData->ManualAddressCount = 0; + Ip6NvData->GatewayAddressCount = 0; + Ip6NvData->DnsAddressCount = 0; +} diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h new file mode 100644 index 0000000000..ef1893c549 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h @@ -0,0 +1,68 @@ +/** @file + The header file of Ip6ConfigNv.c. + + Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IP6_CONFIGNV_H_ +#define _IP6_CONFIGNV_H_ + +#include "Ip6NvData.h" +#include "Ip6ConfigImpl.h" + +extern UINT8 Ip6ConfigBin[]; +extern UINT8 Ip6DxeStrings[]; + +#define IP6_ETHERNET L"Ethernet" +#define IP6_EXPERIMENTAL_ETHERNET L"Experimental Ethernet" +#define IP6_ADDRESS_DELIMITER L' ' +#define IP6_LINK_LOCAL_PREFIX L"FE80::" + +typedef enum { + Ip6InterfaceTypeEthernet = 1, + Ip6InterfaceTypeExperimentalEthernet +} IP6_INTERFACE_TYPE; + +typedef enum { + Ip6ConfigNvHostAddress, + Ip6ConfigNvGatewayAddress, + Ip6ConfigNvDnsAddress, + Ip6ConfigNvRouteTable +} IP6_CONFIG_NV_ADDRESS_TYPE; + +/** + Install HII Config Access protocol for network device and allocate resources. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form. + + @retval EFI_SUCCESS The HII Config Access protocol is installed. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConfigFormInit ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + Uninstall HII Config Access protocol for network device and free resource. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form. + +**/ +VOID +Ip6ConfigFormUnload ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Driver.c b/Core/NetworkPkg/Ip6Dxe/Ip6Driver.c new file mode 100644 index 0000000000..076dc605e6 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Driver.c @@ -0,0 +1,1000 @@ +/** @file + The driver binding and service binding protocol for IP6 driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding = { + Ip6DriverBindingSupported, + Ip6DriverBindingStart, + Ip6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +BOOLEAN mIpSec2Installed = FALSE; + +/** + Callback function for IpSec2 Protocol install. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +IpSec2InstalledCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Close the event so it does not get called again. + // + gBS->CloseEvent (Event); + + mIpSec2Installed = TRUE; + + return; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + The entry point for IP6 driver which installs the driver + binding and component name protocol on its image. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *Registration; + + EfiCreateProtocolNotifyEvent ( + &gEfiIpSec2ProtocolGuid, + TPL_CALLBACK, + IpSec2InstalledCallback, + NULL, + &Registration + ); + + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gIp6DriverBinding, + ImageHandle, + &gIp6ComponentName, + &gIp6ComponentName2 + ); +} + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + // + // Test for the MNP service binding Protocol + // + return gBS->OpenProtocol ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); +} + +/** + Clean up an IP6 service binding instance. It releases all + the resource allocated by the instance. The instance may be + partly initialized, or partly destroyed. If a resource is + destroyed, it is marked as that in case the destroy failed and + being called again later. + + @param[in] IpSb The IP6 service binding instance to clean up. + + @retval EFI_SUCCESS The resource used by the instance are cleaned up. + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +Ip6CleanService ( + IN IP6_SERVICE *IpSb + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS AllNodes; + IP6_NEIGHBOR_ENTRY *NeighborCache; + + Ip6ConfigCleanInstance (&IpSb->Ip6ConfigInstance); + + if (!IpSb->LinkLocalDadFail) { + // + // Leave link-scope all-nodes multicast address (FF02::1) + // + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + + Status = Ip6LeaveGroup (IpSb, &AllNodes); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (IpSb->DefaultInterface != NULL) { + Ip6CleanInterface (IpSb->DefaultInterface, NULL); + IpSb->DefaultInterface = NULL; + } + + Ip6CleanDefaultRouterList (IpSb); + + Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix); + Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix); + + if (IpSb->RouteTable != NULL) { + Ip6CleanRouteTable (IpSb->RouteTable); + IpSb->RouteTable = NULL; + } + + if (IpSb->InterfaceId != NULL) { + FreePool (IpSb->InterfaceId); + } + + IpSb->InterfaceId = NULL; + + Ip6CleanAssembleTable (&IpSb->Assemble); + + if (IpSb->MnpChildHandle != NULL) { + if (IpSb->Mnp != NULL) { + IpSb->Mnp->Cancel (IpSb->Mnp, NULL); + IpSb->Mnp->Configure (IpSb->Mnp, NULL); + gBS->CloseProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + + IpSb->Mnp = NULL; + } + + NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiManagedNetworkServiceBindingProtocolGuid, + IpSb->MnpChildHandle + ); + + IpSb->MnpChildHandle = NULL; + } + + if (IpSb->RecvRequest.MnpToken.Event != NULL) { + gBS->CloseEvent (IpSb->RecvRequest.MnpToken.Event); + } + + if (IpSb->Timer != NULL) { + gBS->SetTimer (IpSb->Timer, TimerCancel, 0); + gBS->CloseEvent (IpSb->Timer); + + IpSb->Timer = NULL; + } + + if (IpSb->FasterTimer != NULL) { + gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0); + gBS->CloseEvent (IpSb->FasterTimer); + + IpSb->FasterTimer = NULL; + } + // + // Free the Neighbor Discovery resources + // + while (!IsListEmpty (&IpSb->NeighborTable)) { + NeighborCache = NET_LIST_HEAD (&IpSb->NeighborTable, IP6_NEIGHBOR_ENTRY, Link); + Ip6FreeNeighborEntry (IpSb, NeighborCache, FALSE, TRUE, EFI_SUCCESS, NULL, NULL); + } + + return EFI_SUCCESS; +} + +/** + Create a new IP6 driver service binding protocol. + + @param[in] Controller The controller that has MNP service binding + installed. + @param[in] ImageHandle The IP6 driver's image handle. + @param[out] Service The variable to receive the newly created IP6 + service. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_SUCCESS A new IP6 service binding private is created. + +**/ +EFI_STATUS +Ip6CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT IP6_SERVICE **Service + ) +{ + IP6_SERVICE *IpSb; + EFI_STATUS Status; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_CONFIG_DATA *Config; + IP6_CONFIG_DATA_ITEM *DataItem; + + ASSERT (Service != NULL); + + *Service = NULL; + + // + // allocate a service private data then initialize all the filed to + // empty resources, so if any thing goes wrong when allocating + // resources, Ip6CleanService can be called to clean it up. + // + IpSb = AllocateZeroPool (sizeof (IP6_SERVICE)); + + if (IpSb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IpSb->Signature = IP6_SERVICE_SIGNATURE; + IpSb->ServiceBinding.CreateChild = Ip6ServiceBindingCreateChild; + IpSb->ServiceBinding.DestroyChild = Ip6ServiceBindingDestroyChild; + IpSb->State = IP6_SERVICE_UNSTARTED; + + IpSb->NumChildren = 0; + InitializeListHead (&IpSb->Children); + + InitializeListHead (&IpSb->Interfaces); + IpSb->DefaultInterface = NULL; + IpSb->RouteTable = NULL; + + IpSb->RecvRequest.Signature = IP6_LINK_RX_SIGNATURE; + IpSb->RecvRequest.CallBack = NULL; + IpSb->RecvRequest.Context = NULL; + MnpToken = &IpSb->RecvRequest.MnpToken; + MnpToken->Event = NULL; + MnpToken->Status = EFI_NOT_READY; + MnpToken->Packet.RxData = NULL; + + Ip6CreateAssembleTable (&IpSb->Assemble); + + IpSb->MldCtrl.Mldv1QuerySeen = 0; + InitializeListHead (&IpSb->MldCtrl.Groups); + + ZeroMem (&IpSb->LinkLocalAddr, sizeof (EFI_IPv6_ADDRESS)); + IpSb->LinkLocalOk = FALSE; + IpSb->LinkLocalDadFail = FALSE; + IpSb->Dhcp6NeedStart = FALSE; + IpSb->Dhcp6NeedInfoRequest = FALSE; + + IpSb->CurHopLimit = IP6_HOP_LIMIT; + IpSb->LinkMTU = IP6_MIN_LINK_MTU; + IpSb->BaseReachableTime = IP6_REACHABLE_TIME; + Ip6UpdateReachableTime (IpSb); + // + // RFC4861 RETRANS_TIMER: 1,000 milliseconds + // + IpSb->RetransTimer = IP6_RETRANS_TIMER; + + IpSb->RoundRobin = 0; + + InitializeListHead (&IpSb->NeighborTable); + InitializeListHead (&IpSb->DefaultRouterList); + InitializeListHead (&IpSb->OnlinkPrefix); + InitializeListHead (&IpSb->AutonomousPrefix); + + IpSb->InterfaceIdLen = IP6_IF_ID_LEN; + IpSb->InterfaceId = NULL; + + IpSb->RouterAdvertiseReceived = FALSE; + IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS; + IpSb->Ticks = 0; + + IpSb->Image = ImageHandle; + IpSb->Controller = Controller; + + IpSb->MnpChildHandle = NULL; + IpSb->Mnp = NULL; + + Config = &IpSb->MnpConfigData; + Config->ReceivedQueueTimeoutValue = 0; + Config->TransmitQueueTimeoutValue = 0; + Config->ProtocolTypeFilter = IP6_ETHER_PROTO; + Config->EnableUnicastReceive = TRUE; + Config->EnableMulticastReceive = TRUE; + Config->EnableBroadcastReceive = TRUE; + Config->EnablePromiscuousReceive = FALSE; + Config->FlushQueuesOnReset = TRUE; + Config->EnableReceiveTimestamps = FALSE; + Config->DisableBackgroundPolling = FALSE; + + ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE)); + + IpSb->Timer = NULL; + IpSb->FasterTimer = NULL; + + ZeroMem (&IpSb->Ip6ConfigInstance, sizeof (IP6_CONFIG_INSTANCE)); + + IpSb->MacString = NULL; + + // + // Create various resources. First create the route table, timer + // event, MNP token event and MNP child. + // + + IpSb->RouteTable = Ip6CreateRouteTable (); + if (IpSb->RouteTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Ip6TimerTicking, + IpSb, + &IpSb->Timer + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Ip6NdFasterTimerTicking, + IpSb, + &IpSb->FasterTimer + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = NetLibCreateServiceChild ( + Controller, + ImageHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + &IpSb->MnpChildHandle + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) (&IpSb->Mnp), + ImageHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip6ServiceConfigMnp (IpSb, TRUE); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpSb->MaxPacketSize = IP6_MIN_LINK_MTU - sizeof (EFI_IP6_HEADER); + if (NetLibGetVlanId (IpSb->Controller) != 0) { + // + // This is a VLAN device, reduce MTU by VLAN tag length + // + IpSb->MaxPacketSize -= NET_VLAN_TAG_LEN; + } + IpSb->OldMaxPacketSize = IpSb->MaxPacketSize; + + // + // Currently only ETHERNET is supported in IPv6 stack, since + // link local address requires an IEEE 802 48-bit MACs for + // EUI-64 format interface identifier mapping. + // + if (IpSb->SnpMode.IfType != NET_IFTYPE_ETHERNET) { + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + Status = Ip6InitMld (IpSb); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip6ConfigInitInstance (&IpSb->Ip6ConfigInstance); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpSb->DefaultInterface = Ip6CreateInterface (IpSb, TRUE); + if (IpSb->DefaultInterface == NULL) { + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6OnFrameReceived, + &IpSb->RecvRequest, + &MnpToken->Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // If there is any manual address, set it. + // + DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress]; + if (DataItem->Data.Ptr != NULL) { + DataItem->SetData ( + &IpSb->Ip6ConfigInstance, + DataItem->DataSize, + DataItem->Data.Ptr + ); + } + + // + // If there is any gateway address, set it. + // + DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeGateway]; + if (DataItem->Data.Ptr != NULL) { + DataItem->SetData ( + &IpSb->Ip6ConfigInstance, + DataItem->DataSize, + DataItem->Data.Ptr + ); + } + + InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link); + + *Service = IpSb; + return EFI_SUCCESS; + +ON_ERROR: + Ip6CleanService (IpSb); + FreePool (IpSb); + return Status; +} + + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + IP6_SERVICE *IpSb; + EFI_STATUS Status; + + // + // Test for the Ip6 service binding protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (Status == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + Status = Ip6CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (IpSb != NULL); + + // + // Install the Ip6ServiceBinding Protocol onto ControlerHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + &IpSb->ServiceBinding, + &gEfiIp6ConfigProtocolGuid, + &IpSb->Ip6ConfigInstance.Ip6Config, + NULL + ); + + if (!EFI_ERROR (Status)) { + // + // ready to go: start the receiving and timer + // + Status = Ip6ReceiveFrame (Ip6AcceptFrame, IpSb); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // The timer expires every 100 (IP6_TIMER_INTERVAL_IN_MS) milliseconds. + // + Status = gBS->SetTimer ( + IpSb->FasterTimer, + TimerPeriodic, + TICKS_PER_MS * IP6_TIMER_INTERVAL_IN_MS + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // The timer expires every 1000 (IP6_ONE_SECOND_IN_MS) milliseconds. + // + Status = gBS->SetTimer ( + IpSb->Timer, + TimerPeriodic, + TICKS_PER_MS * IP6_ONE_SECOND_IN_MS + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Initialize the IP6 ID + // + mIp6Id = NET_RANDOM (NetRandomInitSeed ()); + + return EFI_SUCCESS; + } + +ON_ERROR: + Ip6CleanService (IpSb); + FreePool (IpSb); + return Status; +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +Ip6DestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + ServiceBinding = ((IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (IpInstance->Handle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle); +} + +/** + Stop this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + IP6_SERVICE *IpSb; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + LIST_ENTRY *List; + INTN State; + BOOLEAN IsDhcp6; + IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + + IsDhcp6 = FALSE; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid); + if (NicHandle != NULL) { + IsDhcp6 = TRUE; + } else { + return EFI_SUCCESS; + } + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiIp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + IpSb = IP6_SERVICE_FROM_PROTOCOL (ServiceBinding); + + if (IsDhcp6) { + Status = Ip6ConfigDestroyDhcp6 (&IpSb->Ip6ConfigInstance); + gBS->CloseEvent (IpSb->Ip6ConfigInstance.Dhcp6Event); + IpSb->Ip6ConfigInstance.Dhcp6Event = NULL; + } else if (NumberOfChildren != 0) { + // + // NumberOfChildren is not zero, destroy the IP6 children instances in ChildHandleBuffer. + // + List = &IpSb->Children; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + Ip6DestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } else if (IsListEmpty (&IpSb->Children)) { + State = IpSb->State; + IpSb->State = IP6_SERVICE_DESTROY; + + Status = Ip6CleanService (IpSb); + if (EFI_ERROR (Status)) { + IpSb->State = State; + goto Exit; + } + + Status = gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiIp6ServiceBindingProtocolGuid, + ServiceBinding, + &gEfiIp6ConfigProtocolGuid, + &IpSb->Ip6ConfigInstance.Ip6Config, + NULL + ); + ASSERT_EFI_ERROR (Status); + FreePool (IpSb); + Status = EFI_SUCCESS; + } + +Exit: + return Status; +} + + +/** + Creates a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it + is not NULL, then the I/O services are added to + the existing child handle. + + @retval EFI_SUCCES The child handle was created with the I/O services. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_TPL OldTpl; + EFI_STATUS Status; + VOID *Mnp; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IpSb = IP6_SERVICE_FROM_PROTOCOL (This); + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + IpInstance = AllocatePool (sizeof (IP6_PROTOCOL)); + + if (IpInstance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ip6InitProtocol (IpSb, IpInstance); + + // + // Install Ip6 onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiIp6ProtocolGuid, + &IpInstance->Ip6Proto, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpInstance->Handle = *ChildHandle; + + // + // Open the Managed Network protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) &Mnp, + gIp6DriverBinding.DriverBindingHandle, + IpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiIp6ProtocolGuid, + &IpInstance->Ip6Proto, + NULL + ); + + goto ON_ERROR; + } + + // + // Insert it into the service binding instance. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&IpSb->Children, &IpInstance->Link); + IpSb->NumChildren++; + + gBS->RestoreTPL (OldTpl); + +ON_ERROR: + + if (EFI_ERROR (Status)) { + + Ip6CleanProtocol (IpInstance); + + FreePool (IpInstance); + } + + return Status; +} + +/** + Destroys a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_IP6_PROTOCOL *Ip6; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + IpSb = IP6_SERVICE_FROM_PROTOCOL (This); + + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiIp6ProtocolGuid, + (VOID **) &Ip6, + gIp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (Ip6); + + if (IpInstance->Service != IpSb) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // A child can be destroyed more than once. For example, + // Ip6DriverBindingStop will destroy all of its children. + // when UDP driver is being stopped, it will destroy all + // the IP child it opens. + // + if (IpInstance->InDestroy) { + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + } + + IpInstance->InDestroy = TRUE; + + // + // Close the Managed Network protocol. + // + gBS->CloseProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + gIp6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + // + // Uninstall the IP6 protocol first. Many thing happens during + // this: + // 1. The consumer of the IP6 protocol will be stopped if it + // opens the protocol BY_DRIVER. For eaxmple, if MNP driver is + // stopped, IP driver's stop function will be called, and uninstall + // EFI_IP6_PROTOCOL will trigger the UDP's stop function. This + // makes it possible to create the network stack bottom up, and + // stop it top down. + // 2. the upper layer will recycle the received packet. The recycle + // event's TPL is higher than this function. The recycle events + // will be called back before preceeding. If any packets not recycled, + // that means there is a resource leak. + // + gBS->RestoreTPL (OldTpl); + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiIp6ProtocolGuid, + &IpInstance->Ip6Proto + ); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip6CleanProtocol (IpInstance); + if (EFI_ERROR (Status)) { + gBS->InstallMultipleProtocolInterfaces ( + &ChildHandle, + &gEfiIp6ProtocolGuid, + Ip6, + NULL + ); + + goto ON_ERROR; + } + + RemoveEntryList (&IpInstance->Link); + ASSERT (IpSb->NumChildren > 0); + IpSb->NumChildren--; + + gBS->RestoreTPL (OldTpl); + + FreePool (IpInstance); + return EFI_SUCCESS; + +ON_ERROR: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Driver.h b/Core/NetworkPkg/Ip6Dxe/Ip6Driver.h new file mode 100644 index 0000000000..d16ff48e2a --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Driver.h @@ -0,0 +1,192 @@ +/** @file + The driver binding and service binding protocol for IP6 driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_DRIVER_H__ +#define __EFI_IP6_DRIVER_H__ + +extern EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gIp6ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2; +extern EFI_UNICODE_STRING_TABLE *gIp6ControllerNameTable; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +}IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +/** + Clean up an IP6 service binding instance. It releases all + the resource allocated by the instance. The instance may be + partly initialized, or partly destroyed. If a resource is + destroyed, it is marked as that in case the destroy failed and + being called again later. + + @param[in] IpSb The IP6 service binding instance to clean up. + + @retval EFI_SUCCESS The resource used by the instance are cleaned up. + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +Ip6CleanService ( + IN IP6_SERVICE *IpSb + ); + +// +// Function prototype for the driver's entry point +// + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + The entry point for IP6 driver which installs the driver + binding and component name protocol on its image. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Drivr Binding Protocol +// + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +// +// Function ptototypes for the ServiceBinding Prococol +// + +/** + Creates a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it + is not NULL, then the I/O services are added to + the existing child handle. + + @retval EFI_SUCCES The child handle was created with the I/O services. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Dxe.inf b/Core/NetworkPkg/Ip6Dxe/Ip6Dxe.inf new file mode 100644 index 0000000000..76e068daba --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Dxe.inf @@ -0,0 +1,116 @@ +## @file +# Basic IPv6 packet I/O Service. +# +# This module provides basic network IPv6 packet I/O services which includes support for +# Neighbor Discovery Protocol (ND), Multicast Listener Discovery Protocol (MLD), +# and a subset of the Internet Control Message Protocol (ICMPv6). This driver +# also provides the mechanism to set and get various types of configurations for +# the EFI IPv6 network stack. +# +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Ip6Dxe + FILE_GUID = 5BEDB5CC-D830-4eb2-8742-2D4CC9B54F2C + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Ip6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = Ip6Dxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gIp6DriverBinding +# COMPONENT_NAME = gIp6ComponentName +# COMPONENT_NAME2 = gIp6ComponentName2 +# + +[Sources] + Ip6Output.h + Ip6Option.h + Ip6Input.h + Ip6Nd.h + Ip6Mld.h + Ip6Impl.c + Ip6Driver.c + ComponentName.c + Ip6Nd.c + Ip6Input.c + Ip6ConfigImpl.c + Ip6ConfigImpl.h + Ip6Impl.h + Ip6Option.c + Ip6If.h + Ip6Icmp.h + Ip6Mld.c + Ip6Common.c + Ip6Route.c + Ip6If.c + Ip6Driver.h + Ip6Output.c + Ip6Icmp.c + Ip6Common.h + Ip6Route.h + Ip6DxeStrings.uni + Ip6NvData.h + Ip6ConfigNv.c + Ip6ConfigNv.h + Ip6Config.vfr + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + NetworkPkg/NetworkPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DevicePathLib + HiiLib + UefiHiiServicesLib + PrintLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + UefiLib + DebugLib + NetLib + DpcLib + +[Protocols] + gEfiManagedNetworkServiceBindingProtocolGuid ## TO_START + gEfiManagedNetworkProtocolGuid ## TO_START + gEfiIp6ServiceBindingProtocolGuid ## BY_START + gEfiIp6ProtocolGuid ## BY_START + gEfiIp6ConfigProtocolGuid ## BY_START + gEfiDhcp6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDhcp6ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIpSec2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiHiiConfigAccessProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + ## SOMETIMES_CONSUMES ## HII + gEfiIfrTianoGuid + ## SOMETIMES_CONSUMES ## UNDEFINED # HiiIsConfigHdrMatch mIp6ConfigStorageName + ## SOMETIMES_PRODUCES ## UNDEFINED # HiiConstructConfigHdr mIp6ConfigStorageName + ## SOMETIMES_PRODUCES ## UNDEFINED # HiiAddPackages Ip6DxeStrings Ip6ConfigBin + ## SOMETIMES_CONSUMES ## UNDEFINED # HiiUpdateForm + ## SOMETIMES_CONSUMES ## HII + gIp6ConfigNvDataGuid +[UserExtensions.TianoCore."ExtraFiles"] + Ip6DxeExtra.uni diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Dxe.uni b/Core/NetworkPkg/Ip6Dxe/Ip6Dxe.uni new file mode 100644 index 0000000000..8fee315c70 Binary files /dev/null and b/Core/NetworkPkg/Ip6Dxe/Ip6Dxe.uni differ diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni b/Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni new file mode 100644 index 0000000000..4944567a75 Binary files /dev/null and b/Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni differ diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni b/Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni new file mode 100644 index 0000000000..4558dacc61 Binary files /dev/null and b/Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni differ diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c b/Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c new file mode 100644 index 0000000000..db40b81d5e --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c @@ -0,0 +1,684 @@ +/** @file + The ICMPv6 handle routines to process the ICMPv6 control messages. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[23] = { + + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_NO_ROUTE_TO_DEST + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_COMM_PROHIBITED + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_BEYOND_SCOPE + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_ADDR_UNREACHABLE + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_PORT_UNREACHABLE + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_SOURCE_ADDR_FAILED + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_ROUTE_REJECTED + }, + + { + ICMP_V6_PACKET_TOO_BIG, + ICMP_V6_DEFAULT_CODE + }, + + { + ICMP_V6_TIME_EXCEEDED, + ICMP_V6_TIMEOUT_HOP_LIMIT + }, + { + ICMP_V6_TIME_EXCEEDED, + ICMP_V6_TIMEOUT_REASSEMBLE + }, + + { + ICMP_V6_PARAMETER_PROBLEM, + ICMP_V6_ERRONEOUS_HEADER + }, + { + ICMP_V6_PARAMETER_PROBLEM, + ICMP_V6_UNRECOGNIZE_NEXT_HDR + }, + { + ICMP_V6_PARAMETER_PROBLEM, + ICMP_V6_UNRECOGNIZE_OPTION + }, + + { + ICMP_V6_ECHO_REQUEST, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_ECHO_REPLY, + ICMP_V6_DEFAULT_CODE + }, + + { + ICMP_V6_LISTENER_QUERY, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_LISTENER_REPORT, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_LISTENER_REPORT_2, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_LISTENER_DONE, + ICMP_V6_DEFAULT_CODE + }, + + { + ICMP_V6_ROUTER_SOLICIT, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_ROUTER_ADVERTISE, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_NEIGHBOR_SOLICIT, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_NEIGHBOR_ADVERTISE, + ICMP_V6_DEFAULT_CODE + }, +}; + +/** + Reply an ICMPv6 echo request. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 informational message. + @param[in] Packet The content of the ICMPv6 message with the IP head + removed. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS Successfully answered the ICMPv6 Echo request. + @retval Others Failed to answer the ICMPv6 Echo request. + +**/ +EFI_STATUS +Ip6IcmpReplyEcho ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD *Icmp; + NET_BUF *Data; + EFI_STATUS Status; + EFI_IP6_HEADER ReplyHead; + + Status = EFI_OUT_OF_RESOURCES; + // + // make a copy the packet, it is really a bad idea to + // send the MNP's buffer back to MNP. + // + Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN); + if (Data == NULL) { + goto Exit; + } + + // + // Change the ICMP type to echo reply, exchange the source + // and destination, then send it. The source is updated to + // use specific destination. See RFC1122. SRR/RR option + // update is omitted. + // + Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Data, 0, NULL); + if (Icmp == NULL) { + NetbufFree (Data); + goto Exit; + } + + Icmp->Head.Type = ICMP_V6_ECHO_REPLY; + Icmp->Head.Checksum = 0; + + // + // Generate the IPv6 basic header + // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address, + // the Source address of the Echo Reply must be the same address. + // + ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER)); + + ReplyHead.PayloadLength = HTONS ((UINT16) (Packet->TotalSize)); + ReplyHead.NextHeader = IP6_ICMP; + ReplyHead.HopLimit = IpSb->CurHopLimit; + IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress); + + if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) { + IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress); + } + + // + // If source is unspecified, Ip6Output will select a source for us + // + Status = Ip6Output ( + IpSb, + NULL, + NULL, + Data, + &ReplyHead, + NULL, + 0, + Ip6SysPacketSent, + NULL + ); + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process Packet Too Big message sent by a router in response to a packet that + it cannot forward because the packet is larger than the MTU of outgoing link. + Since this driver already uses IPv6 minimum link MTU as the maximum packet size, + if Packet Too Big message is still received, do not reduce the packet size, but + rather include a Fragment header in the subsequent packets. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 error packet. + @param[in] Packet The content of the ICMPv6 error with the IP head + removed. + + @retval EFI_SUCCESS The ICMPv6 error processed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of + resource. + @retval EFI_NOT_FOUND The packet too big message is not sent to us. + +**/ +EFI_STATUS +Ip6ProcessPacketTooBig ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_ERROR_HEAD Icmp; + UINT32 Mtu; + IP6_ROUTE_ENTRY *RouteEntry; + EFI_IPv6_ADDRESS *DestAddress; + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + Mtu = NTOHL (Icmp.Fourth); + DestAddress = &Icmp.IpHead.DestinationAddress; + + if (Mtu < IP6_MIN_LINK_MTU) { + // + // Normally the multicast address is considered to be on-link and not recorded + // in route table. Here it is added into the table since the MTU information + // need be recorded. + // + if (IP6_IS_MULTICAST (DestAddress)) { + RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL); + if (RouteEntry == NULL) { + NetbufFree (Packet); + return EFI_OUT_OF_RESOURCES; + } + + RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG; + InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link); + IpSb->RouteTable->TotalNum++; + } else { + RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL); + if (RouteEntry == NULL) { + NetbufFree (Packet); + return EFI_NOT_FOUND; + } + + RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG; + + Ip6FreeRouteEntry (RouteEntry); + } + } + + NetbufFree (Packet); + return EFI_SUCCESS; +} + +/** + Process the ICMPv6 error packet, and deliver the packet to upper layer. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 error packet. + @param[in] Packet The content of the ICMPv6 error with the IP head + removed. + + @retval EFI_SUCCESS The ICMPv6 error processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessIcmpError ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_ERROR_HEAD Icmp; + + // + // Check the validity of the packet + // + if (Packet->TotalSize < sizeof (Icmp)) { + goto DROP; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) { + return Ip6ProcessPacketTooBig (IpSb, Head, Packet); + } + + // + // Notify the upper-layer process that an ICMPv6 eror message is received. + // + IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR; + return Ip6Demultiplex (IpSb, Head, Packet); + +DROP: + NetbufFree (Packet); + Packet = NULL; + return EFI_INVALID_PARAMETER; +} + +/** + Process the ICMPv6 informational messages. If it is an ICMPv6 echo + request, answer it. If it is a MLD message, trigger MLD routines to + process it. If it is a ND message, trigger ND routines to process it. + Otherwise, deliver it to upper layer. + + @param[in] IpSb The IP service that receivd the packet. + @param[in] Head The IP head of the ICMPv6 informational packet. + @param[in] Packet The content of the ICMPv6 informational packet + with IP head removed. + + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_SUCCESS The ICMPv6 informational message processed. + @retval Others Failed to process ICMPv6 informational message. + +**/ +EFI_STATUS +Ip6ProcessIcmpInformation ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE); + ASSERT (Head != NULL); + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + Status = EFI_INVALID_PARAMETER; + + switch (Icmp.Head.Type) { + case ICMP_V6_ECHO_REQUEST: + // + // If ICMPv6 echo, reply it + // + if (Icmp.Head.Code == 0) { + Status = Ip6IcmpReplyEcho (IpSb, Head, Packet); + } + break; + case ICMP_V6_LISTENER_QUERY: + Status = Ip6ProcessMldQuery (IpSb, Head, Packet); + break; + case ICMP_V6_LISTENER_REPORT: + case ICMP_V6_LISTENER_REPORT_2: + Status = Ip6ProcessMldReport (IpSb, Head, Packet); + break; + case ICMP_V6_NEIGHBOR_SOLICIT: + Status = Ip6ProcessNeighborSolicit (IpSb, Head, Packet); + break; + case ICMP_V6_NEIGHBOR_ADVERTISE: + Status = Ip6ProcessNeighborAdvertise (IpSb, Head, Packet); + break; + case ICMP_V6_ROUTER_ADVERTISE: + Status = Ip6ProcessRouterAdvertise (IpSb, Head, Packet); + break; + case ICMP_V6_REDIRECT: + Status = Ip6ProcessRedirect (IpSb, Head, Packet); + break; + case ICMP_V6_ECHO_REPLY: + Status = Ip6Demultiplex (IpSb, Head, Packet); + break; + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + return Status; +} + +/** + Handle the ICMPv6 packet. First validate the message format, + then, according to the message types, process it as an informational packet or + an error packet. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 packet with IP head + removed. + + @retval EFI_INVALID_PARAMETER The packet is malformated. + @retval EFI_SUCCESS The ICMPv6 message successfully processed. + @retval Others Failed to handle the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6IcmpHandle ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_HEAD Icmp; + UINT16 PseudoCheckSum; + UINT16 CheckSum; + + // + // Check the validity of the incoming packet. + // + if (Packet->TotalSize < sizeof (Icmp)) { + goto DROP; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + // + // Make sure checksum is valid. + // + PseudoCheckSum = NetIp6PseudoHeadChecksum ( + &Head->SourceAddress, + &Head->DestinationAddress, + IP6_ICMP, + Packet->TotalSize + ); + CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet)); + if (CheckSum != 0) { + goto DROP; + } + + // + // According to the packet type, call corresponding process + // + if (Icmp.Type <= ICMP_V6_ERROR_MAX) { + return Ip6ProcessIcmpError (IpSb, Head, Packet); + } else { + return Ip6ProcessIcmpInformation (IpSb, Head, Packet); + } + +DROP: + NetbufFree (Packet); + return EFI_INVALID_PARAMETER; +} + +/** + Retrieve the Prefix address according to the PrefixLength by clear the useless + bits. + + @param[in] PrefixLength The prefix length of the prefix. + @param[in, out] Prefix On input, points to the original prefix address + with dirty bits; on output, points to the updated + address with useless bit clear. + +**/ +VOID +Ip6GetPrefix ( + IN UINT8 PrefixLength, + IN OUT EFI_IPv6_ADDRESS *Prefix + ) +{ + UINT8 Byte; + UINT8 Bit; + UINT8 Mask; + UINT8 Value; + + ASSERT ((Prefix != NULL) && (PrefixLength < IP6_PREFIX_NUM)); + + if (PrefixLength == 0) { + ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS)); + return ; + } + + if (PrefixLength == IP6_PREFIX_NUM - 1) { + return ; + } + + Byte = (UINT8) (PrefixLength / 8); + Bit = (UINT8) (PrefixLength % 8); + Value = Prefix->Addr[Byte]; + + if ((Byte > 0) && (Byte < 16)) { + ZeroMem (Prefix->Addr + Byte, 16 - Byte); + } + + if (Bit > 0) { + Mask = (UINT8) (0xFF << (8 - Bit)); + Prefix->Addr[Byte] = (UINT8) (Value & Mask); + } + +} + +/** + Check whether the DestinationAddress is an anycast address. + + @param[in] IpSb The IP service that received the packet. + @param[in] DestinationAddress Points to the Destination Address of the packet. + + @retval TRUE The DestinationAddress is anycast address. + @retval FALSE The DestinationAddress is not anycast address. + +**/ +BOOLEAN +Ip6IsAnycast ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *DestinationAddress + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + EFI_IPv6_ADDRESS Prefix; + BOOLEAN Flag; + + ZeroMem (&Prefix, sizeof (EFI_IPv6_ADDRESS)); + + Flag = FALSE; + + // + // If the address is known as on-link or autonomous prefix, record it as + // anycast address. + // + do { + PrefixEntry = Ip6FindPrefixListEntry (IpSb, Flag, 255, DestinationAddress); + if (PrefixEntry != NULL) { + IP6_COPY_ADDRESS (&Prefix, &PrefixEntry->Prefix); + Ip6GetPrefix (PrefixEntry->PrefixLength, &Prefix); + if (EFI_IP6_EQUAL (&Prefix, DestinationAddress)) { + return TRUE; + } + } + + Flag = (BOOLEAN) !Flag; + } while (Flag); + + return FALSE; +} + +/** + Generate ICMPv6 error message and send it out to DestinationAddress. Currently + Destination Unreachable message, Time Exceeded message and Parameter Problem + message are supported. + + @param[in] IpSb The IP service that received the packet. + @param[in] Packet The packet which invoking ICMPv6 error. + @param[in] SourceAddress If not NULL, points to the SourceAddress. + Otherwise, the IP layer will select a source address + according to the DestinationAddress. + @param[in] DestinationAddress Points to the Destination Address of the ICMPv6 + error message. + @param[in] Type The type of the ICMPv6 message. + @param[in] Code The additional level of the ICMPv6 message. + @param[in] Pointer If not NULL, identifies the octet offset within + the invoking packet where the error was detected. + + @retval EFI_INVALID_PARAMETER The packet is malformated. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the + operation. + @retval EFI_SUCCESS The ICMPv6 message was successfully sent out. + @retval Others Failed to generate the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6SendIcmpError ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN UINT8 Type, + IN UINT8 Code, + IN UINT32 *Pointer OPTIONAL + ) +{ + UINT32 PacketLen; + NET_BUF *ErrorMsg; + UINT16 PayloadLen; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + UINT8 *ErrorBody; + + if (DestinationAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // An ICMPv6 error message must not be originated as a result of receiving + // a packet whose source address does not uniquely identify a single node -- + // e.g., the IPv6 Unspecified Address, an IPv6 multicast address, or an address + // known by the ICMP message originator to be an IPv6 anycast address. + // + if (NetIp6IsUnspecifiedAddr (DestinationAddress) || + IP6_IS_MULTICAST (DestinationAddress) || + Ip6IsAnycast (IpSb, DestinationAddress) + ) { + return EFI_INVALID_PARAMETER; + } + + switch (Type) { + case ICMP_V6_DEST_UNREACHABLE: + case ICMP_V6_TIME_EXCEEDED: + break; + + case ICMP_V6_PARAMETER_PROBLEM: + if (Pointer == NULL) { + return EFI_INVALID_PARAMETER; + } + + break; + + default: + return EFI_INVALID_PARAMETER; + } + + PacketLen = sizeof (IP6_ICMP_ERROR_HEAD) + Packet->TotalSize; + + if (PacketLen > IpSb->MaxPacketSize) { + PacketLen = IpSb->MaxPacketSize; + } + + ErrorMsg = NetbufAlloc (PacketLen); + if (ErrorMsg == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + PayloadLen = (UINT16) (PacketLen - sizeof (EFI_IP6_HEADER)); + + // + // Create the basic IPv6 header. + // + ZeroMem (&Head, sizeof (EFI_IP6_HEADER)); + + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IpSb->CurHopLimit; + + if (SourceAddress != NULL) { + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + } else { + ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + + NetbufReserve (ErrorMsg, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP error message head + // + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (ErrorMsg, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + if (IcmpHead == NULL) { + NetbufFree (ErrorMsg); + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = Type; + IcmpHead->Head.Code = Code; + + if (Pointer != NULL) { + IcmpHead->Fourth = HTONL (*Pointer); + } + + // + // Fill in the ICMP error message body + // + PayloadLen -= sizeof (IP6_ICMP_INFORMATION_HEAD); + ErrorBody = NetbufAllocSpace (ErrorMsg, PayloadLen, FALSE); + if (ErrorBody != NULL) { + ZeroMem (ErrorBody, PayloadLen); + NetbufCopy (Packet, 0, PayloadLen, ErrorBody); + } + + // + // Transmit the packet + // + return Ip6Output (IpSb, NULL, NULL, ErrorMsg, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Icmp.h b/Core/NetworkPkg/Ip6Dxe/Ip6Icmp.h new file mode 100644 index 0000000000..6852ae5490 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Icmp.h @@ -0,0 +1,108 @@ +/** @file + Header file for ICMPv6 protocol. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_ICMP_H__ +#define __EFI_IP6_ICMP_H__ + +#define ICMP_V6_DEFAULT_CODE 0 + +#define ICMP_V6_ERROR_MAX 127 + +// +// ICMPv6 message classes, each class of ICMPv6 message shares +// a common message format. INVALID_MESSAGE is only a flag. +// +#define ICMP_V6_INVALID_MESSAGE 0 +#define ICMP_V6_ERROR_MESSAGE 1 +#define ICMP_V6_INFORMATION_MESSAGE 2 + + +extern EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[]; + +/** + Handle the ICMPv6 packet. First validate the message format, + then, according to the message types, process it as an informational packet or + an error packet. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 packet with IP head + removed. + + @retval EFI_INVALID_PARAMETER The packet is malformated. + @retval EFI_SUCCESS The ICMPv6 message successfully processed. + @retval Others Failed to handle the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6IcmpHandle ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Check whether the DestinationAddress is an anycast address. + + @param[in] IpSb The IP service that received the packet. + @param[in] DestinationAddress Points to the Destination Address of the packet. + + @retval TRUE The DestinationAddress is anycast address. + @retval FALSE The DestinationAddress is not anycast address. + +**/ +BOOLEAN +Ip6IsAnycast ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *DestinationAddress + ); + +/** + Generate ICMPv6 error message and send it out to DestinationAddress. Currently + Destination Unreachable message, Time Exceeded message and Parameter Problem + message are supported. + + @param[in] IpSb The IP service that received the packet. + @param[in] Packet The packet which invoking ICMPv6 error. + @param[in] SourceAddress If not NULL, points to the SourceAddress. + Otherwise, the IP layer will select a source address + according to the DestinationAddress. + @param[in] DestinationAddress Points to the Destination Address of the ICMPv6 + error message. + @param[in] Type The type of the ICMPv6 message. + @param[in] Code The additional level of the ICMPv6 message. + @param[in] Pointer If not NULL, identifies the octet offset within + the invoking packet where the error was detected. + + @retval EFI_INVALID_PARAMETER The packet is malformated. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the + operation. + @retval EFI_SUCCESS The ICMPv6 message was successfully sent out. + @retval Others Failed to generate the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6SendIcmpError ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN UINT8 Type, + IN UINT8 Code, + IN UINT32 *Pointer OPTIONAL + ); + +#endif + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6If.c b/Core/NetworkPkg/Ip6Dxe/Ip6If.c new file mode 100644 index 0000000000..280cd764f1 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6If.c @@ -0,0 +1,798 @@ +/** @file + Implement IP6 pesudo interface. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +/** + Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK. + + @param[in] Event The transmit token's event. + @param[in] Context The Context which is pointed to the token. + +**/ +VOID +EFIAPI +Ip6OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Fileter function to cancel all the frame related to an IP instance. + + @param[in] Frame The transmit request to test whether to cancel. + @param[in] Context The context which is the Ip instance that issued + the transmit. + + @retval TRUE The frame belongs to this instance and is to be + removed. + @retval FALSE The frame doesn't belong to this instance. + +**/ +BOOLEAN +Ip6CancelInstanceFrame ( + IN IP6_LINK_TX_TOKEN *Frame, + IN VOID *Context + ) +{ + if (Frame->IpInstance == (IP6_PROTOCOL *) Context) { + return TRUE; + } + + return FALSE; +} + +/** + Set the interface's address. This will trigger the DAD process for the + address to set. To set an already set address, the lifetimes wil be + updated to the new value passed in. + + @param[in] Interface The interface to set the address. + @param[in] Ip6Addr The interface's to be assigned IPv6 address. + @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast. + Otherwise, it is not anycast. + @param[in] PrefixLength The prefix length of the Ip6Addr. + @param[in] ValidLifetime The valid lifetime for this address. + @param[in] PreferredLifetime The preferred lifetime for this address. + @param[in] DadCallback The caller's callback to trigger when DAD finishes. + This is an optional parameter that may be NULL. + @param[in] Context The context that will be passed to DadCallback. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The interface is scheduled to be configured with + the specified address. + @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to + lack of resources. + +**/ +EFI_STATUS +Ip6SetAddress ( + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Ip6Addr, + IN BOOLEAN IsAnycast, + IN UINT8 PrefixLength, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN IP6_DAD_CALLBACK DadCallback OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *AddressInfo; + LIST_ENTRY *Entry; + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + UINT64 Delay; + IP6_DELAY_JOIN_LIST *DelayNode; + + NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE); + + IpSb = Interface->Service; + + if (Ip6IsOneOfSetAddress (IpSb, Ip6Addr, NULL, &AddressInfo)) { + ASSERT (AddressInfo != NULL); + // + // Update the lifetime. + // + AddressInfo->ValidLifetime = ValidLifetime; + AddressInfo->PreferredLifetime = PreferredLifetime; + + if (DadCallback != NULL) { + DadCallback (TRUE, Ip6Addr, Context); + } + + return EFI_SUCCESS; + } + + AddressInfo = (IP6_ADDRESS_INFO *) AllocatePool (sizeof (IP6_ADDRESS_INFO)); + if (AddressInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AddressInfo->Signature = IP6_ADDR_INFO_SIGNATURE; + IP6_COPY_ADDRESS (&AddressInfo->Address, Ip6Addr); + AddressInfo->IsAnycast = IsAnycast; + AddressInfo->PrefixLength = PrefixLength; + AddressInfo->ValidLifetime = ValidLifetime; + AddressInfo->PreferredLifetime = PreferredLifetime; + + if (AddressInfo->PrefixLength == 0) { + // + // Find an appropriate prefix from on-link prefixes and update the prefixlength. + // Longest prefix match is used here. + // + NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { + PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + + if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) { + AddressInfo->PrefixLength = PrefixEntry->PrefixLength; + break; + } + } + } + + if (AddressInfo->PrefixLength == 0) { + // + // If the prefix length is still zero, try the autonomous prefixes. + // Longest prefix match is used here. + // + NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) { + PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + + if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) { + AddressInfo->PrefixLength = PrefixEntry->PrefixLength; + break; + } + } + } + + if (AddressInfo->PrefixLength == 0) { + // + // BUGBUG: Stil fail, use 64 as the default prefix length. + // + AddressInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + } + + + // + // Node should delay joining the solicited-node mulitcast address by a random delay + // between 0 and MAX_RTR_SOLICITATION_DELAY (1 second). + // Thus queue the address to be processed in Duplicate Address Detection module + // after the delay time (in milliseconds). + // + Delay = (UINT64) NET_RANDOM (NetRandomInitSeed ()); + Delay = MultU64x32 (Delay, IP6_ONE_SECOND_IN_MS); + Delay = RShiftU64 (Delay, 32); + + DelayNode = (IP6_DELAY_JOIN_LIST *) AllocatePool (sizeof (IP6_DELAY_JOIN_LIST)); + if (DelayNode == NULL) { + FreePool (AddressInfo); + return EFI_OUT_OF_RESOURCES; + } + + DelayNode->DelayTime = (UINT32) (DivU64x32 (Delay, IP6_TIMER_INTERVAL_IN_MS)); + DelayNode->Interface = Interface; + DelayNode->AddressInfo = AddressInfo; + DelayNode->DadCallback = DadCallback; + DelayNode->Context = Context; + + InsertTailList (&Interface->DelayJoinList, &DelayNode->Link); + return EFI_SUCCESS; +} + +/** + Create an IP6_INTERFACE. + + @param[in] IpSb The IP6 service binding instance. + @param[in] LinkLocal If TRUE, the instance is created for link-local address. + Otherwise, it is not for a link-local address. + + @return Point to the created IP6_INTERFACE, otherwise NULL. + +**/ +IP6_INTERFACE * +Ip6CreateInterface ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN LinkLocal + ) +{ + EFI_STATUS Status; + IP6_INTERFACE *Interface; + EFI_IPv6_ADDRESS *Ip6Addr; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Interface = AllocatePool (sizeof (IP6_INTERFACE)); + if (Interface == NULL) { + return NULL; + } + + Interface->Signature = IP6_INTERFACE_SIGNATURE; + Interface->RefCnt = 1; + + InitializeListHead (&Interface->AddressList); + Interface->AddressCount = 0; + Interface->Configured = FALSE; + + Interface->Service = IpSb; + Interface->Controller = IpSb->Controller; + Interface->Image = IpSb->Image; + + InitializeListHead (&Interface->ArpQues); + InitializeListHead (&Interface->SentFrames); + + Interface->DupAddrDetect = IpSb->Ip6ConfigInstance.DadXmits.DupAddrDetectTransmits; + InitializeListHead (&Interface->DupAddrDetectList); + + InitializeListHead (&Interface->DelayJoinList); + + InitializeListHead (&Interface->IpInstances); + Interface->PromiscRecv = FALSE; + + if (!LinkLocal) { + return Interface; + } + + // + // Get the link local addr + // + Ip6Addr = Ip6CreateLinkLocalAddr (IpSb); + if (Ip6Addr == NULL) { + goto ON_ERROR; + } + + // + // Perform DAD - Duplicate Address Detection. + // + Status = Ip6SetAddress ( + Interface, + Ip6Addr, + FALSE, + IP6_LINK_LOCAL_PREFIX_LENGTH, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + NULL, + NULL + ); + + FreePool (Ip6Addr); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return Interface; + +ON_ERROR: + + FreePool (Interface); + return NULL; +} + +/** + Free the interface used by IpInstance. All the IP instance with + the same Ip/prefix pair share the same interface. It is reference + counted. All the frames that haven't been sent will be cancelled. + Because the IpInstance is optional, the caller must remove + IpInstance from the interface's instance list. + + @param[in] Interface The interface used by the IpInstance. + @param[in] IpInstance The IP instance that free the interface. NULL if + the IP driver is releasing the default interface. + +**/ +VOID +Ip6CleanInterface ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL + ) +{ + IP6_DAD_ENTRY *Duplicate; + IP6_DELAY_JOIN_LIST *Delay; + + NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE); + ASSERT (Interface->RefCnt > 0); + + // + // Remove all the pending transmit token related to this IP instance. + // + Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, IpInstance); + + if (--Interface->RefCnt > 0) { + return; + } + + // + // Destroy the interface if this is the last IP instance. + // Remove all the system transmitted packets + // from this interface, cancel the receive request if exists. + // + Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, NULL); + + ASSERT (IsListEmpty (&Interface->IpInstances)); + ASSERT (IsListEmpty (&Interface->ArpQues)); + ASSERT (IsListEmpty (&Interface->SentFrames)); + + while (!IsListEmpty (&Interface->DupAddrDetectList)) { + Duplicate = NET_LIST_HEAD (&Interface->DupAddrDetectList, IP6_DAD_ENTRY, Link); + NetListRemoveHead (&Interface->DupAddrDetectList); + FreePool (Duplicate); + } + + while (!IsListEmpty (&Interface->DelayJoinList)) { + Delay = NET_LIST_HEAD (&Interface->DelayJoinList, IP6_DELAY_JOIN_LIST, Link); + NetListRemoveHead (&Interface->DelayJoinList); + FreePool (Delay); + } + + Ip6RemoveAddr (Interface->Service, &Interface->AddressList, &Interface->AddressCount, NULL, 0); + + RemoveEntryList (&Interface->Link); + FreePool (Interface); +} + +/** + Create and wrap a transmit request into a newly allocated IP6_LINK_TX_TOKEN. + + @param[in] Interface The interface to send out from. + @param[in] IpInstance The IpInstance that transmit the packet. NULL if + the packet is sent by the IP6 driver itself. + @param[in] Packet The packet to transmit + @param[in] CallBack Call back function to execute if transmission + finished. + @param[in] Context Opaque parameter to the callback. + + @return The wrapped token if succeed or NULL. + +**/ +IP6_LINK_TX_TOKEN * +Ip6CreateLinkTxToken ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN IP6_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData; + IP6_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + UINT32 Count; + + Token = AllocatePool (sizeof (IP6_LINK_TX_TOKEN) + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA)); + + if (Token == NULL) { + return NULL; + } + + Token->Signature = IP6_LINK_TX_SIGNATURE; + InitializeListHead (&Token->Link); + + Token->IpInstance = IpInstance; + Token->CallBack = CallBack; + Token->Packet = Packet; + Token->Context = Context; + ZeroMem (&Token->DstMac, sizeof (EFI_MAC_ADDRESS)); + IP6_COPY_LINK_ADDRESS (&Token->SrcMac, &Interface->Service->SnpMode.CurrentAddress); + + MnpToken = &(Token->MnpToken); + MnpToken->Status = EFI_NOT_READY; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6OnFrameSent, + Token, + &MnpToken->Event + ); + + if (EFI_ERROR (Status)) { + FreePool (Token); + return NULL; + } + + MnpTxData = &Token->MnpTxData; + MnpToken->Packet.TxData = MnpTxData; + + MnpTxData->DestinationAddress = &Token->DstMac; + MnpTxData->SourceAddress = &Token->SrcMac; + MnpTxData->ProtocolType = IP6_ETHER_PROTO; + MnpTxData->DataLength = Packet->TotalSize; + MnpTxData->HeaderLength = 0; + + Count = Packet->BlockOpNum; + + NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count); + MnpTxData->FragmentCount = (UINT16)Count; + + return Token; +} + +/** + Free the link layer transmit token. It will close the event, + then free the memory used. + + @param[in] Token Token to free. + +**/ +VOID +Ip6FreeLinkTxToken ( + IN IP6_LINK_TX_TOKEN *Token + ) +{ + NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE); + + gBS->CloseEvent (Token->MnpToken.Event); + FreePool (Token); +} + +/** + Callback function when the received packet is freed. + Check Ip6OnFrameReceived for information. + + @param[in] Context Points to EFI_MANAGED_NETWORK_RECEIVE_DATA. + +**/ +VOID +EFIAPI +Ip6RecycleFrame ( + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData; + + RxData = (EFI_MANAGED_NETWORK_RECEIVE_DATA *) Context; + + gBS->SignalEvent (RxData->RecycleEvent); +} + +/** + Received a frame from MNP. Wrap it in net buffer then deliver + it to IP's input function. The ownship of the packet also + is transferred to IP. When Ip is finished with this packet, it + will call NetbufFree to release the packet, NetbufFree will + again call the Ip6RecycleFrame to signal MNP's event and free + the token used. + + @param[in] Context Context for the callback. + +**/ +VOID +EFIAPI +Ip6OnFrameReceivedDpc ( + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData; + IP6_LINK_RX_TOKEN *Token; + NET_FRAGMENT Netfrag; + NET_BUF *Packet; + UINT32 Flag; + IP6_SERVICE *IpSb; + + Token = (IP6_LINK_RX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP6_LINK_RX_SIGNATURE); + + // + // First clear the interface's receive request in case the + // caller wants to call Ip6ReceiveFrame in the callback. + // + IpSb = (IP6_SERVICE *) Token->Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + + MnpToken = &Token->MnpToken; + MnpRxData = MnpToken->Packet.RxData; + + if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) { + Token->CallBack (NULL, MnpToken->Status, 0, Token->Context); + return ; + } + + // + // Wrap the frame in a net buffer then deliever it to IP input. + // IP will reassemble the packet, and deliver it to upper layer + // + Netfrag.Len = MnpRxData->DataLength; + Netfrag.Bulk = MnpRxData->PacketData; + + Packet = NetbufFromExt (&Netfrag, 1, IP6_MAX_HEADLEN, 0, Ip6RecycleFrame, Token->MnpToken.Packet.RxData); + + if (Packet == NULL) { + gBS->SignalEvent (MnpRxData->RecycleEvent); + + Token->CallBack (NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context); + + return ; + } + + Flag = (MnpRxData->BroadcastFlag ? IP6_LINK_BROADCAST : 0); + Flag |= (MnpRxData->MulticastFlag ? IP6_LINK_MULTICAST : 0); + Flag |= (MnpRxData->PromiscuousFlag ? IP6_LINK_PROMISC : 0); + + Token->CallBack (Packet, EFI_SUCCESS, Flag, Token->Context); +} + +/** + Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to MNP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip6OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip6OnFrameReceivedDpc, Context); +} + +/** + Request to receive the packet from the interface. + + @param[in] CallBack Function to call when receive finished. + @param[in] IpSb Points to IP6 service binding instance. + + @retval EFI_ALREADY_STARTED There is already a pending receive request. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive. + @retval EFI_SUCCESS The recieve request has been started. + +**/ +EFI_STATUS +Ip6ReceiveFrame ( + IN IP6_FRAME_CALLBACK CallBack, + IN IP6_SERVICE *IpSb + ) +{ + EFI_STATUS Status; + IP6_LINK_RX_TOKEN *Token; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Token = &IpSb->RecvRequest; + Token->CallBack = CallBack; + Token->Context = (VOID *) IpSb; + + Status = IpSb->Mnp->Receive (IpSb->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Callback funtion when frame transmission is finished. It will + call the frame owner's callback function to tell it the result. + + @param[in] Context Context which points to the token. + +**/ +VOID +EFIAPI +Ip6OnFrameSentDpc ( + IN VOID *Context + ) +{ + IP6_LINK_TX_TOKEN *Token; + + Token = (IP6_LINK_TX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE); + + RemoveEntryList (&Token->Link); + + Token->CallBack ( + Token->Packet, + Token->MnpToken.Status, + 0, + Token->Context + ); + + Ip6FreeLinkTxToken (Token); +} + +/** + Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK. + + @param[in] Event The transmit token's event. + @param[in] Context Context which points to the token. + +**/ +VOID +EFIAPI +Ip6OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip6OnFrameSentDpc, Context); +} + +/** + Send a frame from the interface. If the next hop is a multicast address, + it is transmitted immediately. If the next hop is a unicast, + and the NextHop's MAC is not known, it will perform address resolution. + If an error occurred, the CallBack won't be called. So, the caller + must test the return value, and take action when there is an error. + + @param[in] Interface The interface to send the frame from + @param[in] IpInstance The IP child that request the transmission. + NULL if it is the IP6 driver itself. + @param[in] Packet The packet to transmit. + @param[in] NextHop The immediate destination to transmit the packet to. + @param[in] CallBack Function to call back when transmit finished. + @param[in] Context Opaque parameter to the callback. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame. + @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop. + @retval EFI_SUCCESS The packet successfully transmitted. + +**/ +EFI_STATUS +Ip6SendFrame ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *NextHop, + IN IP6_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + IP6_SERVICE *IpSb; + IP6_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + IP6_NEIGHBOR_ENTRY *NeighborCache; + LIST_ENTRY *Entry; + IP6_NEIGHBOR_ENTRY *ArpQue; + + IpSb = Interface->Service; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // Only when link local address is performing DAD, the interface could be used in unconfigured. + // + if (IpSb->LinkLocalOk) { + ASSERT (Interface->Configured); + } + + Token = Ip6CreateLinkTxToken (Interface, IpInstance, Packet, CallBack, Context); + + if (Token == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (IP6_IS_MULTICAST (NextHop)) { + Status = Ip6GetMulticastMac (IpSb->Mnp, NextHop, &Token->DstMac); + if (EFI_ERROR (Status)) { + goto Error; + } + + goto SendNow; + } + + // + // If send to itself, directly send out + // + if (EFI_IP6_EQUAL (&Packet->Ip.Ip6->DestinationAddress, &Packet->Ip.Ip6->SourceAddress)) { + IP6_COPY_LINK_ADDRESS (&Token->DstMac, &IpSb->SnpMode.CurrentAddress); + goto SendNow; + } + + // + // If unicast, check the neighbor state. + // + + NeighborCache = Ip6FindNeighborEntry (IpSb, NextHop); + ASSERT (NeighborCache != NULL); + + if (NeighborCache->Interface == NULL) { + NeighborCache->Interface = Interface; + } + + switch (NeighborCache->State) { + case EfiNeighborStale: + NeighborCache->State = EfiNeighborDelay; + NeighborCache->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME); + // + // Fall through + // + case EfiNeighborReachable: + case EfiNeighborDelay: + case EfiNeighborProbe: + IP6_COPY_LINK_ADDRESS (&Token->DstMac, &NeighborCache->LinkAddress); + goto SendNow; + break; + + default: + break; + } + + // + // Have to do asynchronous ARP resolution. First check whether there is + // already a pending request. + // + NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList); + if (ArpQue == NeighborCache) { + InsertTailList (&NeighborCache->Frames, &Token->Link); + NeighborCache->ArpFree = TRUE; + return EFI_SUCCESS; + } + } + + // + // First frame requires ARP. + // + InsertTailList (&NeighborCache->Frames, &Token->Link); + InsertTailList (&Interface->ArpQues, &NeighborCache->ArpList); + + NeighborCache->ArpFree = TRUE; + + return EFI_SUCCESS; + +SendNow: + // + // Insert the tx token into the SentFrames list before calling Mnp->Transmit. + // Remove it if the returned status is not EFI_SUCCESS. + // + InsertTailList (&Interface->SentFrames, &Token->Link); + Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + RemoveEntryList (&Token->Link); + goto Error; + } + + return EFI_SUCCESS; + +Error: + Ip6FreeLinkTxToken (Token); + return Status; +} + +/** + The heartbeat timer of IP6 service instance. It times out + all of its IP6 children's received-but-not-delivered and + transmitted-but-not-recycle packets. + + @param[in] Event The IP6 service instance's heartbeat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_SERVICE *IpSb; + + IpSb = (IP6_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Ip6PacketTimerTicking (IpSb); + Ip6NdTimerTicking (IpSb); + Ip6MldTimerTicking (IpSb); +} diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6If.h b/Core/NetworkPkg/Ip6Dxe/Ip6If.h new file mode 100644 index 0000000000..736035ec39 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6If.h @@ -0,0 +1,267 @@ +/** @file + Definition for IP6 pesudo interface structure. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_IF_H__ +#define __EFI_IP6_IF_H__ + +#define IP6_LINK_RX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'R') +#define IP6_LINK_TX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'T') +#define IP6_INTERFACE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'I') +#define IP6_ADDR_INFO_SIGNATURE SIGNATURE_32 ('I', 'P', 'A', 'I') + +// +// This prototype is used by both receive and transmission. +// When receiving Netbuf is allocated by IP6_INTERFACE, and +// released by IP6. Flag shows whether the frame is received +// as unicast/multicast/anycast... +// +// When transmitting, the Netbuf is from IP6, and provided +// to the callback as a reference. Flag isn't used. +// +// IpInstance can be NULL which means that it is the IP6 driver +// itself sending the packets. IP6 driver may send packets that +// don't belong to any instance, such as ICMP errors, ICMP +// informational packets. IpInstance is used as a tag in +// this module. +// +typedef +VOID +(*IP6_FRAME_CALLBACK) ( + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ); + +// +// Each receive request is wrapped in an IP6_LINK_RX_TOKEN. +// Upon completion, the Callback will be called. Only one +// receive request is send to MNP. IpInstance is always NULL. +// Reference MNP's spec for information. +// +typedef struct { + UINT32 Signature; + IP6_FRAME_CALLBACK CallBack; + VOID *Context; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken; +} IP6_LINK_RX_TOKEN; + +// +// Each transmit request is wrapped in an IP6_LINK_TX_TOKEN. +// Upon completion, the Callback will be called. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + IP6_PROTOCOL *IpInstance; + IP6_FRAME_CALLBACK CallBack; + NET_BUF *Packet; + VOID *Context; + + EFI_MAC_ADDRESS DstMac; + EFI_MAC_ADDRESS SrcMac; + + EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA MnpTxData; +} IP6_LINK_TX_TOKEN; + +struct _IP6_ADDRESS_INFO { + UINT32 Signature; + LIST_ENTRY Link; + EFI_IPv6_ADDRESS Address; + BOOLEAN IsAnycast; + UINT8 PrefixLength; + UINT32 ValidLifetime; + UINT32 PreferredLifetime; +}; + +// +// Callback to select which frame to cancel. Caller can cancel a +// single frame, or all the frame from an IP instance. +// +typedef +BOOLEAN +(*IP6_FRAME_TO_CANCEL) ( + IP6_LINK_TX_TOKEN *Frame, + VOID *Context + ); + +struct _IP6_INTERFACE { + UINT32 Signature; + LIST_ENTRY Link; + INTN RefCnt; + + // + // IP address and prefix length of the interface. The fileds + // are invalid if (Configured == FALSE) + // + LIST_ENTRY AddressList; + UINT32 AddressCount; + BOOLEAN Configured; + + IP6_SERVICE *Service; + + EFI_HANDLE Controller; + EFI_HANDLE Image; + + + // + // Queues to keep the frames sent and waiting ARP request. + // + LIST_ENTRY ArpQues; + LIST_ENTRY SentFrames; + + + // + // The interface's configuration variables + // + UINT32 DupAddrDetect; + LIST_ENTRY DupAddrDetectList; + LIST_ENTRY DelayJoinList; + + // + // All the IP instances that have the same IP/SubnetMask are linked + // together through IpInstances. If any of the instance enables + // promiscuous receive, PromiscRecv is true. + // + LIST_ENTRY IpInstances; + BOOLEAN PromiscRecv; +}; + +/** + Create an IP6_INTERFACE. + + @param[in] IpSb The IP6 service binding instance. + @param[in] LinkLocal If TRUE, the instance is created for link-local address. + Otherwise, it is not for a link-local address. + + @return Point to the created IP6_INTERFACE, otherwise NULL. + +**/ +IP6_INTERFACE * +Ip6CreateInterface ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN LinkLocal + ); + +/** + Free the interface used by IpInstance. All the IP instance with + the same Ip/prefix pair share the same interface. It is reference + counted. All the frames that haven't been sent will be cancelled. + Because the IpInstance is optional, the caller must remove + IpInstance from the interface's instance list. + + @param[in] Interface The interface used by the IpInstance. + @param[in] IpInstance The IP instance that free the interface. NULL if + the IP driver is releasing the default interface. + +**/ +VOID +Ip6CleanInterface ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL + ); + +/** + Free the link layer transmit token. It will close the event + then free the memory used. + + @param[in] Token Token to free. + +**/ +VOID +Ip6FreeLinkTxToken ( + IN IP6_LINK_TX_TOKEN *Token + ); + +/** + Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK + + @param Event The receive event delivered to MNP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip6OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Request to receive the packet from the interface. + + @param[in] CallBack Function to call when the receive finished. + @param[in] IpSb Points to the IP6 service binding instance. + + @retval EFI_ALREADY_STARTED There is already a pending receive request. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to receive. + @retval EFI_SUCCESS The recieve request has been started. + +**/ +EFI_STATUS +Ip6ReceiveFrame ( + IN IP6_FRAME_CALLBACK CallBack, + IN IP6_SERVICE *IpSb + ); + +/** + Send a frame from the interface. If the next hop is multicast address, + it is transmitted immediately. If the next hop is a unicast, + and the NextHop's MAC is not known, it will perform address resolution. + If some error happened, the CallBack won't be called. So, the caller + must test the return value, and take action when there is an error. + + @param[in] Interface The interface to send the frame from + @param[in] IpInstance The IP child that request the transmission. + NULL if it is the IP6 driver itself. + @param[in] Packet The packet to transmit. + @param[in] NextHop The immediate destination to transmit the packet to. + @param[in] CallBack Function to call back when transmit finished. + @param[in] Context Opaque parameter to the call back. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame. + @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop. + @retval EFI_SUCCESS The packet successfully transmitted. + +**/ +EFI_STATUS +Ip6SendFrame ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *NextHop, + IN IP6_FRAME_CALLBACK CallBack, + IN VOID *Context + ); + +/** + The heartbeat timer of IP6 service instance. It times out + all of its IP6 children's received-but-not-delivered and + transmitted-but-not-recycle packets. + + @param[in] Event The IP6 service instance's heart beat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Impl.c b/Core/NetworkPkg/Ip6Dxe/Ip6Impl.c new file mode 100644 index 0000000000..b186c0af20 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Impl.c @@ -0,0 +1,1847 @@ +/** @file + Implementation of EFI_IP6_PROTOCOL protocol interfaces. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +EFI_IPSEC2_PROTOCOL *mIpSec = NULL; + +EFI_IP6_PROTOCOL mEfiIp6ProtocolTemplete = { + EfiIp6GetModeData, + EfiIp6Configure, + EfiIp6Groups, + EfiIp6Routes, + EfiIp6Neighbors, + EfiIp6Transmit, + EfiIp6Receive, + EfiIp6Cancel, + EfiIp6Poll +}; + +/** + Gets the current operational settings for this instance of the EFI IPv6 Protocol driver. + + The GetModeData() function returns the current operational mode data for this driver instance. + The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to + retrieve the operational mode data of underlying networks or drivers. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[out] Ip6ModeData Pointer to the EFI IPv6 Protocol mode data structure. + @param[out] MnpConfigData Pointer to the managed network configuration data structure. + @param[out] SnpModeData Pointer to the simple network mode data structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIp6GetModeData ( + IN EFI_IP6_PROTOCOL *This, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + IP6_INTERFACE *IpIf; + EFI_IP6_CONFIG_DATA *Config; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + IpIf = IpInstance->Interface; + + if (IpSb->LinkLocalDadFail) { + return EFI_INVALID_PARAMETER; + } + + if (Ip6ModeData != NULL) { + // + // IsStarted is "whether the EfiIp6Configure has been called". + // IsConfigured is "whether the station address has been configured" + // + Ip6ModeData->IsStarted = (BOOLEAN) (IpInstance->State == IP6_STATE_CONFIGED); + Ip6ModeData->MaxPacketSize = IpSb->MaxPacketSize; + CopyMem (&Ip6ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP6_CONFIG_DATA)); + Ip6ModeData->IsConfigured = FALSE; + + Ip6ModeData->AddressCount = 0; + Ip6ModeData->AddressList = NULL; + + Ip6ModeData->GroupCount = IpInstance->GroupCount; + Ip6ModeData->GroupTable = NULL; + + Ip6ModeData->RouteCount = 0; + Ip6ModeData->RouteTable = NULL; + + Ip6ModeData->NeighborCount = 0; + Ip6ModeData->NeighborCache = NULL; + + Ip6ModeData->PrefixCount = 0; + Ip6ModeData->PrefixTable = NULL; + + Ip6ModeData->IcmpTypeCount = 23; + Ip6ModeData->IcmpTypeList = AllocateCopyPool ( + Ip6ModeData->IcmpTypeCount * sizeof (EFI_IP6_ICMP_TYPE), + mIp6SupportedIcmp + ); + if (Ip6ModeData->IcmpTypeList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Return the currently configured IPv6 addresses and corresponding prefix lengths. + // + Status = Ip6BuildEfiAddressList ( + IpSb, + &Ip6ModeData->AddressCount, + &Ip6ModeData->AddressList + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Return the current station address for this IP child. + // If UseAnyStationAddress is set to TRUE, IP6 driver will + // select a source address from its address list. Otherwise use the + // StationAddress in config data. + // + if (Ip6ModeData->IsStarted) { + Config = &Ip6ModeData->ConfigData; + + if (IpIf->Configured || NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) { + Ip6ModeData->IsConfigured = TRUE; + } else { + Ip6ModeData->IsConfigured = FALSE; + } + + // + // Build a EFI route table for user from the internal route table. + // + Status = Ip6BuildEfiRouteTable ( + IpSb->RouteTable, + &Ip6ModeData->RouteCount, + &Ip6ModeData->RouteTable + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + } + + if (Ip6ModeData->IsConfigured) { + // + // Return the joined multicast group addresses. + // + if (IpInstance->GroupCount != 0) { + Ip6ModeData->GroupTable = AllocateCopyPool ( + IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS), + IpInstance->GroupList + ); + if (Ip6ModeData->GroupTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + } + // + // Return the neighbor cache entries + // + Status = Ip6BuildEfiNeighborCache ( + IpInstance, + &Ip6ModeData->NeighborCount, + &Ip6ModeData->NeighborCache + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Return the prefix table entries + // + Status = Ip6BuildPrefixTable ( + IpInstance, + &Ip6ModeData->PrefixCount, + &Ip6ModeData->PrefixTable + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + } + } + + // + // Get fresh mode data from MNP, since underlying media status may change + // + Status = IpSb->Mnp->GetModeData (IpSb->Mnp, MnpConfigData, SnpModeData); + + goto Exit; + +Error: + if (Ip6ModeData != NULL) { + if (Ip6ModeData->AddressList != NULL) { + FreePool (Ip6ModeData->AddressList); + } + + if (Ip6ModeData->GroupTable != NULL) { + FreePool (Ip6ModeData->GroupTable); + } + + if (Ip6ModeData->RouteTable != NULL) { + FreePool (Ip6ModeData->RouteTable); + } + + if (Ip6ModeData->NeighborCache != NULL) { + FreePool (Ip6ModeData->NeighborCache); + } + + if (Ip6ModeData->PrefixTable != NULL) { + FreePool (Ip6ModeData->PrefixTable); + } + + if (Ip6ModeData->IcmpTypeList != NULL) { + FreePool (Ip6ModeData->IcmpTypeList); + } + } + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Validate that Ipv6 address is OK to be used as station address or next hop address/ neighbor. + + @param[in] IpSb The IP6 service instance. + @param[in] Ip The IPv6 address to validate. + @param[in] Flag If TRUE, validate if the address is OK to be used + as station address. If FALSE, validate if the + address is OK to be used as the next hop address/ + neighbor. + + @retval TRUE The Ip address is valid and could be used. + @retval FALSE Invalid Ip address. + +**/ +BOOLEAN +Ip6IsValidAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip, + IN BOOLEAN Flag + ) +{ + if (!NetIp6IsUnspecifiedAddr (Ip)) { + if (!NetIp6IsValidUnicast(Ip)) { + return FALSE; + } + if (Ip6IsOneOfSetAddress (IpSb, Ip, NULL, NULL)) { + return Flag; + } + } else { + return Flag; + } + + return (BOOLEAN) !Flag; +} + +/** + Validate whether the value of protocol is illegal or not. Protocol is the 'Next Header' field + in the last IPv6 extension header, or basic IPv6 header is there's no extension header. + + @param[in] Protocol Default value of 'Next Header' + + @retval TRUE The protocol is illegal. + @retval FALSE The protocol is legal. + +**/ +BOOLEAN +Ip6IsIllegalProtocol ( + IN UINT8 Protocol + ) +{ + if (Protocol == IP6_HOP_BY_HOP || Protocol == EFI_IP_PROTO_ICMP || Protocol == IP4_PROTO_IGMP) { + return TRUE; + } + + if (Protocol == 41 || Protocol == 43 || Protocol == 44 || Protocol == 59 || Protocol == 60 || Protocol == 124) { + return TRUE; + } + + return FALSE; +} + +/** + Intiialize the IP6_PROTOCOL structure to the unconfigured states. + + @param[in] IpSb The IP6 service instance. + @param[in, out] IpInstance The IP6 child instance. + +**/ +VOID +Ip6InitProtocol ( + IN IP6_SERVICE *IpSb, + IN OUT IP6_PROTOCOL *IpInstance + ) +{ + ASSERT ((IpSb != NULL) && (IpInstance != NULL)); + + ZeroMem (IpInstance, sizeof (IP6_PROTOCOL)); + + IpInstance->Signature = IP6_PROTOCOL_SIGNATURE; + IpInstance->State = IP6_STATE_UNCONFIGED; + IpInstance->Service = IpSb; + IpInstance->GroupList = NULL; + CopyMem (&IpInstance->Ip6Proto, &mEfiIp6ProtocolTemplete, sizeof (EFI_IP6_PROTOCOL)); + + NetMapInit (&IpInstance->RxTokens); + NetMapInit (&IpInstance->TxTokens); + InitializeListHead (&IpInstance->Received); + InitializeListHead (&IpInstance->Delivered); + + EfiInitializeLock (&IpInstance->RecycleLock, TPL_NOTIFY); +} + +/** + Configure the IP6 child. If the child is already configured, + change the configuration parameter. Otherwise, configure it + for the first time. The caller should validate the configuration + before deliver them to it. It also don't do configure NULL. + + @param[in, out] IpInstance The IP6 child to configure. + @param[in] Config The configure data. + + @retval EFI_SUCCESS The IP6 child is successfully configured. + @retval EFI_DEVICE_ERROR Failed to free the pending transive or to + configure underlying MNP, or other errors. + @retval EFI_NO_MAPPING The IP6 child is configured to use the default + address, but the default address hasn't been + configured. The IP6 child doesn't need to be + reconfigured when the default address is configured. + @retval EFI_OUT_OF_RESOURCES No more memory space is available. + @retval other Other error occurs. + +**/ +EFI_STATUS +Ip6ConfigProtocol ( + IN OUT IP6_PROTOCOL *IpInstance, + IN EFI_IP6_CONFIG_DATA *Config + ) +{ + IP6_SERVICE *IpSb; + IP6_INTERFACE *IpIf; + EFI_STATUS Status; + EFI_IP6_CONFIG_DATA *Current; + IP6_ADDRESS_INFO *AddressInfo; + BOOLEAN StationZero; + BOOLEAN DestZero; + EFI_IPv6_ADDRESS Source; + BOOLEAN AddrOk; + + IpSb = IpInstance->Service; + Current = &IpInstance->ConfigData; + + // + // User is changing packet filters. It must be stopped + // before the station address can be changed. + // + if (IpInstance->State == IP6_STATE_CONFIGED) { + // + // Cancel all the pending transmit/receive from upper layer + // + Status = Ip6Cancel (IpInstance, NULL); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA)); + return EFI_SUCCESS; + } + + // + // Set up the interface. + // + StationZero = NetIp6IsUnspecifiedAddr (&Config->StationAddress); + DestZero = NetIp6IsUnspecifiedAddr (&Config->DestinationAddress); + + if (StationZero && DestZero) { + // + // StationAddress is still zero. + // + + NET_GET_REF (IpSb->DefaultInterface); + IpInstance->Interface = IpSb->DefaultInterface; + InsertTailList (&IpSb->DefaultInterface->IpInstances, &IpInstance->AddrLink); + + CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA)); + IpInstance->State = IP6_STATE_CONFIGED; + + return EFI_SUCCESS; + } + + if (StationZero && !DestZero) { + Status = Ip6SelectSourceAddress (IpSb, &Config->DestinationAddress, &Source); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + IP6_COPY_ADDRESS (&Source, &Config->StationAddress); + } + + AddrOk = Ip6IsOneOfSetAddress (IpSb, &Source, &IpIf, &AddressInfo); + if (AddrOk) { + if (AddressInfo != NULL) { + IpInstance->PrefixLength = AddressInfo->PrefixLength; + } else { + IpInstance->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + } + } else { + // + // The specified source address is not one of the addresses IPv6 maintains. + // + return EFI_INVALID_PARAMETER; + } + + + NET_GET_REF (IpIf); + IpInstance->Interface = IpIf; + InsertTailList (&IpIf->IpInstances, &IpInstance->AddrLink); + + CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA)); + IP6_COPY_ADDRESS (&Current->StationAddress, &Source); + IpInstance->State = IP6_STATE_CONFIGED; + + return EFI_SUCCESS; +} + +/** + Clean up the IP6 child, and release all the resources used by it. + + @param[in, out] IpInstance The IP6 child to clean up. + + @retval EFI_SUCCESS The IP6 child is cleaned up. + @retval EFI_DEVICE_ERROR Some resources failed to be released. + +**/ +EFI_STATUS +Ip6CleanProtocol ( + IN OUT IP6_PROTOCOL *IpInstance + ) +{ + if (EFI_ERROR (Ip6Cancel (IpInstance, NULL))) { + return EFI_DEVICE_ERROR; + } + + if (EFI_ERROR (Ip6Groups (IpInstance, FALSE, NULL))) { + return EFI_DEVICE_ERROR; + } + + // + // Some packets haven't been recycled. It is because either the + // user forgets to recycle the packets, or because the callback + // hasn't been called. Just leave it alone. + // + if (!IsListEmpty (&IpInstance->Delivered)) { + ; + } + + if (IpInstance->Interface != NULL) { + RemoveEntryList (&IpInstance->AddrLink); + Ip6CleanInterface (IpInstance->Interface, IpInstance); + IpInstance->Interface = NULL; + } + + if (IpInstance->GroupList != NULL) { + FreePool (IpInstance->GroupList); + IpInstance->GroupList = NULL; + IpInstance->GroupCount = 0; + } + + NetMapClean (&IpInstance->TxTokens); + + NetMapClean (&IpInstance->RxTokens); + + return EFI_SUCCESS; +} + +/** + Configure the MNP parameter used by IP. The IP driver uses one MNP + child to transmit/receive frames. By default, it configures MNP + to receive unicast/multicast/broadcast. Also, it will enable/disable + the promiscuous receive according to whether there is IP child + enable that or not. If Force is FALSE, it will iterate through + all the IP children to check whether the promiscuous receive + setting has been changed. If it hasn't been changed, it won't + reconfigure the MNP. If Force is TRUE, the MNP is configured + whether that is changed or not. + + @param[in] IpSb The IP6 service instance that is to be changed. + @param[in] Force Force the configuration or not. + + @retval EFI_SUCCESS The MNP successfully configured/reconfigured. + @retval Others Configuration failed. + +**/ +EFI_STATUS +Ip6ServiceConfigMnp ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN Force + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *ProtoEntry; + IP6_INTERFACE *IpIf; + IP6_PROTOCOL *IpInstance; + BOOLEAN Reconfig; + BOOLEAN PromiscReceive; + EFI_STATUS Status; + + Reconfig = FALSE; + PromiscReceive = FALSE; + + if (!Force) { + // + // Iterate through the IP children to check whether promiscuous + // receive setting has been changed. Update the interface's receive + // filter also. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + IpIf->PromiscRecv = FALSE; + + NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP6_PROTOCOL, AddrLink); + + if (IpInstance->ConfigData.AcceptPromiscuous) { + IpIf->PromiscRecv = TRUE; + PromiscReceive = TRUE; + } + } + } + + // + // If promiscuous receive isn't changed, it isn't necessary to reconfigure. + // + if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) { + return EFI_SUCCESS; + } + + Reconfig = TRUE; + IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive; + } + + Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData); + + // + // recover the original configuration if failed to set the configure. + // + if (EFI_ERROR (Status) && Reconfig) { + IpSb->MnpConfigData.EnablePromiscuousReceive = (BOOLEAN) !PromiscReceive; + } + + return Status; +} + +/** + Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance. + + The Configure() function is used to set, change, or reset the operational parameters and filter + settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic + can be sent or received by this instance. Once the parameters have been reset (by calling this + function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these + parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped + independently of each other by enabling or disabling their receive filter settings with the + Configure() function. + + If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required + to be one of the currently configured IPv6 addresses listed in the EFI IPv6 drivers, or else + EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is + unspecified, the IPv6 driver will bind a source address according to the source address selection + algorithm. Clients could frequently call GetModeData() to check get currently configured IPv6 + address list in the EFI IPv6 driver. If both Ip6ConfigData.StationAddress and + Ip6ConfigData.Destination are unspecified, when transmitting the packet afterwards, the + source address filled in each outgoing IPv6 packet is decided based on the destination of this packet. + + If operational parameters are reset or changed, any pending transmit and receive requests will be + cancelled. Their completion token status will be set to EFI_ABORTED and their events will be + signaled. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Ip6ConfigData Pointer to the EFI IPv6 Protocol configuration data structure. + If NULL, reset the configuration data. + + @retval EFI_SUCCESS The driver instance was successfully opened. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Ip6ConfigData.StationAddress is neither zero nor + a unicast IPv6 address. + - Ip6ConfigData.StationAddress is neither zero nor + one of the configured IP addresses in the EFI IPv6 driver. + - Ip6ConfigData.DefaultProtocol is illegal. + @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for + this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6 + address or prefix length can be changed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6 + Protocol driver instance was not opened. + @retval EFI_UNSUPPORTED Default protocol specified through + Ip6ConfigData.DefaulProtocol isn't supported. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Configure ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA *Ip6ConfigData OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_IP6_CONFIG_DATA *Current; + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_SERVICE *IpSb; + + // + // First, validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail && Ip6ConfigData != NULL) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = EFI_INVALID_PARAMETER; + + // + // Validate the configuration first. + // + if (Ip6ConfigData != NULL) { + // + // Check whether the station address is valid. + // + if (!Ip6IsValidAddress (IpSb, &Ip6ConfigData->StationAddress, TRUE)) { + goto Exit; + } + // + // Check whether the default protocol is valid. + // + if (Ip6IsIllegalProtocol (Ip6ConfigData->DefaultProtocol)) { + goto Exit; + } + + // + // User can only update packet filters when already configured. + // If it wants to change the station address, it must configure(NULL) + // the instance firstly. + // + if (IpInstance->State == IP6_STATE_CONFIGED) { + Current = &IpInstance->ConfigData; + + if (!EFI_IP6_EQUAL (&Current->StationAddress, &Ip6ConfigData->StationAddress)) { + Status = EFI_ALREADY_STARTED; + goto Exit; + } + + if (NetIp6IsUnspecifiedAddr (&Current->StationAddress) && IP6_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + } + } + + // + // Configure the instance or clean it up. + // + if (Ip6ConfigData != NULL) { + Status = Ip6ConfigProtocol (IpInstance, Ip6ConfigData); + } else { + Status = Ip6CleanProtocol (IpInstance); + + // + // Don't change the state if it is DESTROY, consider the following + // valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped, + // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED, + // the unload fails miserably. + // + if (IpInstance->State == IP6_STATE_CONFIGED) { + IpInstance->State = IP6_STATE_UNCONFIGED; + } + } + + // + // Update the MNP's configure data. Ip6ServiceConfigMnp will check + // whether it is necessary to reconfigure the MNP. + // + Ip6ServiceConfigMnp (IpInstance->Service, FALSE); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Joins and leaves multicast groups. + + The Groups() function is used to join and leave multicast group sessions. Joining a group will + enable reception of matching multicast packets. Leaving a group will disable reception of matching + multicast packets. Source-Specific Multicast isn't required to be supported. + + If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join the multicast group session, and FALSE to leave. + @param[in] GroupAddress Pointer to the IPv6 multicast address. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - JoinFlag is TRUE and GroupAddress is NULL. + - GroupAddress is not NULL and *GroupAddress is + not a multicast IPv6 address. + - GroupAddress is not NULL and *GroupAddress is in the + range of SSM destination address. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_OUT_OF_RESOURCES System resources could not be allocated. + @retval EFI_UNSUPPORTED This EFI IPv6 Protocol implementation does not support multicast groups. + @retval EFI_ALREADY_STARTED The group address is already in the group table (when + JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Groups ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + + if ((This == NULL) || (JoinFlag && GroupAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (GroupAddress != NULL && !IP6_IS_MULTICAST (GroupAddress)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + Status = Ip6Groups (IpInstance, JoinFlag, GroupAddress); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Adds and deletes routing table entries. + + The Routes() function adds a route to, or deletes a route from, the routing table. + + Routes are determined by comparing the leftmost PrefixLength bits of Destination with + the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the + configured station address. + + The default route is added with Destination and PrefixLegth both set to all zeros. The + default route matches all destination IPv6 addresses that do not match any other routes. + + All EFI IPv6 Protocol instances share a routing table. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to + FALSE to add this route to the routing table. Destination, + PrefixLength and Gateway are used as the key to each + route entry. + @param[in] Destination The address prefix of the subnet that needs to be routed. + This is an optional parameter that may be NULL. + @param[in] PrefixLength The prefix length of Destination. Ignored if Destination + is NULL. + @param[in] GatewayAddress The unicast gateway IPv6 address for this route. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - When DeleteRoute is TRUE, both Destination and + GatewayAddress are NULL. + - When DeleteRoute is FALSE, either Destination or + GatewayAddress is NULL. + - *GatewayAddress is not a valid unicast IPv6 address. + - *GatewayAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when + DeleteRoute is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Routes ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP6_SERVICE *IpSb; + + if ((This == NULL) || (PrefixLength >= IP6_PREFIX_NUM)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + if (IpInstance->State != IP6_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (DeleteRoute && (Destination == NULL) && (GatewayAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!DeleteRoute && (Destination == NULL || GatewayAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (GatewayAddress != NULL) { + if (!Ip6IsValidAddress (IpSb, GatewayAddress, FALSE)) { + return EFI_INVALID_PARAMETER; + } + + if (!NetIp6IsUnspecifiedAddr (GatewayAddress) && + !NetIp6IsNetEqual (GatewayAddress, &IpInstance->ConfigData.StationAddress, PrefixLength) + ) { + return EFI_INVALID_PARAMETER; + } + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Update the route table + // + if (DeleteRoute) { + Status = Ip6DelRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress); + } else { + Status = Ip6AddRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress); + } + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Add or delete Neighbor cache entries. + + The Neighbors() function is used to add, update, or delete an entry from neighbor cache. + IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as + network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network + traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not + timeout) or dynamic (will timeout). + + The implementation should follow the neighbor cache timeout mechanism which is defined in + RFC4861. The default neighbor cache timeout value should be tuned for the expected network + environment + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteFlag Set to TRUE to delete the specified cache entry, set to FALSE to + add (or update, if it already exists and Override is TRUE) the + specified cache entry. TargetIp6Address is used as the key + to find the requested cache entry. + @param[in] TargetIp6Address Pointer to the Target IPv6 address. + @param[in] TargetLinkAddress Pointer to the link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache, it will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, EFI_ACCESS_DENIED + will be returned if a corresponding cache entry already existed. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - TargetIpAddress is NULL. + - *TargetLinkAddress is invalid when not NULL. + - *TargetIpAddress is not a valid unicast IPv6 address. + - *TargetIpAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is + TRUE or when DeleteFlag is FALSE while + TargetLinkAddress is NULL.). + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when Override + is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Neighbors ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + + if (This == NULL || TargetIp6Address == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (NetIp6IsUnspecifiedAddr (TargetIp6Address)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + if (!Ip6IsValidAddress (IpSb, TargetIp6Address, FALSE)) { + return EFI_INVALID_PARAMETER; + } + + if (TargetLinkAddress != NULL) { + if (!Ip6IsValidLinkAddress (IpSb, TargetLinkAddress)) { + return EFI_INVALID_PARAMETER; + } + } + + if (Ip6IsOneOfSetAddress (IpSb, TargetIp6Address, NULL, NULL)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (DeleteFlag) { + Status = Ip6DelNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override); + } else { + Status = Ip6AddNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override); + } + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Check whether the user's token or event has already + been enqueue on IP6's list. + + @param[in] Map The container of either user's transmit or receive + token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP + @retval EFI_SUCCESS The current item isn't the same token/event as the + context. + +**/ +EFI_STATUS +EFIAPI +Ip6TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_IP6_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_IP6_COMPLETION_TOKEN *) Context; + TokenInItem = (EFI_IP6_COMPLETION_TOKEN *) Item->Key; + + if (Token == TokenInItem || Token->Event == TokenInItem->Event) { + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + +/** + Validate the user's token against the current station address. + + @param[in] Token User's token to validate. + + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_BAD_BUFFER_SIZE The user's option/data is too long. + @retval EFI_SUCCESS The token is OK. + +**/ +EFI_STATUS +Ip6TxTokenValid ( + IN EFI_IP6_COMPLETION_TOKEN *Token + ) +{ + EFI_IP6_TRANSMIT_DATA *TxData; + UINT32 Index; + UINT32 DataLength; + + if (Token == NULL || Token->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + TxData = Token->Packet.TxData; + + if (TxData == NULL || (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (TxData->FragmentCount == 0 || TxData->DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + for (DataLength = 0, Index = 0; Index < TxData->FragmentCount; Index++) { + if (TxData->FragmentTable[Index].FragmentLength == 0 || TxData->FragmentTable[Index].FragmentBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + DataLength += TxData->FragmentTable[Index].FragmentLength; + } + + if (TxData->DataLength != DataLength) { + return EFI_INVALID_PARAMETER; + } + + // + // TODO: Token.Packet.TxData.DataLength is too short to transmit. + // return EFI_BUFFER_TOO_SMALL; + // + + // + // If Token.Packet.TxData.DataLength is beyond the maximum that which can be + // described through the Fragment Offset field in Fragment header when performing + // fragmentation. + // + if (TxData->DataLength > 64 * 1024) { + return EFI_BAD_BUFFER_SIZE; + } + + return EFI_SUCCESS; +} + +/** + The callback function for the net buffer which wraps the user's + transmit token. Although this function seems simple, there + are some subtle aspects. + When user requests the IP to transmit a packet by passing it a + token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data + is wrapped in an net buffer. The net buffer's Free function is + set to Ip6FreeTxToken. The Token and token wrap are added to the + IP child's TxToken map. Then the buffer is passed to Ip6Output for + transmission. If an error happened before that, the buffer + is freed, which in turn frees the token wrap. The wrap may + have been added to the TxToken map or not, and the user's event + shouldn't be fired because we are still in the EfiIp6Transmit. If + the buffer has been sent by Ip6Output, it should be removed from + the TxToken map and user's event signaled. The token wrap and buffer + are bound together. Check the comments in Ip6Output for information + about IP fragmentation. + + @param[in] Context The token's wrap. + +**/ +VOID +EFIAPI +Ip6FreeTxToken ( + IN VOID *Context + ) +{ + IP6_TXTOKEN_WRAP *Wrap; + NET_MAP_ITEM *Item; + + Wrap = (IP6_TXTOKEN_WRAP *) Context; + + // + // Signal IpSecRecycleEvent to inform IPsec free the memory + // + if (Wrap->IpSecRecycleSignal != NULL) { + gBS->SignalEvent (Wrap->IpSecRecycleSignal); + } + + // + // Find the token in the instance's map. EfiIp6Transmit put the + // token to the map. If that failed, NetMapFindKey will return NULL. + // + Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token); + + if (Item != NULL) { + NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL); + } + + if (Wrap->Sent) { + gBS->SignalEvent (Wrap->Token->Event); + + // + // Dispatch the DPC queued by the NotifyFunction of Token->Event. + // + DispatchDpc (); + } + + FreePool (Wrap); +} + + +/** + The callback function to Ip6Output to update the transmit status. + + @param[in] Packet The user's transmit packet. + @param[in] IoStatus The result of the transmission. + @param[in] Flag Not used during transmission. + @param[in] Context The token's wrap. + +**/ +VOID +Ip6OnPacketSent ( + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ) +{ + IP6_TXTOKEN_WRAP *Wrap; + + // + // This is the transmission request from upper layer, + // not the IP6 driver itself. + // + Wrap = (IP6_TXTOKEN_WRAP *) Context; + Wrap->Token->Status = IoStatus; + + NetbufFree (Wrap->Packet); +} + +/** + Places outgoing data packets into the transmit queue. + + The Transmit() function places a sending request in the transmit queue of this + EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some + errors occur, the event in the token will be signaled, and the status is updated. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token Pointer to the transmit token. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing + a source address for this transmission, + but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + - Token.Packet.TxData is NULL. + - Token.Packet.ExtHdrsLength is not zero and + Token.Packet.ExtHdrs is NULL. + - Token.Packet.FragmentCount is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentLength fields is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentBuffer fields is NULL. + - Token.Packet.TxData.DataLength is zero or not + equal to the sum of fragment lengths. + - Token.Packet.TxData.DestinationAddress is non + zero when DestinationAddress is configured as + non-zero when doing Configure() for this + EFI IPv6 protocol instance. + - Token.Packet.TxData.DestinationAddress is + unspecified when DestinationAddress is unspecified + when doing Configure() for this EFI IPv6 protocol + instance. + @retval EFI_ACCESS_DENIED The transmit completion token with the same Token. + Event was already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_NOT_FOUND Not route is found to destination address. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too + short to transmit. + @retval EFI_BAD_BUFFER_SIZE If Token.Packet.TxData.DataLength is beyond the + maximum that which can be described through the + Fragment Offset field in Fragment header when + performing fragmentation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Transmit ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ) +{ + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_IP6_CONFIG_DATA *Config; + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_IP6_HEADER Head; + EFI_IP6_TRANSMIT_DATA *TxData; + EFI_IP6_OVERRIDE_DATA *Override; + IP6_TXTOKEN_WRAP *Wrap; + UINT8 *ExtHdrs; + + // + // Check input parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + ExtHdrs = NULL; + + Status = Ip6TxTokenValid (Token); + if (EFI_ERROR (Status)) { + return Status; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Config = &IpInstance->ConfigData; + + // + // Check whether the token or signal already existed. + // + if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip6TokenExist, Token))) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // Build the IP header, fill in the information from ConfigData or OverrideData + // + ZeroMem (&Head, sizeof(EFI_IP6_HEADER)); + TxData = Token->Packet.TxData; + IP6_COPY_ADDRESS (&Head.SourceAddress, &Config->StationAddress); + IP6_COPY_ADDRESS (&Head.DestinationAddress, &Config->DestinationAddress); + + Status = EFI_INVALID_PARAMETER; + + if (NetIp6IsUnspecifiedAddr (&TxData->DestinationAddress)) { + if (NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) { + goto Exit; + } + + ASSERT (!NetIp6IsUnspecifiedAddr (&Config->StationAddress)); + + } else { + // + // StationAddress is unspecified only when ConfigData.Dest is unspecified. + // Use TxData.Dest to override the DestinationAddress. + // + if (!NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) { + goto Exit; + } + + if (NetIp6IsUnspecifiedAddr (&Config->StationAddress)) { + Status = Ip6SelectSourceAddress ( + IpSb, + &TxData->DestinationAddress, + &Head.SourceAddress + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + IP6_COPY_ADDRESS (&Head.DestinationAddress, &TxData->DestinationAddress); + } + + // + // Fill in Head infos. + // + Head.NextHeader = Config->DefaultProtocol; + if (TxData->ExtHdrsLength != 0) { + Head.NextHeader = TxData->NextHeader; + } + + if (TxData->OverrideData != NULL) { + Override = TxData->OverrideData; + Head.NextHeader = Override->Protocol; + Head.HopLimit = Override->HopLimit; + Head.FlowLabelL = HTONS ((UINT16) Override->FlowLabel); + Head.FlowLabelH = (UINT8) ((Override->FlowLabel >> 16) & 0x0F); + } else { + Head.HopLimit = Config->HopLimit; + Head.FlowLabelL = HTONS ((UINT16) Config->FlowLabel); + Head.FlowLabelH = (UINT8) ((Config->FlowLabel >> 16) & 0x0F); + } + + Head.PayloadLength = HTONS ((UINT16) (TxData->ExtHdrsLength + TxData->DataLength)); + + // + // OK, it survives all the validation check. Wrap the token in + // a IP6_TXTOKEN_WRAP and the data in a netbuf + // + Status = EFI_OUT_OF_RESOURCES; + Wrap = AllocateZeroPool (sizeof (IP6_TXTOKEN_WRAP)); + if (Wrap == NULL) { + goto Exit; + } + + Wrap->IpInstance = IpInstance; + Wrap->Token = Token; + Wrap->Sent = FALSE; + Wrap->Life = IP6_US_TO_SEC (Config->TransmitTimeout); + Wrap->Packet = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + IP6_MAX_HEADLEN, + 0, + Ip6FreeTxToken, + Wrap + ); + + if (Wrap->Packet == NULL) { + FreePool (Wrap); + goto Exit; + } + + Token->Status = EFI_NOT_READY; + + Status = NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + // + // NetbufFree will call Ip6FreeTxToken, which in turn will + // free the IP6_TXTOKEN_WRAP. Now, the token wrap hasn't been + // enqueued. + // + NetbufFree (Wrap->Packet); + goto Exit; + } + + // + // Allocate a new buffer to store IPv6 extension headers to avoid updating + // the original data in EFI_IP6_COMPLETION_TOKEN. + // + if (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs != NULL) { + ExtHdrs = (UINT8 *) AllocateCopyPool (TxData->ExtHdrsLength, TxData->ExtHdrs); + if (ExtHdrs == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } + + // + // Mark the packet sent before output it. Mark it not sent again if the + // returned status is not EFI_SUCCESS; + // + Wrap->Sent = TRUE; + + Status = Ip6Output ( + IpSb, + NULL, + IpInstance, + Wrap->Packet, + &Head, + ExtHdrs, + TxData->ExtHdrsLength, + Ip6OnPacketSent, + Wrap + ); + if (EFI_ERROR (Status)) { + Wrap->Sent = FALSE; + NetbufFree (Wrap->Packet); + } + +Exit: + gBS->RestoreTPL (OldTpl); + + if (ExtHdrs != NULL) { + FreePool (ExtHdrs); + } + + return Status; +} + +/** + Places a receiving request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. + This function is always asynchronous. + + The Token.Event field in the completion token must be filled in by the caller + and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. + + Current Udp implementation creates an IP child for each Udp child. + It initates a asynchronous receive immediately no matter whether + there is no mapping or not. Therefore, disable the returning EFI_NO_MAPPING for now. + To enable it, the following check must be performed: + + if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance, + while no source address is available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system + resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI IPv6 Protocol instance has been reset to startup defaults. + @retval EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already + in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Receive ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP6_SERVICE *IpSb; + + if (This == NULL || Token == NULL || Token->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + // + // Check whether the toke is already on the receive queue. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip6TokenExist, Token); + + if (EFI_ERROR (Status)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // Queue the token then check whether there is pending received packet. + // + Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6InstanceDeliverPacket (IpInstance); + + // + // Dispatch the DPC queued by the NotifyFunction of this instane's receive + // event. + // + DispatchDpc (); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Cancel the transmitted but not recycled packet. If a matching + token is found, it will call Ip6CancelPacket to cancel the + packet. Ip6CancelPacket cancels all the fragments of the + packet. When all the fragments are freed, the IP6_TXTOKEN_WRAP + is deleted from the Map, and user's event is signalled. + Because Ip6CancelPacket and other functions are all called in + line, after Ip6CancelPacket returns, the Item has been freed. + + @param[in] Map The IP6 child's transmit queue. + @param[in] Item The current transmitted packet to test. + @param[in] Context The user's token to cancel. + + @retval EFI_SUCCESS Continue to check the next Item. + @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled. + +**/ +EFI_STATUS +EFIAPI +Ip6CancelTxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + IP6_TXTOKEN_WRAP *Wrap; + + Token = (EFI_IP6_COMPLETION_TOKEN *) Context; + + // + // Return EFI_SUCCESS to check the next item in the map if + // this one doesn't match. + // + if ((Token != NULL) && (Token != Item->Key)) { + return EFI_SUCCESS; + } + + Wrap = (IP6_TXTOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + // + // Don't access the Item, Wrap and Token's members after this point. + // Item and wrap has been freed. And we no longer own the Token. + // + Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED); + + // + // If only one item is to be cancel, return EFI_ABORTED to stop + // iterating the map any more. + // + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + Cancel the receive request. This is simple, because + it is only enqueued in our local receive map. + + @param[in] Map The IP6 child's receive queue. + @param[in] Item Current receive request to cancel. + @param[in] Context The user's token to cancel. + + + @retval EFI_SUCCESS Continue to check the next receive request on the + queue. + @retval EFI_ABORTED The user's token (token != NULL) has been + cancelled. + +**/ +EFI_STATUS +EFIAPI +Ip6CancelRxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_IP6_COMPLETION_TOKEN *This; + + Token = (EFI_IP6_COMPLETION_TOKEN *) Context; + This = Item->Key; + + if ((Token != NULL) && (Token != This)) { + return EFI_SUCCESS; + } + + NetMapRemoveItem (Map, Item, NULL); + + This->Status = EFI_ABORTED; + This->Packet.RxData = NULL; + gBS->SignalEvent (This->Event); + + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Cancel the user's receive/transmit request. It is the worker function of + EfiIp6Cancel API. + + @param[in] IpInstance The IP6 child. + @param[in] Token The token to cancel. If NULL, all token will be + cancelled. + + @retval EFI_SUCCESS The token is cancelled. + @retval EFI_NOT_FOUND The token isn't found on either the + transmit/receive queue. + @retval EFI_DEVICE_ERROR Not all tokens are cancelled when Token is NULL. + +**/ +EFI_STATUS +Ip6Cancel ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // First check the transmitted packet. Ip6CancelTxTokens returns + // EFI_ABORTED to mean that the token has been cancelled when + // token != NULL. So, return EFI_SUCCESS for this condition. + // + Status = NetMapIterate (&IpInstance->TxTokens, Ip6CancelTxTokens, Token); + if (EFI_ERROR (Status)) { + if ((Token != NULL) && (Status == EFI_ABORTED)) { + return EFI_SUCCESS; + } + + return Status; + } + + // + // Check the receive queue. Ip6CancelRxTokens also returns EFI_ABORT + // for Token!=NULL and it is cancelled. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip6CancelRxTokens, Token); + // + // Dispatch the DPCs queued by the NotifyFunction of the canceled rx token's + // events. + // + DispatchDpc (); + if (EFI_ERROR (Status)) { + if ((Token != NULL) && (Status == EFI_ABORTED)) { + return EFI_SUCCESS; + } + + return Status; + } + + // + // OK, if the Token is found when Token != NULL, the NetMapIterate + // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS. + // + if (Token != NULL) { + return EFI_NOT_FOUND; + } + + // + // If Token == NULL, cancel all the tokens. return error if not + // all of them are cancelled. + // + if (!NetMapIsEmpty (&IpInstance->TxTokens) || !NetMapIsEmpty (&IpInstance->RxTokens)) { + + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Abort an asynchronous transmit or receive request. + + The Cancel() function is used to abort a pending transmit or receive request. + If the token is in the transmit or receive request queues, after calling this + function, Token->Status will be set to EFI_ABORTED, and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means the + asynchronous operation has completed, this function will not signal the token, + and EFI_NOT_FOUND is returned. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_IP6_PROTOCOL.Transmit() or + EFI_IP6_PROTOCOL.Receive(). If NULL, all pending + tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is + defined in EFI_IP6_PROTOCOL.Transmit(). + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and + Token->Event was signaled. When Token is NULL, all + pending requests were aborted, and their events were signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was + not found in the transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Cancel ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Ip6Cancel (IpInstance, Token); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Polls for incoming data packets, and processes outgoing data packets. + + The Poll() function polls for incoming data packets and processes outgoing data + packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll() + function to increase the rate that data packets are moved between the communications + device and the transmit and receive queues. + + In some systems the periodic timer event may not poll the underlying communications + device fast enough to transmit and/or receive all data packets without missing + incoming packets or dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function + more often. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Poll ( + IN EFI_IP6_PROTOCOL *This + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + if (IpInstance->State == IP6_STATE_UNCONFIGED) { + return EFI_NOT_STARTED; + } + + Mnp = IpInstance->Service->Mnp; + + // + // Don't lock the Poll function to enable the deliver of + // the packet polled up. + // + return Mnp->Poll (Mnp); + +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Impl.h b/Core/NetworkPkg/Ip6Dxe/Ip6Impl.h new file mode 100644 index 0000000000..d30246b2db --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Impl.h @@ -0,0 +1,752 @@ +/** @file + Implementation of EFI_IP6_PROTOCOL protocol interfaces and type definitions. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_IMPL_H__ +#define __EFI_IP6_IMPL_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Ip6Common.h" +#include "Ip6Driver.h" +#include "Ip6Icmp.h" +#include "Ip6If.h" +#include "Ip6Input.h" +#include "Ip6Mld.h" +#include "Ip6Nd.h" +#include "Ip6Option.h" +#include "Ip6Output.h" +#include "Ip6Route.h" +#include "Ip6ConfigNv.h" +#include "Ip6ConfigImpl.h" + +#define IP6_PROTOCOL_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'P') +#define IP6_SERVICE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'S') + +// +// The state of IP6 protocol. It starts from UNCONFIGED. if it is +// successfully configured, it goes to CONFIGED. if configure NULL +// is called, it becomes UNCONFIGED again. If (partly) destroyed, it +// becomes DESTROY. +// +#define IP6_STATE_UNCONFIGED 0 +#define IP6_STATE_CONFIGED 1 + +// +// The state of IP6 service. It starts from UNSTARTED. It transits +// to STARTED if autoconfigure is started. If default address is +// configured, it becomes CONFIGED. and if partly destroyed, it goes +// to DESTROY. +// +#define IP6_SERVICE_UNSTARTED 0 +#define IP6_SERVICE_STARTED 1 +#define IP6_SERVICE_CONFIGED 2 +#define IP6_SERVICE_DESTROY 3 + +#define IP6_INSTANCE_FROM_PROTOCOL(Ip6) \ + CR ((Ip6), IP6_PROTOCOL, Ip6Proto, IP6_PROTOCOL_SIGNATURE) + +#define IP6_SERVICE_FROM_PROTOCOL(Sb) \ + CR ((Sb), IP6_SERVICE, ServiceBinding, IP6_SERVICE_SIGNATURE) + +#define IP6_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured) + +extern EFI_IPSEC2_PROTOCOL *mIpSec; +extern BOOLEAN mIpSec2Installed; + +// +// IP6_TXTOKEN_WRAP wraps the upper layer's transmit token. +// The user's data is kept in the Packet. When fragment is +// needed, each fragment of the Packet has a reference to the +// Packet, no data is actually copied. The Packet will be +// released when all the fragments of it have been recycled by +// MNP. Upon then, the IP6_TXTOKEN_WRAP will be released, and +// user's event signalled. +// +typedef struct { + IP6_PROTOCOL *IpInstance; + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_EVENT IpSecRecycleSignal; + NET_BUF *Packet; + BOOLEAN Sent; + INTN Life; +} IP6_TXTOKEN_WRAP; + +typedef struct { + EFI_EVENT IpSecRecycleSignal; + NET_BUF *Packet; +} IP6_IPSEC_WRAP; + +// +// IP6_RXDATA_WRAP wraps the data IP6 child delivers to the +// upper layers. The received packet is kept in the Packet. +// The Packet itself may be constructured from some fragments. +// All the fragments of the Packet is organized by a +// IP6_ASSEMBLE_ENTRY structure. If the Packet is recycled by +// the upper layer, the assemble entry and its associated +// fragments will be freed at last. +// +typedef struct { + LIST_ENTRY Link; + IP6_PROTOCOL *IpInstance; + NET_BUF *Packet; + EFI_IP6_RECEIVE_DATA RxData; +} IP6_RXDATA_WRAP; + +struct _IP6_PROTOCOL { + UINT32 Signature; + + EFI_IP6_PROTOCOL Ip6Proto; + EFI_HANDLE Handle; + INTN State; + + IP6_SERVICE *Service; + LIST_ENTRY Link; // Link to all the IP protocol from the service + + UINT8 PrefixLength; // PrefixLength of the configured station address. + // + // User's transmit/receive tokens, and received/deliverd packets + // + NET_MAP RxTokens; + NET_MAP TxTokens; // map between (User's Token, IP6_TXTOKE_WRAP) + LIST_ENTRY Received; // Received but not delivered packet + LIST_ENTRY Delivered; // Delivered and to be recycled packets + EFI_LOCK RecycleLock; + + IP6_INTERFACE *Interface; + LIST_ENTRY AddrLink; // Ip instances with the same IP address. + + EFI_IPv6_ADDRESS *GroupList; // stored in network order. + UINT32 GroupCount; + + EFI_IP6_CONFIG_DATA ConfigData; + BOOLEAN InDestroy; +}; + +struct _IP6_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + INTN State; + + // + // List of all the IP instances and interfaces, and default + // interface and route table and caches. + // + UINTN NumChildren; + LIST_ENTRY Children; + + LIST_ENTRY Interfaces; + + IP6_INTERFACE *DefaultInterface; + IP6_ROUTE_TABLE *RouteTable; + + IP6_LINK_RX_TOKEN RecvRequest; + + // + // Ip reassemble utilities and MLD data + // + IP6_ASSEMBLE_TABLE Assemble; + IP6_MLD_SERVICE_DATA MldCtrl; + + EFI_IPv6_ADDRESS LinkLocalAddr; + BOOLEAN LinkLocalOk; + BOOLEAN LinkLocalDadFail; + BOOLEAN Dhcp6NeedStart; + BOOLEAN Dhcp6NeedInfoRequest; + + // + // ND data + // + UINT8 CurHopLimit; + UINT32 LinkMTU; + UINT32 BaseReachableTime; + UINT32 ReachableTime; + UINT32 RetransTimer; + LIST_ENTRY NeighborTable; + + LIST_ENTRY OnlinkPrefix; + LIST_ENTRY AutonomousPrefix; + + LIST_ENTRY DefaultRouterList; + UINT32 RoundRobin; + + UINT8 InterfaceIdLen; + UINT8 *InterfaceId; + + BOOLEAN RouterAdvertiseReceived; + UINT8 SolicitTimer; + UINT32 Ticks; + + // + // Low level protocol used by this service instance + // + EFI_HANDLE Image; + EFI_HANDLE Controller; + + EFI_HANDLE MnpChildHandle; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + + EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData; + EFI_SIMPLE_NETWORK_MODE SnpMode; + + EFI_EVENT Timer; + EFI_EVENT FasterTimer; + + // + // IPv6 Configuration Protocol instance + // + IP6_CONFIG_INSTANCE Ip6ConfigInstance; + + // + // The string representation of the current mac address of the + // NIC this IP6_SERVICE works on. + // + CHAR16 *MacString; + UINT32 MaxPacketSize; + UINT32 OldMaxPacketSize; +}; + +/** + The callback function for the net buffer which wraps the user's + transmit token. Although this function seems simple, + there are some subtle aspects. + When a user requests the IP to transmit a packet by passing it a + token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data + is wrapped in a net buffer. The net buffer's Free function is + set to Ip6FreeTxToken. The Token and token wrap are added to the + IP child's TxToken map. Then the buffer is passed to Ip6Output for + transmission. If an error occurs before that, the buffer + is freed, which in turn frees the token wrap. The wrap may + have been added to the TxToken map or not, and the user's event + shouldn't be signaled because we are still in the EfiIp6Transmit. If + the buffer has been sent by Ip6Output, it should be removed from + the TxToken map and the user's event signaled. The token wrap and buffer + are bound together. Refer to the comments in Ip6Output for information + about IP fragmentation. + + @param[in] Context The token's wrap. + +**/ +VOID +EFIAPI +Ip6FreeTxToken ( + IN VOID *Context + ); + +/** + Config the MNP parameter used by IP. The IP driver use one MNP + child to transmit/receive frames. By default, it configures MNP + to receive unicast/multicast/broadcast. And it will enable/disable + the promiscuous receive according to whether there is IP child + enable that or not. If Force is FALSE, it will iterate through + all the IP children to check whether the promiscuous receive + setting has been changed. If it hasn't been changed, it won't + reconfigure the MNP. If Force is TRUE, the MNP is configured + whether that is changed or not. + + @param[in] IpSb The IP6 service instance that is to be changed. + @param[in] Force Force the configuration or not. + + @retval EFI_SUCCESS The MNP successfully configured/reconfigured. + @retval Others The configuration failed. + +**/ +EFI_STATUS +Ip6ServiceConfigMnp ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN Force + ); + +/** + Cancel the user's receive/transmit request. It is the worker function of + EfiIp6Cancel API. + + @param[in] IpInstance The IP6 child. + @param[in] Token The token to cancel. If NULL, all tokens will be + cancelled. + + @retval EFI_SUCCESS The token was cancelled. + @retval EFI_NOT_FOUND The token isn't found on either the + transmit or receive queue. + @retval EFI_DEVICE_ERROR Not all tokens are cancelled when Token is NULL. + +**/ +EFI_STATUS +Ip6Cancel ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Initialize the IP6_PROTOCOL structure to the unconfigured states. + + @param[in] IpSb The IP6 service instance. + @param[in, out] IpInstance The IP6 child instance. + +**/ +VOID +Ip6InitProtocol ( + IN IP6_SERVICE *IpSb, + IN OUT IP6_PROTOCOL *IpInstance + ); + +/** + Clean up the IP6 child, release all the resources used by it. + + @param[in, out] IpInstance The IP6 child to clean up. + + @retval EFI_SUCCESS The IP6 child was cleaned up + @retval EFI_DEVICE_ERROR Some resources failed to be released. + +**/ +EFI_STATUS +Ip6CleanProtocol ( + IN OUT IP6_PROTOCOL *IpInstance + ); + +// +// EFI_IP6_PROTOCOL interface prototypes +// + +/** + Gets the current operational settings for this instance of the EFI IPv6 Protocol driver. + + The GetModeData() function returns the current operational mode data for this driver instance. + The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to + retrieve the operational mode data of underlying networks or drivers. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[out] Ip6ModeData The pointer to the EFI IPv6 Protocol mode data structure. + @param[out] MnpConfigData The pointer to the managed network configuration data structure. + @param[out] SnpModeData The pointer to the simple network mode data structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIp6GetModeData ( + IN EFI_IP6_PROTOCOL *This, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance. + + The Configure() function is used to set, change, or reset the operational parameters and filter + settings for this EFI IPv6 Protocol instance. Until these parameters have been set, no network traffic + can be sent or received by this instance. Once the parameters have been reset (by calling this + function with Ip6ConfigData set to NULL), no more traffic can be sent or received until these + parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped + independently of each other by enabling or disabling their receive filter settings with the + Configure() function. + + If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required + to be one of the currently configured IPv6 addresses list in the EFI IPv6 drivers, or else + EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is + unspecified, the IPv6 driver will bind a source address according to the source address selection + algorithm. Clients could frequently call GetModeData() to check get a currently configured IPv6. + If both Ip6ConfigData.StationAddress and Ip6ConfigData.Destination are unspecified, when + transmitting the packet afterwards, the source address filled in each outgoing IPv6 packet + is decided based on the destination of this packet. + + If operational parameters are reset or changed, any pending transmit and receive requests will be + cancelled. Their completion token status will be set to EFI_ABORTED, and their events will be + signaled. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Ip6ConfigData The pointer to the EFI IPv6 Protocol configuration data structure. + If NULL, reset the configuration data. + + @retval EFI_SUCCESS The driver instance was successfully opened. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Ip6ConfigData.StationAddress is neither zero nor + a unicast IPv6 address. + - Ip6ConfigData.StationAddress is neither zero nor + one of the configured IP addresses in the EFI IPv6 driver. + - Ip6ConfigData.DefaultProtocol is illegal. + @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for + this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6 + address or prefix length can be changed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6 + Protocol driver instance was not opened. + @retval EFI_UNSUPPORTED Default protocol specified through + Ip6ConfigData.DefaulProtocol isn't supported. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Configure ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA *Ip6ConfigData OPTIONAL + ); + +/** + Joins and leaves multicast groups. + + The Groups() function is used to join and leave multicast group sessions. Joining a group will + enable reception of matching multicast packets. Leaving a group will disable reception of matching + multicast packets. Source-Specific Multicast isn't required to be supported. + + If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join the multicast group session and FALSE to leave. + @param[in] GroupAddress The pointer to the IPv6 multicast address. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - JoinFlag is TRUE and GroupAddress is NULL. + - GroupAddress is not NULL and *GroupAddress is + not a multicast IPv6 address. + - GroupAddress is not NULL and *GroupAddress is in the + range of SSM destination address. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_OUT_OF_RESOURCES System resources could not be allocated. + @retval EFI_UNSUPPORTED This EFI IPv6 Protocol implementation does not support multicast groups. + @retval EFI_ALREADY_STARTED The group address is already in the group table (when + JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Groups ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ); + +/** + Adds and deletes routing table entries. + + The Routes() function adds a route to or deletes a route from the routing table. + + Routes are determined by comparing the leftmost PrefixLength bits of Destination with + the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the + configured station address. + + The default route is added with Destination and PrefixLegth both set to all zeros. The + default route matches all destination IPv6 addresses that do not match any other routes. + + All EFI IPv6 Protocol instances share a routing table. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to + FALSE to add this route to the routing table. Destination, + PrefixLength and Gateway are used as the key to each + route entry. + @param[in] Destination The address prefix of the subnet that needs to be routed. + This is an optional parameter that may be NULL. + @param[in] PrefixLength The prefix length of Destination. Ignored if Destination + is NULL. + @param[in] GatewayAddress The unicast gateway IPv6 address for this route. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - When DeleteRoute is TRUE, both Destination and + GatewayAddress are NULL. + - When DeleteRoute is FALSE, either Destination or + GatewayAddress is NULL. + - *GatewayAddress is not a valid unicast IPv6 address. + - *GatewayAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table (when DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when + DeleteRoute is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Routes ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ); + +/** + Add or delete Neighbor cache entries. + + The Neighbors() function is used to add, update, or delete an entry from a neighbor cache. + IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as + network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network + traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not + timeout) or dynamic (will timeout). + + The implementation should follow the neighbor cache timeout mechanism defined in + RFC4861. The default neighbor cache timeout value should be tuned for the expected network + environment. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteFlag Set to TRUE to delete the specified cache entry. Set to FALSE to + add (or update, if it already exists and Override is TRUE) the + specified cache entry. TargetIp6Address is used as the key + to find the requested cache entry. + @param[in] TargetIp6Address The pointer to the Target IPv6 address. + @param[in] TargetLinkAddress The pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache, it will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, EFI_ACCESS_DENIED + will be returned if a corresponding cache entry already exists. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - TargetIpAddress is NULL. + - *TargetLinkAddress is invalid when not NULL. + - *TargetIpAddress is not a valid unicast IPv6 address. + - *TargetIpAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is + TRUE or when DeleteFlag is FALSE while + TargetLinkAddress is NULL.). + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when Override + is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Neighbors ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ); + +/** + Places outgoing data packets into the transmit queue. + + The Transmit() function places a sending request in the transmit queue of this + EFI IPv6 Protocol instance. Whenever the packet in the token is sent out or some + errors occur, the event in the token will be signaled and the status is updated. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token The pointer to the transmit token. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing + a source address for this transmission, + but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + - Token.Packet.TxData is NULL. + - Token.Packet.ExtHdrsLength is not zero and + Token.Packet.ExtHdrs is NULL. + - Token.Packet.FragmentCount is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentLength fields is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentBuffer fields is NULL. + - Token.Packet.TxData.DataLength is zero or not + equal to the sum of fragment lengths. + - Token.Packet.TxData.DestinationAddress is non- + zero when DestinationAddress is configured as + non-zero when doing Configure() for this + EFI IPv6 protocol instance. + - Token.Packet.TxData.DestinationAddress is + unspecified when DestinationAddress is unspecified + when doing Configure() for this EFI IPv6 protocol + instance. + @retval EFI_ACCESS_DENIED The transmit completion token with the same Token. + The event was already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_NOT_FOUND Not route is found to the destination address. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too + short to transmit. + @retval EFI_BAD_BUFFER_SIZE If Token.Packet.TxData.DataLength is beyond the + maximum that which can be described through the + Fragment Offset field in Fragment header when + performing fragmentation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Transmit ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ); + +/** + Places a receiving request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. + This function is always asynchronous. + + The Token.Event field in the completion token must be filled in by the caller + and cannot be NULL. When the receive operation completes, the EFI IPv6 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. + + Current Udp implementation creates an IP child for each Udp child. + It initates a asynchronous receive immediately whether or not + there is no mapping. Therefore, disable the returning EFI_NO_MAPPING for now. + To enable it, the following check must be performed: + + if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token The pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance, + while no source address is available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of system + resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI IPv6 Protocol instance has been reset to startup defaults. + @retval EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already + in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Receive ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ); + +/** + Abort an asynchronous transmit or receive request. + + The Cancel() function is used to abort a pending transmit or receive request. + If the token is in the transmit or receive request queues, after calling this + function, Token->Status will be set to EFI_ABORTED, and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means the + asynchronous operation has completed, this function will not signal the token, + and EFI_NOT_FOUND is returned. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token The pointer to a token that has been issued by + EFI_IP6_PROTOCOL.Transmit() or + EFI_IP6_PROTOCOL.Receive(). If NULL, all pending + tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is + defined in EFI_IP6_PROTOCOL.Transmit(). + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and + Token->Event was signaled. When Token is NULL, all + pending requests were aborted, and their events were signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O request was + not found in the transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Cancel ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function polls for incoming data packets and processes outgoing data + packets. Network drivers and applications can call the EFI_IP6_PROTOCOL.Poll() + function to increase the rate that data packets are moved between the communications + device and the transmit and receive queues. + + In some systems the periodic timer event may not poll the underlying communications + device fast enough to transmit and/or receive all data packets without missing + incoming packets or dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the EFI_IP6_PROTOCOL.Poll() function + more often. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Poll ( + IN EFI_IP6_PROTOCOL *This + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Input.c b/Core/NetworkPkg/Ip6Dxe/Ip6Input.c new file mode 100644 index 0000000000..e53e0874b9 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Input.c @@ -0,0 +1,1831 @@ +/** @file + IP6 internal functions to process the incoming packets. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +/** + Create an empty assemble entry for the packet identified by + (Dst, Src, Id). The default life for the packet is 60 seconds. + + @param[in] Dst The destination address. + @param[in] Src The source address. + @param[in] Id The ID field in the IP header. + + @return NULL if failed to allocate memory for the entry. Otherwise, + the pointer to the just created reassemble entry. + +**/ +IP6_ASSEMBLE_ENTRY * +Ip6CreateAssembleEntry ( + IN EFI_IPv6_ADDRESS *Dst, + IN EFI_IPv6_ADDRESS *Src, + IN UINT32 Id + ) +{ + IP6_ASSEMBLE_ENTRY *Assemble; + + Assemble = AllocatePool (sizeof (IP6_ASSEMBLE_ENTRY)); + if (Assemble == NULL) { + return NULL; + } + + IP6_COPY_ADDRESS (&Assemble->Dst, Dst); + IP6_COPY_ADDRESS (&Assemble->Src, Src); + InitializeListHead (&Assemble->Fragments); + + Assemble->Id = Id; + Assemble->Life = IP6_FRAGMENT_LIFE + 1; + + Assemble->TotalLen = 0; + Assemble->CurLen = 0; + Assemble->Head = NULL; + Assemble->Info = NULL; + Assemble->Packet = NULL; + + return Assemble; +} + +/** + Release all the fragments of a packet, then free the assemble entry. + + @param[in] Assemble The assemble entry to free. + +**/ +VOID +Ip6FreeAssembleEntry ( + IN IP6_ASSEMBLE_ENTRY *Assemble + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + NET_BUF *Fragment; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) { + Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + RemoveEntryList (Entry); + NetbufFree (Fragment); + } + + if (Assemble->Packet != NULL) { + NetbufFree (Assemble->Packet); + } + + FreePool (Assemble); +} + +/** + Release all the fragments of the packet. This is the callback for + the assembled packet's OnFree. It will free the assemble entry, + which in turn frees all the fragments of the packet. + + @param[in] Arg The assemble entry to free. + +**/ +VOID +EFIAPI +Ip6OnFreeFragments ( + IN VOID *Arg + ) +{ + Ip6FreeAssembleEntry ((IP6_ASSEMBLE_ENTRY *) Arg); +} + +/** + Trim the packet to fit in [Start, End), and update per the + packet information. + + @param[in, out] Packet Packet to trim. + @param[in] Start The sequence of the first byte to fit in. + @param[in] End One beyond the sequence of last byte to fit in. + +**/ +VOID +Ip6TrimPacket ( + IN OUT NET_BUF *Packet, + IN INTN Start, + IN INTN End + ) +{ + IP6_CLIP_INFO *Info; + INTN Len; + + Info = IP6_GET_CLIP_INFO (Packet); + + ASSERT (Info->Start + Info->Length == Info->End); + ASSERT ((Info->Start < End) && (Start < Info->End)); + + if (Info->Start < Start) { + Len = Start - Info->Start; + + NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD); + Info->Start = (UINT32) Start; + Info->Length -= (UINT32) Len; + } + + if (End < Info->End) { + Len = End - Info->End; + + NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL); + Info->End = (UINT32) End; + Info->Length -= (UINT32) Len; + } +} + +/** + Reassemble the IP fragments. If all the fragments of the packet + have been received, it will wrap the packet in a net buffer then + return it to caller. If the packet can't be assembled, NULL is + returned. + + @param[in, out] Table The assemble table used. A new assemble entry will be created + if the Packet is from a new chain of fragments. + @param[in] Packet The fragment to assemble. It might be freed if the fragment + can't be re-assembled. + + @return NULL if the packet can't be reassembled. The pointer to the just assembled + packet if all the fragments of the packet have arrived. + +**/ +NET_BUF * +Ip6Reassemble ( + IN OUT IP6_ASSEMBLE_TABLE *Table, + IN NET_BUF *Packet + ) +{ + EFI_IP6_HEADER *Head; + IP6_CLIP_INFO *This; + IP6_CLIP_INFO *Node; + IP6_ASSEMBLE_ENTRY *Assemble; + IP6_ASSEMBLE_ENTRY *Entry; + LIST_ENTRY *ListHead; + LIST_ENTRY *Prev; + LIST_ENTRY *Cur; + NET_BUF *Fragment; + NET_BUF *TmpPacket; + NET_BUF *NewPacket; + NET_BUF *Duplicate; + UINT8 *DupHead; + INTN Index; + UINT16 UnFragmentLen; + UINT8 *NextHeader; + + Head = Packet->Ip.Ip6; + This = IP6_GET_CLIP_INFO (Packet); + + ASSERT (Head != NULL); + + // + // Find the corresponding assemble entry by (Dst, Src, Id) + // + Assemble = NULL; + Index = IP6_ASSEMBLE_HASH (&Head->DestinationAddress, &Head->SourceAddress, This->Id); + + NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) { + Entry = NET_LIST_USER_STRUCT (Cur, IP6_ASSEMBLE_ENTRY, Link); + + if (Entry->Id == This->Id && + EFI_IP6_EQUAL (&Entry->Src, &Head->SourceAddress) && + EFI_IP6_EQUAL (&Entry->Dst, &Head->DestinationAddress) + ) { + Assemble = Entry; + break; + } + } + + // + // Create a new entry if can not find an existing one, insert it to assemble table + // + if (Assemble == NULL) { + Assemble = Ip6CreateAssembleEntry ( + &Head->DestinationAddress, + &Head->SourceAddress, + This->Id + ); + + if (Assemble == NULL) { + goto Error; + } + + InsertHeadList (&Table->Bucket[Index], &Assemble->Link); + } + + // + // Find the point to insert the packet: before the first + // fragment with THIS.Start < CUR.Start. the previous one + // has PREV.Start <= THIS.Start < CUR.Start. + // + ListHead = &Assemble->Fragments; + + NET_LIST_FOR_EACH (Cur, ListHead) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (This->Start < IP6_GET_CLIP_INFO (Fragment)->Start) { + break; + } + } + + // + // Check whether the current fragment overlaps with the previous one. + // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to + // check whether THIS.Start < PREV.End for overlap. If two fragments + // overlaps, trim the overlapped part off THIS fragment. + // + if ((Prev = Cur->BackLink) != ListHead) { + Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); + Node = IP6_GET_CLIP_INFO (Fragment); + + if (This->Start < Node->End) { + if (This->End <= Node->End) { + goto Error; + } + + // + // Trim the previous fragment from tail. + // + Ip6TrimPacket (Fragment, Node->Start, This->Start); + } + } + + // + // Insert the fragment into the packet. The fragment may be removed + // from the list by the following checks. + // + NetListInsertBefore (Cur, &Packet->List); + + // + // Check the packets after the insert point. It holds that: + // THIS.Start <= NODE.Start < NODE.End. The equality holds + // if PREV and NEXT are continuous. THIS fragment may fill + // several holes. Remove the completely overlapped fragments + // + while (Cur != ListHead) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Node = IP6_GET_CLIP_INFO (Fragment); + + // + // Remove fragments completely overlapped by this fragment + // + if (Node->End <= This->End) { + Cur = Cur->ForwardLink; + + RemoveEntryList (&Fragment->List); + Assemble->CurLen -= Node->Length; + + NetbufFree (Fragment); + continue; + } + + // + // The conditions are: THIS.Start <= NODE.Start, and THIS.End < + // NODE.End. Two fragments overlaps if NODE.Start < THIS.End. + // If two fragments start at the same offset, remove THIS fragment + // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)). + // + if (Node->Start < This->End) { + if (This->Start == Node->Start) { + RemoveEntryList (&Packet->List); + goto Error; + } + + Ip6TrimPacket (Packet, This->Start, Node->Start); + } + + break; + } + + // + // Update the assemble info: increase the current length. If it is + // the frist fragment, update the packet's IP head and per packet + // info. If it is the last fragment, update the total length. + // + Assemble->CurLen += This->Length; + + if (This->Start == 0) { + // + // Once the first fragment is enqueued, it can't be removed + // from the fragment list. So, Assemble->Head always point + // to valid memory area. + // + if ((Assemble->Head != NULL) || (Assemble->Packet != NULL)) { + goto Error; + } + + // + // Backup the first fragment in case the reasembly of that packet fail. + // + Duplicate = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER)); + if (Duplicate == NULL) { + goto Error; + } + + // + // Revert IP head to network order. + // + DupHead = NetbufGetByte (Duplicate, 0, NULL); + ASSERT (DupHead != NULL); + Duplicate->Ip.Ip6 = Ip6NtohHead ((EFI_IP6_HEADER *) DupHead); + Assemble->Packet = Duplicate; + + // + // Adjust the unfragmentable part in first fragment + // + UnFragmentLen = (UINT16) (This->HeadLen - sizeof (EFI_IP6_HEADER)); + if (UnFragmentLen == 0) { + // + // There is not any unfragmentable extension header. + // + ASSERT (Head->NextHeader == IP6_FRAGMENT); + Head->NextHeader = This->NextHeader; + } else { + NextHeader = NetbufGetByte ( + Packet, + This->FormerNextHeader + sizeof (EFI_IP6_HEADER), + 0 + ); + if (NextHeader == NULL) { + goto Error; + } + + *NextHeader = This->NextHeader; + } + + Assemble->Head = Head; + Assemble->Info = IP6_GET_CLIP_INFO (Packet); + } + + // + // Don't update the length more than once. + // + if ((This->LastFrag != 0) && (Assemble->TotalLen == 0)) { + Assemble->TotalLen = This->End; + } + + // + // Deliver the whole packet if all the fragments received. + // All fragments received if: + // 1. received the last one, so, the totoal length is know + // 2. received all the data. If the last fragment on the + // queue ends at the total length, all data is received. + // + if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) { + + RemoveEntryList (&Assemble->Link); + + // + // If the packet is properly formated, the last fragment's End + // equals to the packet's total length. Otherwise, the packet + // is a fake, drop it now. + // + Fragment = NET_LIST_USER_STRUCT (ListHead->BackLink, NET_BUF, List); + if (IP6_GET_CLIP_INFO (Fragment)->End != (INTN) Assemble->TotalLen) { + Ip6FreeAssembleEntry (Assemble); + goto Error; + } + + Fragment = NET_LIST_HEAD (ListHead, NET_BUF, List); + This = Assemble->Info; + + // + // This TmpPacket is used to hold the unfragmentable part, i.e., + // the IPv6 header and the unfragmentable extension headers. Be noted that + // the Fragment Header is exluded. + // + TmpPacket = NetbufGetFragment (Fragment, 0, This->HeadLen, 0); + ASSERT (TmpPacket != NULL); + + NET_LIST_FOR_EACH (Cur, ListHead) { + // + // Trim off the unfragment part plus the fragment header from all fragments. + // + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + NetbufTrim (Fragment, This->HeadLen + sizeof (IP6_FRAGMENT_HEADER), TRUE); + } + + InsertHeadList (ListHead, &TmpPacket->List); + + // + // Wrap the packet in a net buffer then deliver it up + // + NewPacket = NetbufFromBufList ( + &Assemble->Fragments, + 0, + 0, + Ip6OnFreeFragments, + Assemble + ); + + if (NewPacket == NULL) { + Ip6FreeAssembleEntry (Assemble); + goto Error; + } + + NewPacket->Ip.Ip6 = Assemble->Head; + + CopyMem (IP6_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP6_CLIP_INFO)); + + return NewPacket; + } + + return NULL; + +Error: + NetbufFree (Packet); + return NULL; +} + + +/** + The callback function for the net buffer that wraps the packet processed by + IPsec. It releases the wrap packet and also signals IPsec to free the resources. + + @param[in] Arg The wrap context. + +**/ +VOID +EFIAPI +Ip6IpSecFree ( + IN VOID *Arg + ) +{ + IP6_IPSEC_WRAP *Wrap; + + Wrap = (IP6_IPSEC_WRAP *) Arg; + + if (Wrap->IpSecRecycleSignal != NULL) { + gBS->SignalEvent (Wrap->IpSecRecycleSignal); + } + + NetbufFree (Wrap->Packet); + + FreePool (Wrap); + + return; +} + +/** + The work function to locate the IPsec protocol to process the inbound or + outbound IP packets. The process routine handles the packet with the following + actions: bypass the packet, discard the packet, or protect the packet. + + @param[in] IpSb The IP6 service instance. + @param[in, out] Head The caller-supplied IP6 header. + @param[in, out] LastHead The next header field of last IP header. + @param[in, out] Netbuf The IP6 packet to be processed by IPsec. + @param[in, out] ExtHdrs The caller-supplied options. + @param[in, out] ExtHdrsLen The length of the option. + @param[in] Direction The directionality in an SPD entry, + EfiIPsecInBound, or EfiIPsecOutBound. + @param[in] Context The token's wrap. + + @retval EFI_SUCCESS The IPsec protocol is not available or disabled. + @retval EFI_SUCCESS The packet was bypassed, and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + @retval EFI_OUT_OF_RESOURCES There are not suffcient resources to complete the operation. + @retval EFI_BUFFER_TOO_SMALL The number of non-empty blocks is bigger than the + number of input data blocks when building a fragment table. + +**/ +EFI_STATUS +Ip6IpSecProcessPacket ( + IN IP6_SERVICE *IpSb, + IN OUT EFI_IP6_HEADER **Head, + IN OUT UINT8 *LastHead, + IN OUT NET_BUF **Netbuf, + IN OUT UINT8 **ExtHdrs, + IN OUT UINT32 *ExtHdrsLen, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN VOID *Context + ) +{ + NET_FRAGMENT *FragmentTable; + NET_FRAGMENT *OriginalFragmentTable; + UINT32 FragmentCount; + UINT32 OriginalFragmentCount; + EFI_EVENT RecycleEvent; + NET_BUF *Packet; + IP6_TXTOKEN_WRAP *TxWrap; + IP6_IPSEC_WRAP *IpSecWrap; + EFI_STATUS Status; + EFI_IP6_HEADER *PacketHead; + UINT8 *Buf; + EFI_IP6_HEADER ZeroHead; + + Status = EFI_SUCCESS; + + if (!mIpSec2Installed) { + goto ON_EXIT; + } + + Packet = *Netbuf; + RecycleEvent = NULL; + IpSecWrap = NULL; + FragmentTable = NULL; + PacketHead = NULL; + Buf = NULL; + TxWrap = (IP6_TXTOKEN_WRAP *) Context; + FragmentCount = Packet->BlockOpNum; + ZeroMem (&ZeroHead, sizeof (EFI_IP6_HEADER)); + + if (mIpSec == NULL) { + gBS->LocateProtocol (&gEfiIpSec2ProtocolGuid, NULL, (VOID **) &mIpSec); + + // + // Check whether the ipsec protocol is available. + // + if (mIpSec == NULL) { + goto ON_EXIT; + } + } + + // + // Check whether the ipsec enable variable is set. + // + if (mIpSec->DisabledFlag) { + // + // If IPsec is disabled, restore the original MTU + // + IpSb->MaxPacketSize = IpSb->OldMaxPacketSize; + goto ON_EXIT; + } else { + // + // If IPsec is enabled, use the MTU which reduce the IPsec header length. + // + IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP6_MAX_IPSEC_HEADLEN; + } + + + // + // Bypass all multicast inbound or outbound traffic. + // + if (IP6_IS_MULTICAST (&(*Head)->DestinationAddress) || IP6_IS_MULTICAST (&(*Head)->SourceAddress)) { + goto ON_EXIT; + } + + // + // Rebuild fragment table from netbuf to ease ipsec process. + // + FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT)); + + if (FragmentTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount); + OriginalFragmentTable = FragmentTable; + OriginalFragmentCount = FragmentCount; + + if (EFI_ERROR(Status)) { + FreePool (FragmentTable); + goto ON_EXIT; + } + + // + // Convert host byte order to network byte order + // + Ip6NtohHead (*Head); + + Status = mIpSec->ProcessExt ( + mIpSec, + IpSb->Controller, + IP_VERSION_6, + (VOID *) (*Head), + LastHead, + (VOID **) ExtHdrs, + ExtHdrsLen, + (EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable), + &FragmentCount, + Direction, + &RecycleEvent + ); + // + // Convert back to host byte order + // + Ip6NtohHead (*Head); + + if (EFI_ERROR (Status)) { + FreePool (OriginalFragmentTable); + goto ON_EXIT; + } + + if (OriginalFragmentCount == FragmentCount && OriginalFragmentTable == FragmentTable) { + // + // For ByPass Packet + // + FreePool (FragmentTable); + goto ON_EXIT; + } else { + // + // Free the FragmentTable which allocated before calling the IPsec. + // + FreePool (OriginalFragmentTable); + } + + if (Direction == EfiIPsecOutBound && TxWrap != NULL) { + TxWrap->IpSecRecycleSignal = RecycleEvent; + TxWrap->Packet = NetbufFromExt ( + FragmentTable, + FragmentCount, + IP6_MAX_HEADLEN, + 0, + Ip6FreeTxToken, + TxWrap + ); + if (TxWrap->Packet == NULL) { + TxWrap->Packet = *Netbuf; + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem ( + IP6_GET_CLIP_INFO (TxWrap->Packet), + IP6_GET_CLIP_INFO (Packet), + sizeof (IP6_CLIP_INFO) + ); + + NetIpSecNetbufFree(Packet); + *Netbuf = TxWrap->Packet; + + } else { + + IpSecWrap = AllocateZeroPool (sizeof (IP6_IPSEC_WRAP)); + + if (IpSecWrap == NULL) { + Status = EFI_OUT_OF_RESOURCES; + gBS->SignalEvent (RecycleEvent); + goto ON_EXIT; + } + + IpSecWrap->IpSecRecycleSignal = RecycleEvent; + IpSecWrap->Packet = Packet; + Packet = NetbufFromExt ( + FragmentTable, + FragmentCount, + IP6_MAX_HEADLEN, + 0, + Ip6IpSecFree, + IpSecWrap + ); + + if (Packet == NULL) { + Packet = IpSecWrap->Packet; + gBS->SignalEvent (RecycleEvent); + FreePool (IpSecWrap); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + if (Direction == EfiIPsecInBound && 0 != CompareMem (&ZeroHead, *Head, sizeof (EFI_IP6_HEADER))) { + + PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace ( + Packet, + sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, + NET_BUF_HEAD + ); + if (PacketHead == NULL) { + *Netbuf = Packet; + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (PacketHead, *Head, sizeof (EFI_IP6_HEADER)); + *Head = PacketHead; + Packet->Ip.Ip6 = PacketHead; + + if (*ExtHdrs != NULL) { + Buf = (UINT8 *) (PacketHead + 1); + CopyMem (Buf, *ExtHdrs, *ExtHdrsLen); + } + + NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, TRUE); + CopyMem ( + IP6_GET_CLIP_INFO (Packet), + IP6_GET_CLIP_INFO (IpSecWrap->Packet), + sizeof (IP6_CLIP_INFO) + ); + } + *Netbuf = Packet; + } + +ON_EXIT: + return Status; +} + +/** + Pre-process the IPv6 packet. First validates the IPv6 packet, and + then reassembles packet if it is necessary. + + @param[in] IpSb The IP6 service instance. + @param[in, out] Packet The received IP6 packet to be processed. + @param[in] Flag The link layer flag for the packet received, such + as multicast. + @param[out] Payload The pointer to the payload of the recieved packet. + it starts from the first byte of the extension header. + @param[out] LastHead The pointer of NextHeader of the last extension + header processed by IP6. + @param[out] ExtHdrsLen The length of the whole option. + @param[out] UnFragmentLen The length of unfragmented length of extension headers. + @param[out] Fragmented Indicate whether the packet is fragmented. + @param[out] Head The pointer to the EFI_IP6_Header. + + @retval EFI_SUCCESS The received packet is well format. + @retval EFI_INVALID_PARAMETER The received packet is malformed. + +**/ +EFI_STATUS +Ip6PreProcessPacket ( + IN IP6_SERVICE *IpSb, + IN OUT NET_BUF **Packet, + IN UINT32 Flag, + OUT UINT8 **Payload, + OUT UINT8 **LastHead, + OUT UINT32 *ExtHdrsLen, + OUT UINT32 *UnFragmentLen, + OUT BOOLEAN *Fragmented, + OUT EFI_IP6_HEADER **Head + ) +{ + UINT16 PayloadLen; + UINT16 TotalLen; + UINT32 FormerHeadOffset; + UINT32 HeadLen; + IP6_FRAGMENT_HEADER *FragmentHead; + UINT16 FragmentOffset; + IP6_CLIP_INFO *Info; + EFI_IPv6_ADDRESS Loopback; + + HeadLen = 0; + PayloadLen = 0; + // + // Check whether the input packet is a valid packet + // + if ((*Packet)->TotalSize < IP6_MIN_HEADLEN) { + return EFI_INVALID_PARAMETER; + } + + // + // Get header information of the packet. + // + *Head = (EFI_IP6_HEADER *) NetbufGetByte (*Packet, 0, NULL); + if (*Head == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Multicast addresses must not be used as source addresses in IPv6 packets. + // + if (((*Head)->Version != 6) || (IP6_IS_MULTICAST (&(*Head)->SourceAddress))) { + return EFI_INVALID_PARAMETER; + } + + // + // A packet with a destination address of loopback ::1/128 or unspecified must be dropped. + // + ZeroMem (&Loopback, sizeof (EFI_IPv6_ADDRESS)); + Loopback.Addr[15] = 0x1; + if ((CompareMem (&Loopback, &(*Head)->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) || + (NetIp6IsUnspecifiedAddr (&(*Head)->DestinationAddress))) { + return EFI_INVALID_PARAMETER; + } + + // + // Convert the IP header to host byte order. + // + (*Packet)->Ip.Ip6 = Ip6NtohHead (*Head); + + // + // Get the per packet info. + // + Info = IP6_GET_CLIP_INFO (*Packet); + Info->LinkFlag = Flag; + Info->CastType = 0; + + if (IpSb->MnpConfigData.EnablePromiscuousReceive) { + Info->CastType = Ip6Promiscuous; + } + + if (Ip6IsOneOfSetAddress (IpSb, &(*Head)->DestinationAddress, NULL, NULL)) { + Info->CastType = Ip6Unicast; + } else if (IP6_IS_MULTICAST (&(*Head)->DestinationAddress)) { + if (Ip6FindMldEntry (IpSb, &(*Head)->DestinationAddress) != NULL) { + Info->CastType = Ip6Multicast; + } + } + + // + // Drop the packet that is not delivered to us. + // + if (Info->CastType == 0) { + return EFI_INVALID_PARAMETER; + } + + + PayloadLen = (*Head)->PayloadLength; + + Info->Start = 0; + Info->Length = PayloadLen; + Info->End = Info->Start + Info->Length; + Info->HeadLen = (UINT16) sizeof (EFI_IP6_HEADER); + Info->Status = EFI_SUCCESS; + Info->LastFrag = FALSE; + + TotalLen = (UINT16) (PayloadLen + sizeof (EFI_IP6_HEADER)); + + // + // Mnp may deliver frame trailer sequence up, trim it off. + // + if (TotalLen < (*Packet)->TotalSize) { + NetbufTrim (*Packet, (*Packet)->TotalSize - TotalLen, FALSE); + } + + if (TotalLen != (*Packet)->TotalSize) { + return EFI_INVALID_PARAMETER; + } + + // + // Check the extension headers, if exist validate them + // + if (PayloadLen != 0) { + *Payload = AllocatePool ((UINTN) PayloadLen); + if (*Payload == NULL) { + return EFI_INVALID_PARAMETER; + } + + NetbufCopy (*Packet, sizeof (EFI_IP6_HEADER), PayloadLen, *Payload); + } + + if (!Ip6IsExtsValid ( + IpSb, + *Packet, + &(*Head)->NextHeader, + *Payload, + (UINT32) PayloadLen, + TRUE, + &FormerHeadOffset, + LastHead, + ExtHdrsLen, + UnFragmentLen, + Fragmented + )) { + return EFI_INVALID_PARAMETER; + } + + HeadLen = sizeof (EFI_IP6_HEADER) + *UnFragmentLen; + + if (*Fragmented) { + // + // Get the fragment offset from the Fragment header + // + FragmentHead = (IP6_FRAGMENT_HEADER *) NetbufGetByte (*Packet, HeadLen, NULL); + if (FragmentHead == NULL) { + return EFI_INVALID_PARAMETER; + } + + FragmentOffset = NTOHS (FragmentHead->FragmentOffset); + + if ((FragmentOffset & 0x1) == 0) { + Info->LastFrag = TRUE; + } + + FragmentOffset &= (~0x1); + + // + // This is the first fragment of the packet + // + if (FragmentOffset == 0) { + Info->NextHeader = FragmentHead->NextHeader; + } + + Info->HeadLen = (UINT16) HeadLen; + HeadLen += sizeof (IP6_FRAGMENT_HEADER); + Info->Start = FragmentOffset; + Info->Length = TotalLen - (UINT16) HeadLen; + Info->End = Info->Start + Info->Length; + Info->Id = FragmentHead->Identification; + Info->FormerNextHeader = FormerHeadOffset; + + // + // Fragments should in the unit of 8 octets long except the last one. + // + if ((Info->LastFrag == 0) && (Info->Length % 8 != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // Reassemble the packet. + // + *Packet = Ip6Reassemble (&IpSb->Assemble, *Packet); + if (*Packet == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Re-check the assembled packet to get the right values. + // + *Head = (*Packet)->Ip.Ip6; + PayloadLen = (*Head)->PayloadLength; + if (PayloadLen != 0) { + if (*Payload != NULL) { + FreePool (*Payload); + } + + *Payload = AllocatePool ((UINTN) PayloadLen); + if (*Payload == NULL) { + return EFI_INVALID_PARAMETER; + } + + NetbufCopy (*Packet, sizeof (EFI_IP6_HEADER), PayloadLen, *Payload); + } + + if (!Ip6IsExtsValid ( + IpSb, + *Packet, + &(*Head)->NextHeader, + *Payload, + (UINT32) PayloadLen, + TRUE, + NULL, + LastHead, + ExtHdrsLen, + UnFragmentLen, + Fragmented + )) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Trim the head off, after this point, the packet is headless. + // and Packet->TotalLen == Info->Length. + // + NetbufTrim (*Packet, sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, TRUE); + + return EFI_SUCCESS; +} + +/** + The IP6 input routine. It is called by the IP6_INTERFACE when an + IP6 fragment is received from MNP. + + @param[in] Packet The IP6 packet received. + @param[in] IoStatus The return status of receive request. + @param[in] Flag The link layer flag for the packet received, such + as multicast. + @param[in] Context The IP6 service instance that owns the MNP. + +**/ +VOID +Ip6AcceptFrame ( + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ) +{ + IP6_SERVICE *IpSb; + EFI_IP6_HEADER *Head; + UINT8 *Payload; + UINT8 *LastHead; + UINT32 UnFragmentLen; + UINT32 ExtHdrsLen; + BOOLEAN Fragmented; + EFI_STATUS Status; + EFI_IP6_HEADER ZeroHead; + + IpSb = (IP6_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Payload = NULL; + LastHead = NULL; + + // + // Check input parameters + // + if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) { + goto Drop; + } + + // + // Pre-Process the Ipv6 Packet and then reassemble if it is necessary. + // + Status = Ip6PreProcessPacket ( + IpSb, + &Packet, + Flag, + &Payload, + &LastHead, + &ExtHdrsLen, + &UnFragmentLen, + &Fragmented, + &Head + ); + if (EFI_ERROR (Status)) { + goto Restart; + } + // + // After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer, + // and no need consider any other ahead ext headers. + // + Status = Ip6IpSecProcessPacket ( + IpSb, + &Head, + LastHead, // need get the lasthead value for input + &Packet, + &Payload, + &ExtHdrsLen, + EfiIPsecInBound, + NULL + ); + + if (EFI_ERROR (Status)) { + goto Restart; + } + + // + // If the packet is protected by IPsec Tunnel Mode, Check the Inner Ip Packet. + // + ZeroMem (&ZeroHead, sizeof (EFI_IP6_HEADER)); + if (0 == CompareMem (Head, &ZeroHead, sizeof (EFI_IP6_HEADER))) { + Status = Ip6PreProcessPacket ( + IpSb, + &Packet, + Flag, + &Payload, + &LastHead, + &ExtHdrsLen, + &UnFragmentLen, + &Fragmented, + &Head + ); + if (EFI_ERROR (Status)) { + goto Restart; + } + } + + // + // Check the Packet again. + // + if (Packet == NULL) { + goto Restart; + } + + // + // Packet may have been changed. The ownership of the packet + // is transfered to the packet process logic. + // + Head = Packet->Ip.Ip6; + IP6_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS; + + switch (*LastHead) { + case IP6_ICMP: + Ip6IcmpHandle (IpSb, Head, Packet); + break; + default: + Ip6Demultiplex (IpSb, Head, Packet); + } + + Packet = NULL; + + // + // Dispatch the DPCs queued by the NotifyFunction of the rx token's events + // which are signaled with received data. + // + DispatchDpc (); + +Restart: + if (Payload != NULL) { + FreePool (Payload); + } + + Ip6ReceiveFrame (Ip6AcceptFrame, IpSb); + +Drop: + if (Packet != NULL) { + NetbufFree (Packet); + } + + return ; +} + +/** + Initialize an already allocated assemble table. This is generally + the assemble table embedded in the IP6 service instance. + + @param[in, out] Table The assemble table to initialize. + +**/ +VOID +Ip6CreateAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ) +{ + UINT32 Index; + + for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) { + InitializeListHead (&Table->Bucket[Index]); + } +} + +/** + Clean up the assemble table by removing all of the fragments + and assemble entries. + + @param[in, out] Table The assemble table to clean up. + +**/ +VOID +Ip6CleanAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ASSEMBLE_ENTRY *Assemble; + UINT32 Index; + + for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link); + + RemoveEntryList (Entry); + Ip6FreeAssembleEntry (Assemble); + } + } +} + + +/** + The signal handle of IP6's recycle event. It is called back + when the upper layer releases the packet. + + @param[in] Event The IP6's recycle event. + @param[in] Context The context of the handle, which is a IP6_RXDATA_WRAP. + +**/ +VOID +EFIAPI +Ip6OnRecyclePacket ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_RXDATA_WRAP *Wrap; + + Wrap = (IP6_RXDATA_WRAP *) Context; + + EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock); + RemoveEntryList (&Wrap->Link); + EfiReleaseLock (&Wrap->IpInstance->RecycleLock); + + ASSERT (!NET_BUF_SHARED (Wrap->Packet)); + NetbufFree (Wrap->Packet); + + gBS->CloseEvent (Wrap->RxData.RecycleSignal); + FreePool (Wrap); +} + +/** + Wrap the received packet to a IP6_RXDATA_WRAP, which will be + delivered to the upper layer. Each IP6 child that accepts the + packet will get a not-shared copy of the packet which is wrapped + in the IP6_RXDATA_WRAP. The IP6_RXDATA_WRAP->RxData is passed + to the upper layer. The upper layer will signal the recycle event in + it when it is done with the packet. + + @param[in] IpInstance The IP6 child to receive the packet. + @param[in] Packet The packet to deliver up. + + @return NULL if it failed to wrap the packet; otherwise, the wrapper. + +**/ +IP6_RXDATA_WRAP * +Ip6WrapRxData ( + IN IP6_PROTOCOL *IpInstance, + IN NET_BUF *Packet + ) +{ + IP6_RXDATA_WRAP *Wrap; + EFI_IP6_RECEIVE_DATA *RxData; + EFI_STATUS Status; + + Wrap = AllocatePool (IP6_RXDATA_WRAP_SIZE (Packet->BlockOpNum)); + + if (Wrap == NULL) { + return NULL; + } + + InitializeListHead (&Wrap->Link); + + Wrap->IpInstance = IpInstance; + Wrap->Packet = Packet; + RxData = &Wrap->RxData; + + ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME)); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6OnRecyclePacket, + Wrap, + &RxData->RecycleSignal + ); + + if (EFI_ERROR (Status)) { + FreePool (Wrap); + return NULL; + } + + ASSERT (Packet->Ip.Ip6 != NULL); + + // + // The application expects a network byte order header. + // + RxData->HeaderLength = sizeof (EFI_IP6_HEADER); + RxData->Header = (EFI_IP6_HEADER *) Ip6NtohHead (Packet->Ip.Ip6); + RxData->DataLength = Packet->TotalSize; + + // + // Build the fragment table to be delivered up. + // + RxData->FragmentCount = Packet->BlockOpNum; + NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount); + + return Wrap; +} + +/** + Check whether this IP child accepts the packet. + + @param[in] IpInstance The IP child to check. + @param[in] Head The IP header of the packet. + @param[in] Packet The data of the packet. + + @retval TRUE The child wants to receive the packet. + @retval FALSE The child does not want to receive the packet. + +**/ +BOOLEAN +Ip6InstanceFrameAcceptable ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_ERROR_HEAD Icmp; + EFI_IP6_CONFIG_DATA *Config; + IP6_CLIP_INFO *Info; + UINT8 *Proto; + UINT32 Index; + UINT8 *ExtHdrs; + UINT16 ErrMsgPayloadLen; + UINT8 *ErrMsgPayload; + + Config = &IpInstance->ConfigData; + Proto = NULL; + + // + // Dirty trick for the Tiano UEFI network stack implmentation. If + // ReceiveTimeout == -1, the receive of the packet for this instance + // is disabled. The UEFI spec don't have such captibility. We add + // this to improve the performance because IP will make a copy of + // the received packet for each accepting instance. Some IP instances + // used by UDP/TCP only send packets, they don't wants to receive. + // + if (Config->ReceiveTimeout == (UINT32)(-1)) { + return FALSE; + } + + if (Config->AcceptPromiscuous) { + return TRUE; + } + + // + // Check whether the protocol is acceptable. + // + ExtHdrs = NetbufGetByte (Packet, 0, NULL); + + if (!Ip6IsExtsValid ( + IpInstance->Service, + Packet, + &Head->NextHeader, + ExtHdrs, + (UINT32) Head->PayloadLength, + TRUE, + NULL, + &Proto, + NULL, + NULL, + NULL + )) { + return FALSE; + } + + // + // The upper layer driver may want to receive the ICMPv6 error packet + // invoked by its packet, like UDP. + // + if ((*Proto == IP6_ICMP) && (!Config->AcceptAnyProtocol) && (*Proto != Config->DefaultProtocol)) { + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + if (Icmp.Head.Type <= ICMP_V6_ERROR_MAX) { + if (!Config->AcceptIcmpErrors) { + return FALSE; + } + + // + // Get the protocol of the invoking packet of ICMPv6 error packet. + // + ErrMsgPayloadLen = NTOHS (Icmp.IpHead.PayloadLength); + ErrMsgPayload = NetbufGetByte (Packet, sizeof (Icmp), NULL); + + if (!Ip6IsExtsValid ( + NULL, + NULL, + &Icmp.IpHead.NextHeader, + ErrMsgPayload, + ErrMsgPayloadLen, + TRUE, + NULL, + &Proto, + NULL, + NULL, + NULL + )) { + return FALSE; + } + } + } + + // + // Match the protocol + // + if (!Config->AcceptAnyProtocol && (*Proto != Config->DefaultProtocol)) { + return FALSE; + } + + // + // Check for broadcast, the caller has computed the packet's + // cast type for this child's interface. + // + Info = IP6_GET_CLIP_INFO (Packet); + + // + // If it is a multicast packet, check whether we are in the group. + // + if (Info->CastType == Ip6Multicast) { + // + // Receive the multicast if the instance wants to receive all packets. + // + if (NetIp6IsUnspecifiedAddr (&IpInstance->ConfigData.StationAddress)) { + return TRUE; + } + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, &Head->DestinationAddress)) { + break; + } + } + + return (BOOLEAN)(Index < IpInstance->GroupCount); + } + + return TRUE; +} + +/** + Enqueue a shared copy of the packet to the IP6 child if the + packet is acceptable to it. Here the data of the packet is + shared, but the net buffer isn't. + + @param IpInstance The IP6 child to enqueue the packet to. + @param Head The IP header of the received packet. + @param Packet The data of the received packet. + + @retval EFI_NOT_STARTED The IP child hasn't been configured. + @retval EFI_INVALID_PARAMETER The child doesn't want to receive the packet. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources + @retval EFI_SUCCESS A shared copy the packet is enqueued to the child. + +**/ +EFI_STATUS +Ip6InstanceEnquePacket ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_CLIP_INFO *Info; + NET_BUF *Clone; + + // + // Check whether the packet is acceptable to this instance. + // + if (IpInstance->State != IP6_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (!Ip6InstanceFrameAcceptable (IpInstance, Head, Packet)) { + return EFI_INVALID_PARAMETER; + } + + // + // Enque a shared copy of the packet. + // + Clone = NetbufClone (Packet); + + if (Clone == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the receive time out for the assembled packet. If it expires, + // packet will be removed from the queue. + // + Info = IP6_GET_CLIP_INFO (Clone); + Info->Life = IP6_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout); + + InsertTailList (&IpInstance->Received, &Clone->List); + return EFI_SUCCESS; +} + +/** + Deliver the received packets to the upper layer if there are both received + requests and enqueued packets. If the enqueued packet is shared, it will + duplicate it to a non-shared packet, release the shared packet, then + deliver the non-shared packet up. + + @param[in] IpInstance The IP child to deliver the packet up. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the + packets. + @retval EFI_SUCCESS All the enqueued packets that can be delivered + are delivered up. + +**/ +EFI_STATUS +Ip6InstanceDeliverPacket ( + IN IP6_PROTOCOL *IpInstance + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + IP6_RXDATA_WRAP *Wrap; + NET_BUF *Packet; + NET_BUF *Dup; + UINT8 *Head; + + // + // Deliver a packet if there are both a packet and a receive token. + // + while (!IsListEmpty (&IpInstance->Received) && !NetMapIsEmpty (&IpInstance->RxTokens)) { + + Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List); + + if (!NET_BUF_SHARED (Packet)) { + // + // If this is the only instance that wants the packet, wrap it up. + // + Wrap = Ip6WrapRxData (IpInstance, Packet); + + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RemoveEntryList (&Packet->List); + + } else { + // + // Create a duplicated packet if this packet is shared + // + Dup = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER)); + + if (Dup == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the IP head over. The packet to deliver up is + // headless. Trim the head off after copy. The IP head + // may be not continuous before the data. + // + Head = NetbufAllocSpace (Dup, sizeof (EFI_IP6_HEADER), NET_BUF_HEAD); + ASSERT (Head != NULL); + Dup->Ip.Ip6 = (EFI_IP6_HEADER *) Head; + + CopyMem (Head, Packet->Ip.Ip6, sizeof (EFI_IP6_HEADER)); + NetbufTrim (Dup, sizeof (EFI_IP6_HEADER), TRUE); + + Wrap = Ip6WrapRxData (IpInstance, Dup); + + if (Wrap == NULL) { + NetbufFree (Dup); + return EFI_OUT_OF_RESOURCES; + } + + RemoveEntryList (&Packet->List); + NetbufFree (Packet); + + Packet = Dup; + } + + // + // Insert it into the delivered packet, then get a user's + // receive token, pass the wrapped packet up. + // + EfiAcquireLockOrFail (&IpInstance->RecycleLock); + InsertHeadList (&IpInstance->Delivered, &Wrap->Link); + EfiReleaseLock (&IpInstance->RecycleLock); + + Token = NetMapRemoveHead (&IpInstance->RxTokens, NULL); + Token->Status = IP6_GET_CLIP_INFO (Packet)->Status; + Token->Packet.RxData = &Wrap->RxData; + + gBS->SignalEvent (Token->Event); + } + + return EFI_SUCCESS; +} + +/** + Enqueue a received packet to all the IP children that share + the same interface. + + @param[in] IpSb The IP6 service instance that receive the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + @param[in] IpIf The interface to enqueue the packet to. + + @return The number of the IP6 children that accepts the packet. + +**/ +INTN +Ip6InterfaceEnquePacket ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet, + IN IP6_INTERFACE *IpIf + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_CLIP_INFO *Info; + LIST_ENTRY *Entry; + INTN Enqueued; + INTN LocalType; + INTN SavedType; + + // + // First, check that the packet is acceptable to this interface + // and find the local cast type for the interface. + // + LocalType = 0; + Info = IP6_GET_CLIP_INFO (Packet); + + if (IpIf->PromiscRecv) { + LocalType = Ip6Promiscuous; + } else { + LocalType = Info->CastType; + } + + // + // Iterate through the ip instances on the interface, enqueue + // the packet if filter passed. Save the original cast type, + // and pass the local cast type to the IP children on the + // interface. The global cast type will be restored later. + // + SavedType = Info->CastType; + Info->CastType = (UINT32) LocalType; + + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink); + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + + if (Ip6InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) { + Enqueued++; + } + } + + Info->CastType = (UINT32) SavedType; + return Enqueued; +} + +/** + Deliver the packet for each IP6 child on the interface. + + @param[in] IpSb The IP6 service instance that received the packet. + @param[in] IpIf The IP6 interface to deliver the packet. + +**/ +VOID +Ip6InterfaceDeliverPacket ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *IpIf + ) +{ + IP6_PROTOCOL *IpInstance; + LIST_ENTRY *Entry; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink); + Ip6InstanceDeliverPacket (IpInstance); + } +} + +/** + De-multiplex the packet. the packet delivery is processed in two + passes. The first pass will enqueue a shared copy of the packet + to each IP6 child that accepts the packet. The second pass will + deliver a non-shared copy of the packet to each IP6 child that + has pending receive requests. Data is copied if more than one + child wants to consume the packet, because each IP child needs + its own copy of the packet to make changes. + + @param[in] IpSb The IP6 service instance that received the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + + @retval EFI_NOT_FOUND No IP child accepts the packet. + @retval EFI_SUCCESS The packet is enqueued or delivered to some IP + children. + +**/ +EFI_STATUS +Ip6Demultiplex ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + + LIST_ENTRY *Entry; + IP6_INTERFACE *IpIf; + INTN Enqueued; + + // + // Two pass delivery: first, enque a shared copy of the packet + // to each instance that accept the packet. + // + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + if (IpIf->Configured) { + Enqueued += Ip6InterfaceEnquePacket (IpSb, Head, Packet, IpIf); + } + } + + // + // Second: deliver a duplicate of the packet to each instance. + // Release the local reference first, so that the last instance + // getting the packet will not copy the data. + // + NetbufFree (Packet); + Packet = NULL; + + if (Enqueued == 0) { + return EFI_NOT_FOUND; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + if (IpIf->Configured) { + Ip6InterfaceDeliverPacket (IpSb, IpIf); + } + } + + return EFI_SUCCESS; +} + +/** + Decrease the life of the transmitted packets. If it is + decreased to zero, cancel the packet. This function is + called by Ip6packetTimerTicking that provides timeout for both the + received-but-not-delivered and transmitted-but-not-recycle + packets. + + @param[in] Map The IP6 child's transmit map. + @param[in] Item Current transmitted packet. + @param[in] Context Not used. + + @retval EFI_SUCCESS Always returns EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +Ip6SentPacketTicking ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + IP6_TXTOKEN_WRAP *Wrap; + + Wrap = (IP6_TXTOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + if ((Wrap->Life > 0) && (--Wrap->Life == 0)) { + Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED); + } + + return EFI_SUCCESS; +} + +/** + Timeout the fragments, and the enqueued, and transmitted packets. + + @param[in] IpSb The IP6 service instance to timeout. + +**/ +VOID +Ip6PacketTimerTicking ( + IN IP6_SERVICE *IpSb + ) +{ + LIST_ENTRY *InstanceEntry; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_PROTOCOL *IpInstance; + IP6_ASSEMBLE_ENTRY *Assemble; + NET_BUF *Packet; + IP6_CLIP_INFO *Info; + UINT32 Index; + + // + // First, time out the fragments. The packet's life is counting down + // once the first-arriving fragment of that packet was received. + // + for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &(IpSb->Assemble.Bucket[Index])) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link); + + if ((Assemble->Life > 0) && (--Assemble->Life == 0)) { + // + // If the first fragment (the one with a Fragment Offset of zero) + // has been received, an ICMP Time Exceeded - Fragment Reassembly + // Time Exceeded message should be sent to the source of that fragment. + // + if ((Assemble->Packet != NULL) && + !IP6_IS_MULTICAST (&Assemble->Head->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Assemble->Packet, + NULL, + &Assemble->Head->SourceAddress, + ICMP_V6_TIME_EXCEEDED, + ICMP_V6_TIMEOUT_REASSEMBLE, + NULL + ); + } + + // + // If reassembly of a packet is not completed within 60 seconds of + // the reception of the first-arriving fragment of that packet, the + // reassembly must be abandoned and all the fragments that have been + // received for that packet must be discarded. + // + RemoveEntryList (Entry); + Ip6FreeAssembleEntry (Assemble); + } + } + } + + NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP6_PROTOCOL, Link); + + // + // Second, time out the assembled packets enqueued on each IP child. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) { + Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Info = IP6_GET_CLIP_INFO (Packet); + + if ((Info->Life > 0) && (--Info->Life == 0)) { + RemoveEntryList (Entry); + NetbufFree (Packet); + } + } + + // + // Third: time out the transmitted packets. + // + NetMapIterate (&IpInstance->TxTokens, Ip6SentPacketTicking, NULL); + } +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Input.h b/Core/NetworkPkg/Ip6Dxe/Ip6Input.h new file mode 100644 index 0000000000..4d7ffc1c4f --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Input.h @@ -0,0 +1,235 @@ +/** @file + IP6 internal functions and definitions to process the incoming packets. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_INPUT_H__ +#define __EFI_IP6_INPUT_H__ + +#define IP6_MIN_HEADLEN 40 +#define IP6_MAX_HEADLEN 120 +/// +/// 8(ESP header) + 16(max IV) + 16(max padding) + 2(ESP tail) + 12(max ICV) = 54 +/// +#define IP6_MAX_IPSEC_HEADLEN 54 + + +#define IP6_ASSEMLE_HASH_SIZE 127 +/// +/// Lift time in seconds. +/// +#define IP6_FRAGMENT_LIFE 60 +#define IP6_MAX_PACKET_SIZE 65535 + + +#define IP6_GET_CLIP_INFO(Packet) ((IP6_CLIP_INFO *) ((Packet)->ProtoData)) + +#define IP6_ASSEMBLE_HASH(Dst, Src, Id) \ + ((*((UINT32 *) (Dst)) + *((UINT32 *) (Src)) + (Id)) % IP6_ASSEMLE_HASH_SIZE) + +#define IP6_RXDATA_WRAP_SIZE(NumFrag) \ + (sizeof (IP6_RXDATA_WRAP) + sizeof (EFI_IP6_FRAGMENT_DATA) * ((NumFrag) - 1)) + +// +// Per packet information for input process. LinkFlag specifies whether +// the packet is received as Link layer unicast, multicast or broadcast. +// The CastType is the IP layer cast type, such as IP multicast or unicast. +// Start, End and Length are staffs used to assemble the packets. Start +// is the sequence number of the first byte of data in the packet. Length +// is the number of bytes of data. End = Start + Length, that is, the +// sequence number of last byte + 1. Each assembled packet has a count down +// life. If it isn't consumed before Life reaches zero, the packet is released. +// +typedef struct { + UINT32 LinkFlag; + INT32 CastType; + INT32 Start; + INT32 End; + INT32 Length; + UINT32 Life; + EFI_STATUS Status; + UINT32 Id; + UINT16 HeadLen; + UINT8 NextHeader; + UINT8 LastFrag; + UINT32 FormerNextHeader; +} IP6_CLIP_INFO; + +// +// Structure used to assemble IP packets. +// +typedef struct { + LIST_ENTRY Link; + LIST_ENTRY Fragments; // List of all the fragments of this packet + + // + // Identity of one IP6 packet. Each fragment of a packet has + // the same (Dst, Src, Id). + // + EFI_IPv6_ADDRESS Dst; + EFI_IPv6_ADDRESS Src; + UINT32 Id; + + UINT32 TotalLen; + UINT32 CurLen; + UINT32 Life; // Count down life for the packet. + + EFI_IP6_HEADER *Head; // IP head of the first fragment + IP6_CLIP_INFO *Info; // Per packet information of the first fragment + NET_BUF *Packet; // The first fragment of the packet +} IP6_ASSEMBLE_ENTRY; + +// +// Each Ip service instance has an assemble table to reassemble +// the packets before delivery to its children. It is organized +// as hash table. +// +typedef struct { + LIST_ENTRY Bucket[IP6_ASSEMLE_HASH_SIZE]; +} IP6_ASSEMBLE_TABLE; + +/** + The IP6 input routine. It is called by the IP6_INTERFACE when an + IP6 fragment is received from MNP. + + @param[in] Packet The IP6 packet received. + @param[in] IoStatus The return status of receive request. + @param[in] Flag The link layer flag for the packet received, such + as multicast. + @param[in] Context The IP6 service instance that own the MNP. + +**/ +VOID +Ip6AcceptFrame ( + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ); + +/** + Deliver the received packets to upper layer if there are both received + requests and enqueued packets. If the enqueued packet is shared, it will + duplicate it to a non-shared packet, release the shared packet, then + deliver the non-shared packet up. + + @param[in] IpInstance The IP child to deliver the packet up. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the + packets. + @retval EFI_SUCCESS All the enqueued packets that can be delivered + are delivered up. + +**/ +EFI_STATUS +Ip6InstanceDeliverPacket ( + IN IP6_PROTOCOL *IpInstance + ); + +/** + The work function to locate the IPsec protocol to process the inbound or + outbound IP packets. The process routine handles the packet with the following + actions: bypass the packet, discard the packet, or protect the packet. + + @param[in] IpSb The IP6 service instance. + @param[in, out] Head The caller-supplied IP6 header. + @param[in, out] LastHead The next header field of last IP header. + @param[in, out] Netbuf The IP6 packet to be processed by IPsec. + @param[in, out] ExtHdrs The caller-supplied options. + @param[in, out] ExtHdrsLen The length of the option. + @param[in] Direction The directionality in an SPD entry, + EfiIPsecInBound, or EfiIPsecOutBound. + @param[in] Context The token's wrap. + + @retval EFI_SUCCESS The IPsec protocol is not available or disabled. + @retval EFI_SUCCESS The packet was bypassed, and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + @retval EFI_OUT_OF_RESOURCES There are not suffcient resources to complete the operation. + @retval EFI_BUFFER_TOO_SMALL The number of non-empty blocks is bigger than the + number of input data blocks when building a fragment table. + +**/ +EFI_STATUS +Ip6IpSecProcessPacket ( + IN IP6_SERVICE *IpSb, + IN OUT EFI_IP6_HEADER **Head, + IN OUT UINT8 *LastHead, + IN OUT NET_BUF **Netbuf, + IN OUT UINT8 **ExtHdrs, + IN OUT UINT32 *ExtHdrsLen, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN VOID *Context + ); + +/** + Initialize an already allocated assemble table. This is generally + the assemble table embedded in the IP6 service instance. + + @param[in, out] Table The assemble table to initialize. + +**/ +VOID +Ip6CreateAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ); + +/** + Clean up the assemble table: remove all the fragments + and assemble entries. + + @param[in, out] Table The assemble table to clean up. + +**/ +VOID +Ip6CleanAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ); + +/** + Demultiple the packet. the packet delivery is processed in two + passes. The first pass will enque a shared copy of the packet + to each IP6 child that accepts the packet. The second pass will + deliver a non-shared copy of the packet to each IP6 child that + has pending receive requests. Data is copied if more than one + child wants to consume the packet bacause each IP child need + its own copy of the packet to make changes. + + @param[in] IpSb The IP6 service instance that received the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + + @retval EFI_NOT_FOUND No IP child accepts the packet. + @retval EFI_SUCCESS The packet is enqueued or delivered to some IP + children. + +**/ +EFI_STATUS +Ip6Demultiplex ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Timeout the fragmented, enqueued, and transmitted packets. + + @param[in] IpSb The IP6 service instance to timeout. + +**/ +VOID +Ip6PacketTimerTicking ( + IN IP6_SERVICE *IpSb + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Mld.c b/Core/NetworkPkg/Ip6Dxe/Ip6Mld.c new file mode 100644 index 0000000000..4a418fade5 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Mld.c @@ -0,0 +1,908 @@ +/** @file + Multicast Listener Discovery support routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +/** + Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data. + + @param[in, out] IpSb Points to IP6 service binding instance. + @param[in] MulticastAddr The IPv6 multicast address to be recorded. + @param[in] DelayTimer The maximum allowed delay before sending a responding + report, in units of milliseconds. + @return The created IP6_ML_GROUP list entry or NULL. + +**/ +IP6_MLD_GROUP * +Ip6CreateMldEntry ( + IN OUT IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr, + IN UINT32 DelayTimer + ) +{ + IP6_MLD_GROUP *Entry; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + Entry = AllocatePool (sizeof (IP6_MLD_GROUP)); + if (Entry != NULL) { + Entry->RefCnt = 1; + Entry->DelayTimer = DelayTimer; + Entry->SendByUs = FALSE; + IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr); + InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link); + } + + return Entry; +} + +/** + Search a IP6_MLD_GROUP list entry node from a list array. + + @param[in] IpSb Points to IP6 service binding instance. + @param[in] MulticastAddr The IPv6 multicast address to be searched. + + @return The found IP6_ML_GROUP list entry or NULL. + +**/ +IP6_MLD_GROUP * +Ip6FindMldEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + LIST_ENTRY *Entry; + IP6_MLD_GROUP *Group; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) { + return Group; + } + } + + return NULL; +} + +/** + Count the number of IP6 multicast groups that are mapped to the + same MAC address. Several IP6 multicast address may be mapped to + the same MAC address. + + @param[in] MldCtrl The MLD control block to search in. + @param[in] Mac The MAC address to search. + + @return The number of the IP6 multicast group that mapped to the same + multicast group Mac. + +**/ +INTN +Ip6FindMac ( + IN IP6_MLD_SERVICE_DATA *MldCtrl, + IN EFI_MAC_ADDRESS *Mac + ) +{ + LIST_ENTRY *Entry; + IP6_MLD_GROUP *Group; + INTN Count; + + Count = 0; + + NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + + if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) { + Count++; + } + } + + return Count; +} + +/** + Generate MLD report message and send it out to MulticastAddr. + + @param[in] IpSb The IP service to send the packet. + @param[in] Interface The IP interface to send the packet. + If NULL, a system interface will be selected. + @param[in] MulticastAddr The specific IPv6 multicast address to which + the message sender is listening. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the + operation. + @retval EFI_SUCCESS The MLD report message was successfully sent out. + +**/ +EFI_STATUS +Ip6SendMldReport ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + IP6_MLD_HEAD *MldHead; + NET_BUF *Packet; + EFI_IP6_HEADER Head; + UINT16 PayloadLen; + UINTN OptionLen; + UINT8 *Options; + EFI_STATUS Status; + UINT16 HeadChecksum; + UINT16 PseudoChecksum; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + // + // Generate the packet to be sent + // IPv6 basic header + Hop by Hop option + MLD message + // + + OptionLen = 0; + Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD)); + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // RFC3590: Use link-local address as source address if it is available, + // otherwise use the unspecified address. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_HOP_BY_HOP; + Head.HopLimit = 1; + IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr); + + // + // If Link-Local address is not ready, we use unspecified address. + // + IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header + // + Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE); + ASSERT (Options != NULL); + Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP); + if (EFI_ERROR (Status)) { + NetbufFree (Packet); + Packet = NULL; + return Status; + } + + // + // Fill in MLD message - Report + // + MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE); + ASSERT (MldHead != NULL); + ZeroMem (MldHead, sizeof (IP6_MLD_HEAD)); + MldHead->Head.Type = ICMP_V6_LISTENER_REPORT; + MldHead->Head.Code = 0; + IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr); + + HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD)); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &Head.SourceAddress, + &Head.DestinationAddress, + IP6_ICMP, + sizeof (IP6_MLD_HEAD) + ); + + MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum); + + // + // Transmit the packet + // + return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Generate MLD Done message and send it out to MulticastAddr. + + @param[in] IpSb The IP service to send the packet. + @param[in] MulticastAddr The specific IPv6 multicast address to which + the message sender is ceasing to listen. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the + operation. + @retval EFI_SUCCESS The MLD report message was successfully sent out. + +**/ +EFI_STATUS +Ip6SendMldDone ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + IP6_MLD_HEAD *MldHead; + NET_BUF *Packet; + EFI_IP6_HEADER Head; + UINT16 PayloadLen; + UINTN OptionLen; + UINT8 *Options; + EFI_STATUS Status; + EFI_IPv6_ADDRESS Destination; + UINT16 HeadChecksum; + UINT16 PseudoChecksum; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + // + // Generate the packet to be sent + // IPv6 basic header + Hop by Hop option + MLD message + // + + OptionLen = 0; + Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD)); + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_HOP_BY_HOP; + Head.HopLimit = 1; + + // + // If Link-Local address is not ready, we use unspecified address. + // + IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr); + + Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination); + IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header + // + Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE); + ASSERT (Options != NULL); + Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP); + if (EFI_ERROR (Status)) { + NetbufFree (Packet); + Packet = NULL; + return Status; + } + + // + // Fill in MLD message - Done + // + MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE); + ASSERT (MldHead != NULL); + ZeroMem (MldHead, sizeof (IP6_MLD_HEAD)); + MldHead->Head.Type = ICMP_V6_LISTENER_DONE; + MldHead->Head.Code = 0; + IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr); + + HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD)); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &Head.SourceAddress, + &Head.DestinationAddress, + IP6_ICMP, + sizeof (IP6_MLD_HEAD) + ); + + MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum); + + // + // Transmit the packet + // + return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Init the MLD data of the IP6 service instance. Configure + MNP to receive ALL SYSTEM multicast. + + @param[in] IpSb The IP6 service whose MLD is to be initialized. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resourcet to complete the + operation. + @retval EFI_SUCCESS The MLD module successfully initialized. + +**/ +EFI_STATUS +Ip6InitMld ( + IN IP6_SERVICE *IpSb + ) +{ + EFI_IPv6_ADDRESS AllNodes; + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + // + // Join the link-scope all-nodes multicast address (FF02::1). + // This address is started in Idle Listener state and never transitions to + // another state, and never sends a Report or Done for that address. + // + + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + + Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32) IP6_INFINIT_LIFETIME); + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Configure MNP to receive all-nodes multicast + // + Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ERROR; + } + + return EFI_SUCCESS; + +ERROR: + RemoveEntryList (&Group->Link); + FreePool (Group); + return Status; +} + +/** + Add a group address to the array of group addresses. + The caller should make sure that no duplicated address + existed in the array. + + @param[in, out] IpInstance Points to an IP6_PROTOCOL instance. + @param[in] Group The IP6 multicast address to add. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete + the operation. + @retval EFI_SUCESS The address is added to the group address array. + +**/ +EFI_STATUS +Ip6CombineGroups ( + IN OUT IP6_PROTOCOL *IpInstance, + IN EFI_IPv6_ADDRESS *Group + ) +{ + EFI_IPv6_ADDRESS *GroupList; + + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + ASSERT (Group != NULL && IP6_IS_MULTICAST (Group)); + + IpInstance->GroupCount++; + + GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS)); + if (GroupList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (IpInstance->GroupCount > 1) { + ASSERT (IpInstance->GroupList != NULL); + + CopyMem ( + GroupList, + IpInstance->GroupList, + (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS) + ); + + FreePool (IpInstance->GroupList); + } + + IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group); + + IpInstance->GroupList = GroupList; + + return EFI_SUCCESS; +} + +/** + Remove a group address from the array of group addresses. + Although the function doesn't assume the byte order of Group, + the network byte order is used by the caller. + + @param[in, out] IpInstance Points to an IP6_PROTOCOL instance. + @param[in] Group The IP6 multicast address to remove. + + @retval EFI_NOT_FOUND Cannot find the to be removed group address. + @retval EFI_SUCCESS The group address was successfully removed. + +**/ +EFI_STATUS +Ip6RemoveGroup ( + IN OUT IP6_PROTOCOL *IpInstance, + IN EFI_IPv6_ADDRESS *Group + ) +{ + UINT32 Index; + UINT32 Count; + + Count = IpInstance->GroupCount; + + for (Index = 0; Index < Count; Index++) { + if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) { + break; + } + } + + if (Index == Count) { + return EFI_NOT_FOUND; + } + + while (Index < Count - 1) { + IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1); + Index++; + } + + ASSERT (IpInstance->GroupCount > 0); + IpInstance->GroupCount--; + + return EFI_SUCCESS; +} + +/** + Join the multicast group on behalf of this IP6 service binding instance. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Interface Points to an IP6_INTERFACE structure. + @param[in] Address The group address to join. + + @retval EFI_SUCCESS Successfully join the multicast group. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval Others Failed to join the multicast group. + +**/ +EFI_STATUS +Ip6JoinGroup ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Address + ) +{ + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + Group = Ip6FindMldEntry (IpSb, Address); + if (Group != NULL) { + Group->RefCnt++; + return EFI_SUCCESS; + } + + // + // Repeat the report once or twcie after short delays [Unsolicited Report Interval] (default:10s) + // Simulate this operation as a Multicast-Address-Specific Query was received for that addresss. + // + Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL); + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Group->SendByUs = TRUE; + + Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ERROR; + } + + // + // Send unsolicited report when a node starts listening to a multicast address + // + Status = Ip6SendMldReport (IpSb, Interface, Address); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + return EFI_SUCCESS; + +ERROR: + RemoveEntryList (&Group->Link); + FreePool (Group); + return Status; +} + +/** + Leave the IP6 multicast group. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Address The group address to leave. + + @retval EFI_NOT_FOUND The IP6 service instance isn't in the group. + @retval EFI_SUCCESS Successfully leave the multicast group.. + @retval Others Failed to leave the multicast group. + +**/ +EFI_STATUS +Ip6LeaveGroup ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address + ) +{ + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + Group = Ip6FindMldEntry (IpSb, Address); + if (Group == NULL) { + return EFI_NOT_FOUND; + } + + // + // If more than one instance is in the group, decrease + // the RefCnt then return. + // + if ((Group->RefCnt > 0) && (--Group->RefCnt > 0)) { + return EFI_SUCCESS; + } + + // + // If multiple IP6 group addresses are mapped to the same + // multicast MAC address, don't configure the MNP to leave + // the MAC. + // + if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) { + Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + return Status; + } + } + + // + // Send a leave report if we are the last node to report + // + if (Group->SendByUs) { + Status = Ip6SendMldDone (IpSb, Address); + if (EFI_ERROR (Status)) { + return Status; + } + } + + RemoveEntryList (&Group->Link); + FreePool (Group); + + return EFI_SUCCESS; +} + +/** + Worker function for EfiIp6Groups(). The caller + should make sure that the parameters are valid. + + @param[in] IpInstance The IP6 child to change the setting. + @param[in] JoinFlag TRUE to join the group, otherwise leave it. + @param[in] GroupAddress The target group address. If NULL, leave all + the group addresses. + + @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it + @retval EFI_OUT_OF_RESOURCES Failed to allocate sufficient resources. + @retval EFI_DEVICE_ERROR Failed to set the group configuraton. + @retval EFI_SUCCESS Successfully updated the group setting. + @retval EFI_NOT_FOUND Try to leave the group which it isn't a member. + +**/ +EFI_STATUS +Ip6Groups ( + IN IP6_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + UINT32 Index; + EFI_IPv6_ADDRESS *Group; + + IpSb = IpInstance->Service; + + if (JoinFlag) { + ASSERT (GroupAddress != NULL); + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) { + return EFI_ALREADY_STARTED; + } + } + + Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress); + if (!EFI_ERROR (Status)) { + return Ip6CombineGroups (IpInstance, GroupAddress); + } + + return Status; + } + + // + // Leave the group. Leave all the groups if GroupAddress is NULL. + // + for (Index = IpInstance->GroupCount; Index > 0; Index--) { + Group = IpInstance->GroupList + (Index - 1); + + if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) { + Status = Ip6LeaveGroup (IpInstance->Service, Group); + if (EFI_ERROR (Status)) { + return Status; + } + + Ip6RemoveGroup (IpInstance, Group); + + if (IpInstance->GroupCount == 0) { + ASSERT (Index == 1); + FreePool (IpInstance->GroupList); + IpInstance->GroupList = NULL; + } + + if (GroupAddress != NULL) { + return EFI_SUCCESS; + } + } + } + + return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS); +} + +/** + Set a random value of the delay timer for the multicast address from the range + [0, Maximum Response Delay]. If a timer for any address is already + running, it is reset to the new random value only if the requested + Maximum Response Delay is less than the remaining value of the + running timer. If the Query packet specifies a Maximum Response + Delay of zero, each timer is effectively set to zero, and the action + specified below for timer expiration is performed immediately. + + @param[in] IpSb The IP6 service binding instance. + @param[in] MaxRespDelay The Maximum Response Delay, in milliseconds. + @param[in] MulticastAddr The multicast address. + @param[in, out] Group Points to a IP6_MLD_GROUP list entry node. + + @retval EFI_SUCCESS The delay timer is successfully updated or + timer expiration is performed immediately. + @retval Others Failed to send out MLD report message. + +**/ +EFI_STATUS +Ip6UpdateDelayTimer ( + IN IP6_SERVICE *IpSb, + IN UINT16 MaxRespDelay, + IN EFI_IPv6_ADDRESS *MulticastAddr, + IN OUT IP6_MLD_GROUP *Group + ) +{ + UINT32 Delay; + + // + // If the Query packet specifies a Maximum Response Delay of zero, perform timer + // expiration immediately. + // + if (MaxRespDelay == 0) { + Group->DelayTimer = 0; + return Ip6SendMldReport (IpSb, NULL, MulticastAddr); + } + + Delay = (UINT32) (MaxRespDelay / 1000); + + // + // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay] + // If a timer is already running, resets it if the request Maximum Response Delay + // is less than the remaining value of the running timer. + // + if (Group->DelayTimer == 0 || Delay < Group->DelayTimer) { + Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ()); + } + + return EFI_SUCCESS; +} + +/** + Process the Multicast Listener Query message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD query packet. + @param[in] Packet The content of the MLD query packet with IP head + removed. + + @retval EFI_SUCCESS The MLD query packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessMldQuery ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + EFI_IPv6_ADDRESS AllNodes; + IP6_MLD_GROUP *Group; + IP6_MLD_HEAD MldPacket; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + Status = EFI_INVALID_PARAMETER; + + // + // Check the validity of the packet, generic query or specific query + // + if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + + if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) { + goto Exit; + } + + // + // The Packet points to MLD report raw data without Hop-By-Hop option. + // + NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket); + MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay); + + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) { + // + // Receives a Multicast-Address-Specific Query, check it firstly + // + if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) { + goto Exit; + } + // + // The node is not listening but it receives the specific query. Just return. + // + Group = Ip6FindMldEntry (IpSb, &MldPacket.Group); + if (Group == NULL) { + Status = EFI_SUCCESS; + goto Exit; + } + + Status = Ip6UpdateDelayTimer ( + IpSb, + MldPacket.MaxRespDelay, + &MldPacket.Group, + Group + ); + goto Exit; + } + + // + // Receives a General Query, sets a delay timer for each multicast address it is listening + // + NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the Multicast Listener Report message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD report packet. + @param[in] Packet The content of the MLD report packet with IP head + removed. + + @retval EFI_SUCCESS The MLD report packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + +**/ +EFI_STATUS +Ip6ProcessMldReport ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_MLD_HEAD MldPacket; + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + Status = EFI_INVALID_PARAMETER; + + // + // Validate the incoming message, if invalid, drop it. + // + if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + + if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) { + goto Exit; + } + + // + // The Packet points to MLD report raw data without Hop-By-Hop option. + // + NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket); + if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) { + goto Exit; + } + + Group = Ip6FindMldEntry (IpSb, &MldPacket.Group); + if (Group == NULL) { + goto Exit; + } + + // + // The report is sent by another node, stop its own timer relates to the multicast address and clear + // + + if (!Group->SendByUs) { + Group->DelayTimer = 0; + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + The heartbeat timer of MLD module. It sends out a solicited MLD report when + DelayTimer expires. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6MldTimerTicking ( + IN IP6_SERVICE *IpSb + ) +{ + IP6_MLD_GROUP *Group; + LIST_ENTRY *Entry; + + // + // Send solicited report when timer expires + // + NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) { + Ip6SendMldReport (IpSb, NULL, &Group->Address); + } + } +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Mld.h b/Core/NetworkPkg/Ip6Dxe/Ip6Mld.h new file mode 100644 index 0000000000..e69f5d56b5 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Mld.h @@ -0,0 +1,198 @@ +/** @file + Multicast Listener Discovery support routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_MLD_H__ +#define __EFI_IP6_MLD_H__ + +#define IP6_UNSOLICITED_REPORT_INTERVAL 10 + +#pragma pack(1) +typedef struct { + IP6_ICMP_HEAD Head; + UINT16 MaxRespDelay; + UINT16 Reserved; + EFI_IPv6_ADDRESS Group; +} IP6_MLD_HEAD; +#pragma pack() + +// +// The status of multicast group. It isn't necessary to maintain +// explicit state of host state diagram. A group with finity +// DelayTime (less than 0xffffffff) is in "delaying listener" state. otherwise, it is in +// "idle listener" state. +// +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + EFI_IPv6_ADDRESS Address; + UINT32 DelayTimer; + BOOLEAN SendByUs; + EFI_MAC_ADDRESS Mac; +} IP6_MLD_GROUP; + +// +// The MLD status. Each IP6 service instance has a MLD_SERVICE_DATA +// attached. The Mldv1QuerySeen remember whether the server on this +// connected network is v1 or v2. +// +typedef struct { + INTN Mldv1QuerySeen; + LIST_ENTRY Groups; +} IP6_MLD_SERVICE_DATA; + +/** + Search a IP6_MLD_GROUP list entry node from a list array. + + @param[in] IpSb Points to an IP6 service binding instance. + @param[in] MulticastAddr The IPv6 multicast address to be searched. + + @return The found IP6_ML_GROUP list entry or NULL. + +**/ +IP6_MLD_GROUP * +Ip6FindMldEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr + ); + +/** + Init the MLD data of the IP6 service instance, configure + MNP to receive ALL SYSTEM multicasts. + + @param[in] IpSb The IP6 service whose MLD is to be initialized. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the + operation. + @retval EFI_SUCCESS The MLD module successfully initialized. + +**/ +EFI_STATUS +Ip6InitMld ( + IN IP6_SERVICE *IpSb + ); + +/** + Join the multicast group on behalf of this IP6 service binding instance. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Interface Points to an IP6_INTERFACE structure. + @param[in] Address The group address to join. + + @retval EFI_SUCCESS Successfully joined the multicast group. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval Others Failed to join the multicast group. + +**/ +EFI_STATUS +Ip6JoinGroup ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Address + ); + +/** + Leave the IP6 multicast group. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Address The group address to leave. + + @retval EFI_NOT_FOUND The IP6 service instance isn't in the group. + @retval EFI_SUCCESS Successfully left the multicast group. + @retval Others Failed to leave the multicast group. + +**/ +EFI_STATUS +Ip6LeaveGroup ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address + ); + +/** + Worker function for EfiIp6Groups(). The caller + should verify that the parameters are valid. + + @param[in] IpInstance The IP6 child to change the setting. + @param[in] JoinFlag TRUE to join the group, otherwise leave it. + @param[in] GroupAddress The target group address. If NULL, leave all + the group addresses. + + @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_DEVICE_ERROR Failed to set the group configuraton. + @retval EFI_SUCCESS Successfully updated the group setting. + @retval EFI_NOT_FOUND Tried to leave a group of whom it isn't a member. + +**/ +EFI_STATUS +Ip6Groups ( + IN IP6_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ); + +/** + Process the Multicast Listener Query message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD query packet. + @param[in] Packet The content of the MLD query packet with IP head + removed. + + @retval EFI_SUCCESS The MLD query packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessMldQuery ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the Multicast Listener Report message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD report packet. + @param[in] Packet The content of the MLD report packet with IP head + removed. + + @retval EFI_SUCCESS The MLD report packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + +**/ +EFI_STATUS +Ip6ProcessMldReport ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + + +/** + The heartbeat timer of the MLD module. It sends out solicited MLD report when + DelayTimer expires. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6MldTimerTicking ( + IN IP6_SERVICE *IpSb + ); + +#endif + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Nd.c b/Core/NetworkPkg/Ip6Dxe/Ip6Nd.c new file mode 100644 index 0000000000..2c8be42f09 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Nd.c @@ -0,0 +1,3155 @@ +/** @file + Implementation of Neighbor Discovery support routines. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +EFI_MAC_ADDRESS mZeroMacAddress; + +/** + Update the ReachableTime in IP6 service binding instance data, in milliseconds. + + @param[in, out] IpSb Points to the IP6_SERVICE. + +**/ +VOID +Ip6UpdateReachableTime ( + IN OUT IP6_SERVICE *IpSb + ) +{ + UINT32 Random; + + Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE; + Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED; + IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE; +} + +/** + Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number + of EFI_IP6_NEIGHBOR_CACHE is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] NeighborCount The number of returned neighbor cache entries. + @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE. + + @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiNeighborCache ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *NeighborCount, + OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache + ) +{ + IP6_NEIGHBOR_ENTRY *Neighbor; + LIST_ENTRY *Entry; + IP6_SERVICE *IpSb; + UINT32 Count; + EFI_IP6_NEIGHBOR_CACHE *EfiNeighborCache; + EFI_IP6_NEIGHBOR_CACHE *NeighborCacheTmp; + + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + ASSERT (NeighborCount != NULL && NeighborCache != NULL); + + IpSb = IpInstance->Service; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) { + Count++; + } + + if (Count == 0) { + return EFI_SUCCESS; + } + + NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE)); + if (NeighborCacheTmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *NeighborCount = Count; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) { + Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); + + EfiNeighborCache = NeighborCacheTmp + Count; + + EfiNeighborCache->State = Neighbor->State; + IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor); + IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress); + + Count++; + } + + ASSERT (*NeighborCount == Count); + *NeighborCache = NeighborCacheTmp; + + return EFI_SUCCESS; +} + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of prefix entries is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] PrefixCount The number of returned prefix entries. + @param[out] PrefixTable The pointer to the array of PrefixTable. + + @retval EFI_SUCCESS The prefix table successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table. + +**/ +EFI_STATUS +Ip6BuildPrefixTable ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *PrefixCount, + OUT EFI_IP6_ADDRESS_INFO **PrefixTable + ) +{ + LIST_ENTRY *Entry; + IP6_SERVICE *IpSb; + UINT32 Count; + IP6_PREFIX_LIST_ENTRY *PrefixList; + EFI_IP6_ADDRESS_INFO *EfiPrefix; + EFI_IP6_ADDRESS_INFO *PrefixTableTmp; + + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + ASSERT (PrefixCount != NULL && PrefixTable != NULL); + + IpSb = IpInstance->Service; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { + Count++; + } + + if (Count == 0) { + return EFI_SUCCESS; + } + + PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO)); + if (PrefixTableTmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *PrefixCount = Count; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { + PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + EfiPrefix = PrefixTableTmp + Count; + IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix); + EfiPrefix->PrefixLength = PrefixList->PrefixLength; + + Count++; + } + + ASSERT (*PrefixCount == Count); + *PrefixTable = PrefixTableTmp; + + return EFI_SUCCESS; +} + +/** + Allocate and initialize a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list. + Otherwise, it is created for the autoconfiguration prefix list. + @param[in] ValidLifetime The length of time in seconds that the prefix + is valid for the purpose of on-link determination. + @param[in] PreferredLifetime The length of time in seconds that addresses + generated from the prefix via stateless address + autoconfiguration remain preferred. + @param[in] PrefixLength The prefix length of the Prefix. + @param[in] Prefix The prefix address. + + @return NULL if it failed to allocate memory for the prefix node. Otherwise, point + to the created or existing prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6CreatePrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + IP6_ROUTE_ENTRY *RtEntry; + LIST_ENTRY *ListHead; + LIST_ENTRY *Entry; + IP6_PREFIX_LIST_ENTRY *TmpPrefixEntry; + + if (Prefix == NULL || PreferredLifetime > ValidLifetime || PrefixLength >= IP6_PREFIX_NUM) { + return NULL; + } + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + OnLinkOrAuto, + PrefixLength, + Prefix + ); + if (PrefixEntry != NULL) { + PrefixEntry->RefCnt ++; + return PrefixEntry; + } + + PrefixEntry = AllocatePool (sizeof (IP6_PREFIX_LIST_ENTRY)); + if (PrefixEntry == NULL) { + return NULL; + } + + PrefixEntry->RefCnt = 1; + PrefixEntry->ValidLifetime = ValidLifetime; + PrefixEntry->PreferredLifetime = PreferredLifetime; + PrefixEntry->PrefixLength = PrefixLength; + IP6_COPY_ADDRESS (&PrefixEntry->Prefix, Prefix); + + ListHead = OnLinkOrAuto ? &IpSb->OnlinkPrefix : &IpSb->AutonomousPrefix; + + // + // Create a direct route entry for on-link prefix and insert to route area. + // + if (OnLinkOrAuto) { + RtEntry = Ip6CreateRouteEntry (Prefix, PrefixLength, NULL); + if (RtEntry == NULL) { + FreePool (PrefixEntry); + return NULL; + } + + RtEntry->Flag = IP6_DIRECT_ROUTE; + InsertHeadList (&IpSb->RouteTable->RouteArea[PrefixLength], &RtEntry->Link); + IpSb->RouteTable->TotalNum++; + } + + // + // Insert the prefix entry in the order that a prefix with longer prefix length + // is put ahead in the list. + // + NET_LIST_FOR_EACH (Entry, ListHead) { + TmpPrefixEntry = NET_LIST_USER_STRUCT(Entry, IP6_PREFIX_LIST_ENTRY, Link); + + if (TmpPrefixEntry->PrefixLength < PrefixEntry->PrefixLength) { + break; + } + } + + NetListInsertBefore (Entry, &PrefixEntry->Link); + + return PrefixEntry; +} + +/** + Destroy a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] PrefixEntry The to be destroyed prefix list entry. + @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list. + Otherwise remove from autoconfiguration prefix list. + @param[in] ImmediateDelete If TRUE, remove the entry directly. + Otherwise, check the reference count to see whether + it should be removed. + +**/ +VOID +Ip6DestroyPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_PREFIX_LIST_ENTRY *PrefixEntry, + IN BOOLEAN OnLinkOrAuto, + IN BOOLEAN ImmediateDelete + ) +{ + LIST_ENTRY *Entry; + IP6_INTERFACE *IpIf; + EFI_STATUS Status; + + if ((!ImmediateDelete) && (PrefixEntry->RefCnt > 0) && ((--PrefixEntry->RefCnt) > 0)) { + return ; + } + + if (OnLinkOrAuto) { + // + // Remove the direct route for onlink prefix from route table. + // + do { + Status = Ip6DelRoute ( + IpSb->RouteTable, + &PrefixEntry->Prefix, + PrefixEntry->PrefixLength, + NULL + ); + } while (Status != EFI_NOT_FOUND); + } else { + // + // Remove the corresponding addresses generated from this autonomous prefix. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + Ip6RemoveAddr (IpSb, &IpIf->AddressList, &IpIf->AddressCount, &PrefixEntry->Prefix, PrefixEntry->PrefixLength); + } + } + + RemoveEntryList (&PrefixEntry->Link); + FreePool (PrefixEntry); +} + +/** + Search the list array to find an IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the search the link prefix list, + Otherwise search the autoconfiguration prefix list. + @param[in] PrefixLength The prefix length of the Prefix + @param[in] Prefix The prefix address. + + @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the + pointer to the IP6 prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6FindPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixList; + LIST_ENTRY *Entry; + LIST_ENTRY *ListHead; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Prefix != NULL); + + if (OnLinkOrAuto) { + ListHead = &IpSb->OnlinkPrefix; + } else { + ListHead = &IpSb->AutonomousPrefix; + } + + NET_LIST_FOR_EACH (Entry, ListHead) { + PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (PrefixLength != 255) { + // + // Perform exactly prefix match. + // + if (PrefixList->PrefixLength == PrefixLength && + NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixLength)) { + return PrefixList; + } + } else { + // + // Perform the longest prefix match. The list is already sorted with + // the longest length prefix put at the head of the list. + // + if (NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixList->PrefixLength)) { + return PrefixList; + } + } + } + + return NULL; +} + +/** + Release the resource in the prefix list table, and destroy the list entry and + corresponding addresses or route entries. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] ListHead The list entry head of the prefix list table. + +**/ +VOID +Ip6CleanPrefixListTable ( + IN IP6_SERVICE *IpSb, + IN LIST_ENTRY *ListHead + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixList; + BOOLEAN OnLink; + + OnLink = (BOOLEAN) (ListHead == &IpSb->OnlinkPrefix); + + while (!IsListEmpty (ListHead)) { + PrefixList = NET_LIST_HEAD (ListHead, IP6_PREFIX_LIST_ENTRY, Link); + Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE); + } +} + +/** + Callback function when address resolution is finished. It will cancel + all the queued frames if the address resolution failed, or transmit them + if the request succeeded. + + @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY. + +**/ +VOID +Ip6OnArpResolved ( + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_NEIGHBOR_ENTRY *ArpQue; + IP6_SERVICE *IpSb; + IP6_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + BOOLEAN Sent; + + ArpQue = (IP6_NEIGHBOR_ENTRY *) Context; + if ((ArpQue == NULL) || (ArpQue->Interface == NULL)) { + return ; + } + + IpSb = ArpQue->Interface->Service; + if ((IpSb == NULL) || (IpSb->Signature != IP6_SERVICE_SIGNATURE)) { + return ; + } + + // + // ARP resolve failed for some reason. Release all the frame + // and ARP queue itself. Ip6FreeArpQue will call the frame's + // owner back. + // + if (NET_MAC_EQUAL (&ArpQue->LinkAddress, &mZeroMacAddress, IpSb->SnpMode.HwAddressSize)) { + Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, TRUE, EFI_NO_MAPPING, NULL, NULL); + return ; + } + + // + // ARP resolve succeeded, Transmit all the frame. + // + Sent = FALSE; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) { + RemoveEntryList (Entry); + + Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link); + IP6_COPY_LINK_ADDRESS (&Token->DstMac, &ArpQue->LinkAddress); + + // + // Insert the tx token before transmitting it via MNP as the FrameSentDpc + // may be called before Mnp->Transmit returns which will remove this tx + // token from the SentFrames list. Remove it from the list if the returned + // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the + // FrameSentDpc won't be queued. + // + InsertTailList (&ArpQue->Interface->SentFrames, &Token->Link); + + Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + RemoveEntryList (&Token->Link); + Token->CallBack (Token->Packet, Status, 0, Token->Context); + + Ip6FreeLinkTxToken (Token); + continue; + } else { + Sent = TRUE; + } + } + + // + // Free the ArpQue only but not the whole neighbor entry. + // + Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, FALSE, EFI_SUCCESS, NULL, NULL); + + if (Sent && (ArpQue->State == EfiNeighborStale)) { + ArpQue->State = EfiNeighborDelay; + ArpQue->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME); + } +} + +/** + Allocate and initialize an IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] CallBack The callback function to be called when + address resolution is finished. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + @param[in] LinkAddress Points to the MAC address of the neighbor. + Ignored if NULL. + + @return NULL if failed to allocate memory for the neighbor cache entry. + Otherwise, point to the created neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6CreateNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_ARP_CALLBACK CallBack, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL + ) +{ + IP6_NEIGHBOR_ENTRY *Entry; + IP6_DEFAULT_ROUTER *DefaultRouter; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address!= NULL); + + Entry = AllocateZeroPool (sizeof (IP6_NEIGHBOR_ENTRY)); + if (Entry == NULL) { + return NULL; + } + + Entry->RefCnt = 1; + Entry->IsRouter = FALSE; + Entry->ArpFree = FALSE; + Entry->Dynamic = FALSE; + Entry->State = EfiNeighborInComplete; + Entry->Transmit = IP6_MAX_MULTICAST_SOLICIT + 1; + Entry->CallBack = CallBack; + Entry->Interface = NULL; + + InitializeListHead (&Entry->Frames); + + IP6_COPY_ADDRESS (&Entry->Neighbor, Ip6Address); + + if (LinkAddress != NULL) { + IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, LinkAddress); + } else { + IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, &mZeroMacAddress); + } + + InsertHeadList (&IpSb->NeighborTable, &Entry->Link); + + // + // If corresponding default router entry exists, establish the relationship. + // + DefaultRouter = Ip6FindDefaultRouter (IpSb, Ip6Address); + if (DefaultRouter != NULL) { + DefaultRouter->NeighborCache = Entry; + } + + return Entry; +} + +/** + Search a IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + + @return NULL if it failed to find the matching neighbor cache entry. + Otherwise, point to the found neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6FindNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_NEIGHBOR_ENTRY *Neighbor; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address != NULL); + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) { + Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); + if (EFI_IP6_EQUAL (Ip6Address, &Neighbor->Neighbor)) { + RemoveEntryList (Entry); + InsertHeadList (&IpSb->NeighborTable, Entry); + + return Neighbor; + } + } + + return NULL; +} + +/** + Free a IP6 neighbor cache entry and remove all the frames on the address + resolution queue that pass the FrameToCancel. That is, either FrameToCancel + is NULL, or it returns true for the frame. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] NeighborCache The to be free neighbor cache entry. + @param[in] SendIcmpError If TRUE, send out ICMP error. + @param[in] FullFree If TRUE, remove the neighbor cache entry. + Otherwise remove the pending frames. + @param[in] IoStatus The status returned to the cancelled frames' + callback function. + @param[in] FrameToCancel Function to select which frame to cancel. + This is an optional parameter that may be NULL. + @param[in] Context Opaque parameter to the FrameToCancel. + Ignored if FrameToCancel is NULL. + + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + @retval EFI_SUCCESS The operation finished successfully. + +**/ +EFI_STATUS +Ip6FreeNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_NEIGHBOR_ENTRY *NeighborCache, + IN BOOLEAN SendIcmpError, + IN BOOLEAN FullFree, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + IP6_LINK_TX_TOKEN *TxToken; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_DEFAULT_ROUTER *DefaultRouter; + + // + // If FrameToCancel fails, the token will not be released. + // To avoid the memory leak, stop this usage model. + // + if (FullFree && FrameToCancel != NULL) { + return EFI_INVALID_PARAMETER; + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &NeighborCache->Frames) { + TxToken = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link); + + if (SendIcmpError && !IP6_IS_MULTICAST (&TxToken->Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + TxToken->Packet, + NULL, + &TxToken->Packet->Ip.Ip6->SourceAddress, + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_ADDR_UNREACHABLE, + NULL + ); + } + + if ((FrameToCancel == NULL) || FrameToCancel (TxToken, Context)) { + RemoveEntryList (Entry); + TxToken->CallBack (TxToken->Packet, IoStatus, 0, TxToken->Context); + Ip6FreeLinkTxToken (TxToken); + } + } + + if (NeighborCache->ArpFree && IsListEmpty (&NeighborCache->Frames)) { + RemoveEntryList (&NeighborCache->ArpList); + NeighborCache->ArpFree = FALSE; + } + + if (FullFree) { + if (NeighborCache->IsRouter) { + DefaultRouter = Ip6FindDefaultRouter (IpSb, &NeighborCache->Neighbor); + if (DefaultRouter != NULL) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + + RemoveEntryList (&NeighborCache->Link); + FreePool (NeighborCache); + } + + return EFI_SUCCESS; +} + +/** + Allocate and initialize an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the default router. + @param[in] RouterLifetime The lifetime associated with the default + router, in units of seconds. + + @return NULL if it failed to allocate memory for the default router node. + Otherwise, point to the created default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6CreateDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN UINT16 RouterLifetime + ) +{ + IP6_DEFAULT_ROUTER *Entry; + IP6_ROUTE_ENTRY *RtEntry; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address != NULL); + + Entry = AllocatePool (sizeof (IP6_DEFAULT_ROUTER)); + if (Entry == NULL) { + return NULL; + } + + Entry->RefCnt = 1; + Entry->Lifetime = RouterLifetime; + Entry->NeighborCache = Ip6FindNeighborEntry (IpSb, Ip6Address); + IP6_COPY_ADDRESS (&Entry->Router, Ip6Address); + + // + // Add a default route into route table with both Destination and PrefixLength set to zero. + // + RtEntry = Ip6CreateRouteEntry (NULL, 0, Ip6Address); + if (RtEntry == NULL) { + FreePool (Entry); + return NULL; + } + + InsertHeadList (&IpSb->RouteTable->RouteArea[0], &RtEntry->Link); + IpSb->RouteTable->TotalNum++; + + InsertTailList (&IpSb->DefaultRouterList, &Entry->Link); + + return Entry; +} + +/** + Destroy an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER. + +**/ +VOID +Ip6DestroyDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN IP6_DEFAULT_ROUTER *DefaultRouter + ) +{ + EFI_STATUS Status; + + RemoveEntryList (&DefaultRouter->Link); + + // + // Update the Destination Cache - all entries using the time-out router as next-hop + // should perform next-hop determination again. + // + do { + Status = Ip6DelRoute (IpSb->RouteTable, NULL, 0, &DefaultRouter->Router); + } while (Status != EFI_NOT_FOUND); + + FreePool (DefaultRouter); +} + +/** + Clean an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + +**/ +VOID +Ip6CleanDefaultRouterList ( + IN IP6_SERVICE *IpSb + ) +{ + IP6_DEFAULT_ROUTER *DefaultRouter; + + while (!IsListEmpty (&IpSb->DefaultRouterList)) { + DefaultRouter = NET_LIST_HEAD (&IpSb->DefaultRouterList, IP6_DEFAULT_ROUTER, Link); + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } +} + +/** + Search a default router node from an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the to be searched default router node. + + @return NULL if it failed to find the matching default router node. + Otherwise, point to the found default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6FindDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ) +{ + LIST_ENTRY *Entry; + IP6_DEFAULT_ROUTER *DefaultRouter; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address != NULL); + + NET_LIST_FOR_EACH (Entry, &IpSb->DefaultRouterList) { + DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link); + if (EFI_IP6_EQUAL (Ip6Address, &DefaultRouter->Router)) { + return DefaultRouter; + } + } + + return NULL; +} + +/** + The function to be called after DAD (Duplicate Address Detection) is performed. + + @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed. + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] DadEntry The DAD entry which already performed DAD. + +**/ +VOID +Ip6OnDADFinished ( + IN BOOLEAN IsDadPassed, + IN IP6_INTERFACE *IpIf, + IN IP6_DAD_ENTRY *DadEntry + ) +{ + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *AddrInfo; + EFI_DHCP6_PROTOCOL *Dhcp6; + UINT16 OptBuf[4]; + EFI_DHCP6_PACKET_OPTION *Oro; + EFI_DHCP6_RETRANSMISSION InfoReqReXmit; + EFI_IPv6_ADDRESS AllNodes; + + IpSb = IpIf->Service; + AddrInfo = DadEntry->AddressInfo; + + if (IsDadPassed) { + // + // DAD succeed. + // + if (NetIp6IsLinkLocalAddr (&AddrInfo->Address)) { + ASSERT (!IpSb->LinkLocalOk); + + IP6_COPY_ADDRESS (&IpSb->LinkLocalAddr, &AddrInfo->Address); + IpSb->LinkLocalOk = TRUE; + IpIf->Configured = TRUE; + + // + // Check whether DHCP6 need to be started. + // + Dhcp6 = IpSb->Ip6ConfigInstance.Dhcp6; + + if (IpSb->Dhcp6NeedStart) { + Dhcp6->Start (Dhcp6); + IpSb->Dhcp6NeedStart = FALSE; + } + + if (IpSb->Dhcp6NeedInfoRequest) { + // + // Set the exta options to send. Here we only want the option request option + // with DNS SERVERS. + // + Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf; + Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO); + Oro->OpLen = HTONS (2); + *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS); + + InfoReqReXmit.Irt = 4; + InfoReqReXmit.Mrc = 64; + InfoReqReXmit.Mrt = 60; + InfoReqReXmit.Mrd = 0; + + Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + IpSb->Ip6ConfigInstance.Dhcp6Event, + Ip6ConfigOnDhcp6Reply, + &IpSb->Ip6ConfigInstance + ); + } + + // + // Add an on-link prefix for link-local address. + // + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + IP6_LINK_LOCAL_PREFIX_LENGTH, + &IpSb->LinkLocalAddr + ); + + } else { + // + // Global scope unicast address. + // + Ip6AddAddr (IpIf, AddrInfo); + + // + // Add an on-link prefix for this address. + // + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + AddrInfo->ValidLifetime, + AddrInfo->PreferredLifetime, + AddrInfo->PrefixLength, + &AddrInfo->Address + ); + + IpIf->Configured = TRUE; + } + } else { + // + // Leave the group we joined before. + // + Ip6LeaveGroup (IpSb, &DadEntry->Destination); + } + + if (DadEntry->Callback != NULL) { + DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context); + } + + if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) { + FreePool (AddrInfo); + RemoveEntryList (&DadEntry->Link); + FreePool (DadEntry); + // + // Leave link-scope all-nodes multicast address (FF02::1) + // + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + Ip6LeaveGroup (IpSb, &AllNodes); + // + // Disable IP operation since link-local address is a duplicate address. + // + IpSb->LinkLocalDadFail = TRUE; + IpSb->Mnp->Configure (IpSb->Mnp, NULL); + gBS->SetTimer (IpSb->Timer, TimerCancel, 0); + gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0); + return ; + } + + if (!IsDadPassed || NetIp6IsLinkLocalAddr (&AddrInfo->Address)) { + // + // Free the AddressInfo we hold if DAD fails or it is a link-local address. + // + FreePool (AddrInfo); + } + + RemoveEntryList (&DadEntry->Link); + FreePool (DadEntry); +} + +/** + Create a DAD (Duplicate Address Detection) entry and queue it to be performed. + + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] AddressInfo The address information which needs DAD performed. + @param[in] Callback The callback routine that will be called after DAD + is performed. This is an optional parameter that + may be NULL. + @param[in] Context The opaque parameter for a DAD callback routine. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The DAD entry was created and queued. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the + operation. + + +**/ +EFI_STATUS +Ip6InitDADProcess ( + IN IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddressInfo, + IN IP6_DAD_CALLBACK Callback OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + IP6_DAD_ENTRY *Entry; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits; + IP6_SERVICE *IpSb; + EFI_STATUS Status; + UINT32 MaxDelayTick; + + NET_CHECK_SIGNATURE (IpIf, IP6_INTERFACE_SIGNATURE); + ASSERT (AddressInfo != NULL); + + // + // Do nothing if we have already started DAD on the address. + // + if (Ip6FindDADEntry (IpIf->Service, &AddressInfo->Address, NULL) != NULL) { + return EFI_SUCCESS; + } + + Status = EFI_SUCCESS; + IpSb = IpIf->Service; + DadXmits = &IpSb->Ip6ConfigInstance.DadXmits; + + // + // Allocate the resources and insert info + // + Entry = AllocatePool (sizeof (IP6_DAD_ENTRY)); + if (Entry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Map the incoming unicast address to solicited-node multicast address + // + Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination); + + // + // Join in the solicited-node multicast address. + // + Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination); + if (EFI_ERROR (Status)) { + FreePool (Entry); + return Status; + } + + Entry->Signature = IP6_DAD_ENTRY_SIGNATURE; + Entry->MaxTransmit = DadXmits->DupAddrDetectTransmits; + Entry->Transmit = 0; + Entry->Receive = 0; + MaxDelayTick = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS; + Entry->RetransTick = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5; + Entry->AddressInfo = AddressInfo; + Entry->Callback = Callback; + Entry->Context = Context; + InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link); + + if (Entry->MaxTransmit == 0) { + // + // DAD is disabled on this interface, immediately mark this DAD successful. + // + Ip6OnDADFinished (TRUE, IpIf, Entry); + } + + return EFI_SUCCESS; +} + +/** + Search IP6_DAD_ENTRY from the Duplicate Address Detection List. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Target The address information which needs DAD performed . + @param[out] Interface If not NULL, output the IP6 interface that configures + the tentative address. + + @return NULL if failed to find the matching DAD entry. + Otherwise, point to the found DAD entry. + +**/ +IP6_DAD_ENTRY * +Ip6FindDADEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Target, + OUT IP6_INTERFACE **Interface OPTIONAL + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_DAD_ENTRY *DupAddrDetect; + IP6_ADDRESS_INFO *AddrInfo; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) { + DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE); + AddrInfo = DupAddrDetect->AddressInfo; + if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) { + if (Interface != NULL) { + *Interface = IpIf; + } + return DupAddrDetect; + } + } + } + + return NULL; +} + +/** + Generate router solicit message and send it out to Destination Address or + All Router Link Local scope multicast address. + + @param[in] IpSb The IP service to send the packet. + @param[in] Interface If not NULL, points to the IP6 interface to send + the packet. + @param[in] SourceAddress If not NULL, the source address of the message. + @param[in] DestinationAddress If not NULL, the destination address of the message. + @param[in] SourceLinkAddress If not NULL, the MAC address of the source. + A source link-layer address option will be appended + to the message. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The router solicit message was successfully sent. + +**/ +EFI_STATUS +Ip6SendRouterSolicit ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ) +{ + NET_BUF *Packet; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + UINT16 PayloadLen; + IP6_INTERFACE *IpIf; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + IpIf = Interface; + if (IpIf == NULL && IpSb->DefaultInterface != NULL) { + IpIf = IpSb->DefaultInterface; + } + + // + // Generate the packet to be sent + // + + PayloadLen = (UINT16) sizeof (IP6_ICMP_INFORMATION_HEAD); + if (SourceLinkAddress != NULL) { + PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION); + } + + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IP6_HOP_LIMIT; + + if (SourceAddress != NULL) { + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + } else { + ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + + if (DestinationAddress != NULL) { + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + } else { + Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress); + } + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP header, and Source link-layer address if contained. + // + + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + ASSERT (IcmpHead != NULL); + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT; + IcmpHead->Head.Code = 0; + + LinkLayerOption = NULL; + if (SourceLinkAddress != NULL) { + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace ( + Packet, + sizeof (IP6_ETHER_ADDR_OPTION), + FALSE + ); + ASSERT (LinkLayerOption != NULL); + LinkLayerOption->Type = Ip6OptionEtherSource; + LinkLayerOption->Length = (UINT8) sizeof (IP6_ETHER_ADDR_OPTION); + CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6); + } + + // + // Transmit the packet + // + return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Generate a Neighbor Advertisement message and send it out to Destination Address. + + @param[in] IpSb The IP service to send the packet. + @param[in] SourceAddress The source address of the message. + @param[in] DestinationAddress The destination address of the message. + @param[in] TargetIp6Address The target address field in the Neighbor Solicitation + message that prompted this advertisement. + @param[in] TargetLinkAddress The MAC address for the target, i.e. the sender + of the advertisement. + @param[in] IsRouter If TRUE, indicates the sender is a router. + @param[in] Override If TRUE, indicates the advertisement should override + an existing cache entry and update the MAC address. + @param[in] Solicited If TRUE, indicates the advertisement was sent + in response to a Neighbor Solicitation from + the Destination address. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent. + +**/ +EFI_STATUS +Ip6SendNeighborAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *SourceAddress, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress, + IN BOOLEAN IsRouter, + IN BOOLEAN Override, + IN BOOLEAN Solicited + ) +{ + NET_BUF *Packet; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + EFI_IPv6_ADDRESS *Target; + UINT16 PayloadLen; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // The Neighbor Advertisement message must include a Target link-layer address option + // when responding to multicast solicitation and should include such option when + // responding to unicast solicitation. It also must include such option as unsolicited + // advertisement. + // + ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL); + + PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION)); + + // + // Generate the packet to be sent + // + + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IP6_HOP_LIMIT; + + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP header, Target address, and Target link-layer address. + // Set the Router flag, Solicited flag and Override flag. + // + + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + ASSERT (IcmpHead != NULL); + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE; + IcmpHead->Head.Code = 0; + + if (IsRouter) { + IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG; + } + + if (Solicited) { + IcmpHead->Fourth |= IP6_SOLICITED_FLAG; + } + + if (Override) { + IcmpHead->Fourth |= IP6_OVERRIDE_FLAG; + } + + Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE); + ASSERT (Target != NULL); + IP6_COPY_ADDRESS (Target, TargetIp6Address); + + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace ( + Packet, + sizeof (IP6_ETHER_ADDR_OPTION), + FALSE + ); + ASSERT (LinkLayerOption != NULL); + LinkLayerOption->Type = Ip6OptionEtherTarget; + LinkLayerOption->Length = 1; + CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6); + + // + // Transmit the packet + // + return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Generate the Neighbor Solicitation message and send it to the Destination Address. + + @param[in] IpSb The IP service to send the packet + @param[in] SourceAddress The source address of the message. + @param[in] DestinationAddress The destination address of the message. + @param[in] TargetIp6Address The IP address of the target of the solicitation. + It must not be a multicast address. + @param[in] SourceLinkAddress The MAC address for the sender. If not NULL, + a source link-layer address option will be appended + to the message. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent. + +**/ +EFI_STATUS +Ip6SendNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *SourceAddress, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ) +{ + NET_BUF *Packet; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + EFI_IPv6_ADDRESS *Target; + BOOLEAN IsDAD; + UINT16 PayloadLen; + IP6_NEIGHBOR_ENTRY *Neighbor; + + // + // Check input parameters + // + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + if (DestinationAddress == NULL || TargetIp6Address == NULL) { + return EFI_INVALID_PARAMETER; + } + + IsDAD = FALSE; + + if (SourceAddress == NULL || (SourceAddress != NULL && NetIp6IsUnspecifiedAddr (SourceAddress))) { + IsDAD = TRUE; + } + + // + // The Neighbor Solicitation message should include a source link-layer address option + // if the solicitation is not sent by performing DAD - Duplicate Address Detection. + // Otherwise must not include it. + // + PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS)); + + if (!IsDAD) { + if (SourceLinkAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + + PayloadLen = (UINT16) (PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION)); + } + + // + // Generate the packet to be sent + // + + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IP6_HOP_LIMIT; + + if (SourceAddress != NULL) { + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + } else { + ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP header, Target address, and Source link-layer address. + // + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + ASSERT (IcmpHead != NULL); + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT; + IcmpHead->Head.Code = 0; + + Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE); + ASSERT (Target != NULL); + IP6_COPY_ADDRESS (Target, TargetIp6Address); + + LinkLayerOption = NULL; + if (!IsDAD) { + + // + // Fill in the source link-layer address option + // + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace ( + Packet, + sizeof (IP6_ETHER_ADDR_OPTION), + FALSE + ); + ASSERT (LinkLayerOption != NULL); + LinkLayerOption->Type = Ip6OptionEtherSource; + LinkLayerOption->Length = 1; + CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6); + } + + // + // Create a Neighbor Cache entry in the INCOMPLETE state when performing + // address resolution. + // + if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) { + Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address); + if (Neighbor == NULL) { + Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL); + ASSERT (Neighbor != NULL); + } + } + + // + // Transmit the packet + // + return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Process the Neighbor Solicitation message. The message may be sent for Duplicate + Address Detection or Address Resolution. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + EFI_IPv6_ADDRESS Target; + IP6_ETHER_ADDR_OPTION LinkLayerOption; + BOOLEAN IsDAD; + BOOLEAN IsUnicast; + BOOLEAN IsMaintained; + IP6_DAD_ENTRY *DupAddrDetect; + IP6_INTERFACE *IpIf; + IP6_NEIGHBOR_ENTRY *Neighbor; + BOOLEAN Solicited; + BOOLEAN UpdateCache; + EFI_IPv6_ADDRESS Dest; + UINT16 OptionLen; + UINT8 *Option; + BOOLEAN Provided; + EFI_STATUS Status; + VOID *MacAddress; + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr); + + // + // Perform Message Validation: + // The IP Hop Limit field has a value of 255, i.e., the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // Target Address is not a multicast address. + // + Status = EFI_INVALID_PARAMETER; + + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) { + goto Exit; + } + + // + // ICMP length is 24 or more octets. + // + OptionLen = 0; + if (Head->PayloadLength < IP6_ND_LENGTH) { + goto Exit; + } else { + OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH); + if (OptionLen != 0) { + Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL); + ASSERT (Option != NULL); + + // + // All included options should have a length that is greater than zero. + // + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + } + + IsDAD = NetIp6IsUnspecifiedAddr (&Head->SourceAddress); + IsUnicast = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress); + IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL); + + Provided = FALSE; + if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) { + NetbufCopy ( + Packet, + IP6_ND_LENGTH, + sizeof (IP6_ETHER_ADDR_OPTION), + (UINT8 *) &LinkLayerOption + ); + // + // The solicitation for neighbor discovery should include a source link-layer + // address option. If the option is not recognized, silently ignore it. + // + if (LinkLayerOption.Type == Ip6OptionEtherSource) { + if (IsDAD) { + // + // If the IP source address is the unspecified address, the source + // link-layer address option must not be included in the message. + // + goto Exit; + } + + Provided = TRUE; + } + } + + // + // If the IP source address is the unspecified address, the IP + // destination address is a solicited-node multicast address. + // + if (IsDAD && IsUnicast) { + goto Exit; + } + + // + // If the target address is tentative, and the source address is a unicast address, + // the solicitation's sender is performing address resolution on the target; + // the solicitation should be silently ignored. + // + if (!IsDAD && !IsMaintained) { + goto Exit; + } + + // + // If received unicast neighbor solicitation but destination is not this node, + // drop the packet. + // + if (IsUnicast && !IsMaintained) { + goto Exit; + } + + // + // In DAD, when target address is a tentative address, + // process the received neighbor solicitation message but not send out response. + // + if (IsDAD && !IsMaintained) { + DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf); + if (DupAddrDetect != NULL) { + // + // Check the MAC address of the incoming packet. + // + if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) { + goto Exit; + } + + MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress; + if (MacAddress != NULL) { + if (CompareMem ( + MacAddress, + &IpSb->SnpMode.CurrentAddress, + IpSb->SnpMode.HwAddressSize + ) != 0) { + // + // The NS is from another node to performing DAD on the same address. + // Fail DAD for the tentative address. + // + Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect); + Status = EFI_ICMP_ERROR; + } else { + // + // The below layer loopback the NS we sent. Record it and wait for more. + // + DupAddrDetect->Receive++; + Status = EFI_SUCCESS; + } + } + } + goto Exit; + } + + // + // If the solicitation does not contain a link-layer address, DO NOT create or + // update the neighbor cache entries. + // + if (Provided) { + Neighbor = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress); + UpdateCache = FALSE; + + if (Neighbor == NULL) { + Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL); + if (Neighbor == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + UpdateCache = TRUE; + } else { + if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) { + UpdateCache = TRUE; + } + } + + if (UpdateCache) { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + // + // Send queued packets if exist. + // + Neighbor->CallBack ((VOID *) Neighbor); + } + } + + // + // Sends a Neighbor Advertisement as response. + // Set the Router flag to zero since the node is a host. + // If the source address of the solicitation is unspeicifed, and target address + // is one of the maintained address, reply a unsolicited multicast advertisement. + // + if (IsDAD && IsMaintained) { + Solicited = FALSE; + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest); + } else { + Solicited = TRUE; + IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress); + } + + Status = Ip6SendNeighborAdvertise ( + IpSb, + &Target, + &Dest, + &Target, + &IpSb->SnpMode.CurrentAddress, + FALSE, + TRUE, + Solicited + ); +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the Neighbor Advertisement message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + EFI_IPv6_ADDRESS Target; + IP6_ETHER_ADDR_OPTION LinkLayerOption; + BOOLEAN Provided; + INTN Compare; + IP6_NEIGHBOR_ENTRY *Neighbor; + IP6_DEFAULT_ROUTER *DefaultRouter; + BOOLEAN Solicited; + BOOLEAN IsRouter; + BOOLEAN Override; + IP6_DAD_ENTRY *DupAddrDetect; + IP6_INTERFACE *IpIf; + UINT16 OptionLen; + UINT8 *Option; + EFI_STATUS Status; + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr); + + // + // Validate the incoming Neighbor Advertisement + // + Status = EFI_INVALID_PARAMETER; + // + // The IP Hop Limit field has a value of 255, i.e., the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // Target Address is not a multicast address. + // + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) { + goto Exit; + } + + // + // ICMP length is 24 or more octets. + // + Provided = FALSE; + OptionLen = 0; + if (Head->PayloadLength < IP6_ND_LENGTH) { + goto Exit; + } else { + OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH); + if (OptionLen != 0) { + Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL); + ASSERT (Option != NULL); + + // + // All included options should have a length that is greater than zero. + // + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + } + + // + // If the IP destination address is a multicast address, Solicited Flag is ZERO. + // + Solicited = FALSE; + if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) { + Solicited = TRUE; + } + if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) { + goto Exit; + } + + // + // DAD - Check whether the Target is one of our tentative address. + // + DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf); + if (DupAddrDetect != NULL) { + // + // DAD fails, some other node is using this address. + // + NetbufFree (Packet); + Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect); + return EFI_ICMP_ERROR; + } + + // + // Search the Neighbor Cache for the target's entry. If no entry exists, + // the advertisement should be silently discarded. + // + Neighbor = Ip6FindNeighborEntry (IpSb, &Target); + if (Neighbor == NULL) { + goto Exit; + } + + // + // Get IsRouter Flag and Override Flag + // + IsRouter = FALSE; + Override = FALSE; + if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) { + IsRouter = TRUE; + } + if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) { + Override = TRUE; + } + + // + // Check whether link layer option is included. + // + if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) { + NetbufCopy ( + Packet, + IP6_ND_LENGTH, + sizeof (IP6_ETHER_ADDR_OPTION), + (UINT8 *) &LinkLayerOption + ); + + if (LinkLayerOption.Type == Ip6OptionEtherTarget) { + Provided = TRUE; + } + } + + Compare = 0; + if (Provided) { + Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + } + + if (!Neighbor->IsRouter && IsRouter) { + DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target); + if (DefaultRouter != NULL) { + DefaultRouter->NeighborCache = Neighbor; + } + } + + if (Neighbor->State == EfiNeighborInComplete) { + // + // If the target's Neighbor Cache entry is in INCOMPLETE state and no + // Target Link-Layer address option is included while link layer has + // address, the message should be silently discarded. + // + if (!Provided) { + goto Exit; + } + // + // Update the Neighbor Cache + // + CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + if (Solicited) { + Neighbor->State = EfiNeighborReachable; + Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime); + } else { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + // + // Send any packets queued for the neighbor awaiting address resolution. + // + Neighbor->CallBack ((VOID *) Neighbor); + } + + Neighbor->IsRouter = IsRouter; + + } else { + if (!Override && Compare != 0) { + // + // When the Override Flag is clear and supplied link-layer address differs from + // that in the cache, if the state of the entry is not REACHABLE, ignore the + // message. Otherwise set it to STALE but do not update the entry in any + // other way. + // + if (Neighbor->State == EfiNeighborReachable) { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + } else { + if (Compare != 0) { + CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + } + // + // Update the entry's state + // + if (Solicited) { + Neighbor->State = EfiNeighborReachable; + Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime); + } else { + if (Compare != 0) { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + } + + // + // When IsRouter is changed from TRUE to FALSE, remove the router from the + // Default Router List and remove the Destination Cache entries for all destinations + // using the neighbor as a router. + // + if (Neighbor->IsRouter && !IsRouter) { + DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target); + if (DefaultRouter != NULL) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + + Neighbor->IsRouter = IsRouter; + } + } + + if (Neighbor->State == EfiNeighborReachable) { + Neighbor->CallBack ((VOID *) Neighbor); + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the Router Advertisement message according to RFC4861. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with the IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessRouterAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + UINT32 ReachableTime; + UINT32 RetransTimer; + UINT16 RouterLifetime; + UINT16 Offset; + UINT8 Type; + UINT8 Length; + IP6_ETHER_ADDR_OPTION LinkLayerOption; + UINT32 Fourth; + UINT8 CurHopLimit; + BOOLEAN Mflag; + BOOLEAN Oflag; + IP6_DEFAULT_ROUTER *DefaultRouter; + IP6_NEIGHBOR_ENTRY *NeighborCache; + EFI_MAC_ADDRESS LinkLayerAddress; + IP6_MTU_OPTION MTUOption; + IP6_PREFIX_INFO_OPTION PrefixOption; + IP6_PREFIX_LIST_ENTRY *PrefixList; + BOOLEAN OnLink; + BOOLEAN Autonomous; + EFI_IPv6_ADDRESS StatelessAddress; + EFI_STATUS Status; + UINT16 OptionLen; + UINT8 *Option; + INTN Result; + + Status = EFI_INVALID_PARAMETER; + + if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) { + // + // Skip the process below as it's not required under the current policy. + // + goto Exit; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + // + // Validate the incoming Router Advertisement + // + + // + // The IP source address must be a link-local address + // + if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + // + // The IP Hop Limit field has a value of 255, i.e. the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // ICMP length (derived from the IP length) is 16 or more octets. + // + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || + Head->PayloadLength < IP6_RA_LENGTH) { + goto Exit; + } + + // + // All included options have a length that is greater than zero. + // + OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH); + if (OptionLen != 0) { + Option = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL); + ASSERT (Option != NULL); + + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + + // + // Process Fourth field. + // In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag, + // and Router Lifetime (16 bit). + // + + Fourth = NTOHL (Icmp.Fourth); + CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16)); + + // + // If the source address already in the default router list, update it. + // Otherwise create a new entry. + // A Lifetime of zero indicates that the router is not a default router. + // + DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress); + if (DefaultRouter == NULL) { + if (RouterLifetime != 0) { + DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime); + if (DefaultRouter == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } + } else { + if (RouterLifetime != 0) { + DefaultRouter->Lifetime = RouterLifetime; + // + // Check the corresponding neighbor cache entry here. + // + if (DefaultRouter->NeighborCache == NULL) { + DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress); + } + } else { + // + // If the address is in the host's default router list and the router lifetime is zero, + // immediately time-out the entry. + // + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + + CurHopLimit = *((UINT8 *) &Fourth + 3); + if (CurHopLimit != 0) { + IpSb->CurHopLimit = CurHopLimit; + } + + Mflag = FALSE; + Oflag = FALSE; + if ((*((UINT8 *) &Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) { + Mflag = TRUE; + } else { + if ((*((UINT8 *) &Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) { + Oflag = TRUE; + } + } + + if (Mflag || Oflag) { + // + // Use Ip6Config to get available addresses or other configuration from DHCP. + // + Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag); + } + + // + // Process Reachable Time and Retrans Timer fields. + // + NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *) &ReachableTime); + NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *) &RetransTimer); + ReachableTime = NTOHL (ReachableTime); + RetransTimer = NTOHL (RetransTimer); + + if (ReachableTime != 0 && ReachableTime != IpSb->BaseReachableTime) { + // + // If new value is not unspecified and differs from the previous one, record it + // in BaseReachableTime and recompute a ReachableTime. + // + IpSb->BaseReachableTime = ReachableTime; + Ip6UpdateReachableTime (IpSb); + } + + if (RetransTimer != 0) { + IpSb->RetransTimer = RetransTimer; + } + + // + // IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists. + // + NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress); + if (NeighborCache != NULL) { + NeighborCache->IsRouter = TRUE; + } + + // + // If an valid router advertisment is received, stops router solicitation. + // + IpSb->RouterAdvertiseReceived = TRUE; + + // + // The only defined options that may appear are the Source + // Link-Layer Address, Prefix information and MTU options. + // All included options have a length that is greater than zero. + // + Offset = 16; + while (Offset < Head->PayloadLength) { + NetbufCopy (Packet, Offset, sizeof (UINT8), &Type); + switch (Type) { + case Ip6OptionEtherSource: + // + // Update the neighbor cache + // + NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption); + if (LinkLayerOption.Length <= 0) { + goto Exit; + } + + ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS)); + CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6); + + if (NeighborCache == NULL) { + NeighborCache = Ip6CreateNeighborEntry ( + IpSb, + Ip6OnArpResolved, + &Head->SourceAddress, + &LinkLayerAddress + ); + if (NeighborCache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + NeighborCache->IsRouter = TRUE; + NeighborCache->State = EfiNeighborStale; + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } else { + Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6); + + // + // If the link-local address is the same as that already in the cache, + // the cache entry's state remains unchanged. Otherwise update the + // reachability state to STALE. + // + if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) { + CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6); + + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + + if (NeighborCache->State == EfiNeighborInComplete) { + // + // Send queued packets if exist. + // + NeighborCache->State = EfiNeighborStale; + NeighborCache->CallBack ((VOID *) NeighborCache); + } else { + NeighborCache->State = EfiNeighborStale; + } + } + } + + Offset = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8); + break; + case Ip6OptionPrefixInfo: + NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption); + if (PrefixOption.Length != 4) { + goto Exit; + } + PrefixOption.ValidLifetime = NTOHL (PrefixOption.ValidLifetime); + PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime); + + // + // Get L and A flag, recorded in the lower 2 bits of Reserved1 + // + OnLink = FALSE; + if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) { + OnLink = TRUE; + } + Autonomous = FALSE; + if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) { + Autonomous = TRUE; + } + + // + // If the prefix is the link-local prefix, silently ignore the prefix option. + // + if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH && + NetIp6IsLinkLocalAddr (&PrefixOption.Prefix) + ) { + Offset += sizeof (IP6_PREFIX_INFO_OPTION); + break; + } + // + // Do following if on-link flag is set according to RFC4861. + // + if (OnLink) { + PrefixList = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + // + // Create a new entry for the prefix, if the ValidLifetime is zero, + // silently ignore the prefix option. + // + if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) { + PrefixList = Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + PrefixOption.ValidLifetime, + PrefixOption.PreferredLifetime, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + if (PrefixList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } else if (PrefixList != NULL) { + if (PrefixOption.ValidLifetime != 0) { + PrefixList->ValidLifetime = PrefixOption.ValidLifetime; + } else { + // + // If the prefix exists and incoming ValidLifetime is zero, immediately + // remove the prefix. + Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE); + } + } + } + + // + // Do following if Autonomous flag is set according to RFC4862. + // + if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) { + PrefixList = Ip6FindPrefixListEntry ( + IpSb, + FALSE, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + // + // Create a new entry for the prefix, and form an address by prefix + interface id + // If the sum of the prefix length and interface identifier length + // does not equal 128 bits, the Prefix Information option MUST be ignored. + // + if (PrefixList == NULL && + PrefixOption.ValidLifetime != 0 && + PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128 + ) { + // + // Form the address in network order. + // + CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64)); + CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64)); + + // + // If the address is not yet in the assigned address list, adds it into. + // + if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) { + // + // And also not in the DAD process, check its uniqeness firstly. + // + if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) { + Status = Ip6SetAddress ( + IpSb->DefaultInterface, + &StatelessAddress, + FALSE, + PrefixOption.PrefixLength, + PrefixOption.ValidLifetime, + PrefixOption.PreferredLifetime, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + } + + // + // Adds the prefix option to stateless prefix option list. + // + PrefixList = Ip6CreatePrefixListEntry ( + IpSb, + FALSE, + PrefixOption.ValidLifetime, + PrefixOption.PreferredLifetime, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + if (PrefixList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } else if (PrefixList != NULL) { + + // + // Reset the preferred lifetime of the address if the advertised prefix exists. + // Perform specific action to valid lifetime together. + // + PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime; + if ((PrefixOption.ValidLifetime > 7200) || + (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) { + // + // If the received Valid Lifetime is greater than 2 hours or + // greater than RemainingLifetime, set the valid lifetime of the + // corresponding address to the advertised Valid Lifetime. + // + PrefixList->ValidLifetime = PrefixOption.ValidLifetime; + + } else if (PrefixList->ValidLifetime <= 7200) { + // + // If RemainingLifetime is less than or equls to 2 hours, ignore the + // Prefix Information option with regards to the valid lifetime. + // TODO: If this option has been authenticated, set the valid lifetime. + // + } else { + // + // Otherwise, reset the valid lifetime of the corresponding + // address to 2 hours. + // + PrefixList->ValidLifetime = 7200; + } + } + } + + Offset += sizeof (IP6_PREFIX_INFO_OPTION); + break; + case Ip6OptionMtu: + NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption); + if (MTUOption.Length != 1) { + goto Exit; + } + + // + // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order + // to omit implementation of Path MTU Discovery. Thus ignore the MTU option + // in Router Advertisement. + // + + Offset += sizeof (IP6_MTU_OPTION); + break; + default: + // + // Silently ignore unrecognized options + // + NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length); + if (Length <= 0) { + goto Exit; + } + + Offset = (UINT16) (Offset + (UINT16) Length * 8); + break; + } + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the ICMPv6 redirect message. Find the instance, then update + its route cache. + + @param[in] IpSb The IP6 service binding instance that received + the packet. + @param[in] Head The IP head of the received ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 redirect packet with + the IP head removed. + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insuffcient resources to complete the + operation. + @retval EFI_SUCCESS Successfully updated the route caches. + +**/ +EFI_STATUS +Ip6ProcessRedirect ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD *Icmp; + EFI_IPv6_ADDRESS *Target; + EFI_IPv6_ADDRESS *IcmpDest; + UINT8 *Option; + UINT16 OptionLen; + IP6_ROUTE_ENTRY *RouteEntry; + IP6_ROUTE_CACHE_ENTRY *RouteCache; + IP6_NEIGHBOR_ENTRY *NeighborCache; + INT32 Length; + UINT8 OptLen; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + EFI_MAC_ADDRESS Mac; + UINT32 Index; + BOOLEAN IsRouter; + EFI_STATUS Status; + INTN Result; + + Status = EFI_INVALID_PARAMETER; + + Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL); + if (Icmp == NULL) { + goto Exit; + } + + // + // Validate the incoming Redirect message + // + + // + // The IP Hop Limit field has a value of 255, i.e. the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // ICMP length (derived from the IP length) is 40 or more octets. + // + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 || + Head->PayloadLength < IP6_REDITECT_LENGTH) { + goto Exit; + } + + // + // The IP source address must be a link-local address + // + if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + + // + // The dest of this ICMP redirect message is not us. + // + if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) { + goto Exit; + } + + // + // All included options have a length that is greater than zero. + // + OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH); + if (OptionLen != 0) { + Option = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL); + ASSERT (Option != NULL); + + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + + Target = (EFI_IPv6_ADDRESS *) (Icmp + 1); + IcmpDest = Target + 1; + + // + // The ICMP Destination Address field in the redirect message does not contain + // a multicast address. + // + if (IP6_IS_MULTICAST (IcmpDest)) { + goto Exit; + } + + // + // The ICMP Target Address is either a link-local address (when redirected to + // a router) or the same as the ICMP Destination Address (when redirected to + // the on-link destination). + // + IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest); + if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) { + goto Exit; + } + + // + // Check the options. The only interested option here is the target-link layer + // address option. + // + Length = Packet->TotalSize - 40; + Option = (UINT8 *) (IcmpDest + 1); + LinkLayerOption = NULL; + while (Length > 0) { + switch (*Option) { + case Ip6OptionEtherTarget: + + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) Option; + OptLen = LinkLayerOption->Length; + if (OptLen != 1) { + // + // For ethernet, the length must be 1. + // + goto Exit; + } + break; + + default: + + OptLen = *(Option + 1); + if (OptLen == 0) { + // + // A length of 0 is invalid. + // + goto Exit; + } + break; + } + + Length -= 8 * OptLen; + Option += 8 * OptLen; + } + + if (Length != 0) { + goto Exit; + } + + // + // The IP source address of the Redirect is the same as the current + // first-hop router for the specified ICMP Destination Address. + // + RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress); + if (RouteCache != NULL) { + if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) { + // + // The source of this Redirect message must match the NextHop of the + // corresponding route cache entry. + // + goto Exit; + } + + // + // Update the NextHop. + // + IP6_COPY_ADDRESS (&RouteCache->NextHop, Target); + + if (!IsRouter) { + RouteEntry = (IP6_ROUTE_ENTRY *) RouteCache->Tag; + RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE; + } + + } else { + // + // Get the Route Entry. + // + RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL); + if (RouteEntry == NULL) { + RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL); + if (RouteEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } + + if (!IsRouter) { + RouteEntry->Flag = IP6_DIRECT_ROUTE; + } + + // + // Create a route cache for this. + // + RouteCache = Ip6CreateRouteCacheEntry ( + IcmpDest, + &Head->DestinationAddress, + Target, + (UINTN) RouteEntry + ); + if (RouteCache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Insert the newly created route cache entry. + // + Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress); + InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link); + } + + // + // Try to locate the neighbor cache for the Target. + // + NeighborCache = Ip6FindNeighborEntry (IpSb, Target); + + if (LinkLayerOption != NULL) { + if (NeighborCache == NULL) { + // + // Create a neighbor cache for the Target. + // + ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS)); + CopyMem (&Mac, LinkLayerOption->EtherAddr, 6); + NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac); + if (NeighborCache == NULL) { + // + // Just report a success here. The neighbor cache can be created in + // some other place. + // + Status = EFI_SUCCESS; + goto Exit; + } + + NeighborCache->State = EfiNeighborStale; + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } else { + Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6); + + // + // If the link-local address is the same as that already in the cache, + // the cache entry's state remains unchanged. Otherwise update the + // reachability state to STALE. + // + if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) { + CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6); + + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + + if (NeighborCache->State == EfiNeighborInComplete) { + // + // Send queued packets if exist. + // + NeighborCache->State = EfiNeighborStale; + NeighborCache->CallBack ((VOID *) NeighborCache); + } else { + NeighborCache->State = EfiNeighborStale; + } + } + } + } + + if (NeighborCache != NULL && IsRouter) { + // + // The Target is a router, set IsRouter to TRUE. + // + NeighborCache->IsRouter = TRUE; + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Add Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been added. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache + due to insufficient resources. + @retval EFI_NOT_FOUND TargetLinkAddress is NULL. + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when DeleteFlag + is FALSE). + +**/ +EFI_STATUS +Ip6AddNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ) +{ + IP6_NEIGHBOR_ENTRY *Neighbor; + + Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address); + if (Neighbor != NULL) { + if (!Override) { + return EFI_ACCESS_DENIED; + } else { + if (TargetLinkAddress != NULL) { + IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress); + } + } + } else { + if (TargetLinkAddress == NULL) { + return EFI_NOT_FOUND; + } + + Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress); + if (Neighbor == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + Neighbor->State = EfiNeighborReachable; + + if (Timeout != 0) { + Neighbor->Ticks = IP6_GET_TICKS (Timeout / TICKS_PER_MS); + Neighbor->Dynamic = TRUE; + } else { + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + + return EFI_SUCCESS; +} + +/** + Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache. + +**/ +EFI_STATUS +Ip6DelNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ) +{ + IP6_NEIGHBOR_ENTRY *Neighbor; + + Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address); + if (Neighbor == NULL) { + return EFI_NOT_FOUND; + } + + RemoveEntryList (&Neighbor->Link); + FreePool (Neighbor); + + return EFI_SUCCESS; +} + +/** + The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds. + This time routine handles DAD module and neighbor state transition. + It is also responsible for sending out router solicitations. + + @param[in] Event The IP6 service instance's heartbeat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6NdFasterTimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_DELAY_JOIN_LIST *DelayNode; + EFI_IPv6_ADDRESS Source; + IP6_DAD_ENTRY *DupAddrDetect; + EFI_STATUS Status; + IP6_NEIGHBOR_ENTRY *NeighborCache; + EFI_IPv6_ADDRESS Destination; + IP6_SERVICE *IpSb; + BOOLEAN Flag; + + IpSb = (IP6_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS)); + + // + // A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router + // Solicitation messages, each separated by at least + // RTR_SOLICITATION_INTERVAL (4) seconds. + // + if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) && + !IpSb->RouterAdvertiseReceived && + IpSb->SolicitTimer > 0 + ) { + if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) { + Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL); + if (!EFI_ERROR (Status)) { + IpSb->SolicitTimer--; + IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL); + } + } + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + // + // Process the delay list to join the solicited-node multicast address. + // + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) { + DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link); + if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) { + // + // The timer expires, init the duplicate address detection. + // + Ip6InitDADProcess ( + DelayNode->Interface, + DelayNode->AddressInfo, + DelayNode->DadCallback, + DelayNode->Context + ); + + // + // Remove the delay node + // + RemoveEntryList (&DelayNode->Link); + FreePool (DelayNode); + } + } + + // + // Process the duplicate address detection list. + // + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) { + DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link); + + if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) { + // + // The timer expires, check the remaining transmit counts. + // + if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) { + // + // Send the Neighbor Solicitation message with + // Source - unspecified address, destination - solicited-node multicast address + // Target - the address to be validated + // + Status = Ip6SendNeighborSolicit ( + IpSb, + NULL, + &DupAddrDetect->Destination, + &DupAddrDetect->AddressInfo->Address, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + + DupAddrDetect->Transmit++; + DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer); + } else { + // + // All required solicitation has been sent out, and the RetransTime after the last + // Neighbor Solicit is elapsed, finish the DAD process. + // + Flag = FALSE; + if ((DupAddrDetect->Receive == 0) || + (DupAddrDetect->Transmit <= DupAddrDetect->Receive)) { + Flag = TRUE; + } + + Ip6OnDADFinished (Flag, IpIf, DupAddrDetect); + } + } + } + } + + // + // Polling the state of Neighbor cache + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) { + NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); + + switch (NeighborCache->State) { + case EfiNeighborInComplete: + if (NeighborCache->Ticks > 0) { + --NeighborCache->Ticks; + } + + // + // Retransmit Neighbor Solicitation messages approximately every + // RetransTimer milliseconds while awaiting a response. + // + if (NeighborCache->Ticks == 0) { + if (NeighborCache->Transmit > 1) { + // + // Send out multicast neighbor solicitation for address resolution. + // After last neighbor solicitation message has been sent out, wait + // for RetransTimer and then remove entry if no response is received. + // + Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination); + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &Destination, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return; + } + } + + // + // Update the retransmit times. + // + if (NeighborCache->Transmit > 0) { + --NeighborCache->Transmit; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer); + } + } + + if (NeighborCache->Transmit == 0) { + // + // Timeout, send ICMP destination unreachable packet and then remove entry + // + Status = Ip6FreeNeighborEntry ( + IpSb, + NeighborCache, + TRUE, + TRUE, + EFI_ICMP_ERROR, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + } + + break; + + case EfiNeighborReachable: + // + // This entry is inserted by EfiIp6Neighbors() as static entry + // and will not timeout. + // + if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) { + break; + } + + if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) { + if (NeighborCache->Dynamic) { + // + // This entry is inserted by EfiIp6Neighbors() as dynamic entry + // and will be deleted after timeout. + // + Status = Ip6FreeNeighborEntry ( + IpSb, + NeighborCache, + FALSE, + TRUE, + EFI_TIMEOUT, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + } else { + NeighborCache->State = EfiNeighborStale; + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + } + + break; + + case EfiNeighborDelay: + if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) { + + NeighborCache->State = EfiNeighborProbe; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer); + NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1; + // + // Send out unicast neighbor solicitation for Neighbor Unreachability Detection + // + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &NeighborCache->Neighbor, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return; + } + + NeighborCache->Transmit--; + } + + break; + + case EfiNeighborProbe: + if (NeighborCache->Ticks > 0) { + --NeighborCache->Ticks; + } + + // + // Retransmit Neighbor Solicitation messages approximately every + // RetransTimer milliseconds while awaiting a response. + // + if (NeighborCache->Ticks == 0) { + if (NeighborCache->Transmit > 1) { + // + // Send out unicast neighbor solicitation for Neighbor Unreachability + // Detection. After last neighbor solicitation message has been sent out, + // wait for RetransTimer and then remove entry if no response is received. + // + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &NeighborCache->Neighbor, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return; + } + } + + // + // Update the retransmit times. + // + if (NeighborCache->Transmit > 0) { + --NeighborCache->Transmit; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer); + } + } + + if (NeighborCache->Transmit == 0) { + // + // Delete the neighbor entry. + // + Status = Ip6FreeNeighborEntry ( + IpSb, + NeighborCache, + FALSE, + TRUE, + EFI_TIMEOUT, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + } + + break; + + default: + break; + } + } +} + +/** + The heartbeat timer of ND module in 1 second. This time routine handles following + things: 1) maitain default router list; 2) maintain prefix options; + 3) maintain route caches. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6NdTimerTicking ( + IN IP6_SERVICE *IpSb + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_DEFAULT_ROUTER *DefaultRouter; + IP6_PREFIX_LIST_ENTRY *PrefixOption; + UINT8 Index; + IP6_ROUTE_CACHE_ENTRY *RouteCache; + + // + // Decrease the lifetime of default router, if expires remove it from default router list. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) { + DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link); + if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) { + if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + } + + // + // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) { + PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) { + if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) { + if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) && + (PrefixOption->PreferredLifetime > 0) + ) { + --PrefixOption->PreferredLifetime; + } + } else { + Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE); + } + } + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) { + PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) { + if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) { + Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE); + } + } + } + + // + // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries. + // Remove the entries at the tail of the bucket. These entries + // are likely to be used least. + // Reclaim frequency is set to 1 second. + // + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) { + Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]); + if (Entry == NULL) { + break; + } + + RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + Ip6FreeRouteCacheEntry (RouteCache); + ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0); + IpSb->RouteTable->Cache.CacheNum[Index]--; + } + } +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Nd.h b/Core/NetworkPkg/Ip6Dxe/Ip6Nd.h new file mode 100644 index 0000000000..982203ca5f --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Nd.h @@ -0,0 +1,749 @@ +/** @file + Definition of Neighbor Discovery support routines. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_ND_H__ +#define __EFI_IP6_ND_H__ + +#define IP6_GET_TICKS(Ms) (((Ms) + IP6_TIMER_INTERVAL_IN_MS - 1) / IP6_TIMER_INTERVAL_IN_MS) + +enum { + IP6_INF_ROUTER_LIFETIME = 0xFFFF, + + IP6_MAX_RTR_SOLICITATION_DELAY = 1000, ///< 1000 milliseconds + IP6_MAX_RTR_SOLICITATIONS = 3, + IP6_RTR_SOLICITATION_INTERVAL = 4000, + + IP6_MIN_RANDOM_FACTOR_SCALED = 1, + IP6_MAX_RANDOM_FACTOR_SCALED = 3, + IP6_RANDOM_FACTOR_SCALE = 2, + + IP6_MAX_MULTICAST_SOLICIT = 3, + IP6_MAX_UNICAST_SOLICIT = 3, + IP6_MAX_ANYCAST_DELAY_TIME = 1, + IP6_MAX_NEIGHBOR_ADV = 3, + IP6_REACHABLE_TIME = 30000, + IP6_RETRANS_TIMER = 1000, + IP6_DELAY_FIRST_PROBE_TIME = 5000, + + IP6_MIN_LINK_MTU = 1280, + IP6_MAX_LINK_MTU = 1500, + + IP6_IS_ROUTER_FLAG = 0x80, + IP6_SOLICITED_FLAG = 0x40, + IP6_OVERRIDE_FLAG = 0x20, + + IP6_M_ADDR_CONFIG_FLAG = 0x80, + IP6_O_CONFIG_FLAG = 0x40, + + IP6_ON_LINK_FLAG = 0x80, + IP6_AUTO_CONFIG_FLAG = 0x40, + + IP6_ND_LENGTH = 24, + IP6_RA_LENGTH = 16, + IP6_REDITECT_LENGTH = 40, + IP6_DAD_ENTRY_SIGNATURE = SIGNATURE_32 ('I', 'P', 'D', 'E') +}; + +typedef +VOID +(*IP6_ARP_CALLBACK) ( + VOID *Context + ); + +typedef struct _IP6_ETHE_ADDR_OPTION { + UINT8 Type; + UINT8 Length; + UINT8 EtherAddr[6]; +} IP6_ETHER_ADDR_OPTION; + +typedef struct _IP6_MTU_OPTION { + UINT8 Type; + UINT8 Length; + UINT16 Reserved; + UINT32 Mtu; +} IP6_MTU_OPTION; + +typedef struct _IP6_PREFIX_INFO_OPTION { + UINT8 Type; + UINT8 Length; + UINT8 PrefixLength; + UINT8 Reserved1; + UINT32 ValidLifetime; + UINT32 PreferredLifetime; + UINT32 Reserved2; + EFI_IPv6_ADDRESS Prefix; +} IP6_PREFIX_INFO_OPTION; + +typedef +VOID +(*IP6_DAD_CALLBACK) ( + IN BOOLEAN IsDadPassed, + IN EFI_IPv6_ADDRESS *TargetAddress, + IN VOID *Context + ); + +typedef struct _IP6_DAD_ENTRY { + UINT32 Signature; + LIST_ENTRY Link; + UINT32 MaxTransmit; + UINT32 Transmit; + UINT32 Receive; + UINT32 RetransTick; + IP6_ADDRESS_INFO *AddressInfo; + EFI_IPv6_ADDRESS Destination; + IP6_DAD_CALLBACK Callback; + VOID *Context; +} IP6_DAD_ENTRY; + +typedef struct _IP6_DELAY_JOIN_LIST { + LIST_ENTRY Link; + UINT32 DelayTime; ///< in tick per 50 milliseconds + IP6_INTERFACE *Interface; + IP6_ADDRESS_INFO *AddressInfo; + IP6_DAD_CALLBACK DadCallback; + VOID *Context; +} IP6_DELAY_JOIN_LIST; + +typedef struct _IP6_NEIGHBOR_ENTRY { + LIST_ENTRY Link; + LIST_ENTRY ArpList; + INTN RefCnt; + BOOLEAN IsRouter; + BOOLEAN ArpFree; + BOOLEAN Dynamic; + EFI_IPv6_ADDRESS Neighbor; + EFI_MAC_ADDRESS LinkAddress; + EFI_IP6_NEIGHBOR_STATE State; + UINT32 Transmit; + UINT32 Ticks; + + LIST_ENTRY Frames; + IP6_INTERFACE *Interface; + IP6_ARP_CALLBACK CallBack; +} IP6_NEIGHBOR_ENTRY; + +typedef struct _IP6_DEFAULT_ROUTER { + LIST_ENTRY Link; + INTN RefCnt; + UINT16 Lifetime; + EFI_IPv6_ADDRESS Router; + IP6_NEIGHBOR_ENTRY *NeighborCache; +} IP6_DEFAULT_ROUTER; + +typedef struct _IP6_PREFIX_LIST_ENTRY { + LIST_ENTRY Link; + INTN RefCnt; + UINT32 ValidLifetime; + UINT32 PreferredLifetime; + UINT8 PrefixLength; + EFI_IPv6_ADDRESS Prefix; +} IP6_PREFIX_LIST_ENTRY; + +/** + Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number + of EFI_IP6_NEIGHBOR_CACHE is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] NeighborCount The number of returned neighbor cache entries. + @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE. + + @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiNeighborCache ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *NeighborCount, + OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache + ); + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of prefix entries is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] PrefixCount The number of returned prefix entries. + @param[out] PrefixTable The pointer to the array of PrefixTable. + + @retval EFI_SUCCESS The prefix table successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table. + +**/ +EFI_STATUS +Ip6BuildPrefixTable ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *PrefixCount, + OUT EFI_IP6_ADDRESS_INFO **PrefixTable + ); + +/** + Allocate and initialize an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the default router. + @param[in] RouterLifetime The lifetime associated with the default + router, in units of seconds. + + @return NULL if it failed to allocate memory for the default router node. + Otherwise, point to the created default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6CreateDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN UINT16 RouterLifetime + ); + +/** + Destroy an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER. + +**/ +VOID +Ip6DestroyDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN IP6_DEFAULT_ROUTER *DefaultRouter + ); + +/** + Clean an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + +**/ +VOID +Ip6CleanDefaultRouterList ( + IN IP6_SERVICE *IpSb + ); + +/** + Search a default router node from an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the to be searched default router node. + + @return NULL if it failed to find the matching default router node. + Otherwise, point to the found default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6FindDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ); + +/** + The function to be called after DAD (Duplicate Address Detection) is performed. + + @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed. + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] DadEntry The DAD entry which already performed DAD. + +**/ +VOID +Ip6OnDADFinished ( + IN BOOLEAN IsDadPassed, + IN IP6_INTERFACE *IpIf, + IN IP6_DAD_ENTRY *DadEntry + ); + +/** + Create a DAD (Duplicate Address Detection) entry and queue it to be performed. + + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] AddressInfo The address information which needs DAD performed. + @param[in] Callback The callback routine that will be called after DAD + is performed. This is an optional parameter that + may be NULL. + @param[in] Context The opaque parameter for a DAD callback routine. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The DAD entry was created and queued. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the + operation. + + +**/ +EFI_STATUS +Ip6InitDADProcess ( + IN IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddressInfo, + IN IP6_DAD_CALLBACK Callback OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + Search IP6_DAD_ENTRY from the Duplicate Address Detection List. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Target The address information which needs DAD performed . + @param[out] Interface If not NULL, output the IP6 interface that configures + the tentative address. + + @return NULL if failed to find the matching DAD entry. + Otherwise, point to the found DAD entry. + +**/ +IP6_DAD_ENTRY * +Ip6FindDADEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Target, + OUT IP6_INTERFACE **Interface OPTIONAL + ); + +/** + Allocate and initialize a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list. + Otherwise, it is created for the autoconfiguration prefix list. + @param[in] ValidLifetime The length of time in seconds that the prefix + is valid for the purpose of on-link determination. + @param[in] PreferredLifetime The length of time in seconds that addresses + generated from the prefix via stateless address + autoconfiguration remain preferred. + @param[in] PrefixLength The prefix length of the Prefix. + @param[in] Prefix The prefix address. + + @return NULL if it failed to allocate memory for the prefix node. Otherwise, point + to the created or existing prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6CreatePrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ); + +/** + Destroy a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] PrefixEntry The to be destroyed prefix list entry. + @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list. + Otherwise remove from autoconfiguration prefix list. + @param[in] ImmediateDelete If TRUE, remove the entry directly. + Otherwise, check the reference count to see whether + it should be removed. + +**/ +VOID +Ip6DestroyPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_PREFIX_LIST_ENTRY *PrefixEntry, + IN BOOLEAN OnLinkOrAuto, + IN BOOLEAN ImmediateDelete + ); + +/** + Search the list array to find an IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the search the link prefix list, + Otherwise search the autoconfiguration prefix list. + @param[in] PrefixLength The prefix length of the Prefix + @param[in] Prefix The prefix address. + + @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the + pointer to the IP6 prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6FindPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ); + +/** + Release the resource in prefix list table, and destroy the list entry and + corresponding addresses or route entries. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] ListHead The list entry head of the prefix list table. + +**/ +VOID +Ip6CleanPrefixListTable ( + IN IP6_SERVICE *IpSb, + IN LIST_ENTRY *ListHead + ); + +/** + Allocate and initialize an IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] CallBack The callback function to be called when + address resolution is finished. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + @param[in] LinkAddress Points to the MAC address of the neighbor. + Ignored if NULL. + + @return NULL if failed to allocate memory for the neighbor cache entry. + Otherwise, point to the created neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6CreateNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_ARP_CALLBACK CallBack, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL + ); + +/** + Search a IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + + @return NULL if it failed to find the matching neighbor cache entry. + Otherwise, point to the found neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6FindNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ); + +/** + Free a IP6 neighbor cache entry and remove all the frames on the address + resolution queue that pass the FrameToCancel. That is, either FrameToCancel + is NULL, or it returns true for the frame. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] NeighborCache The to be free neighbor cache entry. + @param[in] SendIcmpError If TRUE, send out ICMP error. + @param[in] FullFree If TRUE, remove the neighbor cache entry. + Otherwise remove the pending frames. + @param[in] IoStatus The status returned to the cancelled frames' + callback function. + @param[in] FrameToCancel Function to select which frame to cancel. + This is an optional parameter that may be NULL. + @param[in] Context Opaque parameter to the FrameToCancel. + Ignored if FrameToCancel is NULL. + + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + @retval EFI_SUCCESS The operation finished successfully. + +**/ +EFI_STATUS +Ip6FreeNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_NEIGHBOR_ENTRY *NeighborCache, + IN BOOLEAN SendIcmpError, + IN BOOLEAN FullFree, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + Add Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been added. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache + due to insufficient resources. + @retval EFI_NOT_FOUND TargetLinkAddress is NULL. + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when DeleteFlag + is FALSE). + +**/ +EFI_STATUS +Ip6AddNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ); + +/** + Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache. + +**/ +EFI_STATUS +Ip6DelNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ); + +/** + Process the Neighbor Solicitation message. The message may be sent for Duplicate + Address Detection or Address Resolution. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the Neighbor Advertisement message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the Router Advertisement message according to RFC4861. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with the IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessRouterAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the ICMPv6 redirect message. Find the instance, then update + its route cache. + + @param[in] IpSb The IP6 service binding instance that received + the packet. + @param[in] Head The IP head of the received ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 redirect packet with + the IP head removed. + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insuffcient resources to complete the + operation. + @retval EFI_SUCCESS Successfully updated the route caches. + +**/ +EFI_STATUS +Ip6ProcessRedirect ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Generate router solicit message and send it out to Destination Address or + All Router Link Local scope multicast address. + + @param[in] IpSb The IP service to send the packet. + @param[in] Interface If not NULL, points to the IP6 interface to send + the packet. + @param[in] SourceAddress If not NULL, the source address of the message. + @param[in] DestinationAddress If not NULL, the destination address of the message. + @param[in] SourceLinkAddress If not NULL, the MAC address of the source. + A source link-layer address option will be appended + to the message. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation. + @retval EFI_SUCCESS The router solicit message was successfully sent. + +**/ +EFI_STATUS +Ip6SendRouterSolicit ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ); + +/** + Generate the Neighbor Solicitation message and send it to the Destination Address. + + @param[in] IpSb The IP service to send the packet + @param[in] SourceAddress The source address of the message. + @param[in] DestinationAddress The destination address of the message. + @param[in] TargetIp6Address The IP address of the target of the solicitation. + It must not be a multicast address. + @param[in] SourceLinkAddress The MAC address for the sender. If not NULL, + a source link-layer address option will be appended + to the message. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent. + +**/ +EFI_STATUS +Ip6SendNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *SourceAddress, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ); + +/** + Set the interface's address. This will trigger the DAD process for the + address to set. To set an already set address, the lifetimes wil be + updated to the new value passed in. + + @param[in] Interface The interface to set the address. + @param[in] Ip6Addr The interface's to be assigned IPv6 address. + @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast. + Otherwise, it is not anycast. + @param[in] PrefixLength The prefix length of the Ip6Addr. + @param[in] ValidLifetime The valid lifetime for this address. + @param[in] PreferredLifetime The preferred lifetime for this address. + @param[in] DadCallback The caller's callback to trigger when DAD finishes. + This is an optional parameter that may be NULL. + @param[in] Context The context that will be passed to DadCallback. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The interface is scheduled to be configured with + the specified address. + @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to + lack of resources. + +**/ +EFI_STATUS +Ip6SetAddress ( + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Ip6Addr, + IN BOOLEAN IsAnycast, + IN UINT8 PrefixLength, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN IP6_DAD_CALLBACK DadCallback OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds. + This time routine handles DAD module and neighbor state transition. + It is also responsible for sending out router solicitations. + + @param[in] Event The IP6 service instance's heartbeat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6NdFasterTimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + The heartbeat timer of ND module in 1 second. This time routine handles following + things: 1) maitain default router list; 2) maintain prefix options; + 3) maintain route caches. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6NdTimerTicking ( + IN IP6_SERVICE *IpSb + ); + +/** + Callback function when address resolution is finished. It will cancel + all the queued frames if the address resolution failed, or transmit them + if the request succeeded. + + @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY. + +**/ +VOID +Ip6OnArpResolved ( + IN VOID *Context + ); + +/** + Update the ReachableTime in IP6 service binding instance data, in milliseconds. + + @param[in, out] IpSb Points to the IP6_SERVICE. + +**/ +VOID +Ip6UpdateReachableTime ( + IN OUT IP6_SERVICE *IpSb + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6NvData.h b/Core/NetworkPkg/Ip6Dxe/Ip6NvData.h new file mode 100644 index 0000000000..09177613fb --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6NvData.h @@ -0,0 +1,69 @@ +/** @file + NVData structure used by the IP6 configuration component. + + Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IP6_NV_DATA_H_ +#define _IP6_NV_DATA_H_ + +#include + +#define FORMID_MAIN_FORM 1 +#define FORMID_MANUAL_CONFIG_FORM 2 +#define FORMID_HEAD_FORM 3 + +#define IP6_POLICY_AUTO 0 +#define IP6_POLICY_MANUAL 1 +#define DAD_MAX_TRANSMIT_COUNT 10 + +#define KEY_INTERFACE_ID 0x101 +#define KEY_MANUAL_ADDRESS 0x102 +#define KEY_GATEWAY_ADDRESS 0x103 +#define KEY_DNS_ADDRESS 0x104 +#define KEY_SAVE_CHANGES 0x105 +#define KEY_SAVE_CONFIG_CHANGES 0x106 +#define KEY_IGNORE_CONFIG_CHANGES 0x107 +#define KEY_GET_CURRENT_SETTING 0x108 + +#define HOST_ADDRESS_LABEL 0x9000 +#define ROUTE_TABLE_LABEL 0xa000 +#define GATEWAY_ADDRESS_LABEL 0xb000 +#define DNS_ADDRESS_LABEL 0xc000 +#define LABEL_END 0xffff + +#define INTERFACE_ID_STR_MIN_SIZE 1 +#define INTERFACE_ID_STR_MAX_SIZE 23 +#define INTERFACE_ID_STR_STORAGE 25 +#define IP6_STR_MAX_SIZE 40 +#define ADDRESS_STR_MIN_SIZE 2 +#define ADDRESS_STR_MAX_SIZE 255 + +/// +/// IP6_CONFIG_IFR_NVDATA contains the IP6 configure +/// parameters for that NIC. +/// +#pragma pack(1) +typedef struct { + UINT8 IfType; ///< interface type + UINT8 Padding[3]; + UINT32 Policy; ///< manual or automatic + UINT32 DadTransmitCount; ///< dad transmits count + CHAR16 InterfaceId[INTERFACE_ID_STR_STORAGE]; ///< alternative interface id + CHAR16 ManualAddress[ADDRESS_STR_MAX_SIZE]; ///< IP addresses + CHAR16 GatewayAddress[ADDRESS_STR_MAX_SIZE]; ///< Gateway address + CHAR16 DnsAddress[ADDRESS_STR_MAX_SIZE]; ///< DNS server address +} IP6_CONFIG_IFR_NVDATA; +#pragma pack() + +#endif + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Option.c b/Core/NetworkPkg/Ip6Dxe/Ip6Option.c new file mode 100644 index 0000000000..9a91fd7cd1 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Option.c @@ -0,0 +1,758 @@ +/** @file + IP6 option support functions and routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is malformated. + + @param[in] IpSb The IP6 service data. + @param[in] Packet The to be validated packet. + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + @param[in] Pointer Identifies the octet offset within + the invoking packet where the error was detected. + + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsOptionValid ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN UINT8 *Option, + IN UINT8 OptionLen, + IN UINT32 Pointer + ) +{ + UINT8 Offset; + UINT8 OptionType; + + Offset = 0; + + while (Offset < OptionLen) { + OptionType = *(Option + Offset); + + switch (OptionType) { + case Ip6OptionPad1: + // + // It is a Pad1 option + // + Offset++; + break; + case Ip6OptionPadN: + // + // It is a PadN option + // + Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2); + break; + case Ip6OptionRouterAlert: + // + // It is a Router Alert Option + // + Offset += 4; + break; + default: + // + // The highest-order two bits specify the action must be taken if + // the processing IPv6 node does not recognize the option type. + // + switch (OptionType & Ip6OptionMask) { + case Ip6OptionSkip: + Offset = (UINT8) (Offset + *(Option + Offset + 1)); + break; + case Ip6OptionDiscard: + return FALSE; + case Ip6OptionParameterProblem: + Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER); + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 2, + &Pointer + ); + return FALSE; + case Ip6OptionMask: + if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER); + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 2, + &Pointer + ); + } + + return FALSE; + break; + } + + break; + } + + } + + return TRUE; +} + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It supports the defined options in Neighbor + Discovery messages. + + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsNDOptionValid ( + IN UINT8 *Option, + IN UINT16 OptionLen + ) +{ + UINT16 Offset; + UINT8 OptionType; + UINT16 Length; + + Offset = 0; + + while (Offset < OptionLen) { + OptionType = *(Option + Offset); + Length = (UINT16) (*(Option + Offset + 1) * 8); + + switch (OptionType) { + case Ip6OptionPrefixInfo: + if (Length != 32) { + return FALSE; + } + + break; + + case Ip6OptionMtu: + if (Length != 8) { + return FALSE; + } + + break; + + default: + // + // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and + // Ip6OptionRedirected here. For unrecognized options, silently ignore + // and continue processsing the message. + // + if (Length == 0) { + return FALSE; + } + + break; + } + + Offset = (UINT16) (Offset + Length); + } + + return TRUE; +} + + +/** + Validate whether the NextHeader is a known valid protocol or one of the user configured + protocols from the upper layer. + + @param[in] IpSb The IP6 service instance. + @param[in] NextHeader The next header field. + + @retval TRUE The NextHeader is a known valid protocol or user configured. + @retval FALSE The NextHeader is not a known valid protocol. + +**/ +BOOLEAN +Ip6IsValidProtocol ( + IN IP6_SERVICE *IpSb, + IN UINT8 NextHeader + ) +{ + LIST_ENTRY *Entry; + IP6_PROTOCOL *IpInstance; + + if (NextHeader == EFI_IP_PROTO_TCP || + NextHeader == EFI_IP_PROTO_UDP || + NextHeader == IP6_ICMP || + NextHeader == IP6_ESP + ) { + return TRUE; + } + + if (IpSb == NULL) { + return FALSE; + } + + if (IpSb->Signature != IP6_SERVICE_SIGNATURE) { + return FALSE; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + if (IpInstance->State == IP6_STATE_CONFIGED) { + if (IpInstance->ConfigData.DefaultProtocol == NextHeader) { + return TRUE; + } + } + } + + return FALSE; +} + +/** + Validate the IP6 extension header format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is mal-formated. + + @param[in] IpSb The IP6 service instance. This is an optional parameter. + @param[in] Packet The data of the packet. Ignored if NULL. + @param[in] NextHeader The next header field in IPv6 basic header. + @param[in] ExtHdrs The first byte of the option. + @param[in] ExtHdrsLen The length of the whole option. + @param[in] Rcvd The option is from the packet we received if TRUE, + otherwise, the option we want to transmit. + @param[out] FormerHeader The offset of NextHeader which points to Fragment + Header when we received, of the ExtHdrs. + Ignored if we transmit. + @param[out] LastHeader The pointer of NextHeader of the last extension + header processed by IP6. + @param[out] RealExtsLen The length of extension headers processed by IP6 layer. + This is an optional parameter that may be NULL. + @param[out] UnFragmentLen The length of unfragmented length of extension headers. + This is an optional parameter that may be NULL. + @param[out] Fragmented Indicate whether the packet is fragmented. + This is an optional parameter that may be NULL. + + @retval TRUE The option is properly formated. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsExtsValid ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN NET_BUF *Packet OPTIONAL, + IN UINT8 *NextHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN BOOLEAN Rcvd, + OUT UINT32 *FormerHeader OPTIONAL, + OUT UINT8 **LastHeader, + OUT UINT32 *RealExtsLen OPTIONAL, + OUT UINT32 *UnFragmentLen OPTIONAL, + OUT BOOLEAN *Fragmented OPTIONAL + ) +{ + UINT32 Pointer; + UINT32 Offset; + UINT8 *Option; + UINT8 OptionLen; + BOOLEAN Flag; + UINT8 CountD; + UINT8 CountA; + IP6_FRAGMENT_HEADER *FragmentHead; + UINT16 FragmentOffset; + IP6_ROUTING_HEADER *RoutingHead; + + if (RealExtsLen != NULL) { + *RealExtsLen = 0; + } + + if (UnFragmentLen != NULL) { + *UnFragmentLen = 0; + } + + if (Fragmented != NULL) { + *Fragmented = FALSE; + } + + *LastHeader = NextHeader; + + if (ExtHdrs == NULL && ExtHdrsLen == 0) { + return TRUE; + } + + if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) { + return FALSE; + } + + Pointer = 0; + Offset = 0; + Flag = FALSE; + CountD = 0; + CountA = 0; + + while (Offset <= ExtHdrsLen) { + + switch (*NextHeader) { + case IP6_HOP_BY_HOP: + if (Offset != 0) { + if (!Rcvd) { + return FALSE; + } + // + // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only. + // If not, generate a ICMP parameter problem message with code value of 1. + // + if (Pointer == 0) { + Pointer = sizeof (EFI_IP6_HEADER); + } else { + Pointer = Offset + sizeof (EFI_IP6_HEADER); + } + + if ((IpSb != NULL) && (Packet != NULL) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 1, + &Pointer + ); + } + return FALSE; + } + + Flag = TRUE; + + // + // Fall through + // + case IP6_DESTINATION: + if (*NextHeader == IP6_DESTINATION) { + CountD++; + } + + if (CountD > 2) { + return FALSE; + } + + NextHeader = ExtHdrs + Offset; + Pointer = Offset; + + Offset++; + Option = ExtHdrs + Offset; + OptionLen = (UINT8) ((*Option + 1) * 8 - 2); + Option++; + Offset++; + + if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) { + return FALSE; + } + + Offset = Offset + OptionLen; + + if (Flag) { + if (UnFragmentLen != NULL) { + *UnFragmentLen = Offset; + } + + Flag = FALSE; + } + + break; + + case IP6_ROUTING: + NextHeader = ExtHdrs + Offset; + RoutingHead = (IP6_ROUTING_HEADER *) NextHeader; + + // + // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095. + // Thus all routing types are processed as unrecognized. + // + if (RoutingHead->SegmentsLeft == 0) { + // + // Ignore the routing header and proceed to process the next header. + // + Offset = Offset + (RoutingHead->HeaderLen + 1) * 8; + + if (UnFragmentLen != NULL) { + *UnFragmentLen = Offset; + } + + } else { + // + // Discard the packet and send an ICMP Parameter Problem, Code 0, message + // to the packet's source address, pointing to the unrecognized routing + // type. + // + Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER); + if ((IpSb != NULL) && (Packet != NULL) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 0, + &Pointer + ); + } + + return FALSE; + } + + break; + + case IP6_FRAGMENT: + + // + // RFC2402, AH header should after fragment header. + // + if (CountA > 1) { + return FALSE; + } + + // + // RFC2460, ICMP Parameter Problem message with code 0 should be sent + // if the length of a fragment is not a multiple of 8 octects and the M + // flag of that fragment is 1, pointing to the Payload length field of the + // fragment packet. + // + if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) { + // + // Check whether it is the last fragment. + // + FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset); + if (FragmentHead == NULL) { + return FALSE; + } + + FragmentOffset = NTOHS (FragmentHead->FragmentOffset); + + if (((FragmentOffset & 0x1) == 0x1) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Pointer = sizeof (UINT32); + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 0, + &Pointer + ); + return FALSE; + } + } + + if (Fragmented != NULL) { + *Fragmented = TRUE; + } + + if (Rcvd && FormerHeader != NULL) { + *FormerHeader = (UINT32) (NextHeader - ExtHdrs); + } + + NextHeader = ExtHdrs + Offset; + Offset = Offset + 8; + break; + + case IP6_AH: + if (++CountA > 1) { + return FALSE; + } + + Option = ExtHdrs + Offset; + NextHeader = Option; + Option++; + // + // RFC2402, Payload length is specified in 32-bit words, minus "2". + // + OptionLen = (UINT8) ((*Option + 2) * 4); + Offset = Offset + OptionLen; + break; + + case IP6_NO_NEXT_HEADER: + *LastHeader = NextHeader; + return FALSE; + break; + + default: + if (Ip6IsValidProtocol (IpSb, *NextHeader)) { + + *LastHeader = NextHeader; + + if (RealExtsLen != NULL) { + *RealExtsLen = Offset; + } + + return TRUE; + } + + // + // The Next Header value is unrecognized by the node, discard the packet and + // send an ICMP parameter problem message with code value of 1. + // + if (Offset == 0) { + // + // The Next Header directly follows IPv6 basic header. + // + Pointer = 6; + } else { + if (Pointer == 0) { + Pointer = sizeof (EFI_IP6_HEADER); + } else { + Pointer = Offset + sizeof (EFI_IP6_HEADER); + } + } + + if ((IpSb != NULL) && (Packet != NULL) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 1, + &Pointer + ); + } + return FALSE; + } + } + + *LastHeader = NextHeader; + + if (RealExtsLen != NULL) { + *RealExtsLen = Offset; + } + + return TRUE; +} + +/** + Generate an IPv6 router alert option in network order and output it through Buffer. + + @param[out] Buffer Points to a buffer to record the generated option. + @param[in, out] BufferLen The length of Buffer, in bytes. + @param[in] NextHeader The 8-bit selector indicates the type of header + immediately following the Hop-by-Hop Options header. + + @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated + option. BufferLen is updated for the required size. + + @retval EFI_SUCCESS The option is generated and filled in to Buffer. + +**/ +EFI_STATUS +Ip6FillHopByHop ( + OUT UINT8 *Buffer, + IN OUT UINTN *BufferLen, + IN UINT8 NextHeader + ) +{ + UINT8 BufferArray[8]; + + if (*BufferLen < 8) { + *BufferLen = 8; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Form the Hop-By-Hop option in network order. + // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN + // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets. + // + ZeroMem (BufferArray, sizeof (BufferArray)); + BufferArray[0] = NextHeader; + BufferArray[2] = 0x5; + BufferArray[3] = 0x2; + BufferArray[6] = 1; + + CopyMem (Buffer, BufferArray, sizeof (BufferArray)); + return EFI_SUCCESS; +} + +/** + Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] NextHeader The extension header type of first extension header. + @param[in] LastHeader The extension header type of last extension header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted. + It's caller's responsiblity to free this buffer. + + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of + resource. + @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not + supported currently. + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +Ip6FillFragmentHeader ( + IN IP6_SERVICE *IpSb, + IN UINT8 NextHeader, + IN UINT8 LastHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN UINT16 FragmentOffset, + OUT UINT8 **UpdatedExtHdrs + ) +{ + UINT32 Length; + UINT8 *Buffer; + UINT32 FormerHeader; + UINT32 Offset; + UINT32 Part1Len; + UINT32 HeaderLen; + UINT8 Current; + IP6_FRAGMENT_HEADER FragmentHead; + + if (UpdatedExtHdrs == NULL) { + return EFI_INVALID_PARAMETER; + } + + Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER); + Buffer = AllocatePool (Length); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Offset = 0; + Part1Len = 0; + FormerHeader = 0; + Current = NextHeader; + + while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) { + switch (NextHeader) { + case IP6_ROUTING: + case IP6_HOP_BY_HOP: + case IP6_DESTINATION: + Current = NextHeader; + NextHeader = *(ExtHdrs + Offset); + + if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) { + // + // Destination Options header should occur at most twice, once before + // a Routing header and once before the upper-layer header. Here we + // find the one before the upper-layer header. Insert the Fragment + // Header before it. + // + CopyMem (Buffer, ExtHdrs, Part1Len); + *(Buffer + FormerHeader) = IP6_FRAGMENT; + // + // Exit the loop. + // + Offset = ExtHdrsLen + 1; + break; + } + + + FormerHeader = Offset; + HeaderLen = (*(ExtHdrs + Offset + 1) + 1) * 8; + Part1Len = Part1Len + HeaderLen; + Offset = Offset + HeaderLen; + break; + + case IP6_FRAGMENT: + Current = NextHeader; + + if (Part1Len != 0) { + CopyMem (Buffer, ExtHdrs, Part1Len); + } + + *(Buffer + FormerHeader) = IP6_FRAGMENT; + + // + // Exit the loop. + // + Offset = ExtHdrsLen + 1; + break; + + case IP6_AH: + Current = NextHeader; + NextHeader = *(ExtHdrs + Offset); + // + // RFC2402, Payload length is specified in 32-bit words, minus "2". + // + HeaderLen = (*(ExtHdrs + Offset + 1) + 2) * 4; + Part1Len = Part1Len + HeaderLen; + Offset = Offset + HeaderLen; + break; + + default: + if (Ip6IsValidProtocol (IpSb, NextHeader)) { + Current = NextHeader; + CopyMem (Buffer, ExtHdrs, Part1Len); + *(Buffer + FormerHeader) = IP6_FRAGMENT; + // + // Exit the loop. + // + Offset = ExtHdrsLen + 1; + break; + } + + FreePool (Buffer); + return EFI_UNSUPPORTED; + } + } + + // + // Append the Fragment header. If the fragment offset indicates the fragment + // is the first fragment. + // + if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) { + FragmentHead.NextHeader = Current; + } else { + FragmentHead.NextHeader = LastHeader; + } + + FragmentHead.Reserved = 0; + FragmentHead.FragmentOffset = HTONS (FragmentOffset); + FragmentHead.Identification = mIp6Id; + + CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER)); + + if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) { + // + // Append the part2 (fragmentable part) of Extension headers + // + CopyMem ( + Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER), + ExtHdrs + Part1Len, + ExtHdrsLen - Part1Len + ); + } + + *UpdatedExtHdrs = Buffer; + + return EFI_SUCCESS; +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Option.h b/Core/NetworkPkg/Ip6Dxe/Ip6Option.h new file mode 100644 index 0000000000..b62a04216e --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Option.h @@ -0,0 +1,191 @@ +/** @file + Definition of IP6 option process routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_OPTION_H__ +#define __EFI_IP6_OPTION_H__ + +#define IP6_FRAGMENT_OFFSET_MASK (~0x3) + +typedef struct _IP6_FRAGMENT_HEADER { + UINT8 NextHeader; + UINT8 Reserved; + UINT16 FragmentOffset; + UINT32 Identification; +} IP6_FRAGMENT_HEADER; + +typedef struct _IP6_ROUTING_HEADER { + UINT8 NextHeader; + UINT8 HeaderLen; + UINT8 RoutingType; + UINT8 SegmentsLeft; +} IP6_ROUTING_HEADER; + +typedef enum { + Ip6OptionPad1 = 0, + Ip6OptionPadN = 1, + Ip6OptionRouterAlert = 5, + Ip6OptionSkip = 0, + Ip6OptionDiscard = 0x40, + Ip6OptionParameterProblem = 0x80, + Ip6OptionMask = 0xc0, + + Ip6OptionEtherSource = 1, + Ip6OptionEtherTarget = 2, + Ip6OptionPrefixInfo = 3, + Ip6OptionRedirected = 4, + Ip6OptionMtu = 5 +} IP6_OPTION_TYPE; + +/** + Validate the IP6 extension header format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is mal-formated. + + @param[in] IpSb The IP6 service instance. This is an optional parameter. + @param[in] Packet The data of the packet. Ignored if NULL. + @param[in] NextHeader The next header field in IPv6 basic header. + @param[in] ExtHdrs The first byte of the option. + @param[in] ExtHdrsLen The length of the whole option. + @param[in] Rcvd The option is from the packet we received if TRUE, + otherwise, the option we want to transmit. + @param[out] FormerHeader The offset of NextHeader which points to Fragment + Header when we received, of the ExtHdrs. + Ignored if we transmit. + @param[out] LastHeader The pointer of NextHeader of the last extension + header processed by IP6. + @param[out] RealExtsLen The length of extension headers processed by IP6 layer. + This is an optional parameter that may be NULL. + @param[out] UnFragmentLen The length of unfragmented length of extension headers. + This is an optional parameter that may be NULL. + @param[out] Fragmented Indicate whether the packet is fragmented. + This is an optional parameter that may be NULL. + + @retval TRUE The option is properly formated. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsExtsValid ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN NET_BUF *Packet OPTIONAL, + IN UINT8 *NextHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN BOOLEAN Rcvd, + OUT UINT32 *FormerHeader OPTIONAL, + OUT UINT8 **LastHeader, + OUT UINT32 *RealExtsLen OPTIONAL, + OUT UINT32 *UnFragmentLen OPTIONAL, + OUT BOOLEAN *Fragmented OPTIONAL + ); + +/** + Generate an IPv6 router alert option in network order and output it through Buffer. + + @param[out] Buffer Points to a buffer to record the generated option. + @param[in, out] BufferLen The length of Buffer, in bytes. + @param[in] NextHeader The 8-bit selector indicates the type of header + immediately following the Hop-by-Hop Options header. + + @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated + option. BufferLen is updated for the required size. + + @retval EFI_SUCCESS The option is generated and filled in to Buffer. + +**/ +EFI_STATUS +Ip6FillHopByHop ( + OUT UINT8 *Buffer, + IN OUT UINTN *BufferLen, + IN UINT8 NextHeader + ); + +/** + Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] NextHeader The extension header type of first extension header. + @param[in] LastHeader The extension header type of last extension header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted. + It's caller's responsiblity to free this buffer. + + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of + resource. + @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not + supported currently. + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +Ip6FillFragmentHeader ( + IN IP6_SERVICE *IpSb, + IN UINT8 NextHeader, + IN UINT8 LastHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN UINT16 FragmentOffset, + OUT UINT8 **UpdatedExtHdrs + ); + +/** + Copy the extension headers from the original to buffer. A Fragment header is + appended to the end. + + @param[in] NextHeader The 8-bit selector indicates the type of + the fragment header's next header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] LastHeader The pointer of next header of last extension header. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[in] UnFragmentHdrLen The length of unfragmented length of extension headers. + @param[in, out] Buf The buffer to copy options to. + @param[in, out] BufLen The length of the buffer. + + @retval EFI_SUCCESS The options are copied over. + @retval EFI_BUFFER_TOO_SMALL The buffer caller provided is too small. + +**/ +EFI_STATUS +Ip6CopyExts ( + IN UINT8 NextHeader, + IN UINT8 *ExtHdrs, + IN UINT8 *LastHeader, + IN UINT16 FragmentOffset, + IN UINT32 UnFragmentHdrLen, + IN OUT UINT8 *Buf, + IN OUT UINT32 *BufLen + ); + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It supports the defined options in Neighbor + Discovery messages. + + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +Ip6IsNDOptionValid ( + IN UINT8 *Option, + IN UINT16 OptionLen + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Output.c b/Core/NetworkPkg/Ip6Dxe/Ip6Output.c new file mode 100644 index 0000000000..fe642d371e --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Output.c @@ -0,0 +1,1091 @@ +/** @file + The internal functions and routines to transmit the IP6 packet. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +UINT32 mIp6Id; + +/** + Output all the available source addresses to a list entry head SourceList. The + number of source addresses are also returned. + + @param[in] IpSb Points to an IP6 service binding instance. + @param[out] SourceList The list entry head of all source addresses. + It is the caller's responsiblity to free the + resources. + @param[out] SourceCount The number of source addresses. + + @retval EFI_SUCCESS The source addresses were copied to a list entry head + SourceList. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + +**/ +EFI_STATUS +Ip6CandidateSource ( + IN IP6_SERVICE *IpSb, + OUT LIST_ENTRY *SourceList, + OUT UINT32 *SourceCount + ) +{ + IP6_INTERFACE *IpIf; + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_ADDRESS_INFO *AddrInfo; + IP6_ADDRESS_INFO *Copy; + + *SourceCount = 0; + + if (IpSb->LinkLocalOk) { + Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO)); + if (Copy == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Copy->Signature = IP6_ADDR_INFO_SIGNATURE; + IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr); + Copy->IsAnycast = FALSE; + Copy->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + Copy->ValidLifetime = (UINT32) IP6_INFINIT_LIFETIME; + Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME; + + InsertTailList (SourceList, &Copy->Link); + (*SourceCount)++; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + if (AddrInfo->IsAnycast) { + // + // Never use an anycast address. + // + continue; + } + + Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo); + if (Copy == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InsertTailList (SourceList, &Copy->Link); + (*SourceCount)++; + } + } + + return EFI_SUCCESS; +} + +/** + Calculate how many bits are the same between two IPv6 addresses. + + @param[in] AddressA Points to an IPv6 address. + @param[in] AddressB Points to another IPv6 address. + + @return The common bits of the AddressA and AddressB. + +**/ +UINT8 +Ip6CommonPrefixLen ( + IN EFI_IPv6_ADDRESS *AddressA, + IN EFI_IPv6_ADDRESS *AddressB + ) +{ + UINT8 Count; + UINT8 Index; + UINT8 ByteA; + UINT8 ByteB; + UINT8 NumBits; + + Count = 0; + Index = 0; + + while (Index < 16) { + ByteA = AddressA->Addr[Index]; + ByteB = AddressB->Addr[Index]; + + if (ByteA == ByteB) { + Count += 8; + Index++; + continue; + } + + // + // Check how many bits are common between the two bytes. + // + NumBits = 8; + ByteA = (UINT8) (ByteA ^ ByteB); + + while (ByteA != 0) { + NumBits--; + ByteA = (UINT8) (ByteA >> 1); + } + + return (UINT8) (Count + NumBits); + } + + return Count; +} + +/** + Output all the available source addresses to a list entry head SourceList. The + number of source addresses are also returned. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] Destination The IPv6 destination address. + @param[out] Source The selected IPv6 source address according to + the Destination. + + @retval EFI_SUCCESS The source addresses were copied to a list entry + head SourceList. + @retval EFI_NO_MAPPING The IPv6 stack is not auto configured. + +**/ +EFI_STATUS +Ip6SelectSourceAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Destination, + OUT EFI_IPv6_ADDRESS *Source + ) +{ + EFI_STATUS Status; + LIST_ENTRY SourceList; + UINT32 SourceCount; + UINT8 ScopeD; + LIST_ENTRY *Entry; + IP6_ADDRESS_INFO *AddrInfo; + IP6_PREFIX_LIST_ENTRY *Prefix; + UINT8 LastCommonLength; + UINT8 CurrentCommonLength; + EFI_IPv6_ADDRESS *TmpAddress; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Status = EFI_SUCCESS; + InitializeListHead (&SourceList); + + if (!IpSb->LinkLocalOk) { + return EFI_NO_MAPPING; + } + + // + // Rule 1: Prefer same address. + // + if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) { + IP6_COPY_ADDRESS (Source, Destination); + goto Exit; + } + + // + // Rule 2: Prefer appropriate scope. + // + if (IP6_IS_MULTICAST (Destination)) { + ScopeD = (UINT8) (Destination->Addr[1] >> 4); + } else if (NetIp6IsLinkLocalAddr (Destination)) { + ScopeD = 0x2; + } else { + ScopeD = 0xE; + } + + if (ScopeD <= 0x2) { + // + // Return the link-local address if it exists + // One IP6_SERVICE only has one link-local address. + // + IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr); + goto Exit; + } + + // + // All candidate source addresses are global unicast address. + // + Ip6CandidateSource (IpSb, &SourceList, &SourceCount); + + if (SourceCount == 0) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr); + + if (SourceCount == 1) { + goto Exit; + } + + // + // Rule 3: Avoid deprecated addresses. + // TODO: check the "deprecated" state of the stateful configured address + // + NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) { + Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (Prefix->PreferredLifetime == 0) { + Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength); + + if (SourceCount == 1) { + goto Exit; + } + } + } + + // + // TODO: Rule 4: Prefer home addresses. + // TODO: Rule 5: Prefer outgoing interface. + // TODO: Rule 6: Prefer matching label. + // TODO: Rule 7: Prefer public addresses. + // + + // + // Rule 8: Use longest matching prefix. + // + LastCommonLength = Ip6CommonPrefixLen (Source, Destination); + TmpAddress = NULL; + + for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination); + if (CurrentCommonLength > LastCommonLength) { + LastCommonLength = CurrentCommonLength; + TmpAddress = &AddrInfo->Address; + } + } + + if (TmpAddress != NULL) { + IP6_COPY_ADDRESS (Source, TmpAddress); + } + +Exit: + + Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0); + + return Status; +} + +/** + Select an interface to send the packet generated in the IP6 driver + itself: that is, not by the requests of the IP6 child's consumer. Such + packets include the ICMPv6 echo replies and other ICMPv6 error packets. + + @param[in] IpSb The IP4 service that wants to send the packets. + @param[in] Destination The destination of the packet. + @param[in, out] Source The source of the packet. + + @return NULL if no proper interface is found, otherwise, the interface that + can be used to send the system packet from. + +**/ +IP6_INTERFACE * +Ip6SelectInterface ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Destination, + IN OUT EFI_IPv6_ADDRESS *Source + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS SelectedSource; + IP6_INTERFACE *IpIf; + BOOLEAN Exist; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Destination != NULL && Source != NULL); + + if (NetIp6IsUnspecifiedAddr (Destination)) { + return NULL; + } + + if (!NetIp6IsUnspecifiedAddr (Source)) { + Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL); + ASSERT (Exist); + + return IpIf; + } + + // + // If source is unspecified, select a source according to the destination. + // + Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource); + if (EFI_ERROR (Status)) { + return IpSb->DefaultInterface; + } + + Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL); + IP6_COPY_ADDRESS (Source, &SelectedSource); + + return IpIf; +} + +/** + The default callback function for the system generated packet. + It will free the packet. + + @param[in] Packet The packet that transmitted. + @param[in] IoStatus The result of the transmission, succeeded or failed. + @param[in] LinkFlag Not used when transmitted. Check IP6_FRAME_CALLBACK + for reference. + @param[in] Context The context provided by us. + +**/ +VOID +Ip6SysPacketSent ( + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ) +{ + NetbufFree (Packet); + Packet = NULL; +} + +/** + Prefix an IP6 basic head and unfragmentable extension headers and a fragment header + to the Packet. Used for IP6 fragmentation. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] Packet The packet to prefix the IP6 header to. + @param[in] Head The caller supplied header. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] LastHeader The pointer of next header of last extension header. + @param[in] HeadLen The length of the unfragmented part of the IP6 header. + + @retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of + Packet. + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +Ip6PrependHead ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN EFI_IP6_HEADER *Head, + IN UINT16 FragmentOffset, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN UINT8 LastHeader, + IN UINT32 HeadLen + ) +{ + UINT32 Len; + UINT32 UnFragExtHdrsLen; + EFI_IP6_HEADER *PacketHead; + UINT8 *UpdatedExtHdrs; + EFI_STATUS Status; + UINT8 NextHeader; + + UpdatedExtHdrs = NULL; + + // + // HeadLen is the length of the fixed part of the sequences of fragments, i.e. + // the unfragment part. + // + PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); + if (PacketHead == NULL) { + return EFI_BAD_BUFFER_SIZE; + } + + // + // Set the head up, convert the host byte order to network byte order + // + CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER)); + PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER))); + Packet->Ip.Ip6 = PacketHead; + + Len = HeadLen - sizeof (EFI_IP6_HEADER); + UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER); + + if (UnFragExtHdrsLen == 0) { + PacketHead->NextHeader = IP6_FRAGMENT; + } + + // + // Append the extension headers: firstly copy the unfragmentable headers, then append + // fragmentation header. + // + if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) { + NextHeader = Head->NextHeader; + } else { + NextHeader = PacketHead->NextHeader; + } + + Status = Ip6FillFragmentHeader ( + IpSb, + NextHeader, + LastHeader, + ExtHdrs, + ExtHdrsLen, + FragmentOffset, + &UpdatedExtHdrs + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem ( + (UINT8 *) (PacketHead + 1), + UpdatedExtHdrs, + UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER) + ); + + FreePool (UpdatedExtHdrs); + return EFI_SUCCESS; +} + +/** + Transmit an IP6 packet. The packet comes either from the IP6 + child's consumer (IpInstance != NULL) or the IP6 driver itself + (IpInstance == NULL). It will route the packet, fragment it, + then transmit all the fragments through an interface. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] Interface The IP6 interface to transmit the packet. Ignored + if NULL. + @param[in] IpInstance The IP6 child that issues the transmission. It is + NULL if the packet is from the system. + @param[in] Packet The user data to send, excluding the IP header. + @param[in] Head The caller supplied header. The caller should set + the following header fields: NextHeader, HopLimit, + Src, Dest, FlowLabel, PayloadLength. This function + will fill in the Ver, TrafficClass. + @param[in] ExtHdrs The extension headers to append to the IPv6 basic + header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] Callback The callback function to issue when transmission + completed. + @param[in] Context The opaque context for the callback. + + @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid. + @retval EFI_NO_MAPPING There is no interface to the destination. + @retval EFI_NOT_FOUND There is no route to the destination. + @retval EFI_SUCCESS The packet successfully transmitted. + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of + resources. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Ip6Output ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IP6_HEADER *Head, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN IP6_FRAME_CALLBACK Callback, + IN VOID *Context + ) +{ + IP6_INTERFACE *IpIf; + EFI_IPv6_ADDRESS NextHop; + IP6_NEIGHBOR_ENTRY *NeighborCache; + IP6_ROUTE_CACHE_ENTRY *RouteCache; + EFI_STATUS Status; + UINT32 Mtu; + UINT32 HeadLen; + UINT16 FragmentOffset; + UINT8 *LastHeader; + UINT32 UnFragmentLen; + UINT32 UnFragmentHdrsLen; + UINT32 FragmentHdrsLen; + UINT16 *Checksum; + UINT16 PacketChecksum; + UINT16 PseudoChecksum; + UINT32 Index; + UINT32 PacketLen; + UINT32 RealExtLen; + UINT32 Offset; + NET_BUF *TmpPacket; + NET_BUF *Fragment; + UINT32 Num; + UINT8 *Buf; + EFI_IP6_HEADER *PacketHead; + IP6_ICMP_HEAD *IcmpHead; + IP6_TXTOKEN_WRAP *Wrap; + IP6_ROUTE_ENTRY *RouteEntry; + UINT8 *UpdatedExtHdrs; + UINT8 NextHeader; + UINT8 LastHeaderBackup; + BOOLEAN FragmentHeadInserted; + UINT8 *ExtHdrsBackup; + UINT8 NextHeaderBackup; + EFI_IPv6_ADDRESS Source; + EFI_IPv6_ADDRESS Destination; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // RFC2460: Each extension header is an integer multiple of 8 octets long, + // in order to retain 8-octet alignment for subsequent headers. + // + if ((ExtHdrsLen & 0x7) != 0) { + return EFI_INVALID_PARAMETER; + } + + LastHeader = NULL; + + Ip6IsExtsValid ( + NULL, + NULL, + &Head->NextHeader, + ExtHdrs, + ExtHdrsLen, + FALSE, + NULL, + &LastHeader, + NULL, + NULL, + NULL + ); + + // + // Select an interface/source for system packet, application + // should select them itself. + // + IpIf = Interface; + if (IpIf == NULL) { + // + // IpInstance->Interface is NULL when IpInstance is configured with both stationaddress + // and destinationaddress is unspecified. + // + if (IpInstance == NULL || IpInstance->Interface == NULL) { + IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress); + if (IpInstance != NULL) { + IpInstance->Interface = IpIf; + } + } else { + IpIf = IpInstance->Interface; + } + } + + if (IpIf == NULL) { + return EFI_NO_MAPPING; + } + + // + // Update the common field in Head here. + // + Head->Version = 6; + Head->TrafficClassL = 0; + Head->TrafficClassH = 0; + + Checksum = NULL; + NextHeader = *LastHeader; + + switch (NextHeader) { + case EFI_IP_PROTO_UDP: + Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Packet->Udp != NULL); + if (Packet->Udp->Checksum == 0) { + Checksum = &Packet->Udp->Checksum; + } + break; + + case EFI_IP_PROTO_TCP: + Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Packet->Tcp != NULL); + if (Packet->Tcp->Checksum == 0) { + Checksum = &Packet->Tcp->Checksum; + } + break; + + case IP6_ICMP: + // + // Don't send ICMP packet to an IPv6 anycast address. + // + if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) { + return EFI_INVALID_PARAMETER; + } + + IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL); + ASSERT (IcmpHead != NULL); + if (IcmpHead->Checksum == 0) { + Checksum = &IcmpHead->Checksum; + } + break; + + default: + break; + } + + if (Checksum != NULL) { + // + // Calculate the checksum for upper layer protocol if it is not calculated due to lack of + // IPv6 source address. + // + PacketChecksum = NetbufChecksum (Packet); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &Head->SourceAddress, + &Head->DestinationAddress, + NextHeader, + Packet->TotalSize + ); + *Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum); + } + + Status = Ip6IpSecProcessPacket ( + IpSb, + &Head, + LastHeader, // no need get the lasthead value for output + &Packet, + &ExtHdrs, + &ExtHdrsLen, + EfiIPsecOutBound, + Context + ); + + if (EFI_ERROR(Status)) { + return Status; + } + + LastHeader = NULL; + // + // Check incoming parameters. + // + if (!Ip6IsExtsValid ( + IpSb, + Packet, + &Head->NextHeader, + ExtHdrs, + ExtHdrsLen, + FALSE, + NULL, + &LastHeader, + &RealExtLen, + &UnFragmentHdrsLen, + NULL + )) { + return EFI_INVALID_PARAMETER; + } + + if ((RealExtLen & 0x7) != 0) { + return EFI_INVALID_PARAMETER; + } + + LastHeaderBackup = *LastHeader; + + // + // Perform next hop determination: + // For multicast packets, the next-hop is always the destination address and + // is considered to be on-link. + // + if (IP6_IS_MULTICAST (&Head->DestinationAddress)) { + IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress); + } else { + // + // For unicast packets, use a combination of the Destination Cache, the Prefix List + // and the Default Router List to determine the IP address of the appropriate next hop. + // + + NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->DestinationAddress); + if (NeighborCache != NULL) { + // + // Hit Neighbor Cache. + // + IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress); + } else { + // + // Not in Neighbor Cache, check Router cache + // + RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress); + if (RouteCache == NULL) { + return EFI_NOT_FOUND; + } + + IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop); + Ip6FreeRouteCacheEntry (RouteCache); + } + } + + // + // Examines the Neighbor Cache for link-layer information about that neighbor. + // DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error. + // + if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) { + NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop); + if (NeighborCache == NULL) { + NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL); + + if (NeighborCache == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Send out multicast neighbor solicitation for address resolution immediatly. + // + Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination); + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &Destination, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + --NeighborCache->Transmit; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1; + } + + NeighborCache->Interface = IpIf; + } + + UpdatedExtHdrs = NULL; + ExtHdrsBackup = NULL; + NextHeaderBackup = 0; + FragmentHeadInserted = FALSE; + + // + // Check whether we received Packet Too Big message for the packet sent to the + // Destination. If yes include a Fragment Header in the subsequent packets. + // + RouteEntry = Ip6FindRouteEntry ( + IpSb->RouteTable, + &Head->DestinationAddress, + NULL + ); + if (RouteEntry != NULL) { + if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) { + + // + // FragmentHead is inserted after Hop-by-Hop Options header, Destination + // Options header (first occur), Routing header, and before Fragment header, + // Authentication header, Encapsulating Security Payload header, and + // Destination Options header (last occur), and upper-layer header. + // + Status = Ip6FillFragmentHeader ( + IpSb, + Head->NextHeader, + LastHeaderBackup, + ExtHdrs, + ExtHdrsLen, + 0, + &UpdatedExtHdrs + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) { + NextHeaderBackup = Head->NextHeader; + Head->NextHeader = IP6_FRAGMENT; + } + + ExtHdrsBackup = ExtHdrs; + ExtHdrs = UpdatedExtHdrs; + ExtHdrsLen = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER); + RealExtLen = RealExtLen + sizeof (IP6_FRAGMENT_HEADER); + + mIp6Id++; + + FragmentHeadInserted = TRUE; + } + + Ip6FreeRouteEntry (RouteEntry); + } + + // + // OK, selected the source and route, fragment the packet then send + // them. Tag each fragment other than the first one as spawn from it. + // Each extension header is an integar multiple of 8 octets long, in + // order to retain 8-octet alignment for subsequent headers. + // + Mtu = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER); + HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen; + + if (Packet->TotalSize + HeadLen > Mtu) { + // + // Remove the inserted Fragment Header since we need fragment the packet. + // + if (FragmentHeadInserted) { + ExtHdrs = ExtHdrsBackup; + ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER); + + if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) { + Head->NextHeader = NextHeaderBackup; + } + } + + FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen; + + // + // The packet is beyond the maximum which can be described through the + // fragment offset field in Fragment header. + // + if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) { + Status = EFI_BAD_BUFFER_SIZE; + goto Error; + } + + if (FragmentHdrsLen != 0) { + // + // Append the fragmentable extension hdrs before the upper layer payload + // to form a new NET_BUF. This NET_BUF contains all the buffer which will + // be fragmented below. + // + TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen); + ASSERT (TmpPacket != NULL); + + // + // Allocate the space to contain the fragmentable hdrs and copy the data. + // + Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE); + ASSERT (Buf != NULL); + CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen); + + // + // Free the old Packet. + // + NetbufFree (Packet); + Packet = TmpPacket; + } + + // + // The unfragment part which appears in every fragmented IPv6 packet includes + // the IPv6 header, the unfragmentable extension hdrs and the fragment header. + // + UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER); + + // + // Mtu now is the length of the fragment part in a full-length fragment. + // + Mtu = (Mtu - UnFragmentLen) & (~0x07); + Num = (Packet->TotalSize + Mtu - 1) / Mtu; + + for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) { + // + // Get fragment from the Packet, append UnFragnmentLen spare buffer + // before the fragmented data, the corresponding data is filled in later. + // + Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen); + if (Fragment == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + FragmentOffset = (UINT16) ((UINT16) Offset | 0x1); + if (Index == Num - 1){ + // + // The last fragment, clear the M flag. + // + FragmentOffset &= (~0x1); + } + + Status = Ip6PrependHead ( + IpSb, + Fragment, + Head, + FragmentOffset, + ExtHdrs, + ExtHdrsLen, + LastHeaderBackup, + UnFragmentLen + ); + ASSERT (Status == EFI_SUCCESS); + + Status = Ip6SendFrame ( + IpIf, + IpInstance, + Fragment, + &NextHop, + Ip6SysPacketSent, + Packet + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // The last fragment of upper layer packet, update the IP6 token status. + // + if ((Index == Num -1) && (Context != NULL)) { + Wrap = (IP6_TXTOKEN_WRAP *) Context; + Wrap->Token->Status = Status; + } + + Offset += PacketLen; + PacketLen = Packet->TotalSize - Offset; + if (PacketLen > Mtu) { + PacketLen = Mtu; + } + } + + NetbufFree (Packet); + mIp6Id++; + + if (UpdatedExtHdrs != NULL) { + FreePool (UpdatedExtHdrs); + } + + return EFI_SUCCESS; + } + + // + // Need not fragment the packet, send it in one frame. + // + PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); + if (PacketHead == NULL) { + Status = EFI_BAD_BUFFER_SIZE; + goto Error; + } + + CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER)); + Packet->Ip.Ip6 = PacketHead; + + if (ExtHdrs != NULL) { + Buf = (UINT8 *) (PacketHead + 1); + CopyMem (Buf, ExtHdrs, ExtHdrsLen); + } + + if (UpdatedExtHdrs != NULL) { + // + // A Fragment Header is inserted to the packet, update the payload length. + // + PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) + + sizeof (IP6_FRAGMENT_HEADER)); + PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength); + FreePool (UpdatedExtHdrs); + } + + return Ip6SendFrame ( + IpIf, + IpInstance, + Packet, + &NextHop, + Callback, + Context + ); + +Error: + if (UpdatedExtHdrs != NULL) { + FreePool (UpdatedExtHdrs); + } + Ip6CancelPacket (IpIf, Packet, Status); + return Status; +} + +/** + The filter function to find a packet and all its fragments. + The packet's fragments have their Context set to the packet. + + @param[in] Frame The frames hold by the low level interface. + @param[in] Context Context to the function, which is the packet. + + @retval TRUE This is the packet to cancel or its fragments. + @retval FALSE This is an unrelated packet. + +**/ +BOOLEAN +Ip6CancelPacketFragments ( + IN IP6_LINK_TX_TOKEN *Frame, + IN VOID *Context + ) +{ + if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) { + return TRUE; + } + + return FALSE; +} + +/** + Remove all the frames on the interface that pass the FrameToCancel, + either queued on ARP queues or that have already been delivered to + MNP and not yet recycled. + + @param[in] Interface Interface to remove the frames from. + @param[in] IoStatus The transmit status returned to the frames' callback. + @param[in] FrameToCancel Function to select the frame to cancel; NULL to select all. + @param[in] Context Opaque parameters passed to FrameToCancel. Ignored if + FrameToCancel is NULL. + +**/ +VOID +Ip6CancelFrames ( + IN IP6_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_LINK_TX_TOKEN *Token; + IP6_SERVICE *IpSb; + IP6_NEIGHBOR_ENTRY *ArpQue; + EFI_STATUS Status; + + IpSb = Interface->Service; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // Cancel all the pending frames on ARP requests + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList); + + Status = Ip6FreeNeighborEntry ( + IpSb, + ArpQue, + FALSE, + FALSE, + IoStatus, + FrameToCancel, + Context + ); + ASSERT_EFI_ERROR (Status); + } + + // + // Cancel all the frames that have been delivered to MNP + // but not yet recycled. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) { + Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link); + + if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) { + IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken); + } + } +} + +/** + Cancel the Packet and all its fragments. + + @param[in] IpIf The interface from which the Packet is sent. + @param[in] Packet The Packet to cancel. + @param[in] IoStatus The status returns to the sender. + +**/ +VOID +Ip6CancelPacket ( + IN IP6_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ) +{ + Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet); +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Output.h b/Core/NetworkPkg/Ip6Dxe/Ip6Output.h new file mode 100644 index 0000000000..80abe858e6 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Output.h @@ -0,0 +1,141 @@ +/** @file + The internal functions and routines to transmit the IP6 packet. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_OUTPUT_H__ +#define __EFI_IP6_OUTPUT_H__ + +extern UINT32 mIp6Id; + +/** + Output all the available source addresses to the list entry head SourceList. The + number of source addresses are also returned. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] Destination The IPv6 destination address. + @param[out] Source The selected IPv6 source address according to + the Destination. + + @retval EFI_SUCCESS The source addresses were copied to the list entry + head SourceList. + @retval EFI_NO_MAPPING The IPv6 stack is not auto configured. + +**/ +EFI_STATUS +Ip6SelectSourceAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Destination, + OUT EFI_IPv6_ADDRESS *Source + ); + +/** + The default callback function for system generated packet. + It will free the packet. + + @param[in] Packet The packet that transmitted. + @param[in] IoStatus The result of the transmission: succeeded or failed. + @param[in] LinkFlag Not used when transmission. Check IP6_FRAME_CALLBACK + for reference. + @param[in] Context The context provided by us. + +**/ +VOID +Ip6SysPacketSent ( + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ); + +/** + Transmit an IP6 packet. The packet comes either from the IP6 + child's consumer (IpInstance != NULL) or the IP6 driver itself + (IpInstance == NULL). It will route the packet, fragment it, + then transmit all the fragments through an interface. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] Interface The IP6 interface to transmit the packet. Ignored + if NULL. + @param[in] IpInstance The IP6 child that issues the transmission. It is + NULL if the packet is from the system. + @param[in] Packet The user data to send, excluding the IP header. + @param[in] Head The caller supplied header. The caller should set + the following header fields: NextHeader, HopLimit, + Src, Dest, FlowLabel, PayloadLength. This function + will fill in the Ver, TrafficClass. + @param[in] ExtHdrs The extension headers to append to the IPv6 basic + header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] Callback The callback function to issue when transmission + completed. + @param[in] Context The opaque context for the callback. + + @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid. + @retval EFI_NO_MAPPING There is no interface to the destination. + @retval EFI_NOT_FOUND There is no route to the destination. + @retval EFI_SUCCESS The packet successfully transmitted. + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of + resources. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Ip6Output ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IP6_HEADER *Head, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN IP6_FRAME_CALLBACK Callback, + IN VOID *Context + ); + +/** + Remove all the frames on the interface that pass the FrameToCancel, + either queued on ARP queues, or that have already been delivered to + MNP and not yet recycled. + + @param[in] Interface Interface to remove the frames from. + @param[in] IoStatus The transmit status returned to the frames' callback. + @param[in] FrameToCancel Function to select the frame to cancel; NULL to select all. + @param[in] Context Opaque parameters passed to FrameToCancel. Ignored if + FrameToCancel is NULL. + +**/ +VOID +Ip6CancelFrames ( + IN IP6_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + Cancel the Packet and all its fragments. + + @param[in] IpIf The interface from which the Packet is sent. + @param[in] Packet The Packet to cancel. + @param[in] IoStatus The status returns to the sender. + +**/ +VOID +Ip6CancelPacket ( + IN IP6_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ); + +#endif diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Route.c b/Core/NetworkPkg/Ip6Dxe/Ip6Route.c new file mode 100644 index 0000000000..bba365c152 --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Route.c @@ -0,0 +1,635 @@ +/** @file + The functions and routines to handle the route caches and route table. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ip6Impl.h" + +/** + This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value + as the index of the route cache bucket according to the prefix of two IPv6 addresses. + + @param[in] Ip1 The IPv6 address. + @param[in] Ip2 The IPv6 address. + + @return The hash value of the prefix of two IPv6 addresses. + +**/ +UINT32 +Ip6RouteCacheHash ( + IN EFI_IPv6_ADDRESS *Ip1, + IN EFI_IPv6_ADDRESS *Ip2 + ) +{ + UINT32 Prefix1; + UINT32 Prefix2; + + Prefix1 = *((UINT32 *) ((UINTN *) (Ip1))); + Prefix2 = *((UINT32 *) ((UINTN *) (Ip2))); + + return ((UINT32) (Prefix1 ^ Prefix2) % IP6_ROUTE_CACHE_HASH_SIZE); +} + +/** + Allocate a route entry then initialize it with the Destination/PrefixLength + and Gateway. + + @param[in] Destination The IPv6 destination address. This is an optional + parameter that may be NULL. + @param[in] PrefixLength The destination network's prefix length. + @param[in] GatewayAddress The next hop address. This is an optional parameter + that may be NULL. + + @return NULL if failed to allocate memeory; otherwise, the newly created route entry. + +**/ +IP6_ROUTE_ENTRY * +Ip6CreateRouteEntry ( + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ) +{ + IP6_ROUTE_ENTRY *RtEntry; + + RtEntry = AllocateZeroPool (sizeof (IP6_ROUTE_ENTRY)); + + if (RtEntry == NULL) { + return NULL; + } + + RtEntry->RefCnt = 1; + RtEntry->Flag = 0; + RtEntry->PrefixLength = PrefixLength; + + if (Destination != NULL) { + IP6_COPY_ADDRESS (&RtEntry->Destination, Destination); + } + + if (GatewayAddress != NULL) { + IP6_COPY_ADDRESS (&RtEntry->NextHop, GatewayAddress); + } + + return RtEntry; +} + +/** + Free the route table entry. It is reference counted. + + @param[in, out] RtEntry The route entry to free. + +**/ +VOID +Ip6FreeRouteEntry ( + IN OUT IP6_ROUTE_ENTRY *RtEntry + ) +{ + ASSERT ((RtEntry != NULL) && (RtEntry->RefCnt > 0)); + + if (--RtEntry->RefCnt == 0) { + FreePool (RtEntry); + } +} + +/** + Search the route table for a most specific match to the Dst. It searches + from the longest route area (prefix length == 128) to the shortest route area + (default routes). In each route area, it will first search the instance's + route table, then the default route table. This is required per the following + requirements: + 1. IP search the route table for a most specific match. + 2. The local route entries have precedence over the default route entry. + + @param[in] RtTable The route table to search from. + @param[in] Destination The destionation address to search. If NULL, search + the route table by NextHop. + @param[in] NextHop The next hop address. If NULL, search the route table + by Destination. + + @return NULL if no route matches the Dst. Otherwise, the point to the + @return most specific route to the Dst. + +**/ +IP6_ROUTE_ENTRY * +Ip6FindRouteEntry ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN EFI_IPv6_ADDRESS *NextHop OPTIONAL + ) +{ + LIST_ENTRY *Entry; + IP6_ROUTE_ENTRY *RtEntry; + INTN Index; + + ASSERT (Destination != NULL || NextHop != NULL); + + RtEntry = NULL; + + for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) { + NET_LIST_FOR_EACH (Entry, &RtTable->RouteArea[Index]) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + if (Destination != NULL) { + if (NetIp6IsNetEqual (Destination, &RtEntry->Destination, RtEntry->PrefixLength)) { + NET_GET_REF (RtEntry); + return RtEntry; + } + } else if (NextHop != NULL) { + if (NetIp6IsNetEqual (NextHop, &RtEntry->NextHop, RtEntry->PrefixLength)) { + NET_GET_REF (RtEntry); + return RtEntry; + } + } + + } + } + + return NULL; +} + +/** + Allocate and initialize a IP6 route cache entry. + + @param[in] Dst The destination address. + @param[in] Src The source address. + @param[in] GateWay The next hop address. + @param[in] Tag The tag from the caller. This marks all the cache entries + spawned from one route table entry. + + @return NULL if failed to allocate memory for the cache. Otherwise, point + to the created route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6CreateRouteCacheEntry ( + IN EFI_IPv6_ADDRESS *Dst, + IN EFI_IPv6_ADDRESS *Src, + IN EFI_IPv6_ADDRESS *GateWay, + IN UINTN Tag + ) +{ + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + + RtCacheEntry = AllocatePool (sizeof (IP6_ROUTE_CACHE_ENTRY)); + + if (RtCacheEntry == NULL) { + return NULL; + } + + RtCacheEntry->RefCnt = 1; + RtCacheEntry->Tag = Tag; + + IP6_COPY_ADDRESS (&RtCacheEntry->Destination, Dst); + IP6_COPY_ADDRESS (&RtCacheEntry->Source, Src); + IP6_COPY_ADDRESS (&RtCacheEntry->NextHop, GateWay); + + return RtCacheEntry; +} + +/** + Free the route cache entry. It is reference counted. + + @param[in, out] RtCacheEntry The route cache entry to free. + +**/ +VOID +Ip6FreeRouteCacheEntry ( + IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry + ) +{ + ASSERT (RtCacheEntry->RefCnt > 0); + + if (--RtCacheEntry->RefCnt == 0) { + FreePool (RtCacheEntry); + } +} + +/** + Find a route cache with the destination and source address. This is + used by the ICMPv6 redirect messasge process. + + @param[in] RtTable The route table to search the cache for. + @param[in] Dest The destination address. + @param[in] Src The source address. + + @return NULL if no route entry to the (Dest, Src). Otherwise, the pointer + to the correct route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6FindRouteCache ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ) +{ + LIST_ENTRY *Entry; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + Index = IP6_ROUTE_CACHE_HASH (Dest, Src); + + NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + + if (EFI_IP6_EQUAL (Dest, &RtCacheEntry->Destination)&& EFI_IP6_EQUAL (Src, &RtCacheEntry->Source)) { + NET_GET_REF (RtCacheEntry); + return RtCacheEntry; + } + } + + return NULL; +} + +/** + Build an array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number + of EFI_IP6_ROUTE_TABLE is also returned. + + @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used. + @param[out] EfiRouteCount The number of returned route entries. + @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE. + If NULL, only the route entry count is returned. + + @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiRouteTable ( + IN IP6_ROUTE_TABLE *RouteTable, + OUT UINT32 *EfiRouteCount, + OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL + ) +{ + LIST_ENTRY *Entry; + IP6_ROUTE_ENTRY *RtEntry; + EFI_IP6_ROUTE_TABLE *EfiTable; + UINT32 Count; + INT32 Index; + + ASSERT (EfiRouteCount != NULL); + + Count = RouteTable->TotalNum; + *EfiRouteCount = Count; + + if ((EfiRouteTable == NULL) || (Count == 0)) { + return EFI_SUCCESS; + } + + if (*EfiRouteTable == NULL) { + *EfiRouteTable = AllocatePool (sizeof (EFI_IP6_ROUTE_TABLE) * Count); + if (*EfiRouteTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + EfiTable = *EfiRouteTable; + + // + // Copy the route entry to EFI route table. + // + Count = 0; + + for (Index = IP6_PREFIX_NUM - 1; Index >= 0; Index--) { + + NET_LIST_FOR_EACH (Entry, &(RouteTable->RouteArea[Index])) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + Ip6CopyAddressByPrefix ( + &EfiTable[Count].Destination, + &RtEntry->Destination, + RtEntry->PrefixLength + ); + + IP6_COPY_ADDRESS (&EfiTable[Count].Gateway, &RtEntry->NextHop); + EfiTable[Count].PrefixLength = RtEntry->PrefixLength; + + Count++; + } + } + + ASSERT (Count == RouteTable->TotalNum); + + return EFI_SUCCESS; +} + +/** + Create an empty route table. This includes its internal route cache. + + @return NULL if failed to allocate memory for the route table. Otherwise, + the point to newly created route table. + +**/ +IP6_ROUTE_TABLE * +Ip6CreateRouteTable ( + VOID + ) +{ + IP6_ROUTE_TABLE *RtTable; + UINT32 Index; + + RtTable = AllocatePool (sizeof (IP6_ROUTE_TABLE)); + if (RtTable == NULL) { + return NULL; + } + + RtTable->RefCnt = 1; + RtTable->TotalNum = 0; + + for (Index = 0; Index < IP6_PREFIX_NUM; Index++) { + InitializeListHead (&RtTable->RouteArea[Index]); + } + + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + InitializeListHead (&RtTable->Cache.CacheBucket[Index]); + RtTable->Cache.CacheNum[Index] = 0; + } + + return RtTable; +} + +/** + Free the route table and its associated route cache. Route + table is reference counted. + + @param[in, out] RtTable The route table to free. + +**/ +VOID +Ip6CleanRouteTable ( + IN OUT IP6_ROUTE_TABLE *RtTable + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ROUTE_ENTRY *RtEntry; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + ASSERT (RtTable->RefCnt > 0); + + if (--RtTable->RefCnt > 0) { + return ; + } + + // + // Free all the route table entry and its route cache. + // + for (Index = 0; Index < IP6_PREFIX_NUM; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->RouteArea[Index]) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + RemoveEntryList (Entry); + Ip6FreeRouteEntry (RtEntry); + } + } + + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->Cache.CacheBucket[Index]) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + RemoveEntryList (Entry); + Ip6FreeRouteCacheEntry (RtCacheEntry); + } + } + + FreePool (RtTable); +} + +/** + Remove all the cache entries bearing the Tag. When a route cache + entry is created, it is tagged with the address of route entry + from which it is spawned. When a route entry is deleted, the cache + entries spawned from it are also deleted. + + @param[in] RtCache Route cache to remove the entries from. + @param[in] Tag The Tag of the entries to remove. + +**/ +VOID +Ip6PurgeRouteCache ( + IN IP6_ROUTE_CACHE *RtCache, + IN UINTN Tag + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) { + + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + + if (RtCacheEntry->Tag == Tag) { + RemoveEntryList (Entry); + Ip6FreeRouteCacheEntry (RtCacheEntry); + } + } + } +} + +/** + Add a route entry to the route table. It is the help function for EfiIp6Routes. + + @param[in, out] RtTable Route table to add route to. + @param[in] Destination The destination of the network. + @param[in] PrefixLength The PrefixLength of the destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_ACCESS_DENIED The same route already exists. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry. + @retval EFI_SUCCESS The route was added successfully. + +**/ +EFI_STATUS +Ip6AddRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ) +{ + LIST_ENTRY *ListHead; + LIST_ENTRY *Entry; + IP6_ROUTE_ENTRY *Route; + + ListHead = &RtTable->RouteArea[PrefixLength]; + + // + // First check whether the route exists + // + NET_LIST_FOR_EACH (Entry, ListHead) { + Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + if (NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength) && + EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) { + return EFI_ACCESS_DENIED; + } + } + + // + // Create a route entry and insert it to the route area. + // + Route = Ip6CreateRouteEntry (Destination, PrefixLength, GatewayAddress); + + if (Route == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (NetIp6IsUnspecifiedAddr (GatewayAddress)) { + Route->Flag = IP6_DIRECT_ROUTE; + } + + InsertHeadList (ListHead, &Route->Link); + RtTable->TotalNum++; + + return EFI_SUCCESS; +} + +/** + Remove a route entry and all the route caches spawn from it. + It is the help function for EfiIp6Routes. + + @param[in, out] RtTable The route table to remove the route from. + @param[in] Destination The destination network. + @param[in] PrefixLength The PrefixLength of the Destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_SUCCESS The route entry was successfully removed. + @retval EFI_NOT_FOUND There is no route entry in the table with that + property. + +**/ +EFI_STATUS +Ip6DelRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ) +{ + LIST_ENTRY *ListHead; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ROUTE_ENTRY *Route; + UINT32 TotalNum; + + ListHead = &RtTable->RouteArea[PrefixLength]; + TotalNum = RtTable->TotalNum; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, ListHead) { + Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + if (Destination != NULL && !NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength)) { + continue; + } + if (GatewayAddress != NULL && !EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) { + continue; + } + + Ip6PurgeRouteCache (&RtTable->Cache, (UINTN) Route); + RemoveEntryList (Entry); + Ip6FreeRouteEntry (Route); + + ASSERT (RtTable->TotalNum > 0); + RtTable->TotalNum--; + } + + return TotalNum == RtTable->TotalNum ? EFI_NOT_FOUND : EFI_SUCCESS; +} + +/** + Search the route table to route the packet. Return/create a route + cache if there is a route to the destination. + + @param[in] IpSb The IP6 service data. + @param[in] Dest The destination address to search for. + @param[in] Src The source address to search for. + + @return NULL if it failed to route the packet. Otherwise, a route cache + entry that can be used to route packets. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6Route ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ) +{ + IP6_ROUTE_TABLE *RtTable; + LIST_ENTRY *ListHead; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + IP6_ROUTE_ENTRY *RtEntry; + EFI_IPv6_ADDRESS NextHop; + UINT32 Index; + + RtTable = IpSb->RouteTable; + + ASSERT (RtTable != NULL); + + // + // Search the destination cache in IP6_ROUTE_TABLE. + // + Index = IP6_ROUTE_CACHE_HASH (Dest, Src); + ListHead = &RtTable->Cache.CacheBucket[Index]; + + RtCacheEntry = Ip6FindRouteCache (RtTable, Dest, Src); + + // + // If found, promote the cache entry to the head of the hash bucket. + // + if (RtCacheEntry != NULL) { + RemoveEntryList (&RtCacheEntry->Link); + InsertHeadList (ListHead, &RtCacheEntry->Link); + return RtCacheEntry; + } + + // + // Search the route table for the most specific route + // + RtEntry = Ip6FindRouteEntry (RtTable, Dest, NULL); + if (RtEntry == NULL) { + return NULL; + } + + // + // Found a route to the Dest, if it is a direct route, the packet + // will be send directly to the destination, such as for connected + // network. Otherwise, it is an indirect route, the packet will be + // send the next hop router. + // + if ((RtEntry->Flag & IP6_DIRECT_ROUTE) == IP6_DIRECT_ROUTE) { + IP6_COPY_ADDRESS (&NextHop, Dest); + } else { + IP6_COPY_ADDRESS (&NextHop, &RtEntry->NextHop); + } + + Ip6FreeRouteEntry (RtEntry); + + // + // Create a route cache entry, and tag it as spawned from this route entry + // + RtCacheEntry = Ip6CreateRouteCacheEntry (Dest, Src, &NextHop, (UINTN) RtEntry); + + if (RtCacheEntry == NULL) { + return NULL; + } + + InsertHeadList (ListHead, &RtCacheEntry->Link); + NET_GET_REF (RtCacheEntry); + RtTable->Cache.CacheNum[Index]++; + + return RtCacheEntry; +} + diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Route.h b/Core/NetworkPkg/Ip6Dxe/Ip6Route.h new file mode 100644 index 0000000000..d81e07b19c --- /dev/null +++ b/Core/NetworkPkg/Ip6Dxe/Ip6Route.h @@ -0,0 +1,299 @@ +/** @file + EFI IP6 route table and route cache table defintions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_IP6_ROUTE_H__ +#define __EFI_IP6_ROUTE_H__ + +#define IP6_DIRECT_ROUTE 0x00000001 +#define IP6_PACKET_TOO_BIG 0x00000010 + +#define IP6_ROUTE_CACHE_HASH_SIZE 31 +/// +/// Max NO. of cache entry per hash bucket +/// +#define IP6_ROUTE_CACHE_MAX 32 + +#define IP6_ROUTE_CACHE_HASH(Ip1, Ip2) Ip6RouteCacheHash ((Ip1), (Ip2)) + +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + UINT32 Flag; + UINT8 PrefixLength; + EFI_IPv6_ADDRESS Destination; + EFI_IPv6_ADDRESS NextHop; +} IP6_ROUTE_ENTRY; + +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + UINTN Tag; + EFI_IPv6_ADDRESS Destination; + EFI_IPv6_ADDRESS Source; + EFI_IPv6_ADDRESS NextHop; +} IP6_ROUTE_CACHE_ENTRY; + +typedef struct { + LIST_ENTRY CacheBucket[IP6_ROUTE_CACHE_HASH_SIZE]; + UINT8 CacheNum[IP6_ROUTE_CACHE_HASH_SIZE]; +} IP6_ROUTE_CACHE; + +// +// Each IP6 instance has its own route table. Each ServiceBinding +// instance has a default route table and default address. +// +// All the route table entries with the same prefix length are linked +// together in one route area. For example, RouteArea[0] contains +// the default routes. A route table also contains a route cache. +// + +typedef struct _IP6_ROUTE_TABLE { + INTN RefCnt; + UINT32 TotalNum; + LIST_ENTRY RouteArea[IP6_PREFIX_NUM]; + IP6_ROUTE_CACHE Cache; +} IP6_ROUTE_TABLE; + +/** + This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value + as the index of the route cache bucket according to the prefix of two IPv6 addresses. + + @param[in] Ip1 The IPv6 address. + @param[in] Ip2 The IPv6 address. + + @return The hash value of the prefix of two IPv6 addresses. + +**/ +UINT32 +Ip6RouteCacheHash ( + IN EFI_IPv6_ADDRESS *Ip1, + IN EFI_IPv6_ADDRESS *Ip2 + ); + +/** + Allocate and initialize an IP6 route cache entry. + + @param[in] Dst The destination address. + @param[in] Src The source address. + @param[in] GateWay The next hop address. + @param[in] Tag The tag from the caller. This marks all the cache entries + spawned from one route table entry. + + @return NULL if it failed to allocate memory for the cache. Otherwise, point + to the created route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6CreateRouteCacheEntry ( + IN EFI_IPv6_ADDRESS *Dst, + IN EFI_IPv6_ADDRESS *Src, + IN EFI_IPv6_ADDRESS *GateWay, + IN UINTN Tag + ); + +/** + Free the route cache entry. It is reference counted. + + @param[in, out] RtCacheEntry The route cache entry to free. + +**/ +VOID +Ip6FreeRouteCacheEntry ( + IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry + ); + +/** + Find a route cache with the destination and source address. This is + used by the ICMPv6 redirect messasge process. + + @param[in] RtTable The route table to search the cache for. + @param[in] Dest The destination address. + @param[in] Src The source address. + + @return NULL if no route entry to the (Dest, Src). Otherwise, point + to the correct route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6FindRouteCache ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ); + +/** + Build a array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number + of EFI_IP6_ROUTE_TABLE is also returned. + + @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used. + @param[out] EfiRouteCount The number of returned route entries. + @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE. + If NULL, only the route entry count is returned. + + @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiRouteTable ( + IN IP6_ROUTE_TABLE *RouteTable, + OUT UINT32 *EfiRouteCount, + OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL + ); + +/** + Create an empty route table, includes its internal route cache. + + @return NULL if failed to allocate memory for the route table. Otherwise, + the point to newly created route table. + +**/ +IP6_ROUTE_TABLE * +Ip6CreateRouteTable ( + VOID + ); + +/** + Free the route table and its associated route cache. Route + table is reference counted. + + @param[in, out] RtTable The route table to free. + +**/ +VOID +Ip6CleanRouteTable ( + IN OUT IP6_ROUTE_TABLE *RtTable + ); + +/** + Allocate a route entry then initialize it with the Destination/PrefixLength + and Gateway. + + @param[in] Destination The IPv6 destination address. This is an optional + parameter that may be NULL. + @param[in] PrefixLength The destination network's prefix length. + @param[in] GatewayAddress The next hop address. This is optional parameter + that may be NULL. + + @return NULL if it failed to allocate memeory. Otherwise, the newly created route entry. + +**/ +IP6_ROUTE_ENTRY * +Ip6CreateRouteEntry ( + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ); + +/** + Search the route table for a most specific match to the Dst. It searches + from the longest route area (prefix length == 128) to the shortest route area + (default routes). In each route area, it will first search the instance's + route table, then the default route table. This is required per the following + requirements: + 1. IP search the route table for a most specific match. + 2. The local route entries have precedence over the default route entry. + + @param[in] RtTable The route table to search from. + @param[in] Destination The destionation address to search. If NULL, search + the route table by NextHop. + @param[in] NextHop The next hop address. If NULL, search the route table + by Destination. + + @return NULL if no route matches the Dst. Otherwise the point to the + most specific route to the Dst. + +**/ +IP6_ROUTE_ENTRY * +Ip6FindRouteEntry ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN EFI_IPv6_ADDRESS *NextHop OPTIONAL + ); + +/** + Free the route table entry. It is reference counted. + + @param[in, out] RtEntry The route entry to free. + +**/ +VOID +Ip6FreeRouteEntry ( + IN OUT IP6_ROUTE_ENTRY *RtEntry + ); + +/** + Add a route entry to the route table. It is the help function for EfiIp6Routes. + + @param[in, out] RtTable Route table to add route to. + @param[in] Destination The destination of the network. + @param[in] PrefixLength The PrefixLength of the destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_ACCESS_DENIED The same route already exists. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry. + @retval EFI_SUCCESS The route was added successfully. + +**/ +EFI_STATUS +Ip6AddRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ); + +/** + Remove a route entry and all the route caches spawn from it. + It is the help function for EfiIp6Routes. + + @param[in, out] RtTable The route table to remove the route from. + @param[in] Destination The destination network. + @param[in] PrefixLength The PrefixLength of the Destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_SUCCESS Successfully removed the route entry. + @retval EFI_NOT_FOUND There is no route entry in the table with that + properity. + +**/ +EFI_STATUS +Ip6DelRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ); + +/** + Search the route table to route the packet. Return/create a route + cache if there is a route to the destination. + + @param[in] IpSb The IP6 service data. + @param[in] Dest The destination address to search for. + @param[in] Src The source address to search for. + + @return NULL if failed to route packet. Otherwise, a route cache + entry that can be used to route packet. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6Route ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ); + +#endif diff --git a/Core/NetworkPkg/IpSecDxe/ComponentName.c b/Core/NetworkPkg/IpSecDxe/ComponentName.c new file mode 100644 index 0000000000..d68b175cc1 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/ComponentName.c @@ -0,0 +1,351 @@ +/** @file + UEFI Component Name(2) protocol implementation for IPsec driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecImpl.h" + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IpSecComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IpSecComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, OPTIONAL + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIpSecComponentName = { + IpSecComponentNameGetDriverName, + IpSecComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IpSecComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IpSecComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIpSecDriverNameTable[] = { + { + "eng;en", + L"IpSec Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIpSecControllerNameTable[] = { + { + "eng;en", + L"IPsec Controller" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This, and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IpSecComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mIpSecDriverNameTable, + DriverName, + (BOOLEAN) (This == &gIpSecComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +IpSecComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, OPTIONAL + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + // + // ChildHandle must be NULL for a Device Driver + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllerHandle + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIpSec2ProtocolGuid, + NULL, + NULL, + NULL, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mIpSecControllerNameTable, + ControllerName, + (BOOLEAN) (This == &gIpSecComponentName) + ); +} diff --git a/Core/NetworkPkg/IpSecDxe/IetfConstants.c b/Core/NetworkPkg/IpSecDxe/IetfConstants.c new file mode 100644 index 0000000000..7bd5c81da8 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IetfConstants.c @@ -0,0 +1,388 @@ +/** @file + Cryptographic Parameter Constant Definitions from IETF; + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ike.h" + +// +// "First Oakley Default Group" from RFC2409, section 6.1. +// +// The prime is: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT8 Modp768Modulus[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x3A, 0x36, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + +// +// "Second Oakley Default Group" from RFC2409, section 6.2. +// +// The prime is: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }. +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT8 Modp1024Modulus[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE6,0x53,0x81, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + }; + +// +// "1536-bit MODP Group" from RFC3526, Section 2. +// +// The prime is: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT8 Modp1536Modulus[]={ + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, + 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, + 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, + 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, + 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, + 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + }; + +// +// "2048-bit MODP Group" from RFC3526, Section 3. +// +// The prime is: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT8 Modp2048Modulus[]={ + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, + 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, + 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, + 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, + 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, + 0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2, + 0xEC,0x07,0xA2,0x8F,0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9, + 0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18,0x39,0x95,0x49,0x7C, + 0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAC,0xAA,0x68,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF, + }; + +// +// "3072-bit MODP Group" from RFC3526, Section 4. +// +// The prime is: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT8 Modp3072Modulus[]={ + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, + 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, + 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, + 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, + 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, + 0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2, + 0xEC,0x07,0xA2,0x8F,0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9, + 0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18,0x39,0x95,0x49,0x7C, + 0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D, + 0x04,0x50,0x7A,0x33,0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64, + 0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A,0x8A,0xEA,0x71,0x57, + 0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, + 0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0, + 0x4A,0x25,0x61,0x9D,0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B, + 0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64,0xD8,0x76,0x02,0x73, + 0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, + 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0, + 0xBA,0xD9,0x46,0xE2,0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31, + 0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E,0x4B,0x82,0xD1,0x20, + 0xA9,0x3A,0xD2,0xCA,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + }; + +// +// "4096-bit MODP Group" from RFC3526, Section 5. +// +// The prime is: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT8 Modp4096Modulus[]={ + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, + 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, + 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, + 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, + 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, + 0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2, + 0xEC,0x07,0xA2,0x8F,0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9, + 0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18,0x39,0x95,0x49,0x7C, + 0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D, + 0x04,0x50,0x7A,0x33,0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64, + 0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A,0x8A,0xEA,0x71,0x57, + 0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, + 0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0, + 0x4A,0x25,0x61,0x9D,0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B, + 0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64,0xD8,0x76,0x02,0x73, + 0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, + 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0, + 0xBA,0xD9,0x46,0xE2,0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31, + 0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E,0x4B,0x82,0xD1,0x20, + 0xA9,0x21,0x08,0x01,0x1A,0x72,0x3C,0x12,0xA7,0x87,0xE6,0xD7, + 0x88,0x71,0x9A,0x10,0xBD,0xBA,0x5B,0x26,0x99,0xC3,0x27,0x18, + 0x6A,0xF4,0xE2,0x3C,0x1A,0x94,0x68,0x34,0xB6,0x15,0x0B,0xDA, + 0x25,0x83,0xE9,0xCA,0x2A,0xD4,0x4C,0xE8,0xDB,0xBB,0xC2,0xDB, + 0x04,0xDE,0x8E,0xF9,0x2E,0x8E,0xFC,0x14,0x1F,0xBE,0xCA,0xA6, + 0x28,0x7C,0x59,0x47,0x4E,0x6B,0xC0,0x5D,0x99,0xB2,0x96,0x4F, + 0xA0,0x90,0xC3,0xA2,0x23,0x3B,0xA1,0x86,0x51,0x5B,0xE7,0xED, + 0x1F,0x61,0x29,0x70,0xCE,0xE2,0xD7,0xAF,0xB8,0x1B,0xDD,0x76, + 0x21,0x70,0x48,0x1C,0xD0,0x06,0x91,0x27,0xD5,0xB0,0x5A,0xA9, + 0x93,0xB4,0xEA,0x98,0x8D,0x8F,0xDD,0xC1,0x86,0xFF,0xB7,0xDC, + 0x90,0xA6,0xC0,0x8F,0x4D,0xF4,0x35,0xC9,0x34,0x06,0x31,0x99, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + }; + +// +// "6144-bit MODP Group" from RFC3526, Section 6. +// +// The prime is: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT8 Modp6144Modulus[]={ + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, + 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, + 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, + 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, + 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, + 0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2, + 0xEC,0x07,0xA2,0x8F,0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9, + 0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18,0x39,0x95,0x49,0x7C, + 0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D, + 0x04,0x50,0x7A,0x33,0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64, + 0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A,0x8A,0xEA,0x71,0x57, + 0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, + 0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0, + 0x4A,0x25,0x61,0x9D,0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B, + 0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64,0xD8,0x76,0x02,0x73, + 0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, + 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0, + 0xBA,0xD9,0x46,0xE2,0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31, + 0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E,0x4B,0x82,0xD1,0x20, + 0xA9,0x21,0x08,0x01,0x1A,0x72,0x3C,0x12,0xA7,0x87,0xE6,0xD7, + 0x88,0x71,0x9A,0x10,0xBD,0xBA,0x5B,0x26,0x99,0xC3,0x27,0x18, + 0x6A,0xF4,0xE2,0x3C,0x1A,0x94,0x68,0x34,0xB6,0x15,0x0B,0xDA, + 0x25,0x83,0xE9,0xCA,0x2A,0xD4,0x4C,0xE8,0xDB,0xBB,0xC2,0xDB, + 0x04,0xDE,0x8E,0xF9,0x2E,0x8E,0xFC,0x14,0x1F,0xBE,0xCA,0xA6, + 0x28,0x7C,0x59,0x47,0x4E,0x6B,0xC0,0x5D,0x99,0xB2,0x96,0x4F, + 0xA0,0x90,0xC3,0xA2,0x23,0x3B,0xA1,0x86,0x51,0x5B,0xE7,0xED, + 0x1F,0x61,0x29,0x70,0xCE,0xE2,0xD7,0xAF,0xB8,0x1B,0xDD,0x76, + 0x21,0x70,0x48,0x1C,0xD0,0x06,0x91,0x27,0xD5,0xB0,0x5A,0xA9, + 0x93,0xB4,0xEA,0x98,0x8D,0x8F,0xDD,0xC1,0x86,0xFF,0xB7,0xDC, + 0x90,0xA6,0xC0,0x8F,0x4D,0xF4,0x35,0xC9,0x34,0x02,0x84,0x92, + 0x36,0xC3,0xFA,0xB4,0xD2,0x7C,0x70,0x26,0xC1,0xD4,0xDC,0xB2, + 0x60,0x26,0x46,0xDE,0xC9,0x75,0x1E,0x76,0x3D,0xBA,0x37,0xBD, + 0xF8,0xFF,0x94,0x06,0xAD,0x9E,0x53,0x0E,0xE5,0xDB,0x38,0x2F, + 0x41,0x30,0x01,0xAE,0xB0,0x6A,0x53,0xED,0x90,0x27,0xD8,0x31, + 0x17,0x97,0x27,0xB0,0x86,0x5A,0x89,0x18,0xDA,0x3E,0xDB,0xEB, + 0xCF,0x9B,0x14,0xED,0x44,0xCE,0x6C,0xBA,0xCE,0xD4,0xBB,0x1B, + 0xDB,0x7F,0x14,0x47,0xE6,0xCC,0x25,0x4B,0x33,0x20,0x51,0x51, + 0x2B,0xD7,0xAF,0x42,0x6F,0xB8,0xF4,0x01,0x37,0x8C,0xD2,0xBF, + 0x59,0x83,0xCA,0x01,0xC6,0x4B,0x92,0xEC,0xF0,0x32,0xEA,0x15, + 0xD1,0x72,0x1D,0x03,0xF4,0x82,0xD7,0xCE,0x6E,0x74,0xFE,0xF6, + 0xD5,0x5E,0x70,0x2F,0x46,0x98,0x0C,0x82,0xB5,0xA8,0x40,0x31, + 0x90,0x0B,0x1C,0x9E,0x59,0xE7,0xC9,0x7F,0xBE,0xC7,0xE8,0xF3, + 0x23,0xA9,0x7A,0x7E,0x36,0xCC,0x88,0xBE,0x0F,0x1D,0x45,0xB7, + 0xFF,0x58,0x5A,0xC5,0x4B,0xD4,0x07,0xB2,0x2B,0x41,0x54,0xAA, + 0xCC,0x8F,0x6D,0x7E,0xBF,0x48,0xE1,0xD8,0x14,0xCC,0x5E,0xD2, + 0x0F,0x80,0x37,0xE0,0xA7,0x97,0x15,0xEE,0xF2,0x9B,0xE3,0x28, + 0x06,0xA1,0xD5,0x8B,0xB7,0xC5,0xDA,0x76,0xF5,0x50,0xAA,0x3D, + 0x8A,0x1F,0xBF,0xF0,0xEB,0x19,0xCC,0xB1,0xA3,0x13,0xD5,0x5C, + 0xDA,0x56,0xC9,0xEC,0x2E,0xF2,0x96,0x32,0x38,0x7F,0xE8,0xD7, + 0x6E,0x3C,0x04,0x68,0x04,0x3E,0x8F,0x66,0x3F,0x48,0x60,0xEE, + 0x12,0xBF,0x2D,0x5B,0x0B,0x74,0x74,0xD6,0xE6,0x94,0xF9,0x1E, + 0x6D,0xCC,0x40,0x24,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + }; + +// +// "8192-bit MODP Group" from RFC3526, Section 7. +// +// The prime is: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT8 Modp8192Modulus[]={ + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, + 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, + 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, + 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, + 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, + 0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2, + 0xEC,0x07,0xA2,0x8F,0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9, + 0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18,0x39,0x95,0x49,0x7C, + 0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D, + 0x04,0x50,0x7A,0x33,0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64, + 0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A,0x8A,0xEA,0x71,0x57, + 0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, + 0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0, + 0x4A,0x25,0x61,0x9D,0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B, + 0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64,0xD8,0x76,0x02,0x73, + 0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, + 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0, + 0xBA,0xD9,0x46,0xE2,0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31, + 0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E,0x4B,0x82,0xD1,0x20, + 0xA9,0x21,0x08,0x01,0x1A,0x72,0x3C,0x12,0xA7,0x87,0xE6,0xD7, + 0x88,0x71,0x9A,0x10,0xBD,0xBA,0x5B,0x26,0x99,0xC3,0x27,0x18, + 0x6A,0xF4,0xE2,0x3C,0x1A,0x94,0x68,0x34,0xB6,0x15,0x0B,0xDA, + 0x25,0x83,0xE9,0xCA,0x2A,0xD4,0x4C,0xE8,0xDB,0xBB,0xC2,0xDB, + 0x04,0xDE,0x8E,0xF9,0x2E,0x8E,0xFC,0x14,0x1F,0xBE,0xCA,0xA6, + 0x28,0x7C,0x59,0x47,0x4E,0x6B,0xC0,0x5D,0x99,0xB2,0x96,0x4F, + 0xA0,0x90,0xC3,0xA2,0x23,0x3B,0xA1,0x86,0x51,0x5B,0xE7,0xED, + 0x1F,0x61,0x29,0x70,0xCE,0xE2,0xD7,0xAF,0xB8,0x1B,0xDD,0x76, + 0x21,0x70,0x48,0x1C,0xD0,0x06,0x91,0x27,0xD5,0xB0,0x5A,0xA9, + 0x93,0xB4,0xEA,0x98,0x8D,0x8F,0xDD,0xC1,0x86,0xFF,0xB7,0xDC, + 0x90,0xA6,0xC0,0x8F,0x4D,0xF4,0x35,0xC9,0x34,0x02,0x84,0x92, + 0x36,0xC3,0xFA,0xB4,0xD2,0x7C,0x70,0x26,0xC1,0xD4,0xDC,0xB2, + 0x60,0x26,0x46,0xDE,0xC9,0x75,0x1E,0x76,0x3D,0xBA,0x37,0xBD, + 0xF8,0xFF,0x94,0x06,0xAD,0x9E,0x53,0x0E,0xE5,0xDB,0x38,0x2F, + 0x41,0x30,0x01,0xAE,0xB0,0x6A,0x53,0xED,0x90,0x27,0xD8,0x31, + 0x17,0x97,0x27,0xB0,0x86,0x5A,0x89,0x18,0xDA,0x3E,0xDB,0xEB, + 0xCF,0x9B,0x14,0xED,0x44,0xCE,0x6C,0xBA,0xCE,0xD4,0xBB,0x1B, + 0xDB,0x7F,0x14,0x47,0xE6,0xCC,0x25,0x4B,0x33,0x20,0x51,0x51, + 0x2B,0xD7,0xAF,0x42,0x6F,0xB8,0xF4,0x01,0x37,0x8C,0xD2,0xBF, + 0x59,0x83,0xCA,0x01,0xC6,0x4B,0x92,0xEC,0xF0,0x32,0xEA,0x15, + 0xD1,0x72,0x1D,0x03,0xF4,0x82,0xD7,0xCE,0x6E,0x74,0xFE,0xF6, + 0xD5,0x5E,0x70,0x2F,0x46,0x98,0x0C,0x82,0xB5,0xA8,0x40,0x31, + 0x90,0x0B,0x1C,0x9E,0x59,0xE7,0xC9,0x7F,0xBE,0xC7,0xE8,0xF3, + 0x23,0xA9,0x7A,0x7E,0x36,0xCC,0x88,0xBE,0x0F,0x1D,0x45,0xB7, + 0xFF,0x58,0x5A,0xC5,0x4B,0xD4,0x07,0xB2,0x2B,0x41,0x54,0xAA, + 0xCC,0x8F,0x6D,0x7E,0xBF,0x48,0xE1,0xD8,0x14,0xCC,0x5E,0xD2, + 0x0F,0x80,0x37,0xE0,0xA7,0x97,0x15,0xEE,0xF2,0x9B,0xE3,0x28, + 0x06,0xA1,0xD5,0x8B,0xB7,0xC5,0xDA,0x76,0xF5,0x50,0xAA,0x3D, + 0x8A,0x1F,0xBF,0xF0,0xEB,0x19,0xCC,0xB1,0xA3,0x13,0xD5,0x5C, + 0xDA,0x56,0xC9,0xEC,0x2E,0xF2,0x96,0x32,0x38,0x7F,0xE8,0xD7, + 0x6E,0x3C,0x04,0x68,0x04,0x3E,0x8F,0x66,0x3F,0x48,0x60,0xEE, + 0x12,0xBF,0x2D,0x5B,0x0B,0x74,0x74,0xD6,0xE6,0x94,0xF9,0x1E, + 0x6D,0xBE,0x11,0x59,0x74,0xA3,0x92,0x6F,0x12,0xFE,0xE5,0xE4, + 0x38,0x77,0x7C,0xB6,0xA9,0x32,0xDF,0x8C,0xD8,0xBE,0xC4,0xD0, + 0x73,0xB9,0x31,0xBA,0x3B,0xC8,0x32,0xB6,0x8D,0x9D,0xD3,0x00, + 0x74,0x1F,0xA7,0xBF,0x8A,0xFC,0x47,0xED,0x25,0x76,0xF6,0x93, + 0x6B,0xA4,0x24,0x66,0x3A,0xAB,0x63,0x9C,0x5A,0xE4,0xF5,0x68, + 0x34,0x23,0xB4,0x74,0x2B,0xF1,0xC9,0x78,0x23,0x8F,0x16,0xCB, + 0xE3,0x9D,0x65,0x2D,0xE3,0xFD,0xB8,0xBE,0xFC,0x84,0x8A,0xD9, + 0x22,0x22,0x2E,0x04,0xA4,0x03,0x7C,0x07,0x13,0xEB,0x57,0xA8, + 0x1A,0x23,0xF0,0xC7,0x34,0x73,0xFC,0x64,0x6C,0xEA,0x30,0x6B, + 0x4B,0xCB,0xC8,0x86,0x2F,0x83,0x85,0xDD,0xFA,0x9D,0x4B,0x7F, + 0xA2,0xC0,0x87,0xE8,0x79,0x68,0x33,0x03,0xED,0x5B,0xDD,0x3A, + 0x06,0x2B,0x3C,0xF5,0xB3,0xA2,0x78,0xA6,0x6D,0x2A,0x13,0xF8, + 0x3F,0x44,0xF8,0x2D,0xDF,0x31,0x0E,0xE0,0x74,0xAB,0x6A,0x36, + 0x45,0x97,0xE8,0x99,0xA0,0x25,0x5D,0xC1,0x64,0xF3,0x1C,0xC5, + 0x08,0x46,0x85,0x1D,0xF9,0xAB,0x48,0x19,0x5D,0xED,0x7E,0xA1, + 0xB1,0xD5,0x10,0xBD,0x7E,0xE7,0x4D,0x73,0xFA,0xF3,0x6B,0xC3, + 0x1E,0xCF,0xA2,0x68,0x35,0x90,0x46,0xF4,0xEB,0x87,0x9F,0x92, + 0x40,0x09,0x43,0x8B,0x48,0x1C,0x6C,0xD7,0x88,0x9A,0x00,0x2E, + 0xD5,0xEE,0x38,0x2B,0xC9,0x19,0x0D,0xA6,0xFC,0x02,0x6E,0x47, + 0x95,0x58,0xE4,0x47,0x56,0x77,0xE9,0xAA,0x9E,0x30,0x50,0xE2, + 0x76,0x56,0x94,0xDF,0xC8,0x1F,0x56,0xE8,0x80,0xB9,0x6E,0x71, + 0x60,0xC9,0x80,0xDD,0x98,0xED,0xD3,0xDF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF, + }; + +// +// Pre-defined Oakley MODP Groups +// +#define DH_GENERATOR_2 2 +GLOBAL_REMOVE_IF_UNREFERENCED CONST MODP_GROUP OakleyModpGroup[] = { + {0, 0, NULL, 0}, //Undefined + {OakleyGroupModp768, 768, Modp768Modulus, DH_GENERATOR_2}, + {OakleyGroupModp1024, 1024, Modp1024Modulus, DH_GENERATOR_2}, + {0, 0, NULL, 0}, // For ECC. UnSupported + {0, 0, NULL, 0}, // For ECC. Unsupported + {OakleyGroupModp1536, 1536, Modp1536Modulus, DH_GENERATOR_2}, + {0, 0, NULL, 0}, //Undefined + {0, 0, NULL, 0}, //Undefined + {0, 0, NULL, 0}, //Undefined + {0, 0, NULL, 0}, //Undefined + {0, 0, NULL, 0}, //Undefined + {0, 0, NULL, 0}, //Undefined + {0, 0, NULL, 0}, //Undefined + {0, 0, NULL, 0}, //Undefined + {OakleyGroupModp2048, 2048, Modp2048Modulus, DH_GENERATOR_2}, + {OakleyGroupModp3072, 3072, Modp3072Modulus, DH_GENERATOR_2}, + {OakleyGroupModp4096, 4096, Modp4096Modulus, DH_GENERATOR_2}, + {OakleyGroupModp6144, 6144, Modp6144Modulus, DH_GENERATOR_2}, + {OakleyGroupModp8192, 8192, Modp8192Modulus, DH_GENERATOR_2}, +}; diff --git a/Core/NetworkPkg/IpSecDxe/Ike.h b/Core/NetworkPkg/IpSecDxe/Ike.h new file mode 100644 index 0000000000..50c680a528 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ike.h @@ -0,0 +1,266 @@ +/** @file + The common definition of IPsec Key Exchange (IKE). + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + +#ifndef _IKE_H_ +#define _IKE_H_ + +#include +#include +#include "IpSecImpl.h" + +#define IKE_VERSION_MAJOR_MASK 0xf0 +#define IKE_VERSION_MINOR_MASK 0x0f + +#define IKE_MAJOR_VERSION(v) (((v) & IKE_VERSION_MAJOR_MASK) >> 4) +#define IKE_MINOR_VERSION(v) ((v) & IKE_VERSION_MINOR_MASK) + +// +// Protocol Value Use in IKEv1 and IKEv2 +// +#define IPSEC_PROTO_ISAKMP 1 +#define IPSEC_PROTO_IPSEC_AH 2 +#define IPSEC_PROTO_IPSEC_ESP 3 +#define IPSEC_PROTO_IPCOMP 4 // For IKEv1 this value is reserved + +// +// For Algorithm search in support list.Last two types are for IKEv2 only. +// +#define IKE_ENCRYPT_TYPE 0 +#define IKE_AUTH_TYPE 1 +#define IKE_PRF_TYPE 2 +#define IKE_DH_TYPE 3 + +// +// Encryption Algorithm present in IKEv1 phasrs2 and IKEv2 transform payload (Transform Type 1) +// +#define IPSEC_ESP_DES_IV64 1 +#define IPSEC_ESP_DES 2 +#define IPSEC_ESP_3DES 3 +#define IPSEC_ESP_RC5 4 +#define IPSEC_ESP_IDEA 5 +#define IPSEC_ESP_CAST 6 +#define IPSEC_ESP_BLOWFISH 7 +#define IPSEC_ESP_3IDEA 8 +#define IPSEC_ESP_DES_IV32 9 +#define IPSEC_ESP_RC4 10 // It's reserved in IKEv2 +#define IPSEC_ESP_NULL 11 +#define IPSEC_ESP_AES 12 + +#define IKE_XCG_TYPE_NONE 0 +#define IKE_XCG_TYPE_BASE 1 +#define IKE_XCG_TYPE_IDENTITY_PROTECT 2 +#define IKE_XCG_TYPE_AUTH_ONLY 3 +#define IKE_XCG_TYPE_AGGR 4 +#define IKE_XCG_TYPE_INFO 5 +#define IKE_XCG_TYPE_QM 32 +#define IKE_XCG_TYPE_NGM 33 +#define IKE_XCG_TYPE_SA_INIT 34 +#define IKE_XCG_TYPE_AUTH 35 +#define IKE_XCG_TYPE_CREATE_CHILD_SA 36 +#define IKE_XCG_TYPE_INFO2 37 + +#define IKE_LIFE_TYPE_SECONDS 1 +#define IKE_LIFE_TYPE_KILOBYTES 2 + +// +// Deafult IKE SA lifetime and CHILD SA lifetime +// +#define IKE_SA_DEFAULT_LIFETIME 1200 +#define CHILD_SA_DEFAULT_LIFETIME 3600 + +// +// Next payload type presented within Proposal payload +// +#define IKE_PROPOSAL_NEXT_PAYLOAD_MORE 2 +#define IKE_PROPOSAL_NEXT_PAYLOAD_NONE 0 + +// +// Next payload type presented within Transform payload +// +#define IKE_TRANSFORM_NEXT_PAYLOAD_MORE 3 +#define IKE_TRANSFORM_NEXT_PAYLOAD_NONE 0 + +// +// Max size of the SA attribute +// +#define MAX_SA_ATTRS_SIZE 48 +#define SA_ATTR_FORMAT_BIT 0x8000 +// +// The definition for Information Message ID. +// +#define INFO_MID_SIGNATURE SIGNATURE_32 ('I', 'N', 'F', 'M') + +// +// Type for the IKE SESSION COMMON +// +typedef enum { + IkeSessionTypeIkeSa, + IkeSessionTypeChildSa, + IkeSessionTypeInfo, + IkeSessionTypeMax +} IKE_SESSION_TYPE; + +// +// The DH Group ID defined RFC3526 and RFC 2409 +// +typedef enum { + OakleyGroupModp768 = 1, + OakleyGroupModp1024 = 2, + OakleyGroupGp155 = 3, // Unsupported Now. + OakleyGroupGp185 = 4, // Unsupported Now. + OakleyGroupModp1536 = 5, + + OakleyGroupModp2048 = 14, + OakleyGroupModp3072 = 15, + OakleyGroupModp4096 = 16, + OakleyGroupModp6144 = 17, + OakleyGroupModp8192 = 18, + OakleyGroupMax +} OAKLEY_GROUP_ID; + +// +// IKE Header +// +#pragma pack(1) +typedef struct { + UINT64 InitiatorCookie; + UINT64 ResponderCookie; + UINT8 NextPayload; + UINT8 Version; + UINT8 ExchangeType; + UINT8 Flags; + UINT32 MessageId; + UINT32 Length; +} IKE_HEADER; +#pragma pack() + +typedef union { + UINT16 AttrLength; + UINT16 AttrValue; +} IKE_SA_ATTR_UNION; + +// +// SA Attribute present in Transform Payload +// +#pragma pack(1) +typedef struct { + UINT16 AttrType; + IKE_SA_ATTR_UNION Attr; +} IKE_SA_ATTRIBUTE; +#pragma pack() + +// +// Contains the IKE packet information. +// +typedef struct { + UINTN RefCount; + BOOLEAN IsHdrExt; + IKE_HEADER *Header; + BOOLEAN IsPayloadsBufExt; + UINT8 *PayloadsBuf; // The whole IkePakcet trimed the IKE header. + UINTN PayloadTotalSize; + LIST_ENTRY PayloadList; + EFI_IP_ADDRESS RemotePeerIp; + BOOLEAN IsEncoded; // whether HTON is done when sending the packet + UINT32 Spi; // For the Delete Information Exchange + BOOLEAN IsDeleteInfo; // For the Delete Information Exchange + IPSEC_PRIVATE_DATA *Private; // For the Delete Information Exchange +} IKE_PACKET; + +// +// The generic structure to all kinds of IKE payloads. +// +typedef struct { + UINT32 Signature; + BOOLEAN IsPayloadBufExt; + UINT8 PayloadType; + UINT8 *PayloadBuf; + UINTN PayloadSize; + LIST_ENTRY ByPacket; +} IKE_PAYLOAD; + +// +// Udp Service +// +typedef struct { + UINT32 Signature; + UINT8 IpVersion; + LIST_ENTRY List; + LIST_ENTRY *ListHead; + EFI_HANDLE NicHandle; + EFI_HANDLE ImageHandle; + UDP_IO *Input; + UDP_IO *Output; + EFI_IP_ADDRESS DefaultAddress; + BOOLEAN IsConfigured; +} IKE_UDP_SERVICE; + +// +// Each IKE session has its own Key sets for local peer and remote peer. +// +typedef struct { + EFI_IPSEC_ALGO_INFO LocalPeerInfo; + EFI_IPSEC_ALGO_INFO RemotePeerInfo; +} SA_KEYMATS; + +// +// Each algorithm has its own Id, Guid, BlockSize and KeyLength. +// This struct contains these information for each algorithm. It is generic structure +// for both encryption and authentication algorithm. +// For authentication algorithm, the AlgSize means IcvSize. For encryption algorithm, +// it means IvSize. +// +#pragma pack(1) +typedef struct { + UINT8 AlgorithmId; // Encryption or Authentication Id used by ESP/AH + EFI_GUID *AlgGuid; + UINT8 AlgSize; // IcvSize or IvSize + UINT8 BlockSize; + UINTN KeyMateLen; +} IKE_ALG_GUID_INFO; // For IPsec Authentication and Encryption Algorithm. +#pragma pack() + +// +// Structure used to store the DH group +// +typedef struct { + UINT8 GroupId; + UINTN Size; + UINT8 *Modulus; + UINTN GroupGenerator; +} MODP_GROUP; + +/** + This is prototype definition of general interface to phase the payloads + after/before the decode/encode. + + @param[in] SessionCommon Point to the SessionCommon + @param[in] PayloadBuf Point to the buffer of Payload. + @param[in] PayloadSize The size of the PayloadBuf in bytes. + @param[in] PayloadType The type of Payload. + +**/ +typedef +VOID +(*IKE_ON_PAYLOAD_FROM_NET) ( + IN UINT8 *SessionCommon, + IN UINT8 *PayloadBuf, + IN UINTN PayloadSize, + IN UINT8 PayloadType + ); + +#endif + diff --git a/Core/NetworkPkg/IpSecDxe/IkeCommon.c b/Core/NetworkPkg/IpSecDxe/IkeCommon.c new file mode 100644 index 0000000000..6fc7c06353 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IkeCommon.c @@ -0,0 +1,254 @@ +/** @file + Common operation of the IKE + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Ike.h" +#include "IkeCommon.h" +#include "IpSecConfigImpl.h" +#include "IpSecDebug.h" + +// +// Initial the SPI +// +UINT32 mNextSpi = IKE_SPI_BASE; + +/** + Call Crypto Lib to generate a random value with eight-octet length. + + @return the 64 byte vaule. + +**/ +UINT64 +IkeGenerateCookie ( + VOID + ) +{ + UINT64 Cookie; + EFI_STATUS Status; + + Status = IpSecCryptoIoGenerateRandomBytes ((UINT8 *)&Cookie, sizeof (UINT64)); + if (EFI_ERROR (Status)) { + return 0; + } else { + return Cookie; + } +} + +/** + Generate the random data for Nonce payload. + + @param[in] NonceSize Size of the data in bytes. + + @return Buffer which contains the random data of the spcified size. + +**/ +UINT8 * +IkeGenerateNonce ( + IN UINTN NonceSize + ) +{ + UINT8 *Nonce; + EFI_STATUS Status; + + Nonce = AllocateZeroPool (NonceSize); + if (Nonce == NULL) { + return NULL; + } + + Status = IpSecCryptoIoGenerateRandomBytes (Nonce, NonceSize); + if (EFI_ERROR (Status)) { + FreePool (Nonce); + return NULL; + } else { + return Nonce; + } +} + +/** + Convert the IKE Header from Network order to Host order. + + @param[in, out] Header The pointer of the IKE_HEADER. + +**/ +VOID +IkeHdrNetToHost ( + IN OUT IKE_HEADER *Header + ) +{ + Header->InitiatorCookie = NTOHLL (Header->InitiatorCookie); + Header->ResponderCookie = NTOHLL (Header->ResponderCookie); + Header->MessageId = NTOHL (Header->MessageId); + Header->Length = NTOHL (Header->Length); +} + +/** + Convert the IKE Header from Host order to Network order. + + @param[in, out] Header The pointer of the IKE_HEADER. + +**/ +VOID +IkeHdrHostToNet ( + IN OUT IKE_HEADER *Header + ) +{ + Header->InitiatorCookie = HTONLL (Header->InitiatorCookie); + Header->ResponderCookie = HTONLL (Header->ResponderCookie); + Header->MessageId = HTONL (Header->MessageId); + Header->Length = HTONL (Header->Length); +} + +/** + Allocate a buffer of IKE_PAYLOAD and set its Signature. + + @return A buffer of IKE_PAYLOAD. + +**/ +IKE_PAYLOAD * +IkePayloadAlloc ( + VOID + ) +{ + IKE_PAYLOAD *IkePayload; + + IkePayload = (IKE_PAYLOAD *) AllocateZeroPool (sizeof (IKE_PAYLOAD)); + if (IkePayload == NULL) { + return NULL; + } + + IkePayload->Signature = IKE_PAYLOAD_SIGNATURE; + + return IkePayload; +} + +/** + Free a specified IKE_PAYLOAD buffer. + + @param[in] IkePayload Pointer of IKE_PAYLOAD to be freed. + +**/ +VOID +IkePayloadFree ( + IN IKE_PAYLOAD *IkePayload + ) +{ + if (IkePayload == NULL) { + return; + } + // + // If this IkePayload is not referred by others, free it. + // + if (!IkePayload->IsPayloadBufExt && (IkePayload->PayloadBuf != NULL)) { + FreePool (IkePayload->PayloadBuf); + } + + FreePool (IkePayload); +} + +/** + Generate an new SPI. + + @return a SPI in 4 bytes. + +**/ +UINT32 +IkeGenerateSpi ( + VOID + ) +{ + // + // TODO: should generate SPI randomly to avoid security issue + // + return mNextSpi++; +} + +/** + Generate a random data for IV + + @param[in] IvBuffer The pointer of the IV buffer. + @param[in] IvSize The IV size. + + @retval EFI_SUCCESS Create a random data for IV. + @retval otherwise Failed. + +**/ +EFI_STATUS +IkeGenerateIv ( + IN UINT8 *IvBuffer, + IN UINTN IvSize + ) +{ + return IpSecCryptoIoGenerateRandomBytes (IvBuffer, IvSize); +} + + +/** + Find SPD entry by a specified SPD selector. + + @param[in] SpdSel Point to SPD Selector to be searched for. + + @retval Point to SPD Entry if the SPD entry found. + @retval NULL if not found. + +**/ +IPSEC_SPD_ENTRY * +IkeSearchSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *SpdSel + ) +{ + IPSEC_SPD_ENTRY *SpdEntry; + LIST_ENTRY *SpdList; + LIST_ENTRY *Entry; + + SpdList = &mConfigData[IPsecConfigDataTypeSpd]; + + NET_LIST_FOR_EACH (Entry, SpdList) { + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + + // + // Find the required SPD entry + // + if (CompareSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel, + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector + )) { + return SpdEntry; + } + + } + + return NULL; +} + +/** + Get the IKE Version from the IKE_SA_SESSION. + + @param[in] Session Pointer of the IKE_SA_SESSION. + +**/ +UINT8 +IkeGetVersionFromSession ( + IN UINT8 *Session + ) +{ + if (*(UINT32 *) Session == IKEV2_SA_SESSION_SIGNATURE) { + return ((IKEV2_SA_SESSION *) Session)->SessionCommon.IkeVer; + } else { + // + // Add IKEv1 support here. + // + return 0; + } +} + diff --git a/Core/NetworkPkg/IpSecDxe/IkeCommon.h b/Core/NetworkPkg/IpSecDxe/IkeCommon.h new file mode 100644 index 0000000000..714ecaa8e3 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IkeCommon.h @@ -0,0 +1,189 @@ +/** @file + Common operation of the IKE. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IKE_COMMON_H_ +#define _IKE_COMMON_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Ikev2/Ikev2.h" +#include "IpSecImpl.h" +#include "IkePacket.h" +#include "IpSecCryptIo.h" + + +#define IKE_DEFAULT_PORT 500 +#define IKE_DEFAULT_TIMEOUT_INTERVAL 10000 // 10s +#define IKE_NONCE_SIZE 16 +#define IKE_MAX_RETRY 4 +#define IKE_SPI_BASE 0x10000 +#define IKE_PAYLOAD_SIGNATURE SIGNATURE_32('I','K','E','P') +#define IKE_PAYLOAD_BY_PACKET(a) CR(a,IKE_PAYLOAD,ByPacket,IKE_PAYLOAD_SIGNATURE) + + +#define IKE_PACKET_APPEND_PAYLOAD(IkePacket,IkePayload) \ + do { \ + InsertTailList(&(IkePacket)->PayloadList, &(IkePayload)->ByPacket); \ + } while (0) + +#define IKE_PACKET_REMOVE_PAYLOAD(IkePacket,IkePayload) \ + do { \ + RemoveEntryList(&(IkePayload)->ByPacket); \ + } while (0) + +#define IKE_PACKET_END_PAYLOAD(IkePacket, Node) \ + Node = GetFirstNode (&(IkePacket)->PayloadList); \ + while (!IsNodeAtEnd (&(IkePacket)->PayloadList, Node)) { \ + Node = GetNextNode (&(IkePacket)->PayloadList, Node); \ + } \ + +/** + Call Crypto Lib to generate a random value with eight-octet length. + + @return the 64 byte vaule. + +**/ +UINT64 +IkeGenerateCookie ( + VOID + ); + +/** + Generate the random data for Nonce payload. + + @param[in] NonceSize Size of the data in bytes. + + @return Buffer which contains the random data of the spcified size. + +**/ +UINT8 * +IkeGenerateNonce ( + IN UINTN NonceSize + ); + +/** + Convert the IKE Header from Network order to Host order. + + @param[in, out] Header The pointer of the IKE_HEADER. + +**/ +VOID +IkeHdrNetToHost ( + IN OUT IKE_HEADER *Header + ); + + +/** + Convert the IKE Header from Host order to Network order. + + @param[in, out] Header The pointer of the IKE_HEADER. + +**/ +VOID +IkeHdrHostToNet ( + IN OUT IKE_HEADER *Header + ); + +/** + Allocate a buffer of IKE_PAYLOAD and set its Signature. + + @return A buffer of IKE_PAYLOAD. + +**/ +IKE_PAYLOAD * +IkePayloadAlloc ( + VOID + ); + +/** + Free a specified IKE_PAYLOAD buffer. + + @param[in] IkePayload Pointer of IKE_PAYLOAD to be freed. + +**/ +VOID +IkePayloadFree ( + IN IKE_PAYLOAD *IkePayload + ); + +/** + Generate an unused SPI + + @return a SPI in 4 bytes. + +**/ +UINT32 +IkeGenerateSpi ( + VOID + ); + +/** + Generate a random data for IV + + @param[in] IvBuffer The pointer of the IV buffer. + @param[in] IvSize The IV size. + + @retval EFI_SUCCESS Create a random data for IV. + @retval otherwise Failed. + +**/ +EFI_STATUS +IkeGenerateIv ( + IN UINT8 *IvBuffer, + IN UINTN IvSize + ); + +/** + Get the IKE Version from the IKE_SA_SESSION. + + @param[in] Session Pointer of the IKE_SA_SESSION. + +**/ +UINT8 +IkeGetVersionFromSession ( + IN UINT8 *Session + ); + +/** + Find SPD entry by a specified SPD selector. + + @param[in] SpdSel Point to SPD Selector to be searched for. + + @retval Point to Spd Entry if the SPD entry found. + @retval NULL if not found. + +**/ +IPSEC_SPD_ENTRY * +IkeSearchSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *SpdSel + ); + +extern MODP_GROUP OakleyModpGroup[]; +extern IKE_ALG_GUID_INFO mIPsecEncrAlgInfo[]; +extern IKE_ALG_GUID_INFO mIPsecAuthAlgInfo[]; + +#endif + diff --git a/Core/NetworkPkg/IpSecDxe/IkePacket.c b/Core/NetworkPkg/IpSecDxe/IkePacket.c new file mode 100644 index 0000000000..14dbb9d5d6 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IkePacket.c @@ -0,0 +1,265 @@ +/** @file + IKE Packet related operation. + + Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecDebug.h" +#include "Ikev2/Utility.h" + +/** + Allocate a buffer for the IKE_PACKET and intitalize its Header and payloadlist. + + @return The pointer of the IKE_PACKET. + +**/ +IKE_PACKET * +IkePacketAlloc ( + VOID + ) +{ + IKE_PACKET *IkePacket; + + IkePacket = (IKE_PACKET *) AllocateZeroPool (sizeof (IKE_PACKET)); + if (IkePacket == NULL) { + return NULL; + } + + IkePacket->RefCount = 1; + InitializeListHead (&IkePacket->PayloadList); + + IkePacket->Header = (IKE_HEADER *) AllocateZeroPool (sizeof (IKE_HEADER)); + if (IkePacket->Header == NULL) { + FreePool (IkePacket); + return NULL; + } + return IkePacket; +} + +/** + Free the IkePacket by the specified IKE_PACKET pointer. + + @param[in] IkePacket The pointer of the IKE_PACKET to be freed. + +**/ +VOID +IkePacketFree ( + IN IKE_PACKET *IkePacket + ) +{ + LIST_ENTRY *Entry; + IKE_PAYLOAD *IkePayload; + + if (IkePacket == NULL) { + return; + } + // + // Check if the Packet is referred by others. + // + if (--IkePacket->RefCount == 0) { + // + // Free IkePacket header + // + if (!IkePacket->IsHdrExt && IkePacket->Header != NULL) { + FreePool (IkePacket->Header); + } + // + // Free the PayloadsBuff + // + if (!IkePacket->IsPayloadsBufExt && IkePacket->PayloadsBuf != NULL) { + FreePool (IkePacket->PayloadsBuf); + } + // + // Iterate payloadlist and free all payloads + // + for (Entry = (IkePacket)->PayloadList.ForwardLink; Entry != &(IkePacket)->PayloadList;) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + Entry = Entry->ForwardLink; + + IkePayloadFree (IkePayload); + } + + FreePool (IkePacket); + } +} + +/** + Callback funtion of NetbufFromExt() + + @param[in] Arg The data passed from the NetBufFromExe(). + +**/ +VOID +EFIAPI +IkePacketNetbufFree ( + IN VOID *Arg + ) +{ + // + // TODO: add something if need. + // +} + +/** + Copy the NetBuf into a IKE_PACKET sturcture. + + Create a IKE_PACKET and fill the received IKE header into the header of IKE_PACKET + and copy the recieved packet without IKE HEADER to the PayloadBuf of IKE_PACKET. + + @param[in] Netbuf The pointer of the Netbuf which contains the whole received + IKE packet. + + @return The pointer of the IKE_PACKET which contains the received packet. + +**/ +IKE_PACKET * +IkePacketFromNetbuf ( + IN NET_BUF *Netbuf + ) +{ + IKE_PACKET *IkePacket; + + IkePacket = NULL; + if (Netbuf->TotalSize < sizeof (IKE_HEADER)) { + goto Error; + } + + IkePacket = IkePacketAlloc (); + if (IkePacket == NULL) { + return NULL; + } + // + // Copy the IKE header from Netbuf to IkePacket->Hdr + // + NetbufCopy (Netbuf, 0, sizeof (IKE_HEADER), (UINT8 *) IkePacket->Header); + // + // Net order to host order + // + IkeHdrNetToHost (IkePacket->Header); + if (IkePacket->Header->Length < Netbuf->TotalSize) { + goto Error; + } + + IkePacket->PayloadTotalSize = IkePacket->Header->Length - sizeof (IKE_HEADER); + IkePacket->PayloadsBuf = (UINT8 *) AllocateZeroPool (IkePacket->PayloadTotalSize); + + if (IkePacket->PayloadsBuf == NULL) { + goto Error; + } + // + // Copy the IKE packet without the header into the IkePacket->PayloadsBuf. + // + NetbufCopy (Netbuf, sizeof (IKE_HEADER), (UINT32) IkePacket->PayloadTotalSize, IkePacket->PayloadsBuf); + return IkePacket; + +Error: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + + return NULL; +} + +/** + Convert the format from IKE_PACKET to NetBuf. + + @param[in] SessionCommon Pointer of related IKE_COMMON_SESSION + @param[in] IkePacket Pointer of IKE_PACKET to be copy to NetBuf + @param[in] IkeType The IKE type to pointer the packet is for which IKE + phase. Now it supports IKE_SA_TYPE, IKE_CHILDSA_TYPE, + IKE_INFO_TYPE. + + @return a pointer of Netbuff which contains the IKE_PACKE in network order. + +**/ +NET_BUF * +IkeNetbufFromPacket ( + IN UINT8 *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINTN IkeType + ) +{ + NET_BUF *Netbuf; + NET_FRAGMENT *Fragments; + UINTN Index; + UINTN NumPayloads; + LIST_ENTRY *PacketEntry; + LIST_ENTRY *Entry; + IKE_PAYLOAD *IkePayload; + EFI_STATUS RetStatus; + + RetStatus = EFI_SUCCESS; + + if (!IkePacket->IsEncoded) { + IkePacket->IsEncoded = TRUE; + // + // Convert Host order to Network order for IKE_PACKET header and payloads + // Encryption payloads if needed + // + if (((IKEV2_SESSION_COMMON *) SessionCommon)->IkeVer == 2) { + RetStatus = Ikev2EncodePacket ((IKEV2_SESSION_COMMON *) SessionCommon, IkePacket, IkeType); + if (EFI_ERROR (RetStatus)) { + return NULL; + } + + } else { + // + // If IKEv1 support, check it here. + // + return NULL; + } + } + + NumPayloads = 0; + // + // Get the number of the payloads + // + NET_LIST_FOR_EACH (PacketEntry, &(IkePacket)->PayloadList) { + + NumPayloads++; + } + // + // Allocate the Framgents according to the numbers of the IkePayload + // + Fragments = (NET_FRAGMENT *) AllocateZeroPool ((1 + NumPayloads) * sizeof (NET_FRAGMENT)); + if (Fragments == NULL) { + return NULL; + } + + Fragments[0].Bulk = (UINT8 *) IkePacket->Header; + Fragments[0].Len = sizeof (IKE_HEADER); + Index = 0; + + // + // Set payloads to the Framgments. + // + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + + Fragments[Index + 1].Bulk = IkePayload->PayloadBuf; + Fragments[Index + 1].Len = (UINT32) IkePayload->PayloadSize; + Index++; + } + + Netbuf = NetbufFromExt ( + Fragments, + (UINT32) (NumPayloads + 1), + 0, + 0, + IkePacketNetbufFree, + NULL + ); + + FreePool (Fragments); + return Netbuf; +} + diff --git a/Core/NetworkPkg/IpSecDxe/IkePacket.h b/Core/NetworkPkg/IpSecDxe/IkePacket.h new file mode 100644 index 0000000000..053d659d9c --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IkePacket.h @@ -0,0 +1,82 @@ +/** @file + IKE Packet related definitions and function declarations. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IKE_V1_PACKET_H_ +#define _IKE_V1_PACKET_H_ + +#include "Ike.h" + +#define IKE_PACKET_REF(p) ((p)->RefCount++) + +/** + Allocate a buffer for the IKE_PACKET and intitalize its Header and payloadlist. + + @return The pointer of the IKE_PACKET. + +**/ +IKE_PACKET * +IkePacketAlloc ( + VOID + ); + + +/** + Free the IkePacket by the specified IKE_PACKET pointer. + + @param[in] IkePacket The pointer of the IKE_PACKET to be freed. + +**/ +VOID +IkePacketFree ( + IN IKE_PACKET *IkePacket + ); + + +/** + Copy the NetBuf into a IKE_PACKET sturcture. + + Create a IKE_PACKET and fill the received IKE header into the header of IKE_PACKET + and copy the recieved packet without IKE HEADER to the PayloadBuf of IKE_PACKET. + + @param[in] Netbuf The pointer of the Netbuf which contains the whole received + IKE packet. + + @return The pointer of the IKE_PACKET which contains the received packet. + +**/ +IKE_PACKET * +IkePacketFromNetbuf ( + IN NET_BUF *Netbuf + ); + +/** + Convert the format from IKE_PACKET to NetBuf. + + @param[in] SessionCommon Pointer of related IKE_COMMON_SESSION + @param[in] IkePacket Pointer of IKE_PACKET to be copy to NetBuf + @param[in] IkeType The IKE type to pointer the packet is for which IKE + phase. Now it supports IKE_SA_TYPE, IKE_CHILDSA_TYPE, + IKE_INFO_TYPE. + + @return A pointer of Netbuff which contains the contents of the IKE_PACKE in network order. +**/ +NET_BUF * +IkeNetbufFromPacket ( + IN UINT8 *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINTN IkeType + ); + +#endif diff --git a/Core/NetworkPkg/IpSecDxe/IkeService.c b/Core/NetworkPkg/IpSecDxe/IkeService.c new file mode 100644 index 0000000000..d8571960a0 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IkeService.c @@ -0,0 +1,794 @@ +/** @file + Provide IPsec Key Exchange (IKE) service general interfaces. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IkeService.h" +#include "IpSecConfigImpl.h" + +IKE_EXCHANGE_INTERFACE *mIkeExchange[] = { + &mIkev1Exchange, + &mIkev2Exchange +}; + +EFI_UDP4_CONFIG_DATA mUdp4Conf = { + FALSE, + FALSE, + FALSE, + TRUE, + // + // IO parameters + // + 0, + 64, + FALSE, + 0, + 1000000, + FALSE, + {{0,0,0,0}}, + {{0,0,0,0}}, + IKE_DEFAULT_PORT, + {{0,0,0,0}}, + 0 +}; + +EFI_UDP6_CONFIG_DATA mUdp6Conf = { + FALSE, + FALSE, + TRUE, + // + // IO parameters + // + 0, + 128, + 0, + 1000000, + //Access Point + {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}, + IKE_DEFAULT_PORT, + {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}, + 0 +}; + +/** + Check if the NIC handle is binded to a Udp service. + + @param[in] Private Pointer of IPSEC_PRIVATE_DATA. + @param[in] Handle The Handle of the NIC card. + @param[in] IpVersion The version of the IP stack. + + @return a pointer of IKE_UDP_SERVICE. + +**/ +IKE_UDP_SERVICE * +IkeLookupUdp ( + IN IPSEC_PRIVATE_DATA *Private, + IN EFI_HANDLE Handle, + IN UINT8 IpVersion + ) +{ + LIST_ENTRY *Head; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IKE_UDP_SERVICE *Udp; + + Udp = NULL; + Head = (IpVersion == IP_VERSION_4) ? &Private->Udp4List : &Private->Udp6List; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) { + + Udp = IPSEC_UDP_SERVICE_FROM_LIST (Entry); + // + // Find the right udp service which installed on the appointed NIC handle. + // + if (Handle == Udp->NicHandle) { + break; + } else { + Udp = NULL; + } + } + + return Udp; +} + +/** + Configure a UDPIO's UDP4 instance. + + This fuction is called by the UdpIoCreateIo() to configures a + UDP4 instance. + + @param[in] UdpIo The UDP_IO to be configured. + @param[in] Context User-defined data when calling UdpIoCreateIo(). + + @retval EFI_SUCCESS The configuration succeeded. + @retval Others The UDP4 instance fails to configure. + +**/ +EFI_STATUS +EFIAPI +IkeConfigUdp4 ( + IN UDP_IO *UdpIo, + IN VOID *Context + ) +{ + EFI_UDP4_CONFIG_DATA Udp4Cfg; + EFI_UDP4_PROTOCOL *Udp4; + + ZeroMem (&Udp4Cfg, sizeof (EFI_UDP4_CONFIG_DATA)); + + Udp4 = UdpIo->Protocol.Udp4; + CopyMem ( + &Udp4Cfg, + &mUdp4Conf, + sizeof (EFI_UDP4_CONFIG_DATA) + ); + + if (Context != NULL) { + // + // Configure udp4 io with local default address. + // + Udp4Cfg.UseDefaultAddress = TRUE; + } + + return Udp4->Configure (Udp4, &Udp4Cfg); +} + +/** + Configure a UDPIO's UDP6 instance. + + This fuction is called by the UdpIoCreateIo()to configure a + UDP6 instance. + + @param[in] UdpIo The UDP_IO to be configured. + @param[in] Context User-defined data when calling UdpIoCreateIo(). + + @retval EFI_SUCCESS The configuration succeeded. + @retval Others The configuration fails. + +**/ +EFI_STATUS +EFIAPI +IkeConfigUdp6 ( + IN UDP_IO *UdpIo, + IN VOID *Context + ) +{ + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA Udp6Cfg; + + ZeroMem (&Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + Udp6 = UdpIo->Protocol.Udp6; + CopyMem ( + &Udp6Cfg, + &mUdp6Conf, + sizeof (EFI_UDP6_CONFIG_DATA) + ); + + if (Context != NULL) { + // + // Configure instance with a destination address to start source address + // selection, and then get the configure data from the mode data to store + // the source address. + // + CopyMem ( + &Udp6Cfg.RemoteAddress, + Context, + sizeof (EFI_IPv6_ADDRESS) + ); + } + + return Udp6->Configure (Udp6, &Udp6Cfg); +} + +/** + Open and configure the related output UDPIO for IKE packet sending. + + If the UdpService is not configured, this fuction calls UdpIoCreatIo() to + create UDPIO to bind this UdpService for IKE packet sending. If the UdpService + has already been configured, then return. + + @param[in] UdpService The UDP_IO to be configured. + @param[in] RemoteIp User-defined data when calling UdpIoCreateIo(). + + @retval EFI_SUCCESS The configuration is successful. + @retval Others The configuration fails. + +**/ +EFI_STATUS +IkeOpenOutputUdp ( + IN IKE_UDP_SERVICE *UdpService, + IN EFI_IP_ADDRESS *RemoteIp + ) +{ + EFI_STATUS Status; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Cfg2; + EFI_IP4_CONFIG2_INTERFACE_INFO *IfInfo; + UINTN BufSize; + EFI_IP6_MODE_DATA Ip6ModeData; + EFI_UDP6_PROTOCOL *Udp6; + + Status = EFI_SUCCESS; + IfInfo = NULL; + BufSize = 0; + + // + // Check whether the input and output udp io are both configured. + // + if (UdpService->IsConfigured) { + goto ON_EXIT; + } + + if (UdpService->IpVersion == UDP_IO_UDP4_VERSION) { + // + // Handle ip4config protocol to get local default address. + // + Status = gBS->HandleProtocol ( + UdpService->NicHandle, + &gEfiIp4Config2ProtocolGuid, + (VOID **) &Ip4Cfg2 + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Get the interface information size. + // + Status = Ip4Cfg2->GetData ( + Ip4Cfg2, + Ip4Config2DataTypeInterfaceInfo, + &BufSize, + NULL + ); + + if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) { + goto ON_EXIT; + } + + IfInfo = AllocateZeroPool (BufSize); + + if (IfInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Get the interface info. + // + Status = Ip4Cfg2->GetData ( + Ip4Cfg2, + Ip4Config2DataTypeInterfaceInfo, + &BufSize, + IfInfo + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + CopyMem ( + &UdpService->DefaultAddress.v4, + &IfInfo->StationAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + + // + // Create udp4 io for output with local default address. + // + UdpService->Output = UdpIoCreateIo ( + UdpService->NicHandle, + UdpService->ImageHandle, + IkeConfigUdp4, + UDP_IO_UDP4_VERSION, + &UdpService->DefaultAddress + ); + + if (UdpService->Output == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + } else { + // + // Create udp6 io for output with remote address. + // + UdpService->Output = UdpIoCreateIo ( + UdpService->NicHandle, + UdpService->ImageHandle, + IkeConfigUdp6, + UDP_IO_UDP6_VERSION, + RemoteIp + ); + + if (UdpService->Output == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + // + // Get ip6 mode data to get the result of source address selection. + // + ZeroMem (&Ip6ModeData, sizeof (EFI_IP6_MODE_DATA)); + + Udp6 = UdpService->Output->Protocol.Udp6; + Status = Udp6->GetModeData (Udp6, NULL, &Ip6ModeData, NULL, NULL); + + if (EFI_ERROR (Status)) { + UdpIoFreeIo (UdpService->Output); + goto ON_EXIT; + } + // + // Reconfigure udp6 io without remote address. + // + Udp6->Configure (Udp6, NULL); + Status = IkeConfigUdp6 (UdpService->Output, NULL); + + // + // Record the selected source address for ipsec process later. + // + CopyMem ( + &UdpService->DefaultAddress.v6, + &Ip6ModeData.ConfigData.StationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } + + UdpService->IsConfigured = TRUE; + +ON_EXIT: + if (IfInfo != NULL) { + FreePool (IfInfo); + } + + return Status; +} + +/** + Open and configure a UDPIO of Udp4 for IKE packet receiving. + + This function is called at the IPsecDriverBinding start. IPsec create a UDP4 and + UDP4 IO for each NIC handle. + + @param[in] Private Point to IPSEC_PRIVATE_DATA + @param[in] Controller Handler for NIC card. + @param[in] ImageHandle The handle that contains the EFI_DRIVER_BINDING_PROTOCOL instance. + + @retval EFI_SUCCESS The Operation is successful. + @retval EFI_OUT_OF_RESOURCE The required system resource can't be allocated. + +**/ +EFI_STATUS +IkeOpenInputUdp4 ( + IN IPSEC_PRIVATE_DATA *Private, + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle + ) +{ + IKE_UDP_SERVICE *Udp4Srv; + + // + // Check whether udp4 io of the controller has already been opened. + // + Udp4Srv = IkeLookupUdp (Private, Controller, IP_VERSION_4); + + if (Udp4Srv != NULL) { + return EFI_ALREADY_STARTED; + } + + Udp4Srv = AllocateZeroPool (sizeof (IKE_UDP_SERVICE)); + + if (Udp4Srv == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Create udp4 io for iutput. + // + Udp4Srv->Input = UdpIoCreateIo ( + Controller, + ImageHandle, + IkeConfigUdp4, + UDP_IO_UDP4_VERSION, + NULL + ); + + if (Udp4Srv->Input == NULL) { + FreePool (Udp4Srv); + return EFI_OUT_OF_RESOURCES; + } + + Udp4Srv->NicHandle = Controller; + Udp4Srv->ImageHandle = ImageHandle; + Udp4Srv->ListHead = &(Private->Udp4List); + Udp4Srv->IpVersion = UDP_IO_UDP4_VERSION; + Udp4Srv->IsConfigured = FALSE; + + ZeroMem (&Udp4Srv->DefaultAddress, sizeof (EFI_IP_ADDRESS)); + + // + // Insert the udp4 io into the list and increase the count. + // + InsertTailList (&Private->Udp4List, &Udp4Srv->List); + + Private->Udp4Num++; + + UdpIoRecvDatagram (Udp4Srv->Input, IkeDispatch, Udp4Srv, 0); + + return EFI_SUCCESS; +} + +/** + Open and configure a UDPIO of Udp6 for IKE packet receiving. + + This function is called at the IPsecDriverBinding start. IPsec create a UDP6 and UDP6 + IO for each NIC handle. + + @param[in] Private Point to IPSEC_PRIVATE_DATA + @param[in] Controller Handler for NIC card. + @param[in] ImageHandle The handle that contains the EFI_DRIVER_BINDING_PROTOCOL instance. + + @retval EFI_SUCCESS The Operation is successful. + @retval EFI_OUT_OF_RESOURCE The required system resource can't be allocated. + +**/ +EFI_STATUS +IkeOpenInputUdp6 ( + IN IPSEC_PRIVATE_DATA *Private, + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle + ) +{ + IKE_UDP_SERVICE *Udp6Srv; + + Udp6Srv = IkeLookupUdp (Private, Controller, IP_VERSION_6); + + if (Udp6Srv != NULL) { + return EFI_ALREADY_STARTED; + } + + Udp6Srv = AllocateZeroPool (sizeof (IKE_UDP_SERVICE)); + + if (Udp6Srv == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Create udp6 io for input. + // + Udp6Srv->Input = UdpIoCreateIo ( + Controller, + ImageHandle, + IkeConfigUdp6, + UDP_IO_UDP6_VERSION, + NULL + ); + + if (Udp6Srv->Input == NULL) { + FreePool (Udp6Srv); + return EFI_OUT_OF_RESOURCES; + } + + Udp6Srv->NicHandle = Controller; + Udp6Srv->ImageHandle = ImageHandle; + Udp6Srv->ListHead = &(Private->Udp6List); + Udp6Srv->IpVersion = UDP_IO_UDP6_VERSION; + Udp6Srv->IsConfigured = FALSE; + + ZeroMem (&Udp6Srv->DefaultAddress, sizeof (EFI_IP_ADDRESS)); + + // + // Insert the udp6 io into the list and increase the count. + // + InsertTailList (&Private->Udp6List, &Udp6Srv->List); + + Private->Udp6Num++; + + UdpIoRecvDatagram (Udp6Srv->Input, IkeDispatch, Udp6Srv, 0); + + return EFI_SUCCESS; +} + +/** + The general interface of starting IPsec Key Exchange. + + This function is called when a IKE negotiation to start getting a Key. + + @param[in] UdpService Point to IKE_UDP_SERVICE which will be used for + IKE packet sending. + @param[in] SpdEntry Point to the SPD entry related to the IKE negotiation. + @param[in] RemoteIp Point to EFI_IP_ADDRESS related to the IKE negotiation. + + @retval EFI_SUCCESS The Operation is successful. + @retval EFI_ACCESS_DENIED No related PAD entry was found. + @retval EFI_INVALID_PARAMETER The IKE version is not supported. + +**/ +EFI_STATUS +IkeNegotiate ( + IN IKE_UDP_SERVICE *UdpService, + IN IPSEC_SPD_ENTRY *SpdEntry, + IN EFI_IP_ADDRESS *RemoteIp + ) +{ + EFI_STATUS Status; + UINT8 *IkeSaSession; + IKE_EXCHANGE_INTERFACE *Exchange; + IPSEC_PRIVATE_DATA *Private; + IPSEC_PAD_ENTRY *PadEntry; + UINT8 IkeVersion; + + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + // + // Try to open udp io for output if it hasn't. + // + Status = IkeOpenOutputUdp (UdpService, RemoteIp); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Try to find the IKE SA session in the IKEv1 and IKEv2 established SA session list. + // + IkeSaSession = (UINT8 *) Ikev2SaSessionLookup (&Private->Ikev2EstablishedList, RemoteIp); + + + if (IkeSaSession == NULL) { + // + // Find the pad entry by the remote ip address. + // + PadEntry = IpSecLookupPadEntry (UdpService->IpVersion, RemoteIp); + if (PadEntry == NULL) { + return EFI_ACCESS_DENIED; + } + // + // Determine the IKE exchange instance by the auth protocol in pad entry. + // + ASSERT (PadEntry->Data->AuthProtocol < EfiIPsecAuthProtocolMaximum); + if (PadEntry->Data->AuthProtocol == EfiIPsecAuthProtocolIKEv1) { + return EFI_INVALID_PARAMETER; + } + Exchange = mIkeExchange[PadEntry->Data->AuthProtocol]; + // + // Start the main mode stage to negotiate IKE SA. + // + Status = Exchange->NegotiateSa (UdpService, SpdEntry, PadEntry, RemoteIp); + } else { + // + // Determine the IKE exchange instance by the IKE version in IKE SA session. + // + IkeVersion = IkeGetVersionFromSession (IkeSaSession); + if (IkeVersion != 2) { + return EFI_INVALID_PARAMETER; + } + + Exchange = mIkeExchange[IkeVersion - 1]; + // + // Start the quick mode stage to negotiate child SA. + // + Status = Exchange->NegotiateChildSa (IkeSaSession, SpdEntry, NULL); + } + + return Status; +} + +/** + The generic interface when receive a IKE packet. + + This function is called when UDP IO receives a IKE packet. + + @param[in] Packet Point to received IKE packet. + @param[in] EndPoint Point to UDP_END_POINT which contains the information of + Remote IP and Port. + @param[in] IoStatus The Status of Recieve Token. + @param[in] Context Point to data passed from the caller. + +**/ +VOID +EFIAPI +IkeDispatch ( + IN NET_BUF *Packet, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + IPSEC_PRIVATE_DATA *Private; + IKE_PACKET *IkePacket; + IKE_HEADER *IkeHdr; + IKE_UDP_SERVICE *UdpService; + IKE_EXCHANGE_INTERFACE *Exchange; + EFI_STATUS Status; + + UdpService = (IKE_UDP_SERVICE *) Context; + IkePacket = NULL; + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + if (EFI_ERROR (IoStatus)) { + goto ON_EXIT; + } + // + // Check whether the ipsec is enabled or not. + // + if (Private->IpSec.DisabledFlag == TRUE) { + goto ON_EXIT; + } + + if (EndPoint->RemotePort != IKE_DEFAULT_PORT) { + goto ON_EXIT; + } + + // + // Build IKE packet from the received netbuf. + // + IkePacket = IkePacketFromNetbuf (Packet); + + if (IkePacket == NULL) { + goto ON_EXIT; + } + // + // Get the remote address from the IKE packet. + // + if (UdpService->IpVersion == IP_VERSION_4) { + *(UINT32 *) IkePacket->RemotePeerIp.Addr = HTONL ((*(UINT32 *) EndPoint->RemoteAddr.Addr)); + } else { + CopyMem ( + &IkePacket->RemotePeerIp, + NTOHLLL (&EndPoint->RemoteAddr.v6), + sizeof (EFI_IPv6_ADDRESS) + ); + } + // + // Try to open udp io for output if hasn't. + // + Status = IkeOpenOutputUdp (UdpService, &IkePacket->RemotePeerIp); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + IkeHdr = IkePacket->Header; + + // + // Determine the IKE exchange instance by the IKE version in IKE header. + // + if (IKE_MAJOR_VERSION (IkeHdr->Version) == 2) { + Exchange = mIkeExchange[IKE_MAJOR_VERSION (IkeHdr->Version) - 1]; + } else { + goto ON_EXIT; + } + + switch (IkeHdr->ExchangeType) { + case IKE_XCG_TYPE_IDENTITY_PROTECT: + case IKE_XCG_TYPE_SA_INIT: + case IKE_XCG_TYPE_AUTH: + Exchange->HandleSa (UdpService, IkePacket); + break; + + case IKE_XCG_TYPE_QM: + case IKE_XCG_TYPE_CREATE_CHILD_SA: + Exchange->HandleChildSa (UdpService, IkePacket); + break; + + case IKE_XCG_TYPE_INFO: + case IKE_XCG_TYPE_INFO2: + Exchange->HandleInfo (UdpService, IkePacket); + break; + + default: + break; + } + +ON_EXIT: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + + if (Packet != NULL) { + NetbufFree (Packet); + } + + UdpIoRecvDatagram (UdpService->Input, IkeDispatch, UdpService, 0); + + return ; +} + +/** + Delete all established IKE SAs and related Child SAs. + + This function is the subfunction of the IpSecCleanupAllSa(). It first calls + IkeDeleteChildSa() to delete all Child SAs then send out the related + Information packet. + + @param[in] Private Pointer of the IPSEC_PRIVATE_DATA + @param[in] IsDisableIpsec Indicate whether needs to disable IPsec. + +**/ +VOID +IkeDeleteAllSas ( + IN IPSEC_PRIVATE_DATA *Private, + IN BOOLEAN IsDisableIpsec + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + IKEV2_SA_SESSION *Ikev2SaSession; + UINT8 Value; + EFI_STATUS Status; + IKE_EXCHANGE_INTERFACE *Exchange; + UINT8 IkeVersion; + + Exchange = NULL; + + // + // If the IKEv1 is supported, first deal with the Ikev1Estatblished list. + // + + // + // If IKEv2 SAs are under establishing, delete it directly. + // + if (!IsListEmpty (&Private->Ikev2SessionList)) { + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->Ikev2SessionList) { + Ikev2SaSession = IKEV2_SA_SESSION_BY_SESSION (Entry); + RemoveEntryList (Entry); + Ikev2SaSessionFree (Ikev2SaSession); + } + } + + // + // If there is no existing established IKE SA, set the Ipsec DisableFlag to TRUE + // and turn off the IsIPsecDisabling flag. + // + if (IsListEmpty (&Private->Ikev2EstablishedList) && IsDisableIpsec) { + Value = IPSEC_STATUS_DISABLED; + Status = gRT->SetVariable ( + IPSECCONFIG_STATUS_NAME, + &gEfiIpSecConfigProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (Value), + &Value + ); + if (!EFI_ERROR (Status)) { + Private->IpSec.DisabledFlag = TRUE; + Private->IsIPsecDisabling = FALSE; + return ; + } + } + + // + // Delete established IKEv2 SAs. + // + if (!IsListEmpty (&Private->Ikev2EstablishedList)) { + for (Entry = Private->Ikev2EstablishedList.ForwardLink; Entry != &Private->Ikev2EstablishedList;) { + Ikev2SaSession = IKEV2_SA_SESSION_BY_SESSION (Entry); + Entry = Entry->ForwardLink; + + Ikev2SaSession->SessionCommon.State = IkeStateSaDeleting; + + // + // Call for Information Exchange. + // + IkeVersion = IkeGetVersionFromSession ((UINT8*)Ikev2SaSession); + if (IkeVersion == 2) { + Exchange = mIkeExchange[IkeVersion - 1]; + Exchange->NegotiateInfo((UINT8*)Ikev2SaSession, NULL); + } + } + } + +} + + + diff --git a/Core/NetworkPkg/IpSecDxe/IkeService.h b/Core/NetworkPkg/IpSecDxe/IkeService.h new file mode 100644 index 0000000000..0e05dfe976 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IkeService.h @@ -0,0 +1,262 @@ +/** @file + Prototypes definitions of IKE service. + + Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IKE_SERVICE_H_ +#define _IKE_SERVICE_H_ + +#include "Ike.h" +#include "IpSecImpl.h" +#include "IkeCommon.h" +#include "Ikev2/Utility.h" + +#define IPSEC_CRYPTO_LIB_MEMORY 128 * 1024 + +/** + This is prototype definition of general interface to intialize a IKE negotiation. + + @param[in] UdpService Point to Udp Servcie used for the IKE packet sending. + @param[in] SpdEntry Point to SPD entry related to this IKE negotiation. + @param[in] PadEntry Point to PAD entry related to this IKE negotiation. + @param[in] RemoteIp Point to IP Address which the remote peer to negnotiate. + + @retval EFI_SUCCESS The operation is successful. + @return Otherwise The operation is failed. + +**/ +typedef +EFI_STATUS +(*IKE_NEGOTIATE_SA) ( + IN IKE_UDP_SERVICE * UdpService, + IN IPSEC_SPD_ENTRY * SpdEntry, + IN IPSEC_PAD_ENTRY * PadEntry, + IN EFI_IP_ADDRESS * RemoteIp + ); + +/** + This is prototype definition fo general interface to start a IKE negotiation at Quick Mode. + + This function will be called when the related IKE SA is existed and start to + create a Child SA. + + @param[in] IkeSaSession Point to IKE SA Session related to this Negotiation. + @param[in] SpdEntry Point to SPD entry related to this Negotiation. + @param[in] Context Point to data passed from the caller. + + @retval EFI_SUCCESS The operation is successful. + @retval Otherwise The operation is failed. + +**/ +typedef +EFI_STATUS +(*IKE_NEGOTIATE_CHILD_SA) ( + IN UINT8 *IkeSaSession, + IN IPSEC_SPD_ENTRY *SpdEntry, + IN UINT8 *Context + ); + +/** + This is prototype definition of the general interface when initialize a Inforamtion + Exchange. + + @param[in] IkeSaSession Point to IKE SA Session related to. + @param[in] Context Point to data passed from caller. + +**/ +typedef +EFI_STATUS +(*IKE_NEGOTIATE_INFO) ( + IN UINT8 *IkeSaSession, + IN UINT8 *Context + ); + +/** + This is prototype definition of the general interface when recived a IKE Pakcet + for the IKE SA establishing. + + @param[in] UdpService Point to UDP service used to send IKE Packet. + @param[in] IkePacket Point to received IKE packet. + +**/ +typedef +VOID +(*IKE_HANDLE_SA) ( + IN IKE_UDP_SERVICE *UdpService, + IN IKE_PACKET *IkePacket + ); + +/** + This is prototyp definition of the general interface when recived a IKE Packet + xfor the Child SA establishing. + + @param[in] UdpService Point to UDP service used to send IKE packet. + @param[in] IkePacket Point to received IKE packet. + +**/ +typedef +VOID +(*IKE_HANDLE_CHILD_SA) ( + IN IKE_UDP_SERVICE *UdpService, + IN IKE_PACKET *IkePacket + ); + +/** + This is prototype definition of the general interface when received a IKE + information Packet. + + @param[in] UdpService Point to UDP service used to send IKE packet. + @param[in] IkePacket Point to received IKE packet. + +**/ +typedef +VOID +(*IKE_HANDLE_INFO) ( + IN IKE_UDP_SERVICE *UdpService, + IN IKE_PACKET *IkePacket + ); + +typedef struct _IKE_EXCHANGE_INTERFACE { + UINT8 IkeVer; + IKE_NEGOTIATE_SA NegotiateSa; + IKE_NEGOTIATE_CHILD_SA NegotiateChildSa; + IKE_NEGOTIATE_INFO NegotiateInfo; + IKE_HANDLE_SA HandleSa; + IKE_HANDLE_CHILD_SA HandleChildSa; + IKE_HANDLE_INFO HandleInfo; +} IKE_EXCHANGE_INTERFACE; + +/** + Open and configure a UDPIO of Udp4 for IKE packet receiving. + + This function is called at the IPsecDriverBinding start. IPsec create a UDP4 and + a UDP4 IO for each NIC handle. + + @param[in] Private Point to IPSEC_PRIVATE_DATA + @param[in] Controller Handler for NIC card. + @param[in] ImageHandle The handle that contains the EFI_DRIVER_BINDING_PROTOCOL instance. + + @retval EFI_SUCCESS The Operation is successful. + @retval EFI_OUT_OF_RESOURCE The required system resource can't be allocated. + +**/ +EFI_STATUS +IkeOpenInputUdp4 ( + IN IPSEC_PRIVATE_DATA *Private, + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle + ); + +/** + Open and configure a UDPIO of Udp6 for IKE packet receiving. + + This function is called at the IPsecDriverBinding start. IPsec create a UDP6 and UDP6 + IO for each NIC handle. + + @param[in] Private Point to IPSEC_PRIVATE_DATA + @param[in] Controller Handler for NIC card. + @param[in] ImageHandle The handle that contains the EFI_DRIVER_BINDING_PROTOCOL instance. + + @retval EFI_SUCCESS The Operation is successful. + @retval EFI_OUT_OF_RESOURCE The required system resource can't be allocated. + +**/ +EFI_STATUS +IkeOpenInputUdp6 ( + IN IPSEC_PRIVATE_DATA *Private, + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle + ); + +/** + The general interface of starting IPsec Key Exchange. + + This function is called when start a IKE negotiation to get a Key. + + @param[in] UdpService Point to IKE_UDP_SERVICE which will be used for + IKE packet sending. + @param[in] SpdEntry Point to the SPD entry related to the IKE negotiation. + @param[in] RemoteIp Point to EFI_IP_ADDRESS related to the IKE negotiation. + + @retval EFI_SUCCESS The Operation is successful. + @retval EFI_ACCESS_DENIED No related PAD entry was found. + +**/ +EFI_STATUS +IkeNegotiate ( + IN IKE_UDP_SERVICE *UdpService, + IN IPSEC_SPD_ENTRY *SpdEntry, + IN EFI_IP_ADDRESS *RemoteIp + ); + +/** + The general interface when receive a IKE packet. + + This function is called when UDP IO receives a IKE packet. + + @param[in] Packet Point to received IKE packet. + @param[in] EndPoint Point to UDP_END_POINT which contains the information of + Remote IP and Port. + @param[in] IoStatus The Status of Recieve Token. + @param[in] Context Point to data passed from the caller. + +**/ +VOID +EFIAPI +IkeDispatch ( + IN NET_BUF *Packet, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + +/** + Check if the NIC handle is binded to a Udp service. + + @param[in] Private Pointer of IPSEC_PRIVATE_DATA + @param[in] Handle The Handle of the NIC card + @param[in] IpVersion The version of the IP stack. + + @return a pointer of IKE_UDP_SERVICE. + +**/ +IKE_UDP_SERVICE * +IkeLookupUdp ( + IN IPSEC_PRIVATE_DATA *Private, + IN EFI_HANDLE Handle, + IN UINT8 IpVersion + ); + + +/** + Delete all established IKE SAs and related Child SAs. + + This function is the subfunction of the IpSecCleanupAllSa(). It first calls + IkeDeleteChildSa() to delete all Child SAs then send out the related + Information packet. + + @param[in] Private Pointer of the IPSEC_PRIVATE_DATA. + @param[in] IsDisableIpsec Indicate whether needs to disable IPsec. + +**/ +VOID +IkeDeleteAllSas ( + IN IPSEC_PRIVATE_DATA *Private, + IN BOOLEAN IsDisableIpsec + ); + + +extern IKE_EXCHANGE_INTERFACE mIkev1Exchange; +extern IKE_EXCHANGE_INTERFACE mIkev2Exchange; + +#endif diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c b/Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c new file mode 100644 index 0000000000..1f0199b22d --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c @@ -0,0 +1,199 @@ +/** @file + The operations for Child SA. + + Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Utility.h" + +/** + Generate IKE Packet for CREATE_CHILD_SA exchange. + + This IKE Packet would be the packet for creating new CHILD SA, or the packet for + rekeying existing IKE SA, or the packet for existing CHILD SA. + + @param[in] SaSession Pointer to related SA session. + @param[in] Context The data passed by the caller. + + return a pointer of IKE packet. + +**/ +IKE_PACKET * +Ikev2CreateChildGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PACKET *IkePacket; + IKE_PAYLOAD *NotifyPayload; + UINT32 *MessageId; + + NotifyPayload = NULL; + MessageId = NULL; + + ChildSaSession = (IKEV2_CHILD_SA_SESSION *) SaSession; + if (ChildSaSession == NULL) { + return NULL; + } + + IkePacket = IkePacketAlloc(); + if (IkePacket == NULL) { + return NULL; + } + + + if (Context != NULL) { + MessageId = (UINT32 *) Context; + } + + IkePacket->Header->Version = (UINT8) (2 << 4); + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_NOTIFY; + IkePacket->Header->ExchangeType = IKE_XCG_TYPE_CREATE_CHILD_SA; + + if (ChildSaSession->SessionCommon.IkeSessionType == IkeSessionTypeChildSa) { + // + // 1.a Fill the IkePacket->Hdr + // + IkePacket->Header->InitiatorCookie = ChildSaSession->IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = ChildSaSession->IkeSaSession->ResponderCookie; + + if (MessageId != NULL) { + IkePacket->Header->MessageId = *MessageId; + } else { + IkePacket->Header->MessageId = ChildSaSession->MessageId; + } + + if (ChildSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_CHILD_INIT; + } else { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + } + + } else { + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + // + // 1.a Fill the IkePacket->Hdr + // + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + + if (MessageId != NULL) { + IkePacket->Header->MessageId = *MessageId; + } else { + IkePacket->Header->MessageId = IkeSaSession->MessageId; + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_CHILD_INIT; + } else { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + } + } + + // + // According to RFC4306, Chapter 4. + // A minimal implementation may support the CREATE_CHILD_SA exchange only to + // recognize requests and reject them with a Notify payload of type NO_ADDITIONAL_SAS. + // + NotifyPayload = Ikev2GenerateNotifyPayload ( + 0, + IKEV2_PAYLOAD_TYPE_NONE, + 0, + IKEV2_NOTIFICATION_NO_ADDITIONAL_SAS, + NULL, + NULL, + 0 + ); + if (NotifyPayload == NULL) { + IkePacketFree (IkePacket); + return NULL; + } + + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload); + // + // TODO: Support the CREATE_CHILD_SA exchange. + // + return IkePacket; +} + +/** + Parse the IKE packet of CREATE_CHILD_SA exchange. + + This function parse the IKE packet and save the related information to further + calculation. + + @param[in] SaSession Pointer to IKEv2_CHILD_SA_SESSION related to this Exchange. + @param[in] IkePacket Received packet to be parsed. + + + @retval EFI_SUCCESS The IKE Packet is acceptable. + @retval EFI_UNSUPPORTED Not support the CREATE_CHILD_SA request. + +**/ +EFI_STATUS +Ikev2CreateChildParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Routine process before the payload decoding. + + @param[in] SessionCommon Pointer to ChildSa SessionCommon. + @param[in] PayloadBuf Pointer to the payload. + @param[in] PayloadSize Size of PayloadBuf in byte. + @param[in] PayloadType Type of Payload. + +**/ +VOID +Ikev2ChildSaBeforeDecodePayload ( + IN UINT8 *SessionCommon, + IN UINT8 *PayloadBuf, + IN UINTN PayloadSize, + IN UINT8 PayloadType + ) +{ + +} + +/** + Routine Process after the payload encoding. + + @param[in] SessionCommon Pointer to ChildSa SessionCommon. + @param[in] PayloadBuf Pointer to the payload. + @param[in] PayloadSize Size of PayloadBuf in byte. + @param[in] PayloadType Type of Payload. + +**/ +VOID +Ikev2ChildSaAfterEncodePayload ( + IN UINT8 *SessionCommon, + IN UINT8 *PayloadBuf, + IN UINTN PayloadSize, + IN UINT8 PayloadType + ) +{ +} + +IKEV2_PACKET_HANDLER mIkev2CreateChild = { + // + // Create Child + // + Ikev2CreateChildParser, + Ikev2CreateChildGenerator +}; diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c new file mode 100644 index 0000000000..1eddbfbcf1 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c @@ -0,0 +1,809 @@ +/** @file + The general interfaces of the IKEv2. + + Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Utility.h" +#include "IpSecDebug.h" +#include "IkeService.h" +#include "IpSecConfigImpl.h" + +/** + General interface to intialize a IKEv2 negotiation. + + @param[in] UdpService Point to Udp Servcie used for the IKE packet sending. + @param[in] SpdEntry Point to SPD entry related to this IKE negotiation. + @param[in] PadEntry Point to PAD entry related to this IKE negotiation. + @param[in] RemoteIp Point to IP Address which the remote peer to negnotiate. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval EFI_INVALID_PARAMETER If UdpService or RemoteIp is NULL. + @return Others The operation is failed. + +**/ +EFI_STATUS +Ikev2NegotiateSa ( + IN IKE_UDP_SERVICE *UdpService, + IN IPSEC_SPD_ENTRY *SpdEntry, + IN IPSEC_PAD_ENTRY *PadEntry, + IN EFI_IP_ADDRESS *RemoteIp + ) +{ + IPSEC_PRIVATE_DATA *Private; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_SESSION_COMMON *SessionCommon; + IKEV2_PACKET_HANDLER Handler; + IKE_PACKET *IkePacket; + EFI_STATUS Status; + + if (UdpService == NULL || RemoteIp == NULL) { + return EFI_INVALID_PARAMETER; + } + + IkePacket = NULL; + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + // + // Lookup the remote ip address in the processing IKE SA session list. + // + IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2SessionList, RemoteIp); + if (IkeSaSession != NULL) { + // + // Drop the packet if already in process. + // + return EFI_SUCCESS; + } + + // + // Create a new IkeSaSession and initiate the common parameters. + // + IkeSaSession = Ikev2SaSessionAlloc (Private, UdpService); + if (IkeSaSession == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the specific parameters and state(IKE_STATE_INIT). + // + IkeSaSession->Spd = SpdEntry; + IkeSaSession->Pad = PadEntry; + SessionCommon = &IkeSaSession->SessionCommon; + SessionCommon->IsInitiator = TRUE; + SessionCommon->State = IkeStateInit; + // + // TODO: Get the prefer DH Group from the IPsec Configuration, after the IPsecconfig application update + // to support it. + // + SessionCommon->PreferDhGroup = IKEV2_TRANSFORM_ID_DH_1024MODP; + + CopyMem ( + &SessionCommon->RemotePeerIp, + RemoteIp, + sizeof (EFI_IP_ADDRESS) + ); + + CopyMem ( + &SessionCommon->LocalPeerIp, + &UdpService->DefaultAddress, + sizeof (EFI_IP_ADDRESS) + ); + + IKEV2_DUMP_STATE (SessionCommon->State, IkeStateInit); + + // + // Initiate the SAD data of the IkeSaSession. + // + IkeSaSession->SaData = Ikev2InitializeSaData (SessionCommon); + if (IkeSaSession->SaData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Generate an IKE request packet and send it out. + // + Handler = mIkev2Initial[IkeSaSession->Pad->Data->AuthMethod][SessionCommon->State]; + IkePacket = Handler.Generator ((UINT8 *) IkeSaSession, NULL); + if (IkePacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) SessionCommon, IkePacket, 0); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Insert the current IkeSaSession into the processing IKE SA list. + // + Ikev2SaSessionInsert (&Private->Ikev2SessionList, IkeSaSession, RemoteIp); + + return EFI_SUCCESS; + +ON_ERROR: + + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + Ikev2SaSessionFree (IkeSaSession); + return Status; +} + +/** + It is general interface to negotiate the Child SA. + + There are three situations which will invoke this function. First, create a CHILD + SA if the input Context is NULL. Second, rekeying the existing IKE SA if the Context + is a IKEv2_SA_SESSION. Third, rekeying the existing CHILD SA if the context is a + IKEv2_CHILD_SA_SESSION. + + @param[in] IkeSaSession Pointer to IKEv2_SA_SESSION related to this operation. + @param[in] SpdEntry Pointer to IPSEC_SPD_ENTRY related to this operation. + @param[in] Context The data pass from the caller. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval EFI_UNSUPPORTED The condition is not support yet. + @return Others The operation is failed. + +**/ +EFI_STATUS +Ikev2NegotiateChildSa ( + IN UINT8 *IkeSaSession, + IN IPSEC_SPD_ENTRY *SpdEntry, + IN UINT8 *Context + ) +{ + EFI_STATUS Status; + IKEV2_SA_SESSION *SaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *ChildSaCommon; + IKE_PACKET *IkePacket; + IKE_UDP_SERVICE *UdpService; + + SaSession = (IKEV2_SA_SESSION*) IkeSaSession; + UdpService = SaSession->SessionCommon.UdpService; + IkePacket = NULL; + + // + // 1. Create another child SA session if context is null. + // 2. Rekeying the IKE SA session if the context is IKE SA session. + // 3. Rekeying the child SA session if the context is child SA session. + // + if (Context == NULL) { + // + // Create a new ChildSaSession and initiate the common parameters. + // + ChildSaSession = Ikev2ChildSaSessionAlloc (UdpService, SaSession); + + if (ChildSaSession == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the specific parameters and state as IKE_STATE_CREATE_CHILD. + // + ChildSaSession->Spd = SpdEntry; + ChildSaCommon = &ChildSaSession->SessionCommon; + ChildSaCommon->IsInitiator = TRUE; + ChildSaCommon->State = IkeStateCreateChild; + + IKEV2_DUMP_STATE (ChildSaCommon->State, IkeStateCreateChild); + + if (SpdEntry->Selector->NextLayerProtocol != EFI_IPSEC_ANY_PROTOCOL) { + ChildSaSession->ProtoId = SpdEntry->Selector->NextLayerProtocol; + } + + if (SpdEntry->Selector->LocalPort != EFI_IPSEC_ANY_PORT) { + ChildSaSession->LocalPort = SpdEntry->Selector->LocalPort; + } + + if (SpdEntry->Selector->RemotePort != EFI_IPSEC_ANY_PORT) { + ChildSaSession->RemotePort = SpdEntry->Selector->RemotePort; + } + // + // Initiate the SAD data parameters of the ChildSaSession. + // + ChildSaSession->SaData = Ikev2InitializeSaData (ChildSaCommon); + if (ChildSaSession->SaData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + // + // Generate an IKE request packet and send it out. + // + IkePacket = mIkev2CreateChild.Generator ((UINT8 *) ChildSaSession, NULL); + + if (IkePacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) ChildSaCommon, IkePacket, 0); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Insert the ChildSaSession into processing child SA list. + // + Ikev2ChildSaSessionInsert (&SaSession->ChildSaSessionList, ChildSaSession); + } else { + // + // TODO: Rekeying IkeSaSession or ChildSaSession, NOT support yet. + // + // Rekey IkeSa, set IkeSaSession->State and pass over IkeSaSession + // Rekey ChildSa, set ChildSaSession->State and pass over ChildSaSession + // + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (ChildSaSession->SaData != NULL) { + FreePool (ChildSaSession->SaData); + } + + if (ChildSaSession->SessionCommon.TimeoutEvent != NULL) { + gBS->CloseEvent (ChildSaSession->SessionCommon.TimeoutEvent); + } + + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + + Ikev2ChildSaSessionFree (ChildSaSession); + return Status; +} + +/** + It is general interface to start the Information Exchange. + + There are three situations which will invoke this function. First, deliver a Delete Information + to delete the IKE SA if the input Context is NULL and the state of related IkeSaSeesion's is on + deleting.Second, deliver a Notify Information without the contents if the input Context is NULL. + Third, deliver a Notify Information if the input Context is not NULL. + + @param[in] IkeSaSession Pointer to IKEv2_SA_SESSION related to this operation. + @param[in] Context Data passed by caller. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval EFI_UNSUPPORTED The condition is not support yet. + @return Otherwise The operation is failed. + +**/ +EFI_STATUS +Ikev2NegotiateInfo ( + IN UINT8 *IkeSaSession, + IN UINT8 *Context + ) +{ + + EFI_STATUS Status; + IKEV2_SA_SESSION *Ikev2SaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *SaCommon; + IKE_PACKET *IkePacket; + IKE_UDP_SERVICE *UdpService; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + Ikev2SaSession = (IKEV2_SA_SESSION *) IkeSaSession; + UdpService = Ikev2SaSession->SessionCommon.UdpService; + SaCommon = &Ikev2SaSession->SessionCommon; + IkePacket = NULL; + Status = EFI_SUCCESS; + + // + // Delete the IKE SA. + // + if (Ikev2SaSession->SessionCommon.State == IkeStateSaDeleting && Context == NULL) { + + // + // Generate Information Packet which contains the Delete Payload. + // + IkePacket = mIkev2Info.Generator ((UINT8 *) Ikev2SaSession, NULL); + if (IkePacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Send out the Packet + // + if (UdpService != NULL && UdpService->Output != NULL) { + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) SaCommon, IkePacket, 0); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + } else if (!IsListEmpty (&Ikev2SaSession->DeleteSaList)) { + // + // Iterate all Deleting Child SAs. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Ikev2SaSession->DeleteSaList) { + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_DEL_SA (Entry); + ChildSaSession->SessionCommon.State = IkeStateSaDeleting; + + // + // Generate Information Packet which contains the Child SA Delete Payload. + // + IkePacket = mIkev2Info.Generator ((UINT8 *) ChildSaSession, NULL); + if (IkePacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Send out the Packet + // + if (UdpService != NULL && UdpService->Output != NULL) { + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) &ChildSaSession->SessionCommon, IkePacket, 0); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + } + } else if (Context == NULL) { + // + // TODO: Deliver null notification message. + // + } else if (Context != NULL) { + // + // TODO: Send out the Information Exchange which contains the Notify Payload. + // + } +ON_ERROR: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + return Status; + +} + +/** + The general interface when received a IKEv2 packet for the IKE SA establishing. + + This function first find the related IKE SA Session according to the IKE packet's + remote IP. Then call the corresponding function to handle this IKE packet according + to the related IKE SA Session's State. + + @param[in] UdpService Pointer of related UDP Service. + @param[in] IkePacket Data passed by caller. + +**/ +VOID +Ikev2HandleSa ( + IN IKE_UDP_SERVICE *UdpService, + IN IKE_PACKET *IkePacket + ) +{ + EFI_STATUS Status; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *IkeSaCommon; + IKEV2_SESSION_COMMON *ChildSaCommon; + IKEV2_PACKET_HANDLER Handler; + IKE_PACKET *Reply; + IPSEC_PAD_ENTRY *PadEntry; + IPSEC_PRIVATE_DATA *Private; + BOOLEAN IsNewSession; + + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + ChildSaSession = NULL; + ChildSaCommon = NULL; + + // + // Lookup the remote ip address in the processing IKE SA session list. + // + IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2SessionList, &IkePacket->RemotePeerIp); + IsNewSession = FALSE; + + if (IkeSaSession == NULL) { + // + // Lookup the remote ip address in the pad. + // + PadEntry = IpSecLookupPadEntry (UdpService->IpVersion, &IkePacket->RemotePeerIp); + if (PadEntry == NULL) { + // + // Drop the packet if no pad entry matched, this is the request from RFC 4301. + // + return ; + } + + // + // Create a new IkeSaSession and initiate the common parameters. + // + IkeSaSession = Ikev2SaSessionAlloc (Private, UdpService); + if (IkeSaSession == NULL) { + return; + } + IkeSaSession->Pad = PadEntry; + IkeSaCommon = &IkeSaSession->SessionCommon; + IkeSaCommon->IsInitiator = FALSE; + IkeSaCommon->State = IkeStateInit; + + IKEV2_DUMP_STATE (IkeSaCommon->State, IkeStateInit); + + CopyMem ( + &IkeSaCommon->RemotePeerIp, + &IkePacket->RemotePeerIp, + sizeof (EFI_IP_ADDRESS) + ); + + CopyMem ( + &IkeSaCommon->LocalPeerIp, + &UdpService->DefaultAddress, + sizeof (EFI_IP_ADDRESS) + ); + + IsNewSession = TRUE; + } + + // + // Validate the IKE packet header. + // + if (!Ikev2ValidateHeader (IkeSaSession, IkePacket->Header)) { + // + // Drop the packet if invalid IKE header. + // + goto ON_ERROR; + } + + // + // Decode all the payloads in the IKE packet. + // + IkeSaCommon = &IkeSaSession->SessionCommon; + Status = Ikev2DecodePacket (IkeSaCommon, IkePacket, IkeSessionTypeIkeSa); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Try to reate the first ChildSa Session of that IkeSaSession. + // If the IkeSaSession is responder, here will create the first ChildSaSession. + // + if (IkeSaCommon->State == IkeStateAuth && IsListEmpty(&IkeSaSession->ChildSaSessionList)) { + // + // Generate a piggyback child SA in IKE_STATE_AUTH state. + // + ASSERT (IsListEmpty (&IkeSaSession->ChildSaSessionList) && + IsListEmpty (&IkeSaSession->ChildSaEstablishSessionList)); + + ChildSaSession = Ikev2ChildSaSessionCreate (IkeSaSession, UdpService); + if (ChildSaSession == NULL) { + goto ON_ERROR; + } + + ChildSaCommon = &ChildSaSession->SessionCommon; + } + + // + // Parse the IKE request packet according to the auth method and current state. + // + Handler = mIkev2Initial[IkeSaSession->Pad->Data->AuthMethod][IkeSaCommon->State]; + Status = Handler.Parser ((UINT8 *)IkeSaSession, IkePacket); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Try to reate the first ChildSa Session of that IkeSaSession. + // If the IkeSaSession is initiator, here will create the first ChildSaSession. + // + if (IkeSaCommon->State == IkeStateAuth && IsListEmpty(&IkeSaSession->ChildSaSessionList)) { + // + // Generate a piggyback child SA in IKE_STATE_AUTH state. + // + ASSERT (IsListEmpty (&IkeSaSession->ChildSaSessionList) && + IsListEmpty (&IkeSaSession->ChildSaEstablishSessionList)); + + ChildSaSession = Ikev2ChildSaSessionCreate (IkeSaSession, UdpService); + if (ChildSaSession == NULL) { + goto ON_ERROR; + } + + ChildSaCommon = &ChildSaSession->SessionCommon; + + // + // Initialize the SA data for Child SA. + // + ChildSaSession->SaData = Ikev2InitializeSaData (ChildSaCommon); + } + + // + // Generate the IKE response packet and send it out if not established. + // + if (IkeSaCommon->State != IkeStateIkeSaEstablished) { + Handler = mIkev2Initial[IkeSaSession->Pad->Data->AuthMethod][IkeSaCommon->State]; + Reply = Handler.Generator ((UINT8 *) IkeSaSession, NULL); + if (Reply == NULL) { + goto ON_ERROR; + } + + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) IkeSaCommon, Reply, 0); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + if (!IkeSaCommon->IsInitiator) { + IkeSaCommon->State ++; + IKEV2_DUMP_STATE (IkeSaCommon->State - 1, IkeSaCommon->State); + } + } + + // + // Insert the new IkeSaSession into the Private processing IkeSaSession List. + // + if (IsNewSession) { + Ikev2SaSessionInsert (&Private->Ikev2SessionList, IkeSaSession, &IkePacket->RemotePeerIp); + } + + // + // Register the IkeSaSession and remove it from processing list. + // + if (IkeSaCommon->State == IkeStateIkeSaEstablished) { + + // + // Remove the Established IKE SA Session from the IKE SA Session Negotiating list + // and insert it into IKE SA Session Established list. + // + Ikev2SaSessionRemove (&Private->Ikev2SessionList, &IkePacket->RemotePeerIp); + Ikev2SaSessionReg (IkeSaSession, Private); + + // + // Remove the Established Child SA Session from the IkeSaSession->ChildSaSessionList + // ,insert it into IkeSaSession->ChildSaEstablishSessionList and save this Child SA + // into SAD. + // + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (IkeSaSession->ChildSaSessionList.BackLink); + Ikev2ChildSaSessionRemove ( + &IkeSaSession->ChildSaSessionList, + ChildSaSession->LocalPeerSpi, + IKEV2_ESTABLISHING_CHILDSA_LIST + ); + Ikev2ChildSaSessionReg (ChildSaSession, Private); + } + + return ; + +ON_ERROR: + if (ChildSaSession != NULL) { + // + // Remove the ChildSa from the list (Established list or Negotiating list). + // + RemoveEntryList (&ChildSaSession->ByIkeSa); + Ikev2ChildSaSessionFree (ChildSaSession); + } + + if (IsNewSession && IkeSaSession != NULL) { + // + // Remove the IkeSa from the list (Established list or Negotiating list). + // + if ((&IkeSaSession->BySessionTable)->ForwardLink != NULL && + !IsListEmpty (&IkeSaSession->BySessionTable + )){ + RemoveEntryList (&IkeSaSession->BySessionTable); + } + Ikev2SaSessionFree (IkeSaSession); + } + + return ; +} + +/** + + The general interface when received a IKEv2 packet for the IKE Child SA establishing + or IKE SA/CHILD SA rekeying. + + This function first find the related IKE SA Session according to the IKE packet's + remote IP. Then call the corresponding function to handle this IKE packet according + to the related IKE Child Session's State. + + @param[in] UdpService Pointer of related UDP Service. + @param[in] IkePacket Data passed by caller. + +**/ +VOID +Ikev2HandleChildSa ( + IN IKE_UDP_SERVICE *UdpService, + IN IKE_PACKET *IkePacket + ) +{ + EFI_STATUS Status; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CREATE_CHILD_REQUEST_TYPE RequestType; + IKE_PACKET *Reply; + IPSEC_PRIVATE_DATA *Private; + + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + Reply = NULL; + + // + // Lookup the remote ip address in the processing IKE SA session list. + // + IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2EstablishedList, &IkePacket->RemotePeerIp); + + if (IkeSaSession == NULL) { + // + // Drop the packet if no IKE SA associated. + // + return ; + } + + // + // Validate the IKE packet header. + // + if (!Ikev2ValidateHeader (IkeSaSession, IkePacket->Header)) { + // + // Drop the packet if invalid IKE header. + // + return; + } + + // + // Decode all the payloads in the IKE packet. + // + Status = Ikev2DecodePacket (&IkeSaSession->SessionCommon, IkePacket, IkeSessionTypeIkeSa); + if (EFI_ERROR (Status)) { + return; + } + + // + // Get the request type: CreateChildSa/RekeyChildSa/RekeyIkeSa. + // + RequestType = Ikev2ChildExchangeRequestType (IkePacket); + + switch (RequestType) { + case IkeRequestTypeCreateChildSa: + case IkeRequestTypeRekeyChildSa: + case IkeRequestTypeRekeyIkeSa: + // + // Parse the IKE request packet. Not support CREATE_CHILD_SA exchange yet, so + // only EFI_UNSUPPORTED will be returned and that will trigger a reply with a + // Notify payload of type NO_ADDITIONAL_SAS. + // + Status = mIkev2CreateChild.Parser ((UINT8 *) IkeSaSession, IkePacket); + if (EFI_ERROR (Status)) { + goto ON_REPLY; + } + + default: + // + // No support. + // + return ; + } + +ON_REPLY: + // + // Generate the reply packet if needed and send it out. + // + if (IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) { + Reply = mIkev2CreateChild.Generator ((UINT8 *) IkeSaSession, &IkePacket->Header->MessageId); + if (Reply != NULL) { + Status = Ikev2SendIkePacket (UdpService, (UINT8 *) &(IkeSaSession->SessionCommon), Reply, 0); + if (EFI_ERROR (Status)) { + // + // Delete Reply payload. + // + if (Reply != NULL) { + IkePacketFree (Reply); + } + } + } + } + return ; +} + +/** + + It is general interface to handle IKEv2 information Exchange. + + @param[in] UdpService Point to IKE UPD Service related to this information exchange. + @param[in] IkePacket The IKE packet to be parsed. + +**/ +VOID +Ikev2HandleInfo ( + IN IKE_UDP_SERVICE *UdpService, + IN IKE_PACKET *IkePacket + ) +{ + EFI_STATUS Status; + IKEV2_SESSION_COMMON *SessionCommon; + IKEV2_SA_SESSION *IkeSaSession; + IPSEC_PRIVATE_DATA *Private; + + Private = (UdpService->IpVersion == IP_VERSION_4) ? + IPSEC_PRIVATE_DATA_FROM_UDP4LIST(UdpService->ListHead) : + IPSEC_PRIVATE_DATA_FROM_UDP6LIST(UdpService->ListHead); + + // + // Lookup the remote ip address in the processing IKE SA session list. + // + IkeSaSession = Ikev2SaSessionLookup (&Private->Ikev2EstablishedList, &IkePacket->RemotePeerIp); + + if (IkeSaSession == NULL) { + // + // Drop the packet if no IKE SA associated. + // + return ; + } + // + // Validate the IKE packet header. + // + if (!Ikev2ValidateHeader (IkeSaSession, IkePacket->Header)) { + + // + // Drop the packet if invalid IKE header. + // + return; + } + + SessionCommon = &IkeSaSession->SessionCommon; + + // + // Decode all the payloads in the IKE packet. + // + Status = Ikev2DecodePacket (SessionCommon, IkePacket, IkeSessionTypeIkeSa); + if (EFI_ERROR (Status)) { + return; + } + + Status = mIkev2Info.Parser ((UINT8 *)IkeSaSession, IkePacket); + + if (EFI_ERROR (Status)) { + // + // Drop the packet if fail to parse. + // + return; + } +} + +IKE_EXCHANGE_INTERFACE mIkev1Exchange = { + 1, + NULL, //Ikev1NegotiateSa + NULL, //Ikev1NegotiateChildSa + NULL, + NULL, //Ikev1HandleSa, + NULL, //Ikev1HandleChildSa + NULL, //Ikev1HandleInfo +}; + +IKE_EXCHANGE_INTERFACE mIkev2Exchange = { + 2, + Ikev2NegotiateSa, + Ikev2NegotiateChildSa, + Ikev2NegotiateInfo, + Ikev2HandleSa, + Ikev2HandleChildSa, + Ikev2HandleInfo +}; + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h b/Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h new file mode 100644 index 0000000000..a2b733a4d2 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h @@ -0,0 +1,258 @@ +/** @file + IKEv2 related definitions. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef _IKE_V2_H_ +#define _IKE_V2_H_ + +#include "Ike.h" +#include "Payload.h" + +#define IKEV2_TS_ANY_PORT 0xffff +#define IKEV2_TS_ANY_PROTOCOL 0 + +#define IKEV2_DELET_CHILDSA_LIST 0 +#define IKEV2_ESTABLISHING_CHILDSA_LIST 1 +#define IKEV2_ESTABLISHED_CHILDSA_LIST 2 + +#define IKEV2_SA_SESSION_SIGNATURE SIGNATURE_32 ('I', 'K', 'E', 'I') +#define IKEV2_SA_SESSION_FROM_COMMON(a) CR (a, IKEV2_SA_SESSION, SessionCommon, IKEV2_SA_SESSION_SIGNATURE) +#define IKEV2_SA_SESSION_BY_SESSION(a) CR (a, IKEV2_SA_SESSION, BySessionTable, IKEV2_SA_SESSION_SIGNATURE) +#define IKEV2_SA_SESSION_BY_ESTABLISHED(a) CR (a, IKEV2_SA_SESSION, ByEstablishedTable, IKEV2_SA_SESSION_SIGNATURE) + +#define IKEV2_CHILD_SA_SESSION_SIGNATURE SIGNATURE_32 ('I', 'K', 'E', 'C') +#define IKEV2_CHILD_SA_SESSION_FROM_COMMON(a) CR (a, IKEV2_CHILD_SA_SESSION, SessionCommon, IKEV2_CHILD_SA_SESSION_SIGNATURE) +#define IKEV2_CHILD_SA_SESSION_BY_IKE_SA(a) CR (a, IKEV2_CHILD_SA_SESSION, ByIkeSa, IKEV2_CHILD_SA_SESSION_SIGNATURE) +#define IKEV2_CHILD_SA_SESSION_BY_DEL_SA(a) CR (a, IKEV2_CHILD_SA_SESSION, ByDelete, IKEV2_CHILD_SA_SESSION_SIGNATURE) + +#define IS_IKEV2_SA_SESSION(s) ((s)->Common.IkeSessionType == IkeSessionTypeIkeSa) +#define IKEV2_SA_FIRST_PROPOSAL(Sa) (IKEV2_PROPOSAL *)((IKEV2_SA *)(Sa)+1) +#define IKEV2_NEXT_TRANSFORM_WITH_SIZE(Transform,TransformSize) \ + (IKEV2_TRANSFORM *) ((UINT8 *)(Transform) + (TransformSize)) + +#define IKEV2_NEXT_PROPOSAL_WITH_SIZE(Proposal, ProposalSize) \ + (IKEV2_PROPOSAL *) ((UINT8 *)(Proposal) + (ProposalSize)) + +#define IKEV2_PROPOSAL_FIRST_TRANSFORM(Proposal) \ + (IKEV2_TRANSFORM *)((UINT8 *)((IKEV2_PROPOSAL *)(Proposal)+1) + \ + (((IKEV2_PROPOSAL *)(Proposal))->SpiSize)) +#define IKEV2_PROPOSAL_FIRST_TRANSFORM(Proposal) \ + (IKEV2_TRANSFORM *)((UINT8 *)((IKEV2_PROPOSAL *)(Proposal)+1) + \ + (((IKEV2_PROPOSAL *)(Proposal))->SpiSize)) + +typedef enum { + IkeStateInit, + IkeStateAuth, + IkeStateIkeSaEstablished, + IkeStateCreateChild, + IkeStateSaRekeying, + IkeStateChildSaEstablished, + IkeStateSaDeleting, + IkeStateMaximum +} IKEV2_SESSION_STATE; + +typedef enum { + IkeRequestTypeCreateChildSa, + IkeRequestTypeRekeyChildSa, + IkeRequestTypeRekeyIkeSa, + IkeRequestTypeMaximum +} IKEV2_CREATE_CHILD_REQUEST_TYPE; + +typedef struct { + UINT8 *GxBuffer; + UINTN GxSize; + UINT8 *GyBuffer; + UINTN GySize; + UINT8 *GxyBuffer; + UINTN GxySize; + UINT8 *DhContext; +} IKEV2_DH_BUFFER; + +typedef struct { + IKEV2_DH_BUFFER *DhBuffer; + UINT8 *SkdKey; + UINTN SkdKeySize; + UINT8 *SkAiKey; + UINTN SkAiKeySize; + UINT8 *SkArKey; + UINTN SkArKeySize; + UINT8 *SkEiKey; + UINTN SkEiKeySize; + UINT8 *SkErKey; + UINTN SkErKeySize; + UINT8 *SkPiKey; + UINTN SkPiKeySize; + UINT8 *SkPrKey; + UINTN SkPrKeySize; +} IKEV2_SESSION_KEYS; + +typedef struct { + UINT16 LifeType; + UINT64 LifeDuration; + UINT16 EncAlgId; + UINTN EnckeyLen; + UINT16 Prf; + UINT16 IntegAlgId; + UINTN IntegKeyLen; + UINT16 DhGroup; + UINT8 ExtSeq; +} IKEV2_SA_PARAMS; + +// +// Internal Payload +// +typedef struct { + IKEV2_SA SaHeader; + UINTN NumProposals; + // + // IKE_PROPOSAL_DATA Proposals[1]; + // +} IKEV2_SA_DATA; + +typedef struct { + UINT8 ProposalIndex; + UINT8 ProtocolId; + UINT8 *Spi; + UINT8 NumTransforms; + // + // IKE_TRANSFORM_DATA Transforms[1]; + // +} IKEV2_PROPOSAL_DATA; + +typedef struct { + UINT8 TransformIndex; + UINT8 TransformType; + UINT16 TransformId; + IKE_SA_ATTRIBUTE Attribute; +} IKEV2_TRANSFORM_DATA; + +typedef struct { + UINT8 IkeVer; + IKE_SESSION_TYPE IkeSessionType; + BOOLEAN IsInitiator; + BOOLEAN IsOnDeleting; // Flag to indicate whether the SA is on deleting. + IKEV2_SESSION_STATE State; + EFI_EVENT TimeoutEvent; + UINT64 TimeoutInterval; + UINTN RetryCount; + IKE_PACKET *LastSentPacket; + IKEV2_SA_PARAMS *SaParams; + UINT16 PreferDhGroup; + EFI_IP_ADDRESS RemotePeerIp; + EFI_IP_ADDRESS LocalPeerIp; + IKE_ON_PAYLOAD_FROM_NET BeforeDecodePayload; + IKE_ON_PAYLOAD_FROM_NET AfterEncodePayload; + IKE_UDP_SERVICE *UdpService; + IPSEC_PRIVATE_DATA *Private; +} IKEV2_SESSION_COMMON; + +typedef struct { + UINT32 Signature; + IKEV2_SESSION_COMMON SessionCommon; + UINT64 InitiatorCookie; + UINT64 ResponderCookie; + // + // Initiator: SA proposals to be sent + // Responder: SA proposals to be matched + // + IKEV2_SA_DATA *SaData; // SA Private struct used for SA payload generation + IKEV2_SESSION_KEYS *IkeKeys; + UINT8 *NiBlock; + UINTN NiBlkSize; + UINT8 *NrBlock; + UINTN NrBlkSize; + UINT8 *NCookie; // Buffer Contains the Notify Cookie + UINTN NCookieSize; // Size of NCookie + IPSEC_PAD_ENTRY *Pad; + IPSEC_SPD_ENTRY *Spd; // SPD that requested the negotiation, TODO: better use SPD selector + LIST_ENTRY ChildSaSessionList; + LIST_ENTRY ChildSaEstablishSessionList; // For Establish Child SA. + LIST_ENTRY InfoMIDList; // For Information MID + LIST_ENTRY DeleteSaList; // For deteling Child SA. + UINT8 *InitPacket; + UINTN InitPacketSize; + UINT8 *RespPacket; + UINTN RespPacketSize; + UINT32 MessageId; + LIST_ENTRY BySessionTable; // Use for all IkeSaSession Links +} IKEV2_SA_SESSION; + +typedef struct { + UINT32 Signature; + IKEV2_SESSION_COMMON SessionCommon; + IKEV2_SA_SESSION *IkeSaSession; + UINT32 MessageId; + IKEV2_SA_DATA *SaData; + UINT8 IpsecProtocol; + UINT32 LocalPeerSpi; + UINT32 RemotePeerSpi; + UINT8 *NiBlock; + UINTN NiBlkSize; + UINT8 *NrBlock; + UINTN NrBlkSize; + SA_KEYMATS ChildKeymats; + IKEV2_DH_BUFFER *DhBuffer; //New DH exchnaged by CREATE_CHILD_SA + IPSEC_SPD_ENTRY *Spd; + EFI_IPSEC_SPD_SELECTOR *SpdSelector; + UINT16 ProtoId; + UINT16 RemotePort; + UINT16 LocalPort; + LIST_ENTRY ByIkeSa; + LIST_ENTRY ByDelete; +} IKEV2_CHILD_SA_SESSION; + +typedef enum { + Ikev2InfoNotify, + Ikev2InfoDelete, + Ikev2InfoLiveCheck +} IKEV2_INFO_TYPE; + +// +// This struct is used to pass the detail infromation to the InfoGenerator() for +// the response Information Exchange Message creatation. +// +typedef struct { + UINT32 MessageId; + IKEV2_INFO_TYPE InfoType; +} IKEV2_INFO_EXCHANGE_CONTEXT; + +typedef struct { + UINTN DataSize; + UINT8 *Data; +} PRF_DATA_FRAGMENT; + +typedef +IKE_PACKET * +(*IKEV2_PACKET_GENERATOR) ( + IN UINT8 *SaSession, + IN VOID *Context +); + +typedef +EFI_STATUS +(*IKEV2_PACKET_PARSER) ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket +); + +typedef struct { + IKEV2_PACKET_PARSER Parser; + IKEV2_PACKET_GENERATOR Generator; +} IKEV2_PACKET_HANDLER; + +extern IKEV2_PACKET_HANDLER mIkev2Initial[][2]; +extern IKEV2_PACKET_HANDLER mIkev2CreateChild; +extern IKEV2_PACKET_HANDLER mIkev2Info; + +#endif + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Info.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Info.c new file mode 100644 index 0000000000..23e47ceea8 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Info.c @@ -0,0 +1,402 @@ +/** @file + The Implementations for Information Exchange. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Utility.h" +#include "IpSecDebug.h" +#include "IpSecConfigImpl.h" + +/** + Generate Information Packet. + + The information Packet may contain one Delete Payload, or Notify Payload, which + dependes on the Context's parameters. + + @param[in] SaSession Pointer to IKE SA Session or Child SA Session which is + related to the information Exchange. + @param[in] Context The Data passed from the caller. If the Context is not NULL + it should contain the information for Notification Data. + + @retval Pointer of IKE_PACKET generated. + +**/ +IKE_PACKET * +Ikev2InfoGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKE_PACKET *IkePacket; + IKE_PAYLOAD *IkePayload; + IKEV2_INFO_EXCHANGE_CONTEXT *InfoContext; + + InfoContext = NULL; + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + IkePacket = IkePacketAlloc (); + if (IkePacket == NULL) { + return NULL; + } + + // + // Fill IkePacket Header. + // + IkePacket->Header->ExchangeType = IKEV2_EXCHANGE_TYPE_INFO; + IkePacket->Header->Version = (UINT8) (2 << 4); + + if (Context != NULL) { + InfoContext = (IKEV2_INFO_EXCHANGE_CONTEXT *) Context; + } + + // + // For Liveness Check + // + if (InfoContext != NULL && + (InfoContext->InfoType == Ikev2InfoLiveCheck || InfoContext->InfoType == Ikev2InfoNotify) + ) { + IkePacket->Header->MessageId = InfoContext->MessageId; + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_NONE; + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + // + // TODO: add Notify Payload for Notification Information. + // + return IkePacket; + } + + // + // For delete SAs + // + if (IkeSaSession->SessionCommon.IkeSessionType == IkeSessionTypeIkeSa) { + + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + + // + // If the information message is response message,the MessageId should + // be same as the request MessageId which passed through the Context. + // + if (InfoContext != NULL) { + IkePacket->Header->MessageId = InfoContext->MessageId; + } else { + IkePacket->Header->MessageId = IkeSaSession->MessageId; + Ikev2SaSessionIncreaseMessageId (IkeSaSession); + } + // + // If the state is on deleting generate a Delete Payload for it. + // + if (IkeSaSession->SessionCommon.State == IkeStateSaDeleting ) { + IkePayload = Ikev2GenerateDeletePayload ( + IkeSaSession, + IKEV2_PAYLOAD_TYPE_NONE, + 0, + 0, + NULL + ); + if (IkePayload == NULL) { + goto ERROR_EXIT; + } + // + // Fill the next payload in IkePacket's Header. + // + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_DELETE; + IKE_PACKET_APPEND_PAYLOAD (IkePacket, IkePayload); + IkePacket->Private = IkeSaSession->SessionCommon.Private; + IkePacket->Spi = 0; + IkePacket->IsDeleteInfo = TRUE; + + } else if (Context != NULL) { + // + // TODO: If contest is not NULL Generate a Notify Payload. + // + } else { + // + // The input parameter is not correct. + // + goto ERROR_EXIT; + } + } else { + // + // Delete the Child SA Information Exchagne + // + ChildSaSession = (IKEV2_CHILD_SA_SESSION *) SaSession; + IkeSaSession = ChildSaSession->IkeSaSession; + IkePacket->Header->InitiatorCookie = ChildSaSession->IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = ChildSaSession->IkeSaSession->ResponderCookie; + + // + // If the information message is response message,the MessageId should + // be same as the request MessageId which passed through the Context. + // + if (InfoContext != NULL && InfoContext->MessageId != 0) { + IkePacket->Header->MessageId = InfoContext->MessageId; + } else { + IkePacket->Header->MessageId = ChildSaSession->IkeSaSession->MessageId; + Ikev2SaSessionIncreaseMessageId (IkeSaSession); + } + + IkePayload = Ikev2GenerateDeletePayload ( + ChildSaSession->IkeSaSession, + IKEV2_PAYLOAD_TYPE_DELETE, + 4, + 1, + (UINT8 *)&ChildSaSession->LocalPeerSpi + ); + if (IkePayload == NULL) { + goto ERROR_EXIT; + } + // + // Fill the Next Payload in IkePacket's Header. + // + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_DELETE; + IKE_PACKET_APPEND_PAYLOAD (IkePacket, IkePayload); + + IkePacket->Private = IkeSaSession->SessionCommon.Private; + IkePacket->Spi = ChildSaSession->LocalPeerSpi; + IkePacket->IsDeleteInfo = TRUE; + + if (!ChildSaSession->SessionCommon.IsInitiator) { + // + // If responder, use the MessageId fromt the initiator. + // + IkePacket->Header->MessageId = ChildSaSession->MessageId; + } + + // + // Change the IsOnDeleting Flag + // + ChildSaSession->SessionCommon.IsOnDeleting = TRUE; + } + + if (InfoContext == NULL) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT; + } else { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + } + return IkePacket; + +ERROR_EXIT: + if (IkePacket != NULL) { + FreePool (IkePacket); + } + return NULL; + +} + +/** + Parse the Info Exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION. + @param[in] IkePacket Pointer to IkePacket related to the Information Exchange. + + @retval EFI_SUCCESS The operation finised successed. + +**/ +EFI_STATUS +Ikev2InfoParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *DeletePayload; + IKE_PAYLOAD *IkePayload; + IKEV2_DELETE *Delete; + LIST_ENTRY *Entry; + LIST_ENTRY *ListEntry; + UINT8 Index; + UINT32 Spi; + UINT8 *SpiBuffer; + IPSEC_PRIVATE_DATA *Private; + UINT8 Value; + EFI_STATUS Status; + IKE_PACKET *RespondPacket; + + IKEV2_INFO_EXCHANGE_CONTEXT Context; + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + + DeletePayload = NULL; + Private = NULL; + RespondPacket = NULL; + Status = EFI_SUCCESS; + + // + // For Liveness Check + // + if (IkePacket->Header->NextPayload == IKEV2_PAYLOAD_TYPE_NONE && + (IkePacket->PayloadTotalSize == 0) + ) { + if (IkePacket->Header->Flags == IKE_HEADER_FLAGS_INIT) { + // + // If it is Liveness check request, reply it. + // + Context.InfoType = Ikev2InfoLiveCheck; + Context.MessageId = IkePacket->Header->MessageId; + RespondPacket = Ikev2InfoGenerator ((UINT8 *)IkeSaSession, &Context); + + if (RespondPacket == NULL) { + Status = EFI_INVALID_PARAMETER; + return Status; + } + Status = Ikev2SendIkePacket ( + IkeSaSession->SessionCommon.UdpService, + (UINT8 *)(&IkeSaSession->SessionCommon), + RespondPacket, + 0 + ); + + } else { + // + // Todo: verify the liveness check response packet. + // + } + return Status; + } + + // + // For SA Delete + // + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + + // + // Iterate payloads to find the Delete/Notify Payload. + // + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_DELETE) { + DeletePayload = IkePayload; + Delete = (IKEV2_DELETE *)DeletePayload->PayloadBuf; + + if (Delete->SpiSize == 0) { + // + // Delete IKE SA. + // + if (IkeSaSession->SessionCommon.State == IkeStateSaDeleting) { + RemoveEntryList (&IkeSaSession->BySessionTable); + Ikev2SaSessionFree (IkeSaSession); + // + // Checking the Private status. + // + // + // when all IKE SAs were disabled by calling "IPsecConfig -disable", the IPsec + // status should be changed. + // + Private = IkeSaSession->SessionCommon.Private; + if (Private != NULL && Private->IsIPsecDisabling) { + // + // After all IKE SAs were deleted, set the IPSEC_STATUS_DISABLED value in + // IPsec status variable. + // + if (IsListEmpty (&Private->Ikev1EstablishedList) && + (IsListEmpty (&Private->Ikev2EstablishedList)) + ) { + Value = IPSEC_STATUS_DISABLED; + Status = gRT->SetVariable ( + IPSECCONFIG_STATUS_NAME, + &gEfiIpSecConfigProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (Value), + &Value + ); + if (!EFI_ERROR (Status)) { + // + // Set the DisabledFlag in Private data. + // + Private->IpSec.DisabledFlag = TRUE; + Private->IsIPsecDisabling = FALSE; + } + } + } + } else { + IkeSaSession->SessionCommon.State = IkeStateSaDeleting; + Context.InfoType = Ikev2InfoDelete; + Context.MessageId = IkePacket->Header->MessageId; + + RespondPacket = Ikev2InfoGenerator ((UINT8 *)IkeSaSession, &Context); + if (RespondPacket == NULL) { + Status = EFI_INVALID_PARAMETER; + return Status; + } + Status = Ikev2SendIkePacket ( + IkeSaSession->SessionCommon.UdpService, + (UINT8 *)(&IkeSaSession->SessionCommon), + RespondPacket, + 0 + ); + } + } else if (Delete->SpiSize == 4) { + // + // Move the Child SAs to DeleteList + // + SpiBuffer = (UINT8 *)(Delete + 1); + for (Index = 0; Index < Delete->NumSpis; Index++) { + Spi = ReadUnaligned32 ((UINT32 *)SpiBuffer); + for (ListEntry = IkeSaSession->ChildSaEstablishSessionList.ForwardLink; + ListEntry != &IkeSaSession->ChildSaEstablishSessionList; + ) { + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (ListEntry); + ListEntry = ListEntry->ForwardLink; + + if (ChildSaSession->RemotePeerSpi == HTONL(Spi)) { + if (ChildSaSession->SessionCommon.State != IkeStateSaDeleting) { + + // + // Insert the ChildSa Session into Delete List. + // + InsertTailList (&IkeSaSession->DeleteSaList, &ChildSaSession->ByDelete); + ChildSaSession->SessionCommon.State = IkeStateSaDeleting; + ChildSaSession->SessionCommon.IsInitiator = FALSE; + ChildSaSession->MessageId = IkePacket->Header->MessageId; + + Context.InfoType = Ikev2InfoDelete; + Context.MessageId = IkePacket->Header->MessageId; + + RespondPacket = Ikev2InfoGenerator ((UINT8 *)ChildSaSession, &Context); + if (RespondPacket == NULL) { + Status = EFI_INVALID_PARAMETER; + return Status; + } + Status = Ikev2SendIkePacket ( + ChildSaSession->SessionCommon.UdpService, + (UINT8 *)(&ChildSaSession->SessionCommon), + RespondPacket, + 0 + ); + } else { + // + // Delete the Child SA. + // + Ikev2ChildSaSilentDelete (IkeSaSession, Spi); + RemoveEntryList (&ChildSaSession->ByDelete); + } + } + } + SpiBuffer = SpiBuffer + sizeof (Spi); + } + } + } + } + + return Status; +} + +GLOBAL_REMOVE_IF_UNREFERENCED IKEV2_PACKET_HANDLER mIkev2Info = { + Ikev2InfoParser, + Ikev2InfoGenerator +}; diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c new file mode 100644 index 0000000000..675ecf6f74 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c @@ -0,0 +1,3369 @@ +/** @file + The implementation of Payloads Creation. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Utility.h" +#include "IpSecDebug.h" +#include "IpSecConfigImpl.h" +#include "IpSecCryptIo.h" + +// +// The Constant String of "Key Pad for IKEv2" for Authentication Payload generation. +// +#define CONSTANT_KEY_SIZE 17 +GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 mConstantKey[CONSTANT_KEY_SIZE] = +{ + 'K', 'e', 'y', ' ', 'P', 'a', 'd', ' ', 'f', 'o', 'r', ' ', 'I', 'K', 'E', 'v', '2' +}; + +/** + Generate Ikev2 SA payload according to SessionSaData + + @param[in] SessionSaData The data used in SA payload. + @param[in] NextPayload The payload type presented in NextPayload field of + SA Payload header. + @param[in] Type The SA type. It MUST be neither (1) for IKE_SA or + (2) for CHILD_SA or (3) for INFO. + + @retval a Pointer to SA IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateSaPayload ( + IN IKEV2_SA_DATA *SessionSaData, + IN UINT8 NextPayload, + IN IKE_SESSION_TYPE Type + ) +{ + IKE_PAYLOAD *SaPayload; + IKEV2_SA_DATA *SaData; + UINTN SaDataSize; + + SaPayload = IkePayloadAlloc (); + if (SaPayload == NULL) { + return NULL; + } + + // + // TODO: Get the Proposal Number and Transform Number from IPsec Config, + // after the Ipsecconfig Application is support it. + // + + if (Type == IkeSessionTypeIkeSa) { + SaDataSize = sizeof (IKEV2_SA_DATA) + + SessionSaData->NumProposals * sizeof (IKEV2_PROPOSAL_DATA) + + sizeof (IKEV2_TRANSFORM_DATA) * SessionSaData->NumProposals * 4; + } else { + SaDataSize = sizeof (IKEV2_SA_DATA) + + SessionSaData->NumProposals * sizeof (IKEV2_PROPOSAL_DATA) + + sizeof (IKEV2_TRANSFORM_DATA) * SessionSaData->NumProposals * 3; + + } + + SaData = AllocateZeroPool (SaDataSize); + if (SaData == NULL) { + IkePayloadFree (SaPayload); + return NULL; + } + + CopyMem (SaData, SessionSaData, SaDataSize); + SaData->SaHeader.Header.NextPayload = NextPayload; + SaPayload->PayloadType = IKEV2_PAYLOAD_TYPE_SA; + SaPayload->PayloadBuf = (UINT8 *) SaData; + + return SaPayload; +} + +/** + Generate a Nonce payload containing the input parameter NonceBuf. + + @param[in] NonceBuf The nonce buffer contains the whole Nonce payload block + except the payload header. + @param[in] NonceSize The buffer size of the NonceBuf + @param[in] NextPayload The payload type presented in the NextPayload field + of Nonce Payload header. + + @retval Pointer to Nonce IKE paload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateNoncePayload ( + IN UINT8 *NonceBuf, + IN UINTN NonceSize, + IN UINT8 NextPayload + ) +{ + IKE_PAYLOAD *NoncePayload; + IKEV2_NONCE *Nonce; + UINTN Size; + UINT8 *NonceBlock; + + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Nonce Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + Size = sizeof (IKEV2_NONCE) + NonceSize; + NonceBlock = NonceBuf; + + Nonce = AllocateZeroPool (Size); + if (Nonce == NULL) { + return NULL; + } + + CopyMem (Nonce + 1, NonceBlock, Size - sizeof (IKEV2_NONCE)); + + Nonce->Header.NextPayload = NextPayload; + Nonce->Header.PayloadLength = (UINT16) Size; + NoncePayload = IkePayloadAlloc (); + if (NoncePayload == NULL) { + FreePool (Nonce); + return NULL; + } + + NoncePayload->PayloadType = IKEV2_PAYLOAD_TYPE_NONCE; + NoncePayload->PayloadBuf = (UINT8 *) Nonce; + NoncePayload->PayloadSize = Size; + + return NoncePayload; +} + +/** + Generate a Key Exchange payload according to the DH group type and save the + public Key into IkeSaSession IkeKey field. + + @param[in, out] IkeSaSession Pointer of the IKE_SA_SESSION. + @param[in] NextPayload The payload type presented in the NextPayload field of Key + Exchange Payload header. + + @retval Pointer to Key IKE payload. + +**/ +IKE_PAYLOAD* +Ikev2GenerateKePayload ( + IN OUT IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload + ) +{ + IKE_PAYLOAD *KePayload; + IKEV2_KEY_EXCHANGE *Ke; + UINTN KeSize; + IKEV2_SESSION_KEYS *IkeKeys; + + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! DH Group # ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Key Exchange Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + IkeKeys = IkeSaSession->IkeKeys; + + if (IkeSaSession->SessionCommon.IsInitiator) { + KeSize = sizeof (IKEV2_KEY_EXCHANGE) + IkeKeys->DhBuffer->GxSize; + } else { + KeSize = sizeof (IKEV2_KEY_EXCHANGE) + IkeKeys->DhBuffer->GxSize; + } + + // + // Allocate buffer for Key Exchange + // + Ke = AllocateZeroPool (KeSize); + if (Ke == NULL) { + return NULL; + } + + Ke->Header.NextPayload = NextPayload; + Ke->Header.PayloadLength = (UINT16) KeSize; + Ke->DhGroup = IkeSaSession->SessionCommon.PreferDhGroup; + + CopyMem (Ke + 1, IkeKeys->DhBuffer->GxBuffer, IkeKeys->DhBuffer->GxSize); + + // + // Create IKE_PAYLOAD to point to Key Exchange payload + // + KePayload = IkePayloadAlloc (); + if (KePayload == NULL) { + FreePool (Ke); + return NULL; + } + + KePayload->PayloadType = IKEV2_PAYLOAD_TYPE_KE; + KePayload->PayloadBuf = (UINT8 *) Ke; + KePayload->PayloadSize = KeSize; + return KePayload; +} + +/** + Generate a ID payload. + + @param[in] CommonSession Pointer to IKEV2_SESSION_COMMON related to ID payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + + @retval Pointer to ID IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateIdPayload ( + IN IKEV2_SESSION_COMMON *CommonSession, + IN UINT8 NextPayload + ) +{ + IKE_PAYLOAD *IdPayload; + IKEV2_ID *Id; + UINTN IdSize; + UINT8 IpVersion; + UINT8 AddrSize; + + // + // ID payload + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload ! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ID Type ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Identification Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + IpVersion = CommonSession->UdpService->IpVersion; + AddrSize = (UINT8) ((IpVersion == IP_VERSION_4) ? sizeof(EFI_IPv4_ADDRESS) : sizeof(EFI_IPv6_ADDRESS)); + IdSize = sizeof (IKEV2_ID) + AddrSize; + + Id = (IKEV2_ID *) AllocateZeroPool (IdSize); + if (Id == NULL) { + return NULL; + } + + IdPayload = IkePayloadAlloc (); + if (IdPayload == NULL) { + FreePool (Id); + return NULL; + } + + IdPayload->PayloadType = (UINT8) ((CommonSession->IsInitiator) ? IKEV2_PAYLOAD_TYPE_ID_INIT : IKEV2_PAYLOAD_TYPE_ID_RSP); + IdPayload->PayloadBuf = (UINT8 *) Id; + IdPayload->PayloadSize = IdSize; + + // + // Set generic header of identification payload + // + Id->Header.NextPayload = NextPayload; + Id->Header.PayloadLength = (UINT16) IdSize; + Id->IdType = (UINT8) ((IpVersion == IP_VERSION_4) ? IKEV2_ID_TYPE_IPV4_ADDR : IKEV2_ID_TYPE_IPV6_ADDR); + CopyMem (Id + 1, &CommonSession->LocalPeerIp, AddrSize); + + return IdPayload; +} + +/** + Generate a ID payload. + + @param[in] CommonSession Pointer to IKEV2_SESSION_COMMON related to ID payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + @param[in] InCert Pointer to the Certificate which distinguished name + will be added into the Id payload. + @param[in] CertSize Size of the Certificate. + + @retval Pointer to ID IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCertIdPayload ( + IN IKEV2_SESSION_COMMON *CommonSession, + IN UINT8 NextPayload, + IN UINT8 *InCert, + IN UINTN CertSize + ) +{ + IKE_PAYLOAD *IdPayload; + IKEV2_ID *Id; + UINTN IdSize; + UINTN SubjectSize; + UINT8 *CertSubject; + + // + // ID payload + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload ! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ID Type ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Identification Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + SubjectSize = 0; + CertSubject = NULL; + IpSecCryptoIoGetSubjectFromCert ( + InCert, + CertSize, + &CertSubject, + &SubjectSize + ); + if (SubjectSize != 0) { + ASSERT (CertSubject != NULL); + } + + IdSize = sizeof (IKEV2_ID) + SubjectSize; + + Id = (IKEV2_ID *) AllocateZeroPool (IdSize); + if (Id == NULL) { + return NULL; + } + + IdPayload = IkePayloadAlloc (); + if (IdPayload == NULL) { + FreePool (Id); + return NULL; + } + + IdPayload->PayloadType = (UINT8) ((CommonSession->IsInitiator) ? IKEV2_PAYLOAD_TYPE_ID_INIT : IKEV2_PAYLOAD_TYPE_ID_RSP); + IdPayload->PayloadBuf = (UINT8 *) Id; + IdPayload->PayloadSize = IdSize; + + // + // Set generic header of identification payload + // + Id->Header.NextPayload = NextPayload; + Id->Header.PayloadLength = (UINT16) IdSize; + Id->IdType = 9; + CopyMem (Id + 1, CertSubject, SubjectSize); + + if (CertSubject != NULL) { + FreePool (CertSubject); + } + return IdPayload; +} + +/** + Generate a Authentication Payload. + + This function is used for both Authentication generation and verification. When the + IsVerify is TRUE, it create a Auth Data for verification. This function choose the + related IKE_SA_INIT Message for Auth data creation according to the IKE Session's type + and the value of IsVerify parameter. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to. + @param[in] IdPayload Pointer to the ID payload to be used for Authentication + payload generation. + @param[in] NextPayload The type filled into the Authentication Payload next + payload field. + @param[in] IsVerify If it is TURE, the Authentication payload is used for + verification. + + @return pointer to IKE Authentication payload for Pre-shared key method. + +**/ +IKE_PAYLOAD * +Ikev2PskGenerateAuthPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *IdPayload, + IN UINT8 NextPayload, + IN BOOLEAN IsVerify + ) +{ + UINT8 *Digest; + UINTN DigestSize; + PRF_DATA_FRAGMENT Fragments[3]; + UINT8 *KeyBuf; + UINTN KeySize; + IKE_PAYLOAD *AuthPayload; + IKEV2_AUTH *PayloadBuf; + EFI_STATUS Status; + + // + // Auth = Prf(Prf(Secret,"Key Pad for IKEv2),IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Auth Method ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Authentication Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + KeyBuf = NULL; + AuthPayload = NULL; + Digest = NULL; + + DigestSize = IpSecGetHmacDigestLength ((UINT8)IkeSaSession->SessionCommon.SaParams->Prf); + Digest = AllocateZeroPool (DigestSize); + if (Digest == NULL) { + return NULL; + } + + if (IdPayload == NULL) { + return NULL; + } + + // + // Calcualte Prf(Seceret, "Key Pad for IKEv2"); + // + Fragments[0].Data = (UINT8 *) mConstantKey; + Fragments[0].DataSize = CONSTANT_KEY_SIZE; + + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->Pad->Data->AuthData, + IkeSaSession->Pad->Data->AuthDataSize, + (HASH_DATA_FRAGMENT *)Fragments, + 1, + Digest, + DigestSize + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Store the AuthKey into KeyBuf + // + KeyBuf = AllocateZeroPool (DigestSize); + if (KeyBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + CopyMem (KeyBuf, Digest, DigestSize); + KeySize = DigestSize; + + // + // Calculate Prf(SK_Pi/r, IDi/r) + // + Fragments[0].Data = IdPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + Fragments[0].DataSize = IdPayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + + if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) || + (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify) + ) { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->IkeKeys->SkPrKey, + IkeSaSession->IkeKeys->SkPrKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + } else { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->IkeKeys->SkPiKey, + IkeSaSession->IkeKeys->SkPiKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + } + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Copy data to Fragments. + // + if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) || + (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify) + ) { + Fragments[0].Data = IkeSaSession->RespPacket; + Fragments[0].DataSize = IkeSaSession->RespPacketSize; + Fragments[1].Data = IkeSaSession->NiBlock; + Fragments[1].DataSize = IkeSaSession->NiBlkSize; + } else { + Fragments[0].Data = IkeSaSession->InitPacket; + Fragments[0].DataSize = IkeSaSession->InitPacketSize; + Fragments[1].Data = IkeSaSession->NrBlock; + Fragments[1].DataSize = IkeSaSession->NrBlkSize; + } + + // + // Copy the result of Prf(SK_Pr, IDi/r) to Fragments[2]. + // + Fragments[2].Data = AllocateZeroPool (DigestSize); + if (Fragments[2].Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + Fragments[2].DataSize = DigestSize; + CopyMem (Fragments[2].Data, Digest, DigestSize); + + // + // Calculate Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) + // + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + KeyBuf, + KeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 3, + Digest, + DigestSize + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Allocate buffer for Auth Payload + // + AuthPayload = IkePayloadAlloc (); + if (AuthPayload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + AuthPayload->PayloadSize = sizeof (IKEV2_AUTH) + DigestSize; + PayloadBuf = (IKEV2_AUTH *) AllocateZeroPool (AuthPayload->PayloadSize); + if (PayloadBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Fill in Auth payload. + // + PayloadBuf->Header.NextPayload = NextPayload; + PayloadBuf->Header.PayloadLength = (UINT16) (AuthPayload->PayloadSize); + if (IkeSaSession->Pad->Data->AuthMethod == EfiIPsecAuthMethodPreSharedSecret) { + // + // Only support Shared Key Message Integrity + // + PayloadBuf->AuthMethod = IKEV2_AUTH_METHOD_SKMI; + } else { + // + // Not support other Auth method. + // + Status = EFI_UNSUPPORTED; + goto EXIT; + } + + // + // Copy the result of Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) to Auth + // payload block. + // + CopyMem ( + PayloadBuf + 1, + Digest, + DigestSize + ); + + // + // Fill in IKE_PACKET + // + AuthPayload->PayloadBuf = (UINT8 *) PayloadBuf; + AuthPayload->PayloadType = IKEV2_PAYLOAD_TYPE_AUTH; + +EXIT: + if (KeyBuf != NULL) { + FreePool (KeyBuf); + } + if (Digest != NULL) { + FreePool (Digest); + } + if (Fragments[2].Data != NULL) { + // + // Free the buffer which contains the result of Prf(SK_Pr, IDi/r) + // + FreePool (Fragments[2].Data); + } + + if (EFI_ERROR (Status)) { + if (AuthPayload != NULL) { + IkePayloadFree (AuthPayload); + } + return NULL; + } else { + return AuthPayload; + } +} + +/** + Generate a Authentication Payload for Certificate Auth method. + + This function has two functions. One is creating a local Authentication + Payload for sending and other is creating the remote Authentication data + for verification when the IsVerify is TURE. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to. + @param[in] IdPayload Pointer to the ID payload to be used for Authentication + payload generation. + @param[in] NextPayload The type filled into the Authentication Payload + next payload field. + @param[in] IsVerify If it is TURE, the Authentication payload is used + for verification. + @param[in] UefiPrivateKey Pointer to the UEFI private key. Ignore it when + verify the authenticate payload. + @param[in] UefiPrivateKeyLen The size of UefiPrivateKey in bytes. Ignore it + when verify the authenticate payload. + @param[in] UefiKeyPwd Pointer to the password of UEFI private key. + Ignore it when verify the authenticate payload. + @param[in] UefiKeyPwdLen The size of UefiKeyPwd in bytes.Ignore it when + verify the authenticate payload. + + @return pointer to IKE Authentication payload for Cerifitcation method. + +**/ +IKE_PAYLOAD * +Ikev2CertGenerateAuthPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *IdPayload, + IN UINT8 NextPayload, + IN BOOLEAN IsVerify, + IN UINT8 *UefiPrivateKey, + IN UINTN UefiPrivateKeyLen, + IN UINT8 *UefiKeyPwd, + IN UINTN UefiKeyPwdLen + ) +{ + UINT8 *Digest; + UINTN DigestSize; + PRF_DATA_FRAGMENT Fragments[3]; + UINT8 *KeyBuf; + IKE_PAYLOAD *AuthPayload; + IKEV2_AUTH *PayloadBuf; + EFI_STATUS Status; + UINT8 *Signature; + UINTN SigSize; + + // + // Auth = Prf(Scert,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Auth Method ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Authentication Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // + // Initial point + // + KeyBuf = NULL; + AuthPayload = NULL; + Digest = NULL; + Signature = NULL; + SigSize = 0; + + if (IdPayload == NULL) { + return NULL; + } + DigestSize = IpSecGetHmacDigestLength ((UINT8)IkeSaSession->SessionCommon.SaParams->Prf); + Digest = AllocateZeroPool (DigestSize); + if (Digest == NULL) { + return NULL; + } + + // + // Store the AuthKey into KeyBuf + // + KeyBuf = AllocateZeroPool (DigestSize); + if (KeyBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + CopyMem (KeyBuf, Digest, DigestSize); + + // + // Calculate Prf(SK_Pi/r, IDi/r) + // + Fragments[0].Data = IdPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + Fragments[0].DataSize = IdPayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + + IpSecDumpBuf ("RestofIDPayload", Fragments[0].Data, Fragments[0].DataSize); + + if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) || + (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify) + ) { + Status = IpSecCryptoIoHmac( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->IkeKeys->SkPrKey, + IkeSaSession->IkeKeys->SkPrKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + IpSecDumpBuf ("MACedIDForR", Digest, DigestSize); + } else { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + IkeSaSession->IkeKeys->SkPiKey, + IkeSaSession->IkeKeys->SkPiKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + IpSecDumpBuf ("MACedIDForI", Digest, DigestSize); + } + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Copy data to Fragments. + // + if ((IkeSaSession->SessionCommon.IsInitiator && IsVerify) || + (!IkeSaSession->SessionCommon.IsInitiator && !IsVerify) + ) { + Fragments[0].Data = IkeSaSession->RespPacket; + Fragments[0].DataSize = IkeSaSession->RespPacketSize; + Fragments[1].Data = IkeSaSession->NiBlock; + Fragments[1].DataSize = IkeSaSession->NiBlkSize; + IpSecDumpBuf ("RealMessage2", Fragments[0].Data, Fragments[0].DataSize); + IpSecDumpBuf ("NonceIDdata", Fragments[1].Data, Fragments[1].DataSize); + } else { + Fragments[0].Data = IkeSaSession->InitPacket; + Fragments[0].DataSize = IkeSaSession->InitPacketSize; + Fragments[1].Data = IkeSaSession->NrBlock; + Fragments[1].DataSize = IkeSaSession->NrBlkSize; + IpSecDumpBuf ("RealMessage1", Fragments[0].Data, Fragments[0].DataSize); + IpSecDumpBuf ("NonceRDdata", Fragments[1].Data, Fragments[1].DataSize); + } + + // + // Copy the result of Prf(SK_Pr, IDi/r) to Fragments[2]. + // + Fragments[2].Data = AllocateZeroPool (DigestSize); + if (Fragments[2].Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + Fragments[2].DataSize = DigestSize; + CopyMem (Fragments[2].Data, Digest, DigestSize); + + // + // Calculate Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) + // + Status = IpSecCryptoIoHash ( + (UINT8)IkeSaSession->SessionCommon.SaParams->Prf, + (HASH_DATA_FRAGMENT *) Fragments, + 3, + Digest, + DigestSize + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + IpSecDumpBuf ("HashSignedOctects", Digest, DigestSize); + // + // Sign the data by the private Key + // + if (!IsVerify) { + IpSecCryptoIoAuthDataWithCertificate ( + Digest, + DigestSize, + UefiPrivateKey, + UefiPrivateKeyLen, + UefiKeyPwd, + UefiKeyPwdLen, + &Signature, + &SigSize + ); + + if (SigSize == 0 || Signature == NULL) { + goto EXIT; + } + } + + // + // Allocate buffer for Auth Payload + // + AuthPayload = IkePayloadAlloc (); + if (AuthPayload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + if (!IsVerify) { + AuthPayload->PayloadSize = sizeof (IKEV2_AUTH) + SigSize; + } else { + AuthPayload->PayloadSize = sizeof (IKEV2_AUTH) + DigestSize; + } + + PayloadBuf = (IKEV2_AUTH *) AllocateZeroPool (AuthPayload->PayloadSize); + if (PayloadBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Fill in Auth payload. + // + PayloadBuf->Header.NextPayload = NextPayload; + PayloadBuf->Header.PayloadLength = (UINT16) (AuthPayload->PayloadSize); + if (IkeSaSession->Pad->Data->AuthMethod == EfiIPsecAuthMethodCertificates) { + PayloadBuf->AuthMethod = IKEV2_AUTH_METHOD_RSA; + } else { + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + + // + // Copy the result of Prf(Key,IKE_SA_INIi/r|Ni/r|Prf(SK_Pr, IDi/r)) to Auth + // payload block. + // + if (!IsVerify) { + CopyMem (PayloadBuf + 1, Signature, SigSize); + } else { + CopyMem (PayloadBuf + 1, Digest, DigestSize); + } + + // + // Fill in IKE_PACKET + // + AuthPayload->PayloadBuf = (UINT8 *) PayloadBuf; + AuthPayload->PayloadType = IKEV2_PAYLOAD_TYPE_AUTH; + +EXIT: + if (KeyBuf != NULL) { + FreePool (KeyBuf); + } + if (Digest != NULL) { + FreePool (Digest); + } + if (Signature != NULL) { + FreePool (Signature); + } + if (Fragments[2].Data != NULL) { + // + // Free the buffer which contains the result of Prf(SK_Pr, IDi/r) + // + FreePool (Fragments[2].Data); + } + + if (EFI_ERROR (Status)) { + if (AuthPayload != NULL) { + IkePayloadFree (AuthPayload); + } + return NULL; + } else { + return AuthPayload; + } +} + +/** + Generate TS payload. + + This function generates TSi or TSr payload according to type of next payload. + If the next payload is Responder TS, gereate TSi Payload. Otherwise, generate + TSr payload. + + @param[in] ChildSa Pointer to IKEV2_CHILD_SA_SESSION related to this TS payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + @param[in] IsTunnel It indicates that if the Ts Payload is after the CP payload. + If yes, it means the Tsi and Tsr payload should be with + Max port range and address range and protocol is marked + as zero. + + @retval Pointer to Ts IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateTsPayload ( + IN IKEV2_CHILD_SA_SESSION *ChildSa, + IN UINT8 NextPayload, + IN BOOLEAN IsTunnel + ) +{ + IKE_PAYLOAD *TsPayload; + IKEV2_TS *TsPayloadBuf; + TRAFFIC_SELECTOR *TsSelector; + UINTN SelectorSize; + UINTN TsPayloadSize; + UINT8 IpVersion; + UINT8 AddrSize; + + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Number of TSs ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + TsPayload = IkePayloadAlloc(); + if (TsPayload == NULL) { + return NULL; + } + + IpVersion = ChildSa->SessionCommon.UdpService->IpVersion; + // + // The Starting Address and Ending Address is variable length depends on + // is IPv4 or IPv6 + // + AddrSize = (UINT8)((IpVersion == IP_VERSION_4) ? sizeof (EFI_IPv4_ADDRESS) : sizeof (EFI_IPv6_ADDRESS)); + SelectorSize = sizeof (TRAFFIC_SELECTOR) + 2 * AddrSize; + TsPayloadSize = sizeof (IKEV2_TS) + SelectorSize; + TsPayloadBuf = AllocateZeroPool (TsPayloadSize); + if (TsPayloadBuf == NULL) { + goto ON_ERROR; + } + + TsPayload->PayloadBuf = (UINT8 *) TsPayloadBuf; + TsSelector = (TRAFFIC_SELECTOR*)(TsPayloadBuf + 1); + + TsSelector->TSType = (UINT8)((IpVersion == IP_VERSION_4) ? IKEV2_TS_TYPE_IPV4_ADDR_RANGE : IKEV2_TS_TYPS_IPV6_ADDR_RANGE); + + // + // For tunnel mode + // + if (IsTunnel) { + TsSelector->IpProtocolId = IKEV2_TS_ANY_PROTOCOL; + TsSelector->SelecorLen = (UINT16) SelectorSize; + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + ZeroMem ((UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR), AddrSize); + SetMem ((UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR) + AddrSize, AddrSize, 0xff); + + } else { + // + // TODO: Support port range and address range + // + if (NextPayload == IKEV2_PAYLOAD_TYPE_TS_RSP){ + // + // Create initiator Traffic Selector + // + TsSelector->SelecorLen = (UINT16)SelectorSize; + + // + // Currently only support the port range from 0~0xffff. Don't support other + // port range. + // TODO: support Port range + // + if (ChildSa->SessionCommon.IsInitiator) { + if (ChildSa->Spd->Selector->LocalPort != 0 && + ChildSa->Spd->Selector->LocalPortRange == 0) { + // + // For not port range. + // + TsSelector->StartPort = ChildSa->Spd->Selector->LocalPort; + TsSelector->EndPort = ChildSa->Spd->Selector->LocalPort; + } else if (ChildSa->Spd->Selector->LocalPort == 0){ + // + // For port from 0~0xffff + // + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + } else { + // + // Not support now. + // + goto ON_ERROR; + } + } else { + if (ChildSa->Spd->Selector->RemotePort != 0 && + ChildSa->Spd->Selector->RemotePortRange == 0) { + // + // For not port range. + // + TsSelector->StartPort = ChildSa->Spd->Selector->RemotePort; + TsSelector->EndPort = ChildSa->Spd->Selector->RemotePort; + } else if (ChildSa->Spd->Selector->RemotePort == 0) { + // + // For port from 0~0xffff + // + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + } else { + // + // Not support now. + // + goto ON_ERROR; + } + } + // + // Copy Address.Currently the address range is not supported. + // The Starting address is same as Ending address + // TODO: Support Address Range. + // + CopyMem ( + (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR), + ChildSa->SessionCommon.IsInitiator ? + ChildSa->Spd->Selector->LocalAddress : + ChildSa->Spd->Selector->RemoteAddress, + AddrSize + ); + CopyMem ( + (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR) + AddrSize, + ChildSa->SessionCommon.IsInitiator ? + ChildSa->Spd->Selector->LocalAddress : + ChildSa->Spd->Selector->RemoteAddress, + AddrSize + ); + // + // If the Next Payload is not TS responder, this TS payload type is the TS responder. + // + TsPayload->PayloadType = IKEV2_PAYLOAD_TYPE_TS_INIT; + }else{ + // + // Create responder Traffic Selector + // + TsSelector->SelecorLen = (UINT16)SelectorSize; + + // + // Currently only support the port range from 0~0xffff. Don't support other + // port range. + // TODO: support Port range + // + if (!ChildSa->SessionCommon.IsInitiator) { + if (ChildSa->Spd->Selector->LocalPort != 0 && + ChildSa->Spd->Selector->LocalPortRange == 0) { + // + // For not port range. + // + TsSelector->StartPort = ChildSa->Spd->Selector->LocalPort; + TsSelector->EndPort = ChildSa->Spd->Selector->LocalPort; + } else if (ChildSa->Spd->Selector->LocalPort == 0){ + // + // For port from 0~0xffff + // + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + } else { + // + // Not support now. + // + goto ON_ERROR; + } + } else { + if (ChildSa->Spd->Selector->RemotePort != 0 && + ChildSa->Spd->Selector->RemotePortRange == 0) { + // + // For not port range. + // + TsSelector->StartPort = ChildSa->Spd->Selector->RemotePort; + TsSelector->EndPort = ChildSa->Spd->Selector->RemotePort; + } else if (ChildSa->Spd->Selector->RemotePort == 0){ + // + // For port from 0~0xffff + // + TsSelector->StartPort = 0; + TsSelector->EndPort = IKEV2_TS_ANY_PORT; + } else { + // + // Not support now. + // + goto ON_ERROR; + } + } + // + // Copy Address.Currently the address range is not supported. + // The Starting address is same as Ending address + // TODO: Support Address Range. + // + CopyMem ( + (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR), + ChildSa->SessionCommon.IsInitiator ? + ChildSa->Spd->Selector->RemoteAddress : + ChildSa->Spd->Selector->LocalAddress, + AddrSize + ); + CopyMem ( + (UINT8*)TsSelector + sizeof(TRAFFIC_SELECTOR) + AddrSize, + ChildSa->SessionCommon.IsInitiator ? + ChildSa->Spd->Selector->RemoteAddress : + ChildSa->Spd->Selector->LocalAddress, + AddrSize + ); + // + // If the Next Payload is not TS responder, this TS payload type is the TS responder. + // + TsPayload->PayloadType = IKEV2_PAYLOAD_TYPE_TS_RSP; + } + } + + if (ChildSa->Spd->Selector->NextLayerProtocol != 0xffff) { + TsSelector->IpProtocolId = (UINT8)ChildSa->Spd->Selector->NextLayerProtocol; + } else { + TsSelector->IpProtocolId = IKEV2_TS_ANY_PROTOCOL; + } + + TsPayloadBuf->Header.NextPayload = NextPayload; + TsPayloadBuf->Header.PayloadLength = (UINT16)TsPayloadSize; + TsPayloadBuf->TSNumbers = 1; + TsPayload->PayloadSize = TsPayloadSize; + goto ON_EXIT; + +ON_ERROR: + if (TsPayload != NULL) { + IkePayloadFree (TsPayload); + TsPayload = NULL; + } +ON_EXIT: + return TsPayload; +} + +/** + Generate the Notify payload. + + Since the structure of Notify payload which defined in RFC 4306 is simple, so + there is no internal data structure for Notify payload. This function generate + Notify payload defined in RFC 4306, but all the fields in this payload are still + in host order and need call Ikev2EncodePayload() to convert those fields from + the host order to network order beforing sending it. + + @param[in] ProtocolId The protocol type ID. For IKE_SA it MUST be one (1). + For IPsec SAs it MUST be neither (2) for AH or (3) + for ESP. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Notify payload. + @param[in] SpiSize Size of the SPI in SPI size field of the Notify Payload. + @param[in] MessageType The message type in NotifyMessageType field of the + Notify Payload. + @param[in] SpiBuf Pointer to buffer contains the SPI value. + @param[in] NotifyData Pointer to buffer contains the notification data. + @param[in] NotifyDataSize The size of NotifyData in bytes. + + + @retval Pointer to IKE Notify Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateNotifyPayload ( + IN UINT8 ProtocolId, + IN UINT8 NextPayload, + IN UINT8 SpiSize, + IN UINT16 MessageType, + IN UINT8 *SpiBuf, + IN UINT8 *NotifyData, + IN UINTN NotifyDataSize + ) +{ + IKE_PAYLOAD *NotifyPayload; + IKEV2_NOTIFY *Notify; + UINT16 NotifyPayloadLen; + UINT8 *MessageData; + + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Protocol ID ! SPI Size ! Notify Message Type ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Security Parameter Index (SPI) ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Notification Data ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // + NotifyPayloadLen = (UINT16) (sizeof (IKEV2_NOTIFY) + NotifyDataSize + SpiSize); + Notify = (IKEV2_NOTIFY *) AllocateZeroPool (NotifyPayloadLen); + if (Notify == NULL) { + return NULL; + } + + // + // Set Delete Payload's Generic Header + // + Notify->Header.NextPayload = NextPayload; + Notify->Header.PayloadLength = NotifyPayloadLen; + Notify->SpiSize = SpiSize; + Notify->ProtocolId = ProtocolId; + Notify->MessageType = MessageType; + + // + // Copy Spi , for Cookie Notify, there is no SPI. + // + if (SpiBuf != NULL && SpiSize != 0 ) { + CopyMem (Notify + 1, SpiBuf, SpiSize); + } + + MessageData = ((UINT8 *) (Notify + 1)) + SpiSize; + + // + // Copy Notification Data + // + if (NotifyDataSize != 0) { + CopyMem (MessageData, NotifyData, NotifyDataSize); + } + + // + // Create Payload for and set type as IKEV2_PAYLOAD_TYPE_NOTIFY + // + NotifyPayload = IkePayloadAlloc (); + if (NotifyPayload == NULL) { + FreePool (Notify); + return NULL; + } + + NotifyPayload->PayloadType = IKEV2_PAYLOAD_TYPE_NOTIFY; + NotifyPayload->PayloadBuf = (UINT8 *) Notify; + NotifyPayload->PayloadSize = NotifyPayloadLen; + return NotifyPayload; +} + +/** + Generate the Delete payload. + + Since the structure of Delete payload which defined in RFC 4306 is simple, + there is no internal data structure for Delete payload. This function generate + Delete payload defined in RFC 4306, but all the fields in this payload are still + in host order and need call Ikev2EncodePayload() to convert those fields from + the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used of Delete payload generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] SpiSize Size of the SPI in SPI size field of the Delete Payload. + @param[in] SpiNum Number of SPI in NumofSPIs field of the Delete Payload. + @param[in] SpiBuf Pointer to buffer contains the SPI value. + + @retval a Pointer of IKE Delete Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateDeletePayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 SpiSize, + IN UINT16 SpiNum, + IN UINT8 *SpiBuf + + ) +{ + IKE_PAYLOAD *DelPayload; + IKEV2_DELETE *Del; + UINT16 SpiBufSize; + UINT16 DelPayloadLen; + + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Protocol ID ! SPI Size ! # of SPIs ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Security Parameter Index(es) (SPI) ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + SpiBufSize = (UINT16) (SpiSize * SpiNum); + if (SpiBufSize != 0 && SpiBuf == NULL) { + return NULL; + } + + DelPayloadLen = (UINT16) (sizeof (IKEV2_DELETE) + SpiBufSize); + + Del = AllocateZeroPool (DelPayloadLen); + if (Del == NULL) { + return NULL; + } + + // + // Set Delete Payload's Generic Header + // + Del->Header.NextPayload = NextPayload; + Del->Header.PayloadLength = DelPayloadLen; + Del->NumSpis = SpiNum; + Del->SpiSize = SpiSize; + + if (SpiSize == 4) { + // + // TODO: should consider the AH if needs to support. + // + Del->ProtocolId = IPSEC_PROTO_IPSEC_ESP; + } else { + Del->ProtocolId = IPSEC_PROTO_ISAKMP; + } + + // + // Set Del Payload's Idntification Data + // + CopyMem (Del + 1, SpiBuf, SpiBufSize); + DelPayload = IkePayloadAlloc (); + if (DelPayload == NULL) { + FreePool (Del); + return NULL; + } + + DelPayload->PayloadType = IKEV2_PAYLOAD_TYPE_DELETE; + DelPayload->PayloadBuf = (UINT8 *) Del; + DelPayload->PayloadSize = DelPayloadLen; + return DelPayload; +} + +/** + Generate the Configuration payload. + + This function generate configuration payload defined in RFC 4306, but all the + fields in this payload are still in host order and need call Ikev2EncodePayload() + to convert those fields from the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used for Delete payload + generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] CfgType The attribute type in the Configuration attribute. + + @retval Pointer to IKE CP Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCpPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 CfgType + ) +{ + IKE_PAYLOAD *CpPayload; + IKEV2_CFG *Cfg; + UINT16 PayloadLen; + IKEV2_CFG_ATTRIBUTES *CfgAttributes; + + // + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! CFG Type ! RESERVED ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ Configuration Attributes ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + PayloadLen = (UINT16) (sizeof (IKEV2_CFG) + sizeof (IKEV2_CFG_ATTRIBUTES)); + Cfg = (IKEV2_CFG *) AllocateZeroPool (PayloadLen); + + if (Cfg == NULL) { + return NULL; + } + + CfgAttributes = (IKEV2_CFG_ATTRIBUTES *)((UINT8 *)Cfg + sizeof (IKEV2_CFG)); + + // + // Only generate the configuration payload with an empty INTERNAL_IP4_ADDRESS + // or INTERNAL_IP6_ADDRESS. + // + + Cfg->Header.NextPayload = NextPayload; + Cfg->Header.PayloadLength = PayloadLen; + Cfg->CfgType = IKEV2_CFG_TYPE_REQUEST; + + CfgAttributes->AttritType = CfgType; + CfgAttributes->ValueLength = 0; + + CpPayload = IkePayloadAlloc (); + if (CpPayload == NULL) { + if (Cfg != NULL) { + FreePool (Cfg); + } + return NULL; + } + + CpPayload->PayloadType = IKEV2_PAYLOAD_TYPE_CP; + CpPayload->PayloadBuf = (UINT8 *) Cfg; + CpPayload->PayloadSize = PayloadLen; + return CpPayload; +} + +/** + Parser the Notify Cookie payload. + + This function parses the Notify Cookie payload.If the Notify ProtocolId is not + IPSEC_PROTO_ISAKMP or if the SpiSize is not zero or if the MessageType is not + the COOKIE, return EFI_INVALID_PARAMETER. + + @param[in] IkeNCookie Pointer to the IKE_PAYLOAD which contians the + Notify Cookie payload. + the Notify payload. + @param[in, out] IkeSaSession Pointer to the relevant IKE SA Session. + + @retval EFI_SUCCESS The Notify Cookie Payload is valid. + @retval EFI_INVALID_PARAMETER The Notify Cookie Payload is invalid. + @retval EFI_OUT_OF_RESOURCE The required resource can't be allocated. + +**/ +EFI_STATUS +Ikev2ParserNotifyCookiePayload ( + IN IKE_PAYLOAD *IkeNCookie, + IN OUT IKEV2_SA_SESSION *IkeSaSession + ) +{ + IKEV2_NOTIFY *NotifyPayload; + UINTN NotifyDataSize; + + NotifyPayload = (IKEV2_NOTIFY *)IkeNCookie->PayloadBuf; + + if ((NotifyPayload->ProtocolId != IPSEC_PROTO_ISAKMP) || + (NotifyPayload->SpiSize != 0) || + (NotifyPayload->MessageType != IKEV2_NOTIFICATION_COOKIE) + ) { + return EFI_INVALID_PARAMETER; + } + + NotifyDataSize = NotifyPayload->Header.PayloadLength - sizeof (IKEV2_NOTIFY); + IkeSaSession->NCookie = AllocateZeroPool (NotifyDataSize); + if (IkeSaSession->NCookie == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IkeSaSession->NCookieSize = NotifyDataSize; + + CopyMem ( + IkeSaSession->NCookie, + (UINT8 *)NotifyPayload + sizeof (IKEV2_NOTIFY), + NotifyDataSize + ); + + return EFI_SUCCESS; +} + + +/** + Generate the Certificate payload or Certificate Request Payload. + + Since the Certificate Payload structure is same with Certificate Request Payload, + the only difference is that one contains the Certificate Data, other contains + the acceptable certificateion CA. This function generate Certificate payload + or Certificate Request Payload defined in RFC 4306, but all the fields + in the payload are still in host order and need call Ikev2EncodePayload() + to convert those fields from the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used of Delete payload + generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] Certificate Pointer of buffer contains the certification data. + @param[in] CertificateLen The length of Certificate in byte. + @param[in] EncodeType Specified the Certificate Encodeing which is defined + in RFC 4306. + @param[in] IsRequest To indicate create Certificate Payload or Certificate + Request Payload. If it is TURE, create Certificate + Payload. Otherwise, create Certificate Request Payload. + + @retval a Pointer to IKE Payload whose payload buffer containing the Certificate + payload or Certificated Request payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCertificatePayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 *Certificate, + IN UINTN CertificateLen, + IN UINT8 EncodeType, + IN BOOLEAN IsRequest + ) +{ + IKE_PAYLOAD *CertPayload; + IKEV2_CERT *Cert; + UINT16 PayloadLen; + UINT8 *PublicKey; + UINTN PublicKeyLen; + HASH_DATA_FRAGMENT Fragment[1]; + UINT8 *HashData; + UINTN HashDataSize; + EFI_STATUS Status; + + // + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload !C! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Cert Encoding ! ! + // +-+-+-+-+-+-+-+-+ ! + // ~ Certificate Data/Authority ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + Status = EFI_SUCCESS; + PublicKey = NULL; + PublicKeyLen = 0; + + if (!IsRequest) { + PayloadLen = (UINT16) (sizeof (IKEV2_CERT) + CertificateLen); + } else { + // + // SHA1 Hash length is 20. + // + PayloadLen = (UINT16) (sizeof (IKEV2_CERT) + 20); + } + + Cert = AllocateZeroPool (PayloadLen); + if (Cert == NULL) { + return NULL; + } + + // + // Generate Certificate Payload or Certificate Request Payload. + // + Cert->Header.NextPayload = NextPayload; + Cert->Header.PayloadLength = PayloadLen; + Cert->CertEncoding = EncodeType; + if (!IsRequest) { + CopyMem ( + ((UINT8 *)Cert) + sizeof (IKEV2_CERT), + Certificate, + CertificateLen + ); + } else { + Status = IpSecCryptoIoGetPublicKeyFromCert ( + Certificate, + CertificateLen, + &PublicKey, + &PublicKeyLen + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Fragment[0].Data = PublicKey; + Fragment[0].DataSize = PublicKeyLen; + HashDataSize = IpSecGetHmacDigestLength (IKE_AALG_SHA1HMAC); + HashData = AllocateZeroPool (HashDataSize); + if (HashData == NULL) { + goto ON_EXIT; + } + + Status = IpSecCryptoIoHash ( + IKE_AALG_SHA1HMAC, + Fragment, + 1, + HashData, + HashDataSize + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + CopyMem ( + ((UINT8 *)Cert) + sizeof (IKEV2_CERT), + HashData, + HashDataSize + ); + } + + CertPayload = IkePayloadAlloc (); + if (CertPayload == NULL) { + goto ON_EXIT; + } + + if (!IsRequest) { + CertPayload->PayloadType = IKEV2_PAYLOAD_TYPE_CERT; + } else { + CertPayload->PayloadType = IKEV2_PAYLOAD_TYPE_CERTREQ; + } + + CertPayload->PayloadBuf = (UINT8 *) Cert; + CertPayload->PayloadSize = PayloadLen; + return CertPayload; + +ON_EXIT: + if (Cert != NULL) { + FreePool (Cert); + } + if (PublicKey != NULL) { + FreePool (PublicKey); + } + return NULL; +} + +/** + Remove and free all IkePayloads in the specified IkePacket. + + @param[in] IkePacket The pointer of IKE_PACKET. + +**/ +VOID +ClearAllPayloads ( + IN IKE_PACKET *IkePacket + ) +{ + LIST_ENTRY *PayloadEntry; + IKE_PAYLOAD *IkePayload; + // + // remove all payloads from list and free each payload. + // + while (!IsListEmpty (&IkePacket->PayloadList)) { + PayloadEntry = IkePacket->PayloadList.ForwardLink; + IkePayload = IKE_PAYLOAD_BY_PACKET (PayloadEntry); + IKE_PACKET_REMOVE_PAYLOAD (IkePacket, IkePayload); + IkePayloadFree (IkePayload); + } +} + +/** + Transfer the intrnal data structure IKEV2_SA_DATA to IKEV2_SA structure defined in RFC. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the SA Session. + @param[in] SaData Pointer to IKEV2_SA_DATA to be transfered. + + @retval return the pointer of IKEV2_SA. + +**/ +IKEV2_SA* +Ikev2EncodeSa ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN IKEV2_SA_DATA *SaData + ) +{ + IKEV2_SA *Sa; + UINTN SaSize; + IKEV2_PROPOSAL_DATA *ProposalData; + IKEV2_TRANSFORM_DATA *TransformData; + UINTN TotalTransforms; + UINTN SaAttrsSize; + UINTN TransformsSize; + UINTN TransformSize; + UINTN ProposalsSize; + UINTN ProposalSize; + UINTN ProposalIndex; + UINTN TransformIndex; + IKE_SA_ATTRIBUTE *SaAttribute; + IKEV2_PROPOSAL *Proposal; + IKEV2_TRANSFORM *Transform; + + // + // Transform IKE_SA_DATA structure to IKE_SA Payload. + // Header length is host order. + // The returned IKE_SA struct should be freed by caller. + // + TotalTransforms = 0; + // + // Calculate the Proposal numbers and Transform numbers. + // + for (ProposalIndex = 0; ProposalIndex < SaData->NumProposals; ProposalIndex++) { + + ProposalData = (IKEV2_PROPOSAL_DATA *) (SaData + 1) + ProposalIndex; + TotalTransforms += ProposalData->NumTransforms; + + } + SaSize = sizeof (IKEV2_SA) + + SaData->NumProposals * sizeof (IKEV2_PROPOSAL) + + TotalTransforms * (sizeof (IKEV2_TRANSFORM) + MAX_SA_ATTRS_SIZE); + // + // Allocate buffer for IKE_SA. + // + Sa = AllocateZeroPool (SaSize); + if (Sa == NULL) { + return NULL; + } + + CopyMem (Sa, SaData, sizeof (IKEV2_SA)); + Sa->Header.PayloadLength = (UINT16) sizeof (IKEV2_SA); + ProposalsSize = 0; + Proposal = (IKEV2_PROPOSAL *) (Sa + 1); + + // + // Set IKE_PROPOSAL + // + ProposalData = (IKEV2_PROPOSAL_DATA *) (SaData + 1); + for (ProposalIndex = 0; ProposalIndex < SaData->NumProposals; ProposalIndex++) { + Proposal->ProposalIndex = ProposalData->ProposalIndex; + Proposal->ProtocolId = ProposalData->ProtocolId; + Proposal->NumTransforms = ProposalData->NumTransforms; + + if (ProposalData->Spi == 0) { + Proposal->SpiSize = 0; + } else { + Proposal->SpiSize = 4; + *(UINT32 *) (Proposal + 1) = HTONL (*((UINT32*)ProposalData->Spi)); + } + + TransformsSize = 0; + Transform = (IKEV2_TRANSFORM *) ((UINT8 *) (Proposal + 1) + Proposal->SpiSize); + + // + // Set IKE_TRANSFORM + // + for (TransformIndex = 0; TransformIndex < ProposalData->NumTransforms; TransformIndex++) { + TransformData = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1) + TransformIndex; + Transform->TransformType = TransformData->TransformType; + Transform->TransformId = HTONS (TransformData->TransformId); + SaAttrsSize = 0; + + // + // If the Encryption Algorithm is variable key length set the key length in attribute. + // Note that only a single attribute type (Key Length) is defined and it is fixed length. + // + if (Transform->TransformType == IKEV2_TRANSFORM_TYPE_ENCR && TransformData->Attribute.Attr.AttrValue != 0) { + SaAttribute = (IKE_SA_ATTRIBUTE *) (Transform + 1); + SaAttribute->AttrType = HTONS (IKEV2_ATTRIBUTE_TYPE_KEYLEN | SA_ATTR_FORMAT_BIT); + SaAttribute->Attr.AttrValue = HTONS (TransformData->Attribute.Attr.AttrValue); + SaAttrsSize = sizeof (IKE_SA_ATTRIBUTE); + } + + // + // If the Integrity Algorithm is variable key length set the key length in attribute. + // + if (Transform->TransformType == IKEV2_TRANSFORM_TYPE_INTEG && TransformData->Attribute.Attr.AttrValue != 0) { + SaAttribute = (IKE_SA_ATTRIBUTE *) (Transform + 1); + SaAttribute->AttrType = HTONS (IKEV2_ATTRIBUTE_TYPE_KEYLEN | SA_ATTR_FORMAT_BIT); + SaAttribute->Attr.AttrValue = HTONS (TransformData->Attribute.Attr.AttrValue); + SaAttrsSize = sizeof (IKE_SA_ATTRIBUTE); + } + + TransformSize = sizeof (IKEV2_TRANSFORM) + SaAttrsSize; + TransformsSize += TransformSize; + + Transform->Header.NextPayload = IKE_TRANSFORM_NEXT_PAYLOAD_MORE; + Transform->Header.PayloadLength = HTONS ((UINT16)TransformSize); + + if (TransformIndex == (UINTN)(ProposalData->NumTransforms - 1)) { + Transform->Header.NextPayload = IKE_TRANSFORM_NEXT_PAYLOAD_NONE; + } + + Transform = (IKEV2_TRANSFORM *)((UINT8 *) Transform + TransformSize); + } + + // + // Set Proposal's Generic Header. + // + ProposalSize = sizeof (IKEV2_PROPOSAL) + Proposal->SpiSize + TransformsSize; + ProposalsSize += ProposalSize; + Proposal->Header.NextPayload = IKE_PROPOSAL_NEXT_PAYLOAD_MORE; + Proposal->Header.PayloadLength = HTONS ((UINT16)ProposalSize); + + if (ProposalIndex == (UINTN)(SaData->NumProposals - 1)) { + Proposal->Header.NextPayload = IKE_PROPOSAL_NEXT_PAYLOAD_NONE; + } + + // + // Point to next Proposal Payload + // + Proposal = (IKEV2_PROPOSAL *) ((UINT8 *) Proposal + ProposalSize); + ProposalData = (IKEV2_PROPOSAL_DATA *)(((UINT8 *)ProposalData) + sizeof (IKEV2_PROPOSAL_DATA) + (TransformIndex * sizeof (IKEV2_TRANSFORM_DATA))); + } + // + // Set SA's Generic Header. + // + Sa->Header.PayloadLength = (UINT16) (Sa->Header.PayloadLength + ProposalsSize); + return Sa; +} + +/** + Decode SA payload. + + This function converts the received SA payload to internal data structure. + + @param[in] SessionCommon Pointer to IKE Common Session used to decode the SA + Payload. + @param[in] Sa Pointer to SA Payload + + @return a Pointer to internal data structure for SA payload. + +**/ +IKEV2_SA_DATA * +Ikev2DecodeSa ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN IKEV2_SA *Sa + ) +{ + IKEV2_SA_DATA *SaData; + EFI_STATUS Status; + IKEV2_PROPOSAL *Proposal; + IKEV2_TRANSFORM *Transform; + UINTN TotalProposals; + UINTN TotalTransforms; + UINTN ProposalNextPayloadSum; + UINTN ProposalIndex; + UINTN TransformIndex; + UINTN SaRemaining; + UINT16 ProposalSize; + UINTN ProposalRemaining; + UINT16 TransformSize; + UINTN SaAttrRemaining; + IKE_SA_ATTRIBUTE *SaAttribute; + IKEV2_PROPOSAL_DATA *ProposalData; + IKEV2_TRANSFORM_DATA *TransformData; + UINT8 *Spi; + + // + // Transfrom from IKE_SA payload to IKE_SA_DATA structure. + // Header length NTOH is already done + // The returned IKE_SA_DATA should be freed by caller + // + SaData = NULL; + Status = EFI_SUCCESS; + + // + // First round sanity check and size calculae + // + TotalProposals = 0; + TotalTransforms = 0; + ProposalNextPayloadSum = 0; + SaRemaining = Sa->Header.PayloadLength - sizeof (IKEV2_SA);// Point to current position in SA + Proposal = (IKEV2_PROPOSAL *)((IKEV2_SA *)(Sa)+1); + + // + // Calculate the number of Proposal payload and the total numbers of + // Transforms payload (the transforms in all proposal payload). + // + while (SaRemaining > sizeof (IKEV2_PROPOSAL)) { + ProposalSize = NTOHS (Proposal->Header.PayloadLength); + if (SaRemaining < ProposalSize) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + if (Proposal->SpiSize != 0 && Proposal->SpiSize != 4) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + TotalProposals++; + TotalTransforms += Proposal->NumTransforms; + SaRemaining -= ProposalSize; + ProposalNextPayloadSum += Proposal->Header.NextPayload; + Proposal = IKEV2_NEXT_PROPOSAL_WITH_SIZE (Proposal, ProposalSize); + } + + // + // Check the proposal number. + // The proposal Substructure, the NextPayLoad field indicates : 0 (last) or 2 (more) + // which Specifies whether this is the last Proposal Substructure in the SA. + // Here suming all Proposal NextPayLoad field to check the proposal number is correct + // or not. + // + if (TotalProposals == 0 || + (TotalProposals - 1) * IKE_PROPOSAL_NEXT_PAYLOAD_MORE != ProposalNextPayloadSum + ) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Second round sanity check and decode. Transform the SA payload into + // a IKE_SA_DATA structure. + // + SaData = (IKEV2_SA_DATA *) AllocateZeroPool ( + sizeof (IKEV2_SA_DATA) + + TotalProposals * sizeof (IKEV2_PROPOSAL_DATA) + + TotalTransforms * sizeof (IKEV2_TRANSFORM_DATA) + ); + if (SaData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem (SaData, Sa, sizeof (IKEV2_SA)); + SaData->NumProposals = TotalProposals; + ProposalData = (IKEV2_PROPOSAL_DATA *) (SaData + 1); + + // + // Proposal Payload + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload ! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Proposal # ! Protocol-Id ! SPI Size !# of Transforms! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! SPI (variable) ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + for (ProposalIndex = 0, Proposal = IKEV2_SA_FIRST_PROPOSAL (Sa); + ProposalIndex < TotalProposals; + ProposalIndex++ + ) { + + // + // TODO: check ProposalId + // + ProposalData->ProposalIndex = Proposal->ProposalIndex; + ProposalData->ProtocolId = Proposal->ProtocolId; + if (Proposal->SpiSize == 0) { + ProposalData->Spi = 0; + } else { + // + // SpiSize == 4 + // + Spi = AllocateZeroPool (Proposal->SpiSize); + if (Spi == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem (Spi, (UINT32 *) (Proposal + 1), Proposal->SpiSize); + *((UINT32*) Spi) = NTOHL (*((UINT32*) Spi)); + ProposalData->Spi = Spi; + } + + ProposalData->NumTransforms = Proposal->NumTransforms; + ProposalSize = NTOHS (Proposal->Header.PayloadLength); + ProposalRemaining = ProposalSize; + // + // Transform Payload + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! Next Payload ! RESERVED ! Payload Length ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // !Transform Type ! RESERVED ! Transform ID ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // ! ! + // ~ SA Attributes ~ + // ! ! + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + Transform = IKEV2_PROPOSAL_FIRST_TRANSFORM (Proposal); + for (TransformIndex = 0; TransformIndex < Proposal->NumTransforms; TransformIndex++) { + + // + // Transfer the IKEV2_TRANSFORM structure into internal IKEV2_TRANSFORM_DATA struture. + // + TransformData = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1) + TransformIndex; + TransformData->TransformId = NTOHS (Transform->TransformId); + TransformData->TransformType = Transform->TransformType; + TransformSize = NTOHS (Transform->Header.PayloadLength); + // + // Check the Proposal Data is correct. + // + if (ProposalRemaining < TransformSize) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // Check if the Transform payload includes Attribution. + // + SaAttrRemaining = TransformSize - sizeof (IKEV2_TRANSFORM); + + // + // According to RFC 4603, currently only the Key length attribute type is + // supported. For each Transform, there is only one attributeion. + // + if (SaAttrRemaining > 0) { + if (SaAttrRemaining != sizeof (IKE_SA_ATTRIBUTE)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + SaAttribute = (IKE_SA_ATTRIBUTE *) ((IKEV2_TRANSFORM *)(Transform) + 1); + TransformData->Attribute.AttrType = (UINT16)((NTOHS (SaAttribute->AttrType)) & ~SA_ATTR_FORMAT_BIT); + TransformData->Attribute.Attr.AttrValue = NTOHS (SaAttribute->Attr.AttrValue); + + // + // Currently, only supports the Key Length Attribution. + // + if (TransformData->Attribute.AttrType != IKEV2_ATTRIBUTE_TYPE_KEYLEN) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + } + + // + // Move to next Transform + // + Transform = IKEV2_NEXT_TRANSFORM_WITH_SIZE (Transform, TransformSize); + } + Proposal = IKEV2_NEXT_PROPOSAL_WITH_SIZE (Proposal, ProposalSize); + ProposalData = (IKEV2_PROPOSAL_DATA *) ((UINT8 *)(ProposalData + 1) + + ProposalData->NumTransforms * + sizeof (IKEV2_TRANSFORM_DATA)); + } + +Exit: + if (EFI_ERROR (Status) && SaData != NULL) { + FreePool (SaData); + SaData = NULL; + } + return SaData; +} + +/** + General interface of payload encoding. + + This function encodes the internal data structure into payload which + is defined in RFC 4306. The IkePayload->PayloadBuf is used to store both the input + payload and converted payload. Only the SA payload use the interal structure + to store the attribute. Other payload use structure which is same with the RFC + defined, for this kind payloads just do host order to network order change of + some fields. + + @param[in] SessionCommon Pointer to IKE Session Common used to encode the payload. + @param[in, out] IkePayload Pointer to IKE payload to be encoded as input, and + store the encoded result as output. + + @retval EFI_INVALID_PARAMETER Meet error when encoding the SA payload. + @retval EFI_SUCCESS Encoded successfully. + +**/ +EFI_STATUS +Ikev2EncodePayload ( + IN UINT8 *SessionCommon, + IN OUT IKE_PAYLOAD *IkePayload + ) +{ + IKEV2_SA_DATA *SaData; + IKEV2_SA *SaPayload; + IKEV2_COMMON_PAYLOAD_HEADER *PayloadHdr; + IKEV2_NOTIFY *NotifyPayload; + IKEV2_DELETE *DeletePayload; + IKEV2_KEY_EXCHANGE *KeyPayload; + IKEV2_TS *TsPayload; + IKEV2_CFG_ATTRIBUTES *CfgAttribute; + UINT8 *TsBuffer; + UINT8 Index; + TRAFFIC_SELECTOR *TrafficSelector; + + // + // Transform the Internal IKE structure to IKE payload. + // Only the SA payload use the interal structure to store the attribute. + // Other payload use structure which same with the RFC defined, so there is + // no need to tranform them to IKE payload. + // + switch (IkePayload->PayloadType) { + case IKEV2_PAYLOAD_TYPE_SA: + // + // Transform IKE_SA_DATA to IK_SA payload + // + SaData = (IKEV2_SA_DATA *) IkePayload->PayloadBuf; + SaPayload = Ikev2EncodeSa ((IKEV2_SESSION_COMMON *) SessionCommon, SaData); + + if (SaPayload == NULL) { + return EFI_INVALID_PARAMETER; + } + if (!IkePayload->IsPayloadBufExt) { + FreePool (IkePayload->PayloadBuf); + } + IkePayload->PayloadBuf = (UINT8 *) SaPayload; + IkePayload->IsPayloadBufExt = FALSE; + break; + + case IKEV2_PAYLOAD_TYPE_NOTIFY: + NotifyPayload = (IKEV2_NOTIFY *) IkePayload->PayloadBuf; + NotifyPayload->MessageType = HTONS (NotifyPayload->MessageType); + break; + + case IKEV2_PAYLOAD_TYPE_DELETE: + DeletePayload = (IKEV2_DELETE *) IkePayload->PayloadBuf; + DeletePayload->NumSpis = HTONS (DeletePayload->NumSpis); + break; + + case IKEV2_PAYLOAD_TYPE_KE: + KeyPayload = (IKEV2_KEY_EXCHANGE *) IkePayload->PayloadBuf; + KeyPayload->DhGroup = HTONS (KeyPayload->DhGroup); + break; + + case IKEV2_PAYLOAD_TYPE_TS_INIT: + case IKEV2_PAYLOAD_TYPE_TS_RSP: + TsPayload = (IKEV2_TS *) IkePayload->PayloadBuf; + TsBuffer = IkePayload->PayloadBuf + sizeof (IKEV2_TS); + + for (Index = 0; Index < TsPayload->TSNumbers; Index++) { + TrafficSelector = (TRAFFIC_SELECTOR *) TsBuffer; + TsBuffer = TsBuffer + TrafficSelector->SelecorLen; + // + // Host order to network order + // + TrafficSelector->SelecorLen = HTONS (TrafficSelector->SelecorLen); + TrafficSelector->StartPort = HTONS (TrafficSelector->StartPort); + TrafficSelector->EndPort = HTONS (TrafficSelector->EndPort); + + } + + break; + + case IKEV2_PAYLOAD_TYPE_CP: + CfgAttribute = (IKEV2_CFG_ATTRIBUTES *)(((IKEV2_CFG *) IkePayload->PayloadBuf) + 1); + CfgAttribute->AttritType = HTONS (CfgAttribute->AttritType); + CfgAttribute->ValueLength = HTONS (CfgAttribute->ValueLength); + + case IKEV2_PAYLOAD_TYPE_ID_INIT: + case IKEV2_PAYLOAD_TYPE_ID_RSP: + case IKEV2_PAYLOAD_TYPE_AUTH: + default: + break; + } + + PayloadHdr = (IKEV2_COMMON_PAYLOAD_HEADER *) IkePayload->PayloadBuf; + IkePayload->PayloadSize = PayloadHdr->PayloadLength; + PayloadHdr->PayloadLength = HTONS (PayloadHdr->PayloadLength); + IKEV2_DUMP_PAYLOAD (IkePayload); + return EFI_SUCCESS; +} + +/** + The general interface for decoding Payload. + + This function converts the received Payload into internal structure. + + @param[in] SessionCommon Pointer to IKE Session Common used for decoding. + @param[in, out] IkePayload Pointer to IKE payload to be decoded as input, and + store the decoded result as output. + + @retval EFI_INVALID_PARAMETER Meet error when decoding the SA payload. + @retval EFI_SUCCESS Decoded successfully. + +**/ +EFI_STATUS +Ikev2DecodePayload ( + IN UINT8 *SessionCommon, + IN OUT IKE_PAYLOAD *IkePayload + ) +{ + IKEV2_COMMON_PAYLOAD_HEADER *PayloadHdr; + UINT16 PayloadSize; + UINT8 PayloadType; + IKEV2_SA_DATA *SaData; + EFI_STATUS Status; + IKEV2_NOTIFY *NotifyPayload; + IKEV2_DELETE *DeletePayload; + UINT16 TsTotalSize; + TRAFFIC_SELECTOR *TsSelector; + IKEV2_TS *TsPayload; + IKEV2_KEY_EXCHANGE *KeyPayload; + IKEV2_CFG_ATTRIBUTES *CfgAttribute; + UINT8 Index; + + // + // Transform the IKE payload to Internal IKE structure. + // Only the SA payload and Hash Payload use the interal + // structure to store the attribute. Other payloads use + // structure which is same with the definitions in RFC, + // so there is no need to tranform them to internal IKE + // structure. + // + Status = EFI_SUCCESS; + PayloadSize = (UINT16) IkePayload->PayloadSize; + PayloadType = IkePayload->PayloadType; + PayloadHdr = (IKEV2_COMMON_PAYLOAD_HEADER *) IkePayload->PayloadBuf; + // + // The PayloadSize is the size of whole payload. + // Replace HTONS operation to assignment statements, since the result is same. + // + PayloadHdr->PayloadLength = PayloadSize; + + IKEV2_DUMP_PAYLOAD (IkePayload); + switch (PayloadType) { + case IKEV2_PAYLOAD_TYPE_SA: + if (PayloadSize < sizeof (IKEV2_SA)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + SaData = Ikev2DecodeSa ((IKEV2_SESSION_COMMON *) SessionCommon, (IKEV2_SA *) PayloadHdr); + if (SaData == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + if (!IkePayload->IsPayloadBufExt) { + FreePool (IkePayload->PayloadBuf); + } + + IkePayload->PayloadBuf = (UINT8 *) SaData; + IkePayload->IsPayloadBufExt = FALSE; + break; + + case IKEV2_PAYLOAD_TYPE_ID_INIT: + case IKEV2_PAYLOAD_TYPE_ID_RSP : + if (PayloadSize < sizeof (IKEV2_ID)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + break; + + case IKEV2_PAYLOAD_TYPE_NOTIFY: + if (PayloadSize < sizeof (IKEV2_NOTIFY)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + NotifyPayload = (IKEV2_NOTIFY *) PayloadHdr; + NotifyPayload->MessageType = NTOHS (NotifyPayload->MessageType); + break; + + case IKEV2_PAYLOAD_TYPE_DELETE: + if (PayloadSize < sizeof (IKEV2_DELETE)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + DeletePayload = (IKEV2_DELETE *) PayloadHdr; + DeletePayload->NumSpis = NTOHS (DeletePayload->NumSpis); + break; + + case IKEV2_PAYLOAD_TYPE_AUTH: + if (PayloadSize < sizeof (IKEV2_AUTH)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + break; + + case IKEV2_PAYLOAD_TYPE_KE: + KeyPayload = (IKEV2_KEY_EXCHANGE *) IkePayload->PayloadBuf; + KeyPayload->DhGroup = HTONS (KeyPayload->DhGroup); + break; + + case IKEV2_PAYLOAD_TYPE_TS_INIT: + case IKEV2_PAYLOAD_TYPE_TS_RSP : + TsTotalSize = 0; + if (PayloadSize < sizeof (IKEV2_TS)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + // + // Parse each traffic selector and transfer network-order to host-order + // + TsPayload = (IKEV2_TS *) IkePayload->PayloadBuf; + TsSelector = (TRAFFIC_SELECTOR *) (IkePayload->PayloadBuf + sizeof (IKEV2_TS)); + + for (Index = 0; Index < TsPayload->TSNumbers; Index++) { + TsSelector->SelecorLen = NTOHS (TsSelector->SelecorLen); + TsSelector->StartPort = NTOHS (TsSelector->StartPort); + TsSelector->EndPort = NTOHS (TsSelector->EndPort); + + TsTotalSize = (UINT16) (TsTotalSize + TsSelector->SelecorLen); + TsSelector = (TRAFFIC_SELECTOR *) ((UINT8 *) TsSelector + TsSelector->SelecorLen); + } + // + // Check if the total size of Traffic Selectors is correct. + // + if (TsTotalSize != PayloadSize - sizeof(IKEV2_TS)) { + Status = EFI_INVALID_PARAMETER; + } + + case IKEV2_PAYLOAD_TYPE_CP: + CfgAttribute = (IKEV2_CFG_ATTRIBUTES *)(((IKEV2_CFG *) IkePayload->PayloadBuf) + 1); + CfgAttribute->AttritType = NTOHS (CfgAttribute->AttritType); + CfgAttribute->ValueLength = NTOHS (CfgAttribute->ValueLength); + + default: + break; + } + + Exit: + return Status; +} + +/** + Decode the IKE packet. + + This function first decrypts the IKE packet if needed , then separates the whole + IKE packet from the IkePacket->PayloadBuf into IkePacket payload list. + + @param[in] SessionCommon Pointer to IKEV1_SESSION_COMMON containing + some parameter used by IKE packet decoding. + @param[in, out] IkePacket The IKE Packet to be decoded on input, and + the decoded result on return. + @param[in] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supported. + + @retval EFI_SUCCESS The IKE packet is decoded successfully. + @retval Otherwise The IKE packet decoding is failed. + +**/ +EFI_STATUS +Ikev2DecodePacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN UINTN IkeType + ) +{ + EFI_STATUS Status; + IKEV2_COMMON_PAYLOAD_HEADER *PayloadHdr; + UINT8 PayloadType; + UINTN RemainBytes; + UINT16 PayloadSize; + IKE_PAYLOAD *IkePayload; + IKE_HEADER *IkeHeader; + IKEV2_SA_SESSION *IkeSaSession; + + IkeHeader = NULL; + + // + // Check if the IkePacket need decrypt. + // + if (SessionCommon->State >= IkeStateAuth) { + Status = Ikev2DecryptPacket (SessionCommon, IkePacket, IkeType); + if (EFI_ERROR (Status)) { + return Status; + } + } + + Status = EFI_SUCCESS; + + // + // If the IkePacket doesn't contain any payload return invalid parameter. + // + if (IkePacket->Header->NextPayload == IKEV2_PAYLOAD_TYPE_NONE) { + if ((SessionCommon->State >= IkeStateAuth) && + (IkePacket->Header->ExchangeType == IKEV2_EXCHANGE_TYPE_INFO) + ) { + // + // If it is Liveness check, there will be no payload load in the encrypt payload. + // + Status = EFI_SUCCESS; + } else { + Status = EFI_INVALID_PARAMETER; + } + } + + // + // If the PayloadTotalSize < Header length, return invalid parameter. + // + RemainBytes = IkePacket->PayloadTotalSize; + if (RemainBytes < sizeof (IKEV2_COMMON_PAYLOAD_HEADER)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // If the packet is first or second message, store whole message in + // IkeSa->InitiPacket or IkeSa->RespPacket for following Auth Payload + // calculate. + // + if (IkePacket->Header->ExchangeType == IKEV2_EXCHANGE_TYPE_INIT) { + IkeHeader = AllocateZeroPool (sizeof (IKE_HEADER)); + if (IkeHeader == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem (IkeHeader, IkePacket->Header, sizeof (IKE_HEADER)); + + // + // Before store the whole packet, roll back the host order to network order, + // since the header order was changed in the IkePacketFromNetbuf. + // + IkeHdrNetToHost (IkeHeader); + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + if (SessionCommon->IsInitiator) { + IkeSaSession->RespPacket = AllocateZeroPool (IkePacket->Header->Length); + if (IkeSaSession->RespPacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->RespPacketSize = IkePacket->Header->Length; + CopyMem (IkeSaSession->RespPacket, IkeHeader, sizeof (IKE_HEADER)); + CopyMem ( + IkeSaSession->RespPacket + sizeof (IKE_HEADER), + IkePacket->PayloadsBuf, + IkePacket->Header->Length - sizeof (IKE_HEADER) + ); + } else { + IkeSaSession->InitPacket = AllocateZeroPool (IkePacket->Header->Length); + if (IkeSaSession->InitPacket == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->InitPacketSize = IkePacket->Header->Length; + CopyMem (IkeSaSession->InitPacket, IkeHeader, sizeof (IKE_HEADER)); + CopyMem ( + IkeSaSession->InitPacket + sizeof (IKE_HEADER), + IkePacket->PayloadsBuf, + IkePacket->Header->Length - sizeof (IKE_HEADER) + ); + } + } + + // + // Point to the first Payload + // + PayloadHdr = (IKEV2_COMMON_PAYLOAD_HEADER *) IkePacket->PayloadsBuf; + PayloadType = IkePacket->Header->NextPayload; + + // + // Parse each payload + // + while (RemainBytes >= sizeof (IKEV2_COMMON_PAYLOAD_HEADER)) { + PayloadSize = NTOHS (PayloadHdr->PayloadLength); + + // + //Check the size of the payload is correct. + // + if (RemainBytes < PayloadSize) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + // + // At certain states, it should save some datas before decoding. + // + if (SessionCommon->BeforeDecodePayload != NULL) { + SessionCommon->BeforeDecodePayload ( + (UINT8 *) SessionCommon, + (UINT8 *) PayloadHdr, + PayloadSize, + PayloadType + ); + } + + // + // Initial IkePayload + // + IkePayload = IkePayloadAlloc (); + if (IkePayload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + IkePayload->PayloadType = PayloadType; + IkePayload->PayloadBuf = (UINT8 *) PayloadHdr; + IkePayload->PayloadSize = PayloadSize; + IkePayload->IsPayloadBufExt = TRUE; + + Status = Ikev2DecodePayload ((UINT8 *) SessionCommon, IkePayload); + if (EFI_ERROR (Status)) { + goto Exit; + } + + IPSEC_DUMP_BUF ("After Decoding Payload", IkePayload->PayloadBuf, IkePayload->PayloadSize); + // + // Add each payload into packet + // Notice, the IkePacket->Hdr->Lenght still recode the whole IkePacket length + // which is before the decoding. + // + IKE_PACKET_APPEND_PAYLOAD (IkePacket, IkePayload); + + RemainBytes -= PayloadSize; + PayloadType = PayloadHdr->NextPayload; + if (PayloadType == IKEV2_PAYLOAD_TYPE_NONE) { + break; + } + + PayloadHdr = (IKEV2_COMMON_PAYLOAD_HEADER *) ((UINT8 *) PayloadHdr + PayloadSize); + } + + if (PayloadType != IKEV2_PAYLOAD_TYPE_NONE) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + +Exit: + if (EFI_ERROR (Status)) { + ClearAllPayloads (IkePacket); + } + + if (IkeHeader != NULL) { + FreePool (IkeHeader); + } + return Status; +} + +/** + Encode the IKE packet. + + This function puts all Payloads into one payload then encrypt it if needed. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON containing + some parameter used during IKE packet encoding. + @param[in, out] IkePacket Pointer to IKE_PACKET to be encoded as input, + and the encoded result as output. + @param[in] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS Encode IKE packet successfully. + @retval Otherwise Encode IKE packet failed. + +**/ +EFI_STATUS +Ikev2EncodePacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN UINTN IkeType + ) +{ + IKE_PAYLOAD *IkePayload; + UINTN PayloadTotalSize; + LIST_ENTRY *Entry; + EFI_STATUS Status; + IKEV2_SA_SESSION *IkeSaSession; + + PayloadTotalSize = 0; + // + // Encode each payload + // + for (Entry = IkePacket->PayloadList.ForwardLink; Entry != &(IkePacket->PayloadList);) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + Entry = Entry->ForwardLink; + Status = Ikev2EncodePayload ((UINT8 *) SessionCommon, IkePayload); + if (EFI_ERROR (Status)) { + return Status; + } + + if (SessionCommon->AfterEncodePayload != NULL) { + // + // For certain states, save some payload for further calculation + // + SessionCommon->AfterEncodePayload ( + (UINT8 *) SessionCommon, + IkePayload->PayloadBuf, + IkePayload->PayloadSize, + IkePayload->PayloadType + ); + } + + PayloadTotalSize += IkePayload->PayloadSize; + } + IkePacket->PayloadTotalSize = PayloadTotalSize; + + Status = EFI_SUCCESS; + if (SessionCommon->State >= IkeStateAuth) { + // + // Encrypt all payload and transfer IKE packet header from Host order to Network order. + // + Status = Ikev2EncryptPacket (SessionCommon, IkePacket); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // Fill in the lenght into IkePacket header and transfer Host order to Network order. + // + IkePacket->Header->Length = (UINT32) (sizeof (IKE_HEADER) + IkePacket->PayloadTotalSize); + IkeHdrHostToNet (IkePacket->Header); + } + + // + // If the packet is first message, store whole message in IkeSa->InitiPacket + // for following Auth Payload calculation. + // + if (IkePacket->Header->ExchangeType == IKEV2_EXCHANGE_TYPE_INIT) { + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + if (SessionCommon->IsInitiator) { + IkeSaSession->InitPacketSize = IkePacket->PayloadTotalSize + sizeof (IKE_HEADER); + IkeSaSession->InitPacket = AllocateZeroPool (IkeSaSession->InitPacketSize); + if (IkeSaSession->InitPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (IkeSaSession->InitPacket, IkePacket->Header, sizeof (IKE_HEADER)); + PayloadTotalSize = 0; + for (Entry = IkePacket->PayloadList.ForwardLink; Entry != &(IkePacket->PayloadList);) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + Entry = Entry->ForwardLink; + CopyMem ( + IkeSaSession->InitPacket + sizeof (IKE_HEADER) + PayloadTotalSize, + IkePayload->PayloadBuf, + IkePayload->PayloadSize + ); + PayloadTotalSize = PayloadTotalSize + IkePayload->PayloadSize; + } + } else { + IkeSaSession->RespPacketSize = IkePacket->PayloadTotalSize + sizeof(IKE_HEADER); + IkeSaSession->RespPacket = AllocateZeroPool (IkeSaSession->RespPacketSize); + if (IkeSaSession->RespPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (IkeSaSession->RespPacket, IkePacket->Header, sizeof (IKE_HEADER)); + PayloadTotalSize = 0; + for (Entry = IkePacket->PayloadList.ForwardLink; Entry != &(IkePacket->PayloadList);) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + Entry = Entry->ForwardLink; + + CopyMem ( + IkeSaSession->RespPacket + sizeof (IKE_HEADER) + PayloadTotalSize, + IkePayload->PayloadBuf, + IkePayload->PayloadSize + ); + PayloadTotalSize = PayloadTotalSize + IkePayload->PayloadSize; + } + } + } + + return Status; +} + +/** + Decrypt IKE packet. + + This function decrypts the Encrypted IKE packet and put the result into IkePacket->PayloadBuf. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON containing + some parameter used during decrypting. + @param[in, out] IkePacket Pointer to IKE_PACKET to be decrypted as input, + and the decrypted result as output. + @param[in, out] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_INVALID_PARAMETER If the IKE packet length is zero or the + IKE packet length is not aligned with Algorithm Block Size + @retval EFI_SUCCESS Decrypt IKE packet successfully. + +**/ +EFI_STATUS +Ikev2DecryptPacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN OUT UINTN IkeType + ) +{ + UINT8 CryptBlockSize; // Encrypt Block Size + UINTN DecryptedSize; // Encrypted IKE Payload Size + UINT8 *DecryptedBuf; // Encrypted IKE Payload buffer + UINTN IntegritySize; + UINT8 *IntegrityBuffer; + UINTN IvSize; // Iv Size + UINT8 CheckSumSize; // Integrity Check Sum Size depends on intergrity Auth + UINT8 *CheckSumData; // Check Sum data + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + EFI_STATUS Status; + UINT8 PadLen; + HASH_DATA_FRAGMENT Fragments[1]; + + IvSize = 0; + IkeSaSession = NULL; + CryptBlockSize = 0; + CheckSumSize = 0; + + // + // Check if the first payload is the Encrypted payload + // + if (IkePacket->Header->NextPayload != IKEV2_PAYLOAD_TYPE_ENCRYPT) { + return EFI_ACCESS_DENIED; + } + CheckSumData = NULL; + DecryptedBuf = NULL; + IntegrityBuffer = NULL; + + // + // Get the Block Size + // + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + + CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) SessionCommon->SaParams->EncAlgId); + + CheckSumSize = (UINT8) IpSecGetIcvLength ((UINT8) SessionCommon->SaParams->IntegAlgId); + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + + } else if (SessionCommon->IkeSessionType == IkeSessionTypeChildSa) { + + ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); + IkeSaSession = ChildSaSession->IkeSaSession; + CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId); + CheckSumSize = (UINT8) IpSecGetIcvLength ((UINT8) IkeSaSession->SessionCommon.SaParams->IntegAlgId); + } else { + // + // The type of SA Session would either be IkeSa or ChildSa. + // + return EFI_INVALID_PARAMETER; + } + + CheckSumData = AllocateZeroPool (CheckSumSize); + if (CheckSumData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Fill in the Integrity buffer + // + IntegritySize = IkePacket->PayloadTotalSize + sizeof (IKE_HEADER); + IntegrityBuffer = AllocateZeroPool (IntegritySize); + if (IntegrityBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (IntegrityBuffer, IkePacket->Header, sizeof(IKE_HEADER)); + CopyMem (IntegrityBuffer + sizeof (IKE_HEADER), IkePacket->PayloadsBuf, IkePacket->PayloadTotalSize); + + // + // Change Host order to Network order, since the header order was changed + // in the IkePacketFromNetbuf. + // + IkeHdrHostToNet ((IKE_HEADER *)IntegrityBuffer); + + // + // Calculate the Integrity CheckSum Data + // + Fragments[0].Data = IntegrityBuffer; + Fragments[0].DataSize = IntegritySize - CheckSumSize; + + if (SessionCommon->IsInitiator) { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId, + IkeSaSession->IkeKeys->SkArKey, + IkeSaSession->IkeKeys->SkArKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + CheckSumData, + CheckSumSize + ); + } else { + Status = IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId, + IkeSaSession->IkeKeys->SkAiKey, + IkeSaSession->IkeKeys->SkAiKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + CheckSumData, + CheckSumSize + ); + } + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Compare the Integrity CheckSum Data with the one in IkePacket + // + if (CompareMem ( + IkePacket->PayloadsBuf + IkePacket->PayloadTotalSize - CheckSumSize, + CheckSumData, + CheckSumSize + ) != 0) { + DEBUG ((DEBUG_ERROR, "Error auth verify payload\n")); + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + IvSize = CryptBlockSize; + + // + // Decrypt the payload with the key. + // + DecryptedSize = IkePacket->PayloadTotalSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER) - IvSize - CheckSumSize; + DecryptedBuf = AllocateZeroPool (DecryptedSize); + if (DecryptedBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem ( + DecryptedBuf, + IkePacket->PayloadsBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER) + IvSize, + DecryptedSize + ); + + if (SessionCommon->IsInitiator) { + Status = IpSecCryptoIoDecrypt ( + (UINT8) SessionCommon->SaParams->EncAlgId, + IkeSaSession->IkeKeys->SkErKey, + IkeSaSession->IkeKeys->SkErKeySize << 3, + IkePacket->PayloadsBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + DecryptedBuf, + DecryptedSize, + DecryptedBuf + ); + } else { + Status = IpSecCryptoIoDecrypt ( + (UINT8) SessionCommon->SaParams->EncAlgId, + IkeSaSession->IkeKeys->SkEiKey, + IkeSaSession->IkeKeys->SkEiKeySize << 3, + IkePacket->PayloadsBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + DecryptedBuf, + DecryptedSize, + DecryptedBuf + ); + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error decrypt buffer with %r\n", Status)); + goto ON_EXIT; + } + + // + // Get the Padding length + // + // + PadLen = (UINT8) (*(DecryptedBuf + DecryptedSize - sizeof (IKEV2_PAD_LEN))); + + // + // Save the next payload of encrypted payload into IkePacket->Hdr->NextPayload + // + IkePacket->Header->NextPayload = ((IKEV2_ENCRYPTED *) IkePacket->PayloadsBuf)->Header.NextPayload; + + // + // Free old IkePacket->PayloadBuf and point it to decrypted paylaod buffer. + // + FreePool (IkePacket->PayloadsBuf); + IkePacket->PayloadsBuf = DecryptedBuf; + IkePacket->PayloadTotalSize = DecryptedSize - PadLen; + + IPSEC_DUMP_BUF ("Decrypted Buffer", DecryptedBuf, DecryptedSize); + + +ON_EXIT: + if (CheckSumData != NULL) { + FreePool (CheckSumData); + } + + if (EFI_ERROR (Status) && DecryptedBuf != NULL) { + FreePool (DecryptedBuf); + } + + if (IntegrityBuffer != NULL) { + FreePool (IntegrityBuffer); + } + + return Status; +} + +/** + Encrypt IKE packet. + + This function encrypt IKE packet before sending it. The Encrypted IKE packet + is put in to IKEV2 Encrypted Payload. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the IKE packet. + @param[in, out] IkePacket Pointer to IKE packet to be encrypted. + + @retval EFI_SUCCESS Operation is successful. + @retval Others Operation is failed. + +**/ +EFI_STATUS +Ikev2EncryptPacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket + ) +{ + UINT8 CryptBlockSize; // Encrypt Block Size + UINT8 CryptBlockSizeMask; // Block Mask + UINTN EncryptedSize; // Encrypted IKE Payload Size + UINT8 *EncryptedBuf; // Encrypted IKE Payload buffer + UINT8 *EncryptPayloadBuf; // Contain whole Encrypted Payload + UINTN EncryptPayloadSize; // Total size of the Encrypted payload + UINT8 *IntegrityBuf; // Buffer to be intergity + UINT8 *IvBuffer; // Initialization Vector + UINT8 IvSize; // Iv Size + UINT8 CheckSumSize; // Integrity Check Sum Size depends on intergrity Auth + UINT8 *CheckSumData; // Check Sum data + UINTN Index; + IKE_PAYLOAD *EncryptPayload; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + EFI_STATUS Status; + LIST_ENTRY *Entry; + IKE_PAYLOAD *IkePayload; + HASH_DATA_FRAGMENT Fragments[1]; + + Status = EFI_SUCCESS; + + // + // Initial all buffers to NULL. + // + EncryptedBuf = NULL; + EncryptPayloadBuf = NULL; + IvBuffer = NULL; + CheckSumData = NULL; + IkeSaSession = NULL; + CryptBlockSize = 0; + CheckSumSize = 0; + IntegrityBuf = NULL; + // + // Get the Block Size + // + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + + CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) SessionCommon->SaParams->EncAlgId); + CheckSumSize = (UINT8) IpSecGetIcvLength ((UINT8) SessionCommon->SaParams->IntegAlgId); + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + + } else if (SessionCommon->IkeSessionType == IkeSessionTypeChildSa) { + + ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); + IkeSaSession = ChildSaSession->IkeSaSession; + CryptBlockSize = (UINT8) IpSecGetEncryptBlockSize ((UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId); + CheckSumSize = (UINT8) IpSecGetIcvLength ((UINT8) IkeSaSession->SessionCommon.SaParams->IntegAlgId); + } + + // + // Calcualte the EncryptPayloadSize and the PAD length + // + CryptBlockSizeMask = (UINT8) (CryptBlockSize - 1); + EncryptedSize = (IkePacket->PayloadTotalSize + sizeof (IKEV2_PAD_LEN) + CryptBlockSizeMask) & ~CryptBlockSizeMask; + EncryptedBuf = (UINT8 *) AllocateZeroPool (EncryptedSize); + if (EncryptedBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Copy all payload into EncryptedIkePayload + // + Index = 0; + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + + CopyMem (EncryptedBuf + Index, IkePayload->PayloadBuf, IkePayload->PayloadSize); + Index += IkePayload->PayloadSize; + + }; + + // + // Fill in the Pading Length + // + *(EncryptedBuf + EncryptedSize - 1) = (UINT8)(EncryptedSize - IkePacket->PayloadTotalSize - 1); + + // + // The IV size is equal with block size + // + IvSize = CryptBlockSize; + IvBuffer = (UINT8 *) AllocateZeroPool (IvSize); + if (IvBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Generate IV + // + IkeGenerateIv (IvBuffer, IvSize); + + // + // Encrypt payload buf + // + if (SessionCommon->IsInitiator) { + Status = IpSecCryptoIoEncrypt ( + (UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId, + IkeSaSession->IkeKeys->SkEiKey, + IkeSaSession->IkeKeys->SkEiKeySize << 3, + IvBuffer, + EncryptedBuf, + EncryptedSize, + EncryptedBuf + ); + } else { + Status = IpSecCryptoIoEncrypt ( + (UINT8) IkeSaSession->SessionCommon.SaParams->EncAlgId, + IkeSaSession->IkeKeys->SkErKey, + IkeSaSession->IkeKeys->SkErKeySize << 3, + IvBuffer, + EncryptedBuf, + EncryptedSize, + EncryptedBuf + ); + } + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Allocate the buffer for the whole IKE payload (Encrypted Payload). + // + EncryptPayloadSize = sizeof(IKEV2_ENCRYPTED) + IvSize + EncryptedSize + CheckSumSize; + EncryptPayloadBuf = AllocateZeroPool (EncryptPayloadSize); + if (EncryptPayloadBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Fill in Header of Encrypted Payload + // + ((IKEV2_ENCRYPTED *) EncryptPayloadBuf)->Header.NextPayload = IkePacket->Header->NextPayload; + ((IKEV2_ENCRYPTED *) EncryptPayloadBuf)->Header.PayloadLength = HTONS ((UINT16)EncryptPayloadSize); + + // + // Fill in Iv + // + CopyMem (EncryptPayloadBuf + sizeof (IKEV2_ENCRYPTED), IvBuffer, IvSize); + + // + // Fill in encrypted data + // + CopyMem (EncryptPayloadBuf + sizeof (IKEV2_ENCRYPTED) + IvSize, EncryptedBuf, EncryptedSize); + + // + // Fill in the IKE Packet header + // + IkePacket->PayloadTotalSize = EncryptPayloadSize; + IkePacket->Header->Length = (UINT32) (sizeof (IKE_HEADER) + IkePacket->PayloadTotalSize); + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ENCRYPT; + + IntegrityBuf = AllocateZeroPool (IkePacket->Header->Length); + if (IntegrityBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + IkeHdrHostToNet (IkePacket->Header); + + CopyMem (IntegrityBuf, IkePacket->Header, sizeof (IKE_HEADER)); + CopyMem (IntegrityBuf + sizeof (IKE_HEADER), EncryptPayloadBuf, EncryptPayloadSize); + + // + // Calcualte Integrity CheckSum + // + Fragments[0].Data = IntegrityBuf; + Fragments[0].DataSize = EncryptPayloadSize + sizeof (IKE_HEADER) - CheckSumSize; + + CheckSumData = AllocateZeroPool (CheckSumSize); + if (CheckSumData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + if (SessionCommon->IsInitiator) { + + IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId, + IkeSaSession->IkeKeys->SkAiKey, + IkeSaSession->IkeKeys->SkAiKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + CheckSumData, + CheckSumSize + ); + } else { + + IpSecCryptoIoHmac ( + (UINT8)IkeSaSession->SessionCommon.SaParams->IntegAlgId, + IkeSaSession->IkeKeys->SkArKey, + IkeSaSession->IkeKeys->SkArKeySize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + CheckSumData, + CheckSumSize + ); + } + + // + // Copy CheckSum into Encrypted Payload + // + CopyMem (EncryptPayloadBuf + EncryptPayloadSize - CheckSumSize, CheckSumData, CheckSumSize); + + IPSEC_DUMP_BUF ("Encrypted payload buffer", EncryptPayloadBuf, EncryptPayloadSize); + IPSEC_DUMP_BUF ("Integrith CheckSum Data", CheckSumData, CheckSumSize); + + // + // Clean all payload under IkePacket->PayloadList. + // + ClearAllPayloads (IkePacket); + + // + // Create Encrypted Payload and add into IkePacket->PayloadList + // + EncryptPayload = IkePayloadAlloc (); + if (EncryptPayload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Fill the encrypted payload into the IKE_PAYLOAD structure. + // + EncryptPayload->PayloadBuf = EncryptPayloadBuf; + EncryptPayload->PayloadSize = EncryptPayloadSize; + EncryptPayload->PayloadType = IKEV2_PAYLOAD_TYPE_ENCRYPT; + + IKE_PACKET_APPEND_PAYLOAD (IkePacket, EncryptPayload); + +ON_EXIT: + if (EncryptedBuf != NULL) { + FreePool (EncryptedBuf); + } + + if (EFI_ERROR (Status) && EncryptPayloadBuf != NULL) { + FreePool (EncryptPayloadBuf); + } + + if (IvBuffer != NULL) { + FreePool (IvBuffer); + } + + if (CheckSumData != NULL) { + FreePool (CheckSumData); + } + + if (IntegrityBuf != NULL) { + FreePool (IntegrityBuf); + } + + return Status; +} + +/** + Save some useful payloads after accepting the Packet. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the operation. + @param[in] IkePacket Pointer to received IkePacet. + @param[in] IkeType The type used to indicate it is in IkeSa or ChildSa or Info + exchange. + +**/ +VOID +Ikev2OnPacketAccepted ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINT8 IkeType + ) +{ + return; +} + +/** + + The notification function. It will be called when the related UDP_TX_TOKEN's event + is signaled. + + This function frees the Net Buffer pointed to the input Packet. + + @param[in] Packet Pointer to Net buffer containing the sending IKE packet. + @param[in] EndPoint Pointer to UDP_END_POINT containing the remote and local + address information. + @param[in] IoStatus The Status of the related UDP_TX_TOKEN. + @param[in] Context Pointer to data passed from the caller. + +**/ +VOID +EFIAPI +Ikev2OnPacketSent ( + IN NET_BUF *Packet, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + UINT8 Value; + IPSEC_PRIVATE_DATA *Private; + EFI_STATUS Status; + + IkePacket = (IKE_PACKET *) Context; + Private = NULL; + + if (EFI_ERROR (IoStatus)) { + DEBUG ((DEBUG_ERROR, "Error send the last packet in IkeSessionTypeIkeSa with %r\n", IoStatus)); + } + + NetbufFree (Packet); + + if (IkePacket->IsDeleteInfo) { + // + // For each RemotePeerIP, there are only one IKESA. + // + IkeSaSession = Ikev2SaSessionLookup ( + &IkePacket->Private->Ikev2EstablishedList, + &IkePacket->RemotePeerIp + ); + if (IkeSaSession == NULL) { + IkePacketFree (IkePacket); + return; + } + + Private = IkePacket->Private; + if (IkePacket->Spi != 0 ) { + // + // At that time, the established Child SA still in eht ChildSaEstablishSessionList. + // And meanwhile, if the Child SA is in the the ChildSa in Delete list, + // remove it from delete list and delete it direclty. + // + ChildSaSession = Ikev2ChildSaSessionLookupBySpi ( + &IkeSaSession->ChildSaEstablishSessionList, + IkePacket->Spi + ); + if (ChildSaSession != NULL) { + Ikev2ChildSaSessionRemove ( + &IkeSaSession->DeleteSaList, + ChildSaSession->LocalPeerSpi, + IKEV2_DELET_CHILDSA_LIST + ); + + // + // Delete the Child SA. + // + Ikev2ChildSaSilentDelete ( + IkeSaSession, + IkePacket->Spi + ); + } + + } else { + // + // Delete the IKE SA + // + DEBUG ( + (DEBUG_INFO, + "\n------ deleted Packet (cookie_i, cookie_r):(0x%lx, 0x%lx)------\n", + IkeSaSession->InitiatorCookie, + IkeSaSession->ResponderCookie) + ); + + RemoveEntryList (&IkeSaSession->BySessionTable); + Ikev2SaSessionFree (IkeSaSession); + } + } + IkePacketFree (IkePacket); + + // + // when all IKE SAs were disabled by calling "IPsecConfig -disable", the IPsec status + // should be changed. + // + if (Private != NULL && Private->IsIPsecDisabling) { + // + // After all IKE SAs were deleted, set the IPSEC_STATUS_DISABLED value in + // IPsec status variable. + // + if (IsListEmpty (&Private->Ikev1EstablishedList) && IsListEmpty (&Private->Ikev2EstablishedList)) { + Value = IPSEC_STATUS_DISABLED; + Status = gRT->SetVariable ( + IPSECCONFIG_STATUS_NAME, + &gEfiIpSecConfigProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (Value), + &Value + ); + if (!EFI_ERROR (Status)) { + // + // Set the DisabledFlag in Private data. + // + Private->IpSec.DisabledFlag = TRUE; + Private->IsIPsecDisabling = FALSE; + } + } + } +} + +/** + Send out IKEV2 packet. + + @param[in] IkeUdpService Pointer to IKE_UDP_SERVICE used to send the IKE packet. + @param[in] SessionCommon Pointer to IKEV1_SESSION_COMMON related to the IKE packet. + @param[in] IkePacket Pointer to IKE_PACKET to be sent out. + @param[in] IkeType The type of IKE to point what's kind of the IKE + packet is to be sent out. IKE_SA_TYPE, IKE_INFO_TYPE + and IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS The operation complete successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +Ikev2SendIkePacket ( + IN IKE_UDP_SERVICE *IkeUdpService, + IN UINT8 *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINTN IkeType + ) +{ + EFI_STATUS Status; + NET_BUF *IkePacketNetbuf; + UDP_END_POINT EndPoint; + IKEV2_SESSION_COMMON *Common; + + Common = (IKEV2_SESSION_COMMON *) SessionCommon; + + // + // Set the resend interval + // + if (Common->TimeoutInterval == 0) { + Common->TimeoutInterval = IKE_DEFAULT_TIMEOUT_INTERVAL; + } + + // + // Retransfer the packet if it is initial packet. + // + if (IkePacket->Header->Flags == IKE_HEADER_FLAGS_INIT) { + // + // Set timer for next retry, this will cancel previous timer + // + Status = gBS->SetTimer ( + Common->TimeoutEvent, + TimerRelative, + MultU64x32 (Common->TimeoutInterval, 10000) // ms->100ns + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + IKE_PACKET_REF (IkePacket); + // + // If the last sent packet is same with this round packet, the packet is resent packet. + // + if (IkePacket != Common->LastSentPacket && Common->LastSentPacket != NULL) { + IkePacketFree (Common->LastSentPacket); + } + + Common->LastSentPacket = IkePacket; + + // + // Transform IkePacke to NetBuf + // + IkePacketNetbuf = IkeNetbufFromPacket ((UINT8 *) SessionCommon, IkePacket, IkeType); + if (IkePacketNetbuf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (&EndPoint, sizeof (UDP_END_POINT)); + EndPoint.RemotePort = IKE_DEFAULT_PORT; + CopyMem (&IkePacket->RemotePeerIp, &Common->RemotePeerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&EndPoint.RemoteAddr, &Common->RemotePeerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&EndPoint.LocalAddr, &Common->LocalPeerIp, sizeof (EFI_IP_ADDRESS)); + + IPSEC_DUMP_PACKET (IkePacket, EfiIPsecOutBound, IkeUdpService->IpVersion); + + if (IkeUdpService->IpVersion == IP_VERSION_4) { + EndPoint.RemoteAddr.Addr[0] = HTONL (EndPoint.RemoteAddr.Addr[0]); + EndPoint.LocalAddr.Addr[0] = HTONL (EndPoint.LocalAddr.Addr[0]); + } + + // + // Call UDPIO to send out the IKE packet. + // + Status = UdpIoSendDatagram ( + IkeUdpService->Output, + IkePacketNetbuf, + &EndPoint, + NULL, + Ikev2OnPacketSent, + (VOID*)IkePacket + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error send packet with %r\n", Status)); + } + + return Status; +} + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h new file mode 100644 index 0000000000..6096a3ba77 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h @@ -0,0 +1,438 @@ +/** @file + The Definitions related to IKEv2 payload. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef _IKE_V2_PAYLOAD_H_ +#define _IKE_V2_PAYLOAD_H_ + +// +// Payload Type for IKEv2 +// +#define IKEV2_PAYLOAD_TYPE_NONE 0 +#define IKEV2_PAYLOAD_TYPE_SA 33 +#define IKEV2_PAYLOAD_TYPE_KE 34 +#define IKEV2_PAYLOAD_TYPE_ID_INIT 35 +#define IKEV2_PAYLOAD_TYPE_ID_RSP 36 +#define IKEV2_PAYLOAD_TYPE_CERT 37 +#define IKEV2_PAYLOAD_TYPE_CERTREQ 38 +#define IKEV2_PAYLOAD_TYPE_AUTH 39 +#define IKEV2_PAYLOAD_TYPE_NONCE 40 +#define IKEV2_PAYLOAD_TYPE_NOTIFY 41 +#define IKEV2_PAYLOAD_TYPE_DELETE 42 +#define IKEV2_PAYLOAD_TYPE_VENDOR 43 +#define IKEV2_PAYLOAD_TYPE_TS_INIT 44 +#define IKEV2_PAYLOAD_TYPE_TS_RSP 45 +#define IKEV2_PAYLOAD_TYPE_ENCRYPT 46 +#define IKEV2_PAYLOAD_TYPE_CP 47 +#define IKEV2_PAYLOAD_TYPE_EAP 48 + +// +// IKE header Flag for IKEv2 +// +#define IKE_HEADER_FLAGS_INIT 0x08 +#define IKE_HEADER_FLAGS_RESPOND 0x20 +#define IKE_HEADER_FLAGS_CHILD_INIT 0 + +// +// IKE Header Exchange Type for IKEv2 +// +#define IKEV2_EXCHANGE_TYPE_INIT 34 +#define IKEV2_EXCHANGE_TYPE_AUTH 35 +#define IKEV2_EXCHANGE_TYPE_CREATE_CHILD 36 +#define IKEV2_EXCHANGE_TYPE_INFO 37 + +#pragma pack(1) +typedef struct { + UINT8 NextPayload; + UINT8 Reserved; + UINT16 PayloadLength; +} IKEV2_COMMON_PAYLOAD_HEADER; +#pragma pack() + +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + // + // Proposals + // +} IKEV2_SA; +#pragma pack() + +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 ProposalIndex; + UINT8 ProtocolId; + UINT8 SpiSize; + UINT8 NumTransforms; +} IKEV2_PROPOSAL; +#pragma pack() + +// +// IKEv2 Transform Type Values presented within Transform Payload +// +#define IKEV2_TRANSFORM_TYPE_ENCR 1 // Encryption Algorithm +#define IKEV2_TRANSFORM_TYPE_PRF 2 // Pseduo-Random Func +#define IKEV2_TRANSFORM_TYPE_INTEG 3 // Integrity Algorithm +#define IKEV2_TRANSFORM_TYPE_DH 4 // DH Group +#define IKEV2_TRANSFORM_TYPE_ESN 5 // Extended Sequence Number + +// +// IKEv2 Transform ID for Encrypt Algorithm (ENCR) +// +#define IKEV2_TRANSFORM_ID_ENCR_DES_IV64 1 +#define IKEV2_TRANSFORM_ID_ENCR_DES 2 +#define IKEV2_TRANSFORM_ID_ENCR_3DES 3 +#define IKEV2_TRANSFORM_ID_ENCR_RC5 4 +#define IKEV2_TRANSFORM_ID_ENCR_IDEA 5 +#define IKEV2_TRANSFORM_ID_ENCR_CAST 6 +#define IKEV2_TRANSFORM_ID_ENCR_BLOWFISH 7 +#define IKEV2_TRANSFORM_ID_ENCR_3IDEA 8 +#define IKEV2_TRANSFORM_ID_ENCR_DES_IV32 9 +#define IKEV2_TRANSFORM_ID_ENCR_NULL 11 +#define IKEV2_TRANSFORM_ID_ENCR_AES_CBC 12 +#define IKEV2_TRANSFORM_ID_ENCR_AES_CTR 13 + +// +// IKEv2 Transform ID for Pseudo-Random Function (PRF) +// +#define IKEV2_TRANSFORM_ID_PRF_HMAC_MD5 1 +#define IKEV2_TRANSFORM_ID_PRF_HMAC_SHA1 2 +#define IKEV2_TRANSFORM_ID_PRF_HMAC_TIGER 3 +#define IKEV2_TRANSFORM_ID_PRF_AES128_XCBC 4 + +// +// IKEv2 Transform ID for Integrity Algorithm (INTEG) +// +#define IKEV2_TRANSFORM_ID_AUTH_NONE 0 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_MD5_96 1 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_SHA1_96 2 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_DES_MAC 3 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_KPDK_MD5 4 +#define IKEV2_TRANSFORM_ID_AUTH_HMAC_AES_XCBC_96 5 + +// +// IKEv2 Transform ID for Diffie-Hellman Group (DH) +// +#define IKEV2_TRANSFORM_ID_DH_768MODP 1 +#define IKEV2_TRANSFORM_ID_DH_1024MODP 2 +#define IKEV2_TRANSFORM_ID_DH_2048MODP 14 + +// +// IKEv2 Attribute Type Values +// +#define IKEV2_ATTRIBUTE_TYPE_KEYLEN 14 + +// +// Transform Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 TransformType; + UINT8 Reserved; + UINT16 TransformId; + // + // SA Attributes + // +} IKEV2_TRANSFORM; +#pragma pack() + +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT16 DhGroup; + UINT16 Reserved; + // + // Remaining part contains the key exchanged + // +} IKEV2_KEY_EXCHANGE; +#pragma pack() + +// +// Identification Type Values presented within Ikev2 ID payload +// +#define IKEV2_ID_TYPE_IPV4_ADDR 1 +#define IKEV2_ID_TYPE_FQDN 2 +#define IKEV2_ID_TYPE_RFC822_ADDR 3 +#define IKEV2_ID_TYPE_IPV6_ADDR 5 +#define IKEV2_ID_TYPE_DER_ASN1_DN 9 +#define IKEV2_ID_TYPE_DER_ASN1_GN 10 +#define IKEV2_ID_TYPE_KEY_ID 11 + +// +// Identification Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 IdType; + UINT8 Reserver1; + UINT16 Reserver2; + // + // Identification Data + // +} IKEV2_ID; +#pragma pack() + +// +// Encoding Type presented in IKEV2 Cert Payload +// +#define IKEV2_CERT_ENCODEING_RESERVED 0 +#define IKEV2_CERT_ENCODEING_X509_CERT_WRAP 1 +#define IKEV2_CERT_ENCODEING_PGP_CERT 2 +#define IKEV2_CERT_ENCODEING_DNS_SIGN_KEY 3 +#define IKEV2_CERT_ENCODEING_X509_CERT_SIGN 4 +#define IKEV2_CERT_ENCODEING_KERBEROS_TOKEN 6 +#define IKEV2_CERT_ENCODEING_REVOCATION_LIST_CERT 7 +#define IKEV2_CERT_ENCODEING_AUTH_REVOCATION_LIST 8 +#define IKEV2_CERT_ENCODEING_SPKI_CERT 9 +#define IKEV2_CERT_ENCODEING_X509_CERT_ATTRIBUTE 10 +#define IKEV2_CERT_ENCODEING_RAW_RSA_KEY 11 +#define IKEV2_CERT_ENCODEING_HASH_AND_URL_OF_X509_CERT 12 + +// +// IKEV2 Certificate Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 CertEncoding; + // + // Cert Data + // +} IKEV2_CERT; +#pragma pack() + +// +// IKEV2 Certificate Request Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 CertEncoding; + // + // Cert Authority + // +} IKEV2_CERT_REQ; +#pragma pack() + +// +// Authentication Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 AuthMethod; + UINT8 Reserved1; + UINT16 Reserved2; + // + // Auth Data + // +} IKEV2_AUTH; +#pragma pack() + +// +// Authmethod in Authentication Payload +// +#define IKEV2_AUTH_METHOD_RSA 1; // RSA Digital Signature +#define IKEV2_AUTH_METHOD_SKMI 2; // Shared Key Message Integrity +#define IKEV2_AUTH_METHOD_DSS 3; // DSS Digital Signature + +// +// IKEv2 Nonce Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + // + // Nonce Data + // +} IKEV2_NONCE; +#pragma pack() + +// +// Notification Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 ProtocolId; + UINT8 SpiSize; + UINT16 MessageType; + // + // SPI and Notification Data + // +} IKEV2_NOTIFY; +#pragma pack() + +// +// Notify Message Types presented within IKEv2 Notify Payload +// +#define IKEV2_NOTIFICATION_UNSUPPORT_CRITICAL_PAYLOAD 1 +#define IKEV2_NOTIFICATION_INVALID_IKE_SPI 4 +#define IKEV2_NOTIFICATION_INVALID_MAJOR_VERSION 5 +#define IKEV2_NOTIFICATION_INVALID_SYNTAX 7 +#define IKEV2_NOTIFICATION_INVALID_MESSAGE_ID 9 +#define IKEV2_NOTIFICATION_INVALID_SPI 11 +#define IKEV2_NOTIFICATION_NO_PROPOSAL_CHOSEN 14 +#define IKEV2_NOTIFICATION_INVALID_KEY_PAYLOAD 17 +#define IKEV2_NOTIFICATION_AUTHENTICATION_FAILED 24 +#define IKEV2_NOTIFICATION_SINGLE_PAIR_REQUIRED 34 +#define IKEV2_NOTIFICATION_NO_ADDITIONAL_SAS 35 +#define IKEV2_NOTIFICATION_INTERNAL_ADDRESS_FAILURE 36 +#define IKEV2_NOTIFICATION_FAILED_CP_REQUIRED 37 +#define IKEV2_NOTIFICATION_TS_UNCCEPTABLE 38 +#define IKEV2_NOTIFICATION_INVALID_SELECTORS 39 +#define IKEV2_NOTIFICATION_COOKIE 16390 +#define IKEV2_NOTIFICATION_USE_TRANSPORT_MODE 16391 +#define IKEV2_NOTIFICATION_REKEY_SA 16393 + +// +// IKEv2 Protocol ID +// +// +// IKEv2 Delete Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 ProtocolId; + UINT8 SpiSize; + UINT16 NumSpis; + // + // SPIs + // +} IKEV2_DELETE; +#pragma pack() + +// +// Traffic Selector Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 TSNumbers; + UINT8 Reserved1; + UINT16 Reserved2; + // + // Traffic Selector + // +} IKEV2_TS; +#pragma pack() + +// +// Traffic Selector +// +#pragma pack(1) +typedef struct { + UINT8 TSType; + UINT8 IpProtocolId; + UINT16 SelecorLen; + UINT16 StartPort; + UINT16 EndPort; + // + // Starting Address && Ending Address + // +} TRAFFIC_SELECTOR; +#pragma pack() + +// +// Ts Type in Traffic Selector +// +#define IKEV2_TS_TYPE_IPV4_ADDR_RANGE 7 +#define IKEV2_TS_TYPS_IPV6_ADDR_RANGE 8 + +// +// Vendor Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + // + // Vendor ID + // +} IKEV2_VENDOR; +#pragma pack() + +// +// Encrypted Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + // + // IV, Encrypted IKE Payloads, Padding, PAD length, Integrity CheckSum + // +} IKEV2_ENCRYPTED; +#pragma pack() + +#pragma pack(1) +typedef struct { + UINT8 PadLength; +} IKEV2_PAD_LEN; +#pragma pack() + +// +// Configuration Payload +// +#pragma pack(1) +typedef struct { + IKEV2_COMMON_PAYLOAD_HEADER Header; + UINT8 CfgType; + UINT8 Reserve1; + UINT16 Reserve2; + // + // Configuration Attributes + // +} IKEV2_CFG; +#pragma pack() + +// +// Configuration Payload CPG type +// +#define IKEV2_CFG_TYPE_REQUEST 1 +#define IKEV2_CFG_TYPE_REPLY 2 +#define IKEV2_CFG_TYPE_SET 3 +#define IKEV2_CFG_TYPE_ACK 4 + +// +// Configuration Attributes +// +#pragma pack(1) +typedef struct { + UINT16 AttritType; + UINT16 ValueLength; +} IKEV2_CFG_ATTRIBUTES; +#pragma pack() + +// +// Configuration Attributes +// +#define IKEV2_CFG_ATTR_INTERNAL_IP4_ADDRESS 1 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_NBTMASK 2 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_DNS 3 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_NBNS 4 +#define IKEV2_CFG_ATTR_INTERNA_ADDRESS_BXPIRY 5 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_DHCP 6 +#define IKEV2_CFG_ATTR_APPLICATION_VERSION 7 +#define IKEV2_CFG_ATTR_INTERNAL_IP6_ADDRESS 8 +#define IKEV2_CFG_ATTR_INTERNAL_IP6_DNS 10 +#define IKEV2_CFG_ATTR_INTERNAL_IP6_NBNS 11 +#define IKEV2_CFG_ATTR_INTERNAL_IP6_DHCP 12 +#define IKEV2_CFG_ATTR_INTERNAL_IP4_SUBNET 13 +#define IKEV2_CFG_ATTR_SUPPORTED_ATTRIBUTES 14 +#define IKEV2_CFG_ATTR_IP6_SUBNET 15 + +#endif + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c new file mode 100644 index 0000000000..4cbfac33b1 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c @@ -0,0 +1,2262 @@ +/** @file + The operations for IKEv2 SA. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Utility.h" +#include "IpSecDebug.h" +#include "IkeService.h" +#include "Ikev2.h" + +/** + Generates the DH Key. + + This generates the DH local public key and store it in the IKEv2 SA Session's GxBuffer. + + @param[in] IkeSaSession Pointer to related IKE SA Session. + + @retval EFI_SUCCESS The operation succeeded. + @retval Others The operation failed. + +**/ +EFI_STATUS +Ikev2GenerateSaDhPublicKey ( + IN IKEV2_SA_SESSION *IkeSaSession + ); + +/** + Generates the IKEv2 SA key for the furthure IKEv2 exchange. + + @param[in] IkeSaSession Pointer to IKEv2 SA Session. + @param[in] KePayload Pointer to Key payload used to generate the Key. + + @retval EFI_UNSUPPORTED If the Algorithm Id is not supported. + @retval EFI_SUCCESS The operation succeeded. + +**/ +EFI_STATUS +Ikev2GenerateSaKeys ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *KePayload + ); + +/** + Generates the Keys for the furthure IPsec Protocol. + + @param[in] ChildSaSession Pointer to IKE Child SA Session. + @param[in] KePayload Pointer to Key payload used to generate the Key. + + @retval EFI_UNSUPPORTED If one or more Algorithm Id is unsupported. + @retval EFI_SUCCESS The operation succeeded. + +**/ +EFI_STATUS +Ikev2GenerateChildSaKeys ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession, + IN IKE_PAYLOAD *KePayload + ); + +/** + Gernerates IKEv2 packet for IKE_SA_INIT exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION related to the exchange. + @param[in] Context Context Data passed by caller. + + @retval EFI_SUCCESS The IKEv2 packet generation succeeded. + @retval Others The IKEv2 packet generation failed. + +**/ +IKE_PACKET * +Ikev2InitPskGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *KePayload; + IKE_PAYLOAD *NoncePayload; + IKE_PAYLOAD *NotifyPayload; + EFI_STATUS Status; + + SaPayload = NULL; + KePayload = NULL; + NoncePayload = NULL; + NotifyPayload = NULL; + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + + // + // 1. Allocate IKE packet + // + IkePacket = IkePacketAlloc (); + if (IkePacket == NULL) { + goto CheckError; + } + + // + // 1.a Fill the IkePacket->Hdr + // + IkePacket->Header->ExchangeType = IKEV2_EXCHANGE_TYPE_INIT; + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + IkePacket->Header->Version = (UINT8) (2 << 4); + IkePacket->Header->MessageId = 0; + + if (IkeSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT; + } else { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + } + + // + // If the NCookie is not NULL, this IKE_SA_INIT packet is resent by the NCookie + // and the NCookie payload should be the first payload in this packet. + // + if (IkeSaSession->NCookie != NULL) { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_NOTIFY; + NotifyPayload = Ikev2GenerateNotifyPayload ( + IPSEC_PROTO_ISAKMP, + IKEV2_PAYLOAD_TYPE_SA, + 0, + IKEV2_NOTIFICATION_COOKIE, + NULL, + IkeSaSession->NCookie, + IkeSaSession->NCookieSize + ); + } else { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_SA; + } + + // + // 2. Generate SA Payload according to the SaData & SaParams + // + SaPayload = Ikev2GenerateSaPayload ( + IkeSaSession->SaData, + IKEV2_PAYLOAD_TYPE_KE, + IkeSessionTypeIkeSa + ); + + // + // 3. Generate DH public key. + // The DhPrivate Key has been generated in Ikev2InitPskParser, if the + // IkeSaSession is responder. If resending IKE_SA_INIT with Cookie Notify + // No need to recompute the Public key. + // + if ((IkeSaSession->SessionCommon.IsInitiator) && (IkeSaSession->NCookie == NULL)) { + Status = Ikev2GenerateSaDhPublicKey (IkeSaSession); + if (EFI_ERROR (Status)) { + goto CheckError; + } + } + + // + // 4. Generate KE Payload according to SaParams->DhGroup + // + KePayload = Ikev2GenerateKePayload ( + IkeSaSession, + IKEV2_PAYLOAD_TYPE_NONCE + ); + + // + // 5. Generate Nonce Payload + // If resending IKE_SA_INIT with Cookie Notify paylaod, no need to regenerate + // the Nonce Payload. + // + if ((IkeSaSession->SessionCommon.IsInitiator) && (IkeSaSession->NCookie == NULL)) { + IkeSaSession->NiBlkSize = IKE_NONCE_SIZE; + IkeSaSession->NiBlock = IkeGenerateNonce (IKE_NONCE_SIZE); + if (IkeSaSession->NiBlock == NULL) { + goto CheckError; + } + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + NoncePayload = Ikev2GenerateNoncePayload ( + IkeSaSession->NiBlock, + IkeSaSession->NiBlkSize, + IKEV2_PAYLOAD_TYPE_NONE + ); + } else { + // + // The Nonce Payload has been created in Ikev2PskParser if the IkeSaSession is + // responder. + // + NoncePayload = Ikev2GenerateNoncePayload ( + IkeSaSession->NrBlock, + IkeSaSession->NrBlkSize, + IKEV2_PAYLOAD_TYPE_NONE + ); + } + + if (NotifyPayload != NULL) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload); + } + if (SaPayload != NULL) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, SaPayload); + } + if (KePayload != NULL) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, KePayload); + } + if (NoncePayload != NULL) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NoncePayload); + } + + return IkePacket; + +CheckError: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + if (SaPayload != NULL) { + IkePayloadFree (SaPayload); + } + return NULL; +} + +/** + Parses the IKEv2 packet for IKE_SA_INIT exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION related to the exchange. + @param[in] IkePacket The received IKE packet to be parsed. + + @retval EFI_SUCCESS The IKEv2 packet is acceptable and the relative data is + saved for furthure communication. + @retval EFI_INVALID_PARAMETER The IKEv2 packet is malformed or the SA proposal is unacceptable. + +**/ +EFI_STATUS +Ikev2InitPskParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *KeyPayload; + IKE_PAYLOAD *IkePayload; + IKE_PAYLOAD *NoncePayload; + IKE_PAYLOAD *NotifyPayload; + UINT8 *NonceBuffer; + UINTN NonceSize; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + KeyPayload = NULL; + SaPayload = NULL; + NoncePayload = NULL; + IkePayload = NULL; + NotifyPayload = NULL; + + // + // Iterate payloads to find the SaPayload and KeyPayload. + // + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_SA) { + SaPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_KE) { + KeyPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_NONCE) { + NoncePayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_NOTIFY) { + NotifyPayload = IkePayload; + } + } + + // + // According to RFC 4306 - 2.6. If the responder responds with the COOKIE Notify + // payload with the cookie data, initiator MUST retry the IKE_SA_INIT with a + // Notify payload of type COOKIE containing the responder suppplied cookie data + // as first payload and all other payloads unchanged. + // + if (IkeSaSession->SessionCommon.IsInitiator) { + if (NotifyPayload != NULL) { + Status = Ikev2ParserNotifyCookiePayload (NotifyPayload, IkeSaSession); + return Status; + } + } + + if ((KeyPayload == NULL) || (SaPayload == NULL) || (NoncePayload == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Store NoncePayload for SKEYID computing. + // + NonceSize = NoncePayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER); + NonceBuffer = (UINT8 *) AllocatePool (NonceSize); + if (NonceBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto CheckError; + } + + CopyMem ( + NonceBuffer, + NoncePayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + NonceSize + ); + + // + // Check if IkePacket Header matches the state + // + if (IkeSaSession->SessionCommon.IsInitiator) { + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_RESPOND + // + if (IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + + // + // 2. Parse the SA Payload and Key Payload to find out the cryptographic + // suite and fill in the Sa paramse into CommonSession->SaParams + // + if (!Ikev2SaParseSaPayload (IkeSaSession, SaPayload, IkePacket->Header->Flags)) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + + // + // 3. If Initiator, the NoncePayload is Nr_b. + // + IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateAuth); + IkeSaSession->NrBlock = NonceBuffer; + IkeSaSession->NrBlkSize = NonceSize; + IkeSaSession->SessionCommon.State = IkeStateAuth; + IkeSaSession->ResponderCookie = IkePacket->Header->ResponderCookie; + + // + // 4. Change the state of IkeSaSession + // + IkeSaSession->SessionCommon.State = IkeStateAuth; + } else { + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_INIT + // + if (IkePacket->Header->Flags != IKE_HEADER_FLAGS_INIT) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + + // + // 2. Parse the SA payload and find out the perfered one + // and fill in the SA parameters into CommonSession->SaParams and SaData into + // IkeSaSession for the responder SA payload generation. + // + if (!Ikev2SaParseSaPayload (IkeSaSession, SaPayload, IkePacket->Header->Flags)) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + + // + // 3. Generat Dh Y parivate Key + // + Status = Ikev2GenerateSaDhPublicKey (IkeSaSession); + if (EFI_ERROR (Status)) { + goto CheckError; + } + + // + // 4. If Responder, the NoncePayload is Ni_b and go to generate Nr_b. + // + IkeSaSession->NiBlock = NonceBuffer; + IkeSaSession->NiBlkSize = NonceSize; + + // + // 5. Generate Nr_b + // + IkeSaSession->NrBlock = IkeGenerateNonce (IKE_NONCE_SIZE); + ASSERT (IkeSaSession->NrBlock != NULL); + IkeSaSession->NrBlkSize = IKE_NONCE_SIZE; + + // + // 6. Save the Cookies + // + IkeSaSession->InitiatorCookie = IkePacket->Header->InitiatorCookie; + IkeSaSession->ResponderCookie = IkeGenerateCookie (); + } + + if (IkeSaSession->SessionCommon.PreferDhGroup != ((IKEV2_KEY_EXCHANGE *)KeyPayload->PayloadBuf)->DhGroup) { + Status = EFI_INVALID_PARAMETER; + goto CheckError; + } + // + // Call Ikev2GenerateSaKeys to create SKEYID, SKEYID_d, SKEYID_a, SKEYID_e. + // + Status = Ikev2GenerateSaKeys (IkeSaSession, KeyPayload); + if (EFI_ERROR(Status)) { + goto CheckError; + } + return EFI_SUCCESS; + +CheckError: + if (NonceBuffer != NULL) { + FreePool (NonceBuffer); + } + + return Status; +} + +/** + Generates the IKEv2 packet for IKE_AUTH exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION. + @param[in] Context Context data passed by caller. + + @retval Pointer to IKE Packet to be sent out. + +**/ +IKE_PACKET * +Ikev2AuthPskGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *IdPayload; + IKE_PAYLOAD *AuthPayload; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *TsiPayload; + IKE_PAYLOAD *TsrPayload; + IKE_PAYLOAD *NotifyPayload; + IKE_PAYLOAD *CpPayload; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList)); + + IkePacket = NULL; + IdPayload = NULL; + AuthPayload = NULL; + SaPayload = NULL; + TsiPayload = NULL; + TsrPayload = NULL; + NotifyPayload = NULL; + CpPayload = NULL; + NotifyPayload = NULL; + + // + // 1. Allocate IKE Packet + // + IkePacket= IkePacketAlloc (); + if (IkePacket == NULL) { + return NULL; + } + + // + // 1.a Fill the IkePacket Header. + // + IkePacket->Header->ExchangeType = IKEV2_EXCHANGE_TYPE_AUTH; + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + IkePacket->Header->Version = (UINT8)(2 << 4); + if (ChildSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ID_INIT; + } else { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ID_RSP; + } + + // + // According to RFC4306_2.2, For the IKE_SA_INIT message the MessageID should + // be always number 0 and 1; + // + IkePacket->Header->MessageId = 1; + + if (IkeSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT; + } else { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + } + + // + // 2. Generate ID Payload according to IP version and address. + // + IdPayload = Ikev2GenerateIdPayload ( + &IkeSaSession->SessionCommon, + IKEV2_PAYLOAD_TYPE_AUTH + ); + if (IdPayload == NULL) { + goto CheckError; + } + + // + // 3. Generate Auth Payload + // If it is tunnel mode, should create the configuration payload after the + // Auth payload. + // + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + + AuthPayload = Ikev2PskGenerateAuthPayload ( + ChildSaSession->IkeSaSession, + IdPayload, + IKEV2_PAYLOAD_TYPE_SA, + FALSE + ); + } else { + AuthPayload = Ikev2PskGenerateAuthPayload ( + ChildSaSession->IkeSaSession, + IdPayload, + IKEV2_PAYLOAD_TYPE_CP, + FALSE + ); + if (IkeSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) { + CpPayload = Ikev2GenerateCpPayload ( + ChildSaSession->IkeSaSession, + IKEV2_PAYLOAD_TYPE_SA, + IKEV2_CFG_ATTR_INTERNAL_IP4_ADDRESS + ); + } else { + CpPayload = Ikev2GenerateCpPayload ( + ChildSaSession->IkeSaSession, + IKEV2_PAYLOAD_TYPE_SA, + IKEV2_CFG_ATTR_INTERNAL_IP6_ADDRESS + ); + } + + if (CpPayload == NULL) { + goto CheckError; + } + } + + if (AuthPayload == NULL) { + goto CheckError; + } + + // + // 4. Generate SA Payload according to the SA Data in ChildSaSession + // + SaPayload = Ikev2GenerateSaPayload ( + ChildSaSession->SaData, + IKEV2_PAYLOAD_TYPE_TS_INIT, + IkeSessionTypeChildSa + ); + if (SaPayload == NULL) { + goto CheckError; + } + + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + // + // Generate Tsi and Tsr. + // + TsiPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_TS_RSP, + FALSE + ); + + TsrPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_NOTIFY, + FALSE + ); + + // + // Generate Notify Payload. If transport mode, there should have Notify + // payload with TRANSPORT_MODE notification. + // + NotifyPayload = Ikev2GenerateNotifyPayload ( + 0, + IKEV2_PAYLOAD_TYPE_NONE, + 0, + IKEV2_NOTIFICATION_USE_TRANSPORT_MODE, + NULL, + NULL, + 0 + ); + if (NotifyPayload == NULL) { + goto CheckError; + } + } else { + // + // Generate Tsr for Tunnel mode. + // + TsiPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_TS_RSP, + TRUE + ); + TsrPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_NONE, + FALSE + ); + } + + if (TsiPayload == NULL || TsrPayload == NULL) { + goto CheckError; + } + + IKE_PACKET_APPEND_PAYLOAD (IkePacket, IdPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, AuthPayload); + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CpPayload); + } + IKE_PACKET_APPEND_PAYLOAD (IkePacket, SaPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsiPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsrPayload); + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload); + } + + return IkePacket; + +CheckError: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + + if (IdPayload != NULL) { + IkePayloadFree (IdPayload); + } + + if (AuthPayload != NULL) { + IkePayloadFree (AuthPayload); + } + + if (CpPayload != NULL) { + IkePayloadFree (CpPayload); + } + + if (SaPayload != NULL) { + IkePayloadFree (SaPayload); + } + + if (TsiPayload != NULL) { + IkePayloadFree (TsiPayload); + } + + if (TsrPayload != NULL) { + IkePayloadFree (TsrPayload); + } + + if (NotifyPayload != NULL) { + IkePayloadFree (NotifyPayload); + } + + return NULL; +} + +/** + Parses IKE_AUTH packet. + + @param[in] SaSession Pointer to the IKE_SA_SESSION related to this packet. + @param[in] IkePacket Pointer to the IKE_AUTH packet to be parsered. + + @retval EFI_INVALID_PARAMETER The IKE packet is malformed or the SA + proposal is unacceptable. + @retval EFI_SUCCESS The IKE packet is acceptable and the + relative data is saved for furthure communication. + +**/ +EFI_STATUS +Ikev2AuthPskParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *IkePayload; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *IdiPayload; + IKE_PAYLOAD *IdrPayload; + IKE_PAYLOAD *AuthPayload; + IKE_PAYLOAD *TsiPayload; + IKE_PAYLOAD *TsrPayload; + IKE_PAYLOAD *VerifiedAuthPayload; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList)); + + SaPayload = NULL; + IdiPayload = NULL; + IdrPayload = NULL; + AuthPayload = NULL; + TsiPayload = NULL; + TsrPayload = NULL; + + // + // Iterate payloads to find the SaPayload/ID/AUTH/TS Payload. + // + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_INIT) { + IdiPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_RSP) { + IdrPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_SA) { + SaPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_AUTH) { + AuthPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_INIT) { + TsiPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_RSP) { + TsrPayload = IkePayload; + } + } + + if ((SaPayload == NULL) || (AuthPayload == NULL) || (TsiPayload == NULL) || (TsrPayload == NULL)) { + return EFI_INVALID_PARAMETER; + } + if ((IdiPayload == NULL) && (IdrPayload == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check IkePacket Header is match the state + // + if (IkeSaSession->SessionCommon.IsInitiator) { + + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_RESPOND + // + if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) || + (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH) + ) { + return EFI_INVALID_PARAMETER; + } + + } else { + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_INIT + // + if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_INIT) || + (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH) + ) { + return EFI_INVALID_PARAMETER; + } + + // + // 2. Parse the SA payload and Key Payload and find out the perferable one + // and fill in the Sa paramse into CommonSession->SaParams and SaData into + // IkeSaSession for the responder SA payload generation. + // + } + + // + // Verify the Auth Payload. + // + VerifiedAuthPayload = Ikev2PskGenerateAuthPayload ( + IkeSaSession, + IkeSaSession->SessionCommon.IsInitiator ? IdrPayload : IdiPayload, + IKEV2_PAYLOAD_TYPE_SA, + TRUE + ); + if ((VerifiedAuthPayload != NULL) && + (0 != CompareMem ( + VerifiedAuthPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + AuthPayload->PayloadBuf + sizeof (IKEV2_COMMON_PAYLOAD_HEADER), + VerifiedAuthPayload->PayloadSize - sizeof (IKEV2_COMMON_PAYLOAD_HEADER) + ))) { + return EFI_INVALID_PARAMETER; + }; + + // + // 3. Parse the SA Payload to find out the cryptographic suite + // and fill in the Sa paramse into CommonSession->SaParams. If no acceptable + // porposal found, return EFI_INVALID_PARAMETER. + // + if (!Ikev2ChildSaParseSaPayload (ChildSaSession, SaPayload, IkePacket->Header->Flags)) { + return EFI_INVALID_PARAMETER; + } + + // + // 4. Parse TSi, TSr payloads. + // + if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != + ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId) && + (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) && + (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) + ) { + return EFI_INVALID_PARAMETER; + } + + if (!IkeSaSession->SessionCommon.IsInitiator) { + // + //TODO:check the Port range. Only support any port and one certain port here. + // + ChildSaSession->ProtoId = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId; + ChildSaSession->LocalPort = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort; + ChildSaSession->RemotePort = ((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort; + // + // Association a SPD with this SA. + // + Status = Ikev2ChildSaAssociateSpdEntry (ChildSaSession); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + // + // Associate the IkeSaSession's SPD to the first ChildSaSession's SPD. + // + if (ChildSaSession->IkeSaSession->Spd == NULL) { + ChildSaSession->IkeSaSession->Spd = ChildSaSession->Spd; + Status = Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession); + if (EFI_ERROR (Status)) { + return Status; + } + } + } else { + // + //TODO:check the Port range. + // + if ((((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) && + (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->RemotePort) + ) { + return EFI_INVALID_PARAMETER; + } + if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) && + (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->LocalPort) + ) { + return EFI_INVALID_PARAMETER; + } + // + // For the tunnel mode, it should add the vitual IP address into the SA's SPD Selector. + // + if (ChildSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + if (!ChildSaSession->IkeSaSession->SessionCommon.IsInitiator) { + // + // If it is tunnel mode, the UEFI part must be the initiator. + // + return EFI_INVALID_PARAMETER; + } + // + // Get the Virtual IP address from the Tsi traffic selector. + // TODO: check the CFG reply payload + // + CopyMem ( + &ChildSaSession->SpdSelector->LocalAddress[0].Address, + TsiPayload->PayloadBuf + sizeof (IKEV2_TS) + sizeof (TRAFFIC_SELECTOR), + (ChildSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) ? + sizeof (EFI_IPv4_ADDRESS) : sizeof (EFI_IPv6_ADDRESS) + ); + } + } + + // + // 5. Generate keymats for IPsec protocol. + // + Status = Ikev2GenerateChildSaKeys (ChildSaSession, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + // + // 6. Change the state of IkeSaSession + // + IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateIkeSaEstablished); + IkeSaSession->SessionCommon.State = IkeStateIkeSaEstablished; + } + + return EFI_SUCCESS; +} + +/** + Gernerates IKEv2 packet for IKE_SA_INIT exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION related to the exchange. + @param[in] Context Context Data passed by caller. + + @retval EFI_SUCCESS The IKE packet generation succeeded. + @retval Others The IKE packet generation failed. + +**/ +IKE_PACKET* +Ikev2InitCertGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKE_PAYLOAD *CertReqPayload; + LIST_ENTRY *Node; + IKE_PAYLOAD *NoncePayload; + + if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) { + return NULL; + } + + // + // The first two messages exchange is same between PSK and Cert. + // + IkePacket = Ikev2InitPskGenerator (SaSession, Context); + + if ((IkePacket != NULL) && (!((IKEV2_SA_SESSION *)SaSession)->SessionCommon.IsInitiator)) { + // + // Add the Certification Request Payload + // + CertReqPayload = Ikev2GenerateCertificatePayload ( + (IKEV2_SA_SESSION *)SaSession, + IKEV2_PAYLOAD_TYPE_NONE, + (UINT8*)PcdGetPtr(PcdIpsecUefiCaFile), + PcdGet32(PcdIpsecUefiCaFileSize), + IKEV2_CERT_ENCODEING_HASH_AND_URL_OF_X509_CERT, + TRUE + ); + // + // Change Nonce Payload Next payload type. + // + IKE_PACKET_END_PAYLOAD (IkePacket, Node); + NoncePayload = IKE_PAYLOAD_BY_PACKET (Node); + ((IKEV2_NONCE *)NoncePayload->PayloadBuf)->Header.NextPayload = IKEV2_PAYLOAD_TYPE_CERTREQ; + + // + // Add Certification Request Payload + // + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CertReqPayload); + } + + return IkePacket; +} + +/** + Parses the IKEv2 packet for IKE_SA_INIT exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION related to the exchange. + @param[in] IkePacket The received IKEv2 packet to be parsed. + + @retval EFI_SUCCESS The IKEv2 packet is acceptable and the relative data is + saved for furthure communication. + @retval EFI_INVALID_PARAMETER The IKE packet is malformed or the SA proposal is unacceptable. + @retval EFI_UNSUPPORTED The certificate authentication is not supported. + +**/ +EFI_STATUS +Ikev2InitCertParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) { + return EFI_UNSUPPORTED; + } + + // + // The first two messages exchange is same between PSK and Cert. + // Todo: Parse Certificate Request from responder Initial Exchange. + // + return Ikev2InitPskParser (SaSession, IkePacket); +} + +/** + Generates the IKEv2 packet for IKE_AUTH exchange. + + @param[in] SaSession Pointer to IKEV2_SA_SESSION. + @param[in] Context Context data passed by caller. + + @retval Pointer to IKEv2 Packet to be sent out. + +**/ +IKE_PACKET * +Ikev2AuthCertGenerator ( + IN UINT8 *SaSession, + IN VOID *Context + ) +{ + IKE_PACKET *IkePacket; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *IdPayload; + IKE_PAYLOAD *AuthPayload; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *TsiPayload; + IKE_PAYLOAD *TsrPayload; + IKE_PAYLOAD *NotifyPayload; + IKE_PAYLOAD *CpPayload; + IKE_PAYLOAD *CertPayload; + IKE_PAYLOAD *CertReqPayload; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + + if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) { + return NULL; + } + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList)); + + IkePacket = NULL; + IdPayload = NULL; + AuthPayload = NULL; + CpPayload = NULL; + SaPayload = NULL; + TsiPayload = NULL; + TsrPayload = NULL; + NotifyPayload = NULL; + CertPayload = NULL; + CertReqPayload = NULL; + + // + // 1. Allocate IKE Packet + // + IkePacket= IkePacketAlloc (); + if (IkePacket == NULL) { + return NULL; + } + + // + // 1.a Fill the IkePacket Header. + // + IkePacket->Header->ExchangeType = IKEV2_EXCHANGE_TYPE_AUTH; + IkePacket->Header->InitiatorCookie = IkeSaSession->InitiatorCookie; + IkePacket->Header->ResponderCookie = IkeSaSession->ResponderCookie; + IkePacket->Header->Version = (UINT8)(2 << 4); + if (ChildSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ID_INIT; + } else { + IkePacket->Header->NextPayload = IKEV2_PAYLOAD_TYPE_ID_RSP; + } + + // + // According to RFC4306_2.2, For the IKE_SA_INIT message the MessageID should + // be always number 0 and 1; + // + IkePacket->Header->MessageId = 1; + + if (IkeSaSession->SessionCommon.IsInitiator) { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT; + } else { + IkePacket->Header->Flags = IKE_HEADER_FLAGS_RESPOND; + } + + // + // 2. Generate ID Payload according to IP version and address. + // + IdPayload = Ikev2GenerateCertIdPayload ( + &IkeSaSession->SessionCommon, + IKEV2_PAYLOAD_TYPE_CERT, + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificate), + PcdGet32 (PcdIpsecUefiCertificateSize) + ); + if (IdPayload == NULL) { + goto CheckError; + } + + // + // 3. Generate Certificate Payload + // + CertPayload = Ikev2GenerateCertificatePayload ( + IkeSaSession, + (UINT8)(IkeSaSession->SessionCommon.IsInitiator ? IKEV2_PAYLOAD_TYPE_CERTREQ : IKEV2_PAYLOAD_TYPE_AUTH), + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificate), + PcdGet32 (PcdIpsecUefiCertificateSize), + IKEV2_CERT_ENCODEING_X509_CERT_SIGN, + FALSE + ); + if (CertPayload == NULL) { + goto CheckError; + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + CertReqPayload = Ikev2GenerateCertificatePayload ( + IkeSaSession, + IKEV2_PAYLOAD_TYPE_AUTH, + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificate), + PcdGet32 (PcdIpsecUefiCertificateSize), + IKEV2_CERT_ENCODEING_HASH_AND_URL_OF_X509_CERT, + TRUE + ); + if (CertReqPayload == NULL) { + goto CheckError; + } + } + + // + // 4. Generate Auth Payload + // If it is tunnel mode, should create the configuration payload after the + // Auth payload. + // + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + AuthPayload = Ikev2CertGenerateAuthPayload ( + ChildSaSession->IkeSaSession, + IdPayload, + IKEV2_PAYLOAD_TYPE_SA, + FALSE, + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificateKey), + PcdGet32 (PcdIpsecUefiCertificateKeySize), + ChildSaSession->IkeSaSession->Pad->Data->AuthData, + ChildSaSession->IkeSaSession->Pad->Data->AuthDataSize + ); + } else { + AuthPayload = Ikev2CertGenerateAuthPayload ( + ChildSaSession->IkeSaSession, + IdPayload, + IKEV2_PAYLOAD_TYPE_CP, + FALSE, + (UINT8 *)PcdGetPtr (PcdIpsecUefiCertificateKey), + PcdGet32 (PcdIpsecUefiCertificateKeySize), + ChildSaSession->IkeSaSession->Pad->Data->AuthData, + ChildSaSession->IkeSaSession->Pad->Data->AuthDataSize + ); + if (IkeSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) { + CpPayload = Ikev2GenerateCpPayload ( + ChildSaSession->IkeSaSession, + IKEV2_PAYLOAD_TYPE_SA, + IKEV2_CFG_ATTR_INTERNAL_IP4_ADDRESS + ); + } else { + CpPayload = Ikev2GenerateCpPayload ( + ChildSaSession->IkeSaSession, + IKEV2_PAYLOAD_TYPE_SA, + IKEV2_CFG_ATTR_INTERNAL_IP6_ADDRESS + ); + } + + if (CpPayload == NULL) { + goto CheckError; + } + } + + if (AuthPayload == NULL) { + goto CheckError; + } + + // + // 5. Generate SA Payload according to the Sa Data in ChildSaSession + // + SaPayload = Ikev2GenerateSaPayload ( + ChildSaSession->SaData, + IKEV2_PAYLOAD_TYPE_TS_INIT, + IkeSessionTypeChildSa + ); + if (SaPayload == NULL) { + goto CheckError; + } + + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + // + // Generate Tsi and Tsr. + // + TsiPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_TS_RSP, + FALSE + ); + + TsrPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_NOTIFY, + FALSE + ); + + // + // Generate Notify Payload. If transport mode, there should have Notify + // payload with TRANSPORT_MODE notification. + // + NotifyPayload = Ikev2GenerateNotifyPayload ( + 0, + IKEV2_PAYLOAD_TYPE_NONE, + 0, + IKEV2_NOTIFICATION_USE_TRANSPORT_MODE, + NULL, + NULL, + 0 + ); + if (NotifyPayload == NULL) { + goto CheckError; + } + } else { + // + // Generate Tsr for Tunnel mode. + // + TsiPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_TS_RSP, + TRUE + ); + TsrPayload = Ikev2GenerateTsPayload ( + ChildSaSession, + IKEV2_PAYLOAD_TYPE_NONE, + FALSE + ); + } + + if (TsiPayload == NULL || TsrPayload == NULL) { + goto CheckError; + } + + IKE_PACKET_APPEND_PAYLOAD (IkePacket, IdPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CertPayload); + if (IkeSaSession->SessionCommon.IsInitiator) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CertReqPayload); + } + IKE_PACKET_APPEND_PAYLOAD (IkePacket, AuthPayload); + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, CpPayload); + } + IKE_PACKET_APPEND_PAYLOAD (IkePacket, SaPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsiPayload); + IKE_PACKET_APPEND_PAYLOAD (IkePacket, TsrPayload); + if (IkeSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTransport) { + IKE_PACKET_APPEND_PAYLOAD (IkePacket, NotifyPayload); + } + + return IkePacket; + +CheckError: + if (IkePacket != NULL) { + IkePacketFree (IkePacket); + } + + if (IdPayload != NULL) { + IkePayloadFree (IdPayload); + } + + if (CertPayload != NULL) { + IkePayloadFree (CertPayload); + } + + if (CertReqPayload != NULL) { + IkePayloadFree (CertReqPayload); + } + + if (AuthPayload != NULL) { + IkePayloadFree (AuthPayload); + } + + if (CpPayload != NULL) { + IkePayloadFree (CpPayload); + } + + if (SaPayload != NULL) { + IkePayloadFree (SaPayload); + } + + if (TsiPayload != NULL) { + IkePayloadFree (TsiPayload); + } + + if (TsrPayload != NULL) { + IkePayloadFree (TsrPayload); + } + + if (NotifyPayload != NULL) { + IkePayloadFree (NotifyPayload); + } + + return NULL; +} + +/** + Parses IKE_AUTH packet. + + @param[in] SaSession Pointer to the IKE_SA_SESSION related to this packet. + @param[in] IkePacket Pointer to the IKE_AUTH packet to be parsered. + + @retval EFI_INVALID_PARAMETER The IKEv2 packet is malformed or the SA + proposal is unacceptable. + @retval EFI_SUCCESS The IKE packet is acceptable and the + relative data is saved for furthure communication. + @retval EFI_UNSUPPORTED The certificate authentication is not supported. + +**/ +EFI_STATUS +Ikev2AuthCertParser ( + IN UINT8 *SaSession, + IN IKE_PACKET *IkePacket + ) +{ + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SA_SESSION *IkeSaSession; + IKE_PAYLOAD *IkePayload; + IKE_PAYLOAD *SaPayload; + IKE_PAYLOAD *IdiPayload; + IKE_PAYLOAD *IdrPayload; + IKE_PAYLOAD *AuthPayload; + IKE_PAYLOAD *TsiPayload; + IKE_PAYLOAD *TsrPayload; + IKE_PAYLOAD *CertPayload; + IKE_PAYLOAD *VerifiedAuthPayload; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + if (!FeaturePcdGet (PcdIpsecCertificateEnabled)) { + return EFI_UNSUPPORTED; + } + + IkeSaSession = (IKEV2_SA_SESSION *) SaSession; + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (GetFirstNode (&IkeSaSession->ChildSaSessionList)); + + SaPayload = NULL; + IdiPayload = NULL; + IdrPayload = NULL; + AuthPayload = NULL; + TsiPayload = NULL; + TsrPayload = NULL; + CertPayload = NULL; + VerifiedAuthPayload = NULL; + Status = EFI_INVALID_PARAMETER; + + // + // Iterate payloads to find the SaPayload/ID/AUTH/TS Payload. + // + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_INIT) { + IdiPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_ID_RSP) { + IdrPayload = IkePayload; + } + + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_SA) { + SaPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_AUTH) { + AuthPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_INIT) { + TsiPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_RSP) { + TsrPayload = IkePayload; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_CERT) { + CertPayload = IkePayload; + } + } + + if ((SaPayload == NULL) || (AuthPayload == NULL) || (TsiPayload == NULL) || + (TsrPayload == NULL) || (CertPayload == NULL)) { + goto Exit; + } + if ((IdiPayload == NULL) && (IdrPayload == NULL)) { + goto Exit; + } + + // + // Check IkePacket Header is match the state + // + if (IkeSaSession->SessionCommon.IsInitiator) { + + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_RESPOND + // + if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_RESPOND) || + (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH)) { + goto Exit; + } + } else { + // + // 1. Check the IkePacket->Hdr == IKE_HEADER_FLAGS_INIT + // + if ((IkePacket->Header->Flags != IKE_HEADER_FLAGS_INIT) || + (IkePacket->Header->ExchangeType != IKEV2_EXCHANGE_TYPE_AUTH)) { + goto Exit; + } + } + + // + // Verify the Auth Payload. + // + VerifiedAuthPayload = Ikev2CertGenerateAuthPayload ( + IkeSaSession, + IkeSaSession->SessionCommon.IsInitiator ? IdrPayload:IdiPayload, + IKEV2_PAYLOAD_TYPE_SA, + TRUE, + NULL, + 0, + NULL, + 0 + ); + + if ((VerifiedAuthPayload != NULL) && + (!IpSecCryptoIoVerifySignDataByCertificate ( + CertPayload->PayloadBuf + sizeof (IKEV2_CERT), + CertPayload->PayloadSize - sizeof (IKEV2_CERT), + (UINT8 *)PcdGetPtr (PcdIpsecUefiCaFile), + PcdGet32 (PcdIpsecUefiCaFileSize), + VerifiedAuthPayload->PayloadBuf + sizeof (IKEV2_AUTH), + VerifiedAuthPayload->PayloadSize - sizeof (IKEV2_AUTH), + AuthPayload->PayloadBuf + sizeof (IKEV2_AUTH), + AuthPayload->PayloadSize - sizeof (IKEV2_AUTH) + ))) { + goto Exit; + } + + // + // 3. Parse the SA Payload to find out the cryptographic suite + // and fill in the SA paramse into CommonSession->SaParams. If no acceptable + // porposal found, return EFI_INVALID_PARAMETER. + // + if (!Ikev2ChildSaParseSaPayload (ChildSaSession, SaPayload, IkePacket->Header->Flags)) { + goto Exit; + } + + // + // 4. Parse TSi, TSr payloads. + // + if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != + ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId) && + (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) && + (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId != 0) + ) { + goto Exit; + } + + if (!IkeSaSession->SessionCommon.IsInitiator) { + // + //Todo:check the Port range. Only support any port and one certain port here. + // + ChildSaSession->ProtoId = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->IpProtocolId; + ChildSaSession->LocalPort = ((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort; + ChildSaSession->RemotePort = ((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort; + // + // Association a SPD with this SA. + // + if (EFI_ERROR (Ikev2ChildSaAssociateSpdEntry (ChildSaSession))) { + goto Exit; + } + // + // Associate the IkeSaSession's SPD to the first ChildSaSession's SPD. + // + if (ChildSaSession->IkeSaSession->Spd == NULL) { + ChildSaSession->IkeSaSession->Spd = ChildSaSession->Spd; + Status = Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + } else { + // + // Todo:check the Port range. + // + if ((((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) && + (((TRAFFIC_SELECTOR *)(TsrPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->RemotePort) + ) { + goto Exit; + } + if ((((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != 0) && + (((TRAFFIC_SELECTOR *)(TsiPayload->PayloadBuf + sizeof (IKEV2_TS)))->StartPort != ChildSaSession->LocalPort) + ) { + goto Exit; + } + // + // For the tunnel mode, it should add the vitual IP address into the SA's SPD Selector. + // + if (ChildSaSession->Spd->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + if (!ChildSaSession->IkeSaSession->SessionCommon.IsInitiator) { + // + // If it is tunnel mode, the UEFI part must be the initiator. + // + goto Exit; + } + // + // Get the Virtual IP address from the Tsi traffic selector. + // TODO: check the CFG reply payload + // + CopyMem ( + &ChildSaSession->SpdSelector->LocalAddress[0].Address, + TsiPayload->PayloadBuf + sizeof (IKEV2_TS) + sizeof (TRAFFIC_SELECTOR), + (ChildSaSession->SessionCommon.UdpService->IpVersion == IP_VERSION_4) ? + sizeof (EFI_IPv4_ADDRESS) : sizeof (EFI_IPv6_ADDRESS) + ); + } + } + + // + // 5. Generat keymats for IPsec protocol. + // + Status = Ikev2GenerateChildSaKeys (ChildSaSession, NULL); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (IkeSaSession->SessionCommon.IsInitiator) { + // + // 6. Change the state of IkeSaSession + // + IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateIkeSaEstablished); + IkeSaSession->SessionCommon.State = IkeStateIkeSaEstablished; + } + + Status = EFI_SUCCESS; + +Exit: + if (VerifiedAuthPayload != NULL) { + IkePayloadFree (VerifiedAuthPayload); + } + return Status; +} + +/** + Generates the DH Public Key. + + This generates the DH local public key and store it in the IKE SA Session's GxBuffer. + + @param[in] IkeSaSession Pointer to related IKE SA Session. + + @retval EFI_SUCCESS The operation succeeded. + @retval Others The operation failed. + +**/ +EFI_STATUS +Ikev2GenerateSaDhPublicKey ( + IN IKEV2_SA_SESSION *IkeSaSession + ) +{ + EFI_STATUS Status; + IKEV2_SESSION_KEYS *IkeKeys; + + IkeSaSession->IkeKeys = AllocateZeroPool (sizeof (IKEV2_SESSION_KEYS)); + if (IkeSaSession->IkeKeys == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IkeKeys = IkeSaSession->IkeKeys; + IkeKeys->DhBuffer = AllocateZeroPool (sizeof (IKEV2_DH_BUFFER)); + if (IkeKeys->DhBuffer == NULL) { + FreePool (IkeSaSession->IkeKeys); + return EFI_OUT_OF_RESOURCES; + } + + // + // Init DH with the certain DH Group Description. + // + IkeKeys->DhBuffer->GxSize = OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].Size >> 3; + IkeKeys->DhBuffer->GxBuffer = AllocateZeroPool (IkeKeys->DhBuffer->GxSize); + if (IkeKeys->DhBuffer->GxBuffer == NULL) { + FreePool (IkeKeys->DhBuffer); + FreePool (IkeSaSession->IkeKeys); + return EFI_OUT_OF_RESOURCES; + } + + // + // Get X PublicKey + // + Status = IpSecCryptoIoDhGetPublicKey ( + &IkeKeys->DhBuffer->DhContext, + OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].GroupGenerator, + OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].Size, + OakleyModpGroup[(UINT8)IkeSaSession->SessionCommon.PreferDhGroup].Modulus, + IkeKeys->DhBuffer->GxBuffer, + &IkeKeys->DhBuffer->GxSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error CPLKeyManGetKeyParam X public key error Status = %r\n", Status)); + + FreePool (IkeKeys->DhBuffer->GxBuffer); + + FreePool (IkeKeys->DhBuffer); + + FreePool (IkeSaSession->IkeKeys); + + return Status; + } + + IPSEC_DUMP_BUF ("DH Public Key (g^x) Dump", IkeKeys->DhBuffer->GxBuffer, IkeKeys->DhBuffer->GxSize); + + return EFI_SUCCESS; +} + +/** + Computes the DH Shared/Exchange Key. + + Given peer's public key, this function computes the exchanged common key and + stores it in the IKEv2 SA Session's GxyBuffer. + + @param[in] DhBuffer Pointer to buffer of peer's puliic key. + @param[in] KePayload Pointer to received key payload. + + @retval EFI_SUCCESS The operation succeeded. + @retval Otherwise The operation failed. + +**/ +EFI_STATUS +Ikev2GenerateSaDhComputeKey ( + IN IKEV2_DH_BUFFER *DhBuffer, + IN IKE_PAYLOAD *KePayload + ) +{ + EFI_STATUS Status; + IKEV2_KEY_EXCHANGE *Ke; + UINT8 *PubKey; + UINTN PubKeySize; + + Ke = (IKEV2_KEY_EXCHANGE *) KePayload->PayloadBuf; + PubKey = (UINT8 *) (Ke + 1); + PubKeySize = KePayload->PayloadSize - sizeof (IKEV2_KEY_EXCHANGE); + DhBuffer->GxySize = DhBuffer->GxSize; + DhBuffer->GxyBuffer = AllocateZeroPool (DhBuffer->GxySize); + if (DhBuffer->GxyBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get GxyBuf + // + Status = IpSecCryptoIoDhComputeKey ( + DhBuffer->DhContext, + PubKey, + PubKeySize, + DhBuffer->GxyBuffer, + &DhBuffer->GxySize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error CPLKeyManGetKeyParam Y session key error Status = %r\n", Status)); + + FreePool (DhBuffer->GxyBuffer); + + return Status; + } + + // + // Create GxyBuf. + // + DhBuffer->GySize = PubKeySize; + DhBuffer->GyBuffer = AllocateZeroPool (DhBuffer->GySize); + if (DhBuffer->GyBuffer == NULL) { + FreePool (DhBuffer->GxyBuffer); + + return Status; + } + + CopyMem (DhBuffer->GyBuffer, PubKey, DhBuffer->GySize); + + IPSEC_DUMP_BUF ("DH Public Key (g^y) Dump", DhBuffer->GyBuffer, DhBuffer->GySize); + IPSEC_DUMP_BUF ("DH Shared Key (g^xy) Dump", DhBuffer->GxyBuffer, DhBuffer->GxySize); + + return EFI_SUCCESS; +} + +/** + Generates the IKE SKEYSEED and seven other secrets. SK_d, SK_ai, SK_ar, SK_ei, SK_er, + SK_pi, SK_pr are keys for the furthure IKE exchange. + + @param[in] IkeSaSession Pointer to IKE SA Session. + @param[in] KePayload Pointer to Key payload used to generate the Key. + + @retval EFI_UNSUPPORTED If one or more Algorithm Id is not supported. + @retval EFI_OUT_OF_RESOURCES If there is no enough resource to be allocated to + meet the requirement. + @retval EFI_SUCCESS The operation succeeded. + +**/ +EFI_STATUS +Ikev2GenerateSaKeys ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *KePayload + ) +{ + EFI_STATUS Status; + IKEV2_SA_PARAMS *SaParams; + PRF_DATA_FRAGMENT Fragments[4]; + UINT64 InitiatorCookieNet; + UINT64 ResponderCookieNet; + UINT8 *KeyBuffer; + UINTN KeyBufferSize; + UINTN AuthAlgKeyLen; + UINTN EncryptAlgKeyLen; + UINTN IntegrityAlgKeyLen; + UINTN PrfAlgKeyLen; + UINT8 *OutputKey; + UINTN OutputKeyLength; + UINT8 *Digest; + UINTN DigestSize; + + Digest = NULL; + OutputKey = NULL; + KeyBuffer = NULL; + Status = EFI_SUCCESS; + + // + // Generate Gxy + // + Status = Ikev2GenerateSaDhComputeKey (IkeSaSession->IkeKeys->DhBuffer, KePayload); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get the key length of Authenticaion, Encryption, PRF, and Integrity. + // + SaParams = IkeSaSession->SessionCommon.SaParams; + AuthAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->Prf); + EncryptAlgKeyLen = IpSecGetEncryptKeyLength ((UINT8)SaParams->EncAlgId); + IntegrityAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->IntegAlgId); + PrfAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->Prf); + + // + // If one or more algorithm is not support, return EFI_UNSUPPORTED. + // + if (AuthAlgKeyLen == 0 || + EncryptAlgKeyLen == 0 || + IntegrityAlgKeyLen == 0 || + PrfAlgKeyLen == 0 + ) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // Compute SKEYSEED = prf(Ni | Nr, g^ir) + // + KeyBufferSize = IkeSaSession->NiBlkSize + IkeSaSession->NrBlkSize; + KeyBuffer = AllocateZeroPool (KeyBufferSize); + if (KeyBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem (KeyBuffer, IkeSaSession->NiBlock, IkeSaSession->NiBlkSize); + CopyMem (KeyBuffer + IkeSaSession->NiBlkSize, IkeSaSession->NrBlock, IkeSaSession->NrBlkSize); + + Fragments[0].Data = IkeSaSession->IkeKeys->DhBuffer->GxyBuffer; + Fragments[0].DataSize = IkeSaSession->IkeKeys->DhBuffer->GxySize; + + DigestSize = IpSecGetHmacDigestLength ((UINT8)SaParams->Prf); + Digest = AllocateZeroPool (DigestSize); + + if (Digest == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + IpSecCryptoIoHmac ( + (UINT8)SaParams->Prf, + KeyBuffer, + KeyBufferSize, + (HASH_DATA_FRAGMENT *) Fragments, + 1, + Digest, + DigestSize + ); + + // + // {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } = prf+ + // (SKEYSEED, Ni | Nr | SPIi | SPIr ) + // + Fragments[0].Data = IkeSaSession->NiBlock; + Fragments[0].DataSize = IkeSaSession->NiBlkSize; + Fragments[1].Data = IkeSaSession->NrBlock; + Fragments[1].DataSize = IkeSaSession->NrBlkSize; + InitiatorCookieNet = HTONLL (IkeSaSession->InitiatorCookie); + ResponderCookieNet = HTONLL (IkeSaSession->ResponderCookie); + Fragments[2].Data = (UINT8 *)(&InitiatorCookieNet); + Fragments[2].DataSize = sizeof (IkeSaSession->InitiatorCookie); + Fragments[3].Data = (UINT8 *)(&ResponderCookieNet); + Fragments[3].DataSize = sizeof (IkeSaSession->ResponderCookie); + + IPSEC_DUMP_BUF (">>> NiBlock", IkeSaSession->NiBlock, IkeSaSession->NiBlkSize); + IPSEC_DUMP_BUF (">>> NrBlock", IkeSaSession->NrBlock, IkeSaSession->NrBlkSize); + IPSEC_DUMP_BUF (">>> InitiatorCookie", (UINT8 *)&IkeSaSession->InitiatorCookie, sizeof(UINT64)); + IPSEC_DUMP_BUF (">>> ResponderCookie", (UINT8 *)&IkeSaSession->ResponderCookie, sizeof(UINT64)); + + OutputKeyLength = PrfAlgKeyLen + + 2 * EncryptAlgKeyLen + + 2 * AuthAlgKeyLen + + 2 * IntegrityAlgKeyLen; + OutputKey = AllocateZeroPool (OutputKeyLength); + if (OutputKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Generate Seven Keymates. + // + Status = Ikev2SaGenerateKey ( + (UINT8)SaParams->Prf, + Digest, + DigestSize, + OutputKey, + OutputKeyLength, + Fragments, + 4 + ); + if (EFI_ERROR(Status)) { + goto Exit; + } + + // + // Save the seven keys into KeySession. + // First, SK_d + // + IkeSaSession->IkeKeys->SkdKey = AllocateZeroPool (PrfAlgKeyLen); + if (IkeSaSession->IkeKeys->SkdKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkdKeySize = PrfAlgKeyLen; + CopyMem (IkeSaSession->IkeKeys->SkdKey, OutputKey, PrfAlgKeyLen); + + IPSEC_DUMP_BUF (">>> SK_D Key", IkeSaSession->IkeKeys->SkdKey, PrfAlgKeyLen); + + // + // Second, Sk_ai + // + IkeSaSession->IkeKeys->SkAiKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (IkeSaSession->IkeKeys->SkAiKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkAiKeySize = IntegrityAlgKeyLen; + CopyMem (IkeSaSession->IkeKeys->SkAiKey, OutputKey + PrfAlgKeyLen, IntegrityAlgKeyLen); + + IPSEC_DUMP_BUF (">>> SK_Ai Key", IkeSaSession->IkeKeys->SkAiKey, IkeSaSession->IkeKeys->SkAiKeySize); + + // + // Third, Sk_ar + // + IkeSaSession->IkeKeys->SkArKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (IkeSaSession->IkeKeys->SkArKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkArKeySize = IntegrityAlgKeyLen; + CopyMem ( + IkeSaSession->IkeKeys->SkArKey, + OutputKey + PrfAlgKeyLen + IntegrityAlgKeyLen, + IntegrityAlgKeyLen + ); + + IPSEC_DUMP_BUF (">>> SK_Ar Key", IkeSaSession->IkeKeys->SkArKey, IkeSaSession->IkeKeys->SkArKeySize); + + // + // Fourth, Sk_ei + // + IkeSaSession->IkeKeys->SkEiKey = AllocateZeroPool (EncryptAlgKeyLen); + if (IkeSaSession->IkeKeys->SkEiKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkEiKeySize = EncryptAlgKeyLen; + + CopyMem ( + IkeSaSession->IkeKeys->SkEiKey, + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen, + EncryptAlgKeyLen + ); + IPSEC_DUMP_BUF ( + ">>> SK_Ei Key", + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen, + EncryptAlgKeyLen + ); + + // + // Fifth, Sk_er + // + IkeSaSession->IkeKeys->SkErKey = AllocateZeroPool (EncryptAlgKeyLen); + if (IkeSaSession->IkeKeys->SkErKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkErKeySize = EncryptAlgKeyLen; + + CopyMem ( + IkeSaSession->IkeKeys->SkErKey, + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + EncryptAlgKeyLen, + EncryptAlgKeyLen + ); + IPSEC_DUMP_BUF ( + ">>> SK_Er Key", + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + EncryptAlgKeyLen, + EncryptAlgKeyLen + ); + + // + // Sixth, Sk_pi + // + IkeSaSession->IkeKeys->SkPiKey = AllocateZeroPool (AuthAlgKeyLen); + if (IkeSaSession->IkeKeys->SkPiKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkPiKeySize = AuthAlgKeyLen; + + CopyMem ( + IkeSaSession->IkeKeys->SkPiKey, + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen, + AuthAlgKeyLen + ); + IPSEC_DUMP_BUF ( + ">>> SK_Pi Key", + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen, + AuthAlgKeyLen + ); + + // + // Seventh, Sk_pr + // + IkeSaSession->IkeKeys->SkPrKey = AllocateZeroPool (AuthAlgKeyLen); + if (IkeSaSession->IkeKeys->SkPrKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + IkeSaSession->IkeKeys->SkPrKeySize = AuthAlgKeyLen; + + CopyMem ( + IkeSaSession->IkeKeys->SkPrKey, + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen + AuthAlgKeyLen, + AuthAlgKeyLen + ); + IPSEC_DUMP_BUF ( + ">>> SK_Pr Key", + OutputKey + AuthAlgKeyLen + 2 * IntegrityAlgKeyLen + 2 * EncryptAlgKeyLen + AuthAlgKeyLen, + AuthAlgKeyLen + ); + + +Exit: + if (Digest != NULL) { + FreePool (Digest); + } + if (KeyBuffer != NULL) { + FreePool (KeyBuffer); + } + if (OutputKey != NULL) { + FreePool (OutputKey); + } + + if (EFI_ERROR(Status)) { + if (IkeSaSession->IkeKeys->SkdKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkdKey); + } + if (IkeSaSession->IkeKeys->SkAiKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkAiKey); + } + if (IkeSaSession->IkeKeys->SkArKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkArKey); + } + if (IkeSaSession->IkeKeys->SkEiKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkEiKey); + } + if (IkeSaSession->IkeKeys->SkErKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkErKey); + } + if (IkeSaSession->IkeKeys->SkPiKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkPiKey); + } + if (IkeSaSession->IkeKeys->SkPrKey != NULL) { + FreePool (IkeSaSession->IkeKeys->SkPrKey); + } + } + + + return Status; +} + +/** + Generates the Keys for the furthure IPsec Protocol. + + @param[in] ChildSaSession Pointer to IKE Child SA Session. + @param[in] KePayload Pointer to Key payload used to generate the Key. + + @retval EFI_UNSUPPORTED If one or more Algorithm Id is not supported. + @retval EFI_SUCCESS The operation succeeded. + +**/ +EFI_STATUS +Ikev2GenerateChildSaKeys ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession, + IN IKE_PAYLOAD *KePayload + ) +{ + EFI_STATUS Status; + IKEV2_SA_PARAMS *SaParams; + PRF_DATA_FRAGMENT Fragments[3]; + UINTN EncryptAlgKeyLen; + UINTN IntegrityAlgKeyLen; + UINT8* OutputKey; + UINTN OutputKeyLength; + + Status = EFI_SUCCESS; + OutputKey = NULL; + + if (KePayload != NULL) { + // + // Generate Gxy + // + Status = Ikev2GenerateSaDhComputeKey (ChildSaSession->DhBuffer, KePayload); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Fragments[0].Data = ChildSaSession->DhBuffer->GxyBuffer; + Fragments[0].DataSize = ChildSaSession->DhBuffer->GxySize; + } + + Fragments[1].Data = ChildSaSession->NiBlock; + Fragments[1].DataSize = ChildSaSession->NiBlkSize; + Fragments[2].Data = ChildSaSession->NrBlock; + Fragments[2].DataSize = ChildSaSession->NrBlkSize; + + // + // Get the key length of Authenticaion, Encryption, PRF, and Integrity. + // + SaParams = ChildSaSession->SessionCommon.SaParams; + EncryptAlgKeyLen = IpSecGetEncryptKeyLength ((UINT8)SaParams->EncAlgId); + IntegrityAlgKeyLen = IpSecGetHmacDigestLength ((UINT8)SaParams->IntegAlgId); + OutputKeyLength = 2 * EncryptAlgKeyLen + 2 * IntegrityAlgKeyLen; + + if ((EncryptAlgKeyLen == 0) || (IntegrityAlgKeyLen == 0)) { + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // + // If KePayload is not NULL, calculate KEYMAT = prf+(SK_d, g^ir (new) | Ni | Nr ), + // otherwise, KEYMAT = prf+(SK_d, Ni | Nr ) + // + OutputKey = AllocateZeroPool (OutputKeyLength); + if (OutputKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Derive Key from the SkdKey Buffer. + // + Status = Ikev2SaGenerateKey ( + (UINT8)ChildSaSession->IkeSaSession->SessionCommon.SaParams->Prf, + ChildSaSession->IkeSaSession->IkeKeys->SkdKey, + ChildSaSession->IkeSaSession->IkeKeys->SkdKeySize, + OutputKey, + OutputKeyLength, + KePayload == NULL ? &Fragments[1] : Fragments, + KePayload == NULL ? 2 : 3 + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Copy KEYMATE (SK_ENCRYPT_i | SK_ENCRYPT_r | SK_INTEG_i | SK_INTEG_r) to + // ChildKeyMates. + // + if (!ChildSaSession->SessionCommon.IsInitiator) { + + // + // Initiator Encryption Key + // + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncAlgoId = (UINT8)SaParams->EncAlgId; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey = AllocateZeroPool (EncryptAlgKeyLen); + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey, + OutputKey, + EncryptAlgKeyLen + ); + + // + // Initiator Authentication Key + // + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthAlgoId = (UINT8)SaParams->IntegAlgId; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey, + OutputKey + EncryptAlgKeyLen, + IntegrityAlgKeyLen + ); + + // + // Responder Encrypt Key + // + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncAlgoId = (UINT8)SaParams->EncAlgId; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey = AllocateZeroPool (EncryptAlgKeyLen); + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey, + OutputKey + EncryptAlgKeyLen + IntegrityAlgKeyLen, + EncryptAlgKeyLen + ); + + // + // Responder Authentication Key + // + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthAlgoId = (UINT8)SaParams->IntegAlgId; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey, + OutputKey + 2 * EncryptAlgKeyLen + IntegrityAlgKeyLen, + IntegrityAlgKeyLen + ); + } else { + // + // Initiator Encryption Key + // + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncAlgoId = (UINT8)SaParams->EncAlgId; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey = AllocateZeroPool (EncryptAlgKeyLen); + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey, + OutputKey, + EncryptAlgKeyLen + ); + + // + // Initiator Authentication Key + // + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthAlgoId = (UINT8)SaParams->IntegAlgId; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen; + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey, + OutputKey + EncryptAlgKeyLen, + IntegrityAlgKeyLen + ); + + // + // Responder Encryption Key + // + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncAlgoId = (UINT8)SaParams->EncAlgId; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKeyLength = EncryptAlgKeyLen; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey = AllocateZeroPool (EncryptAlgKeyLen); + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey, + OutputKey + EncryptAlgKeyLen + IntegrityAlgKeyLen, + EncryptAlgKeyLen + ); + + // + // Responder Authentication Key + // + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthAlgoId = (UINT8)SaParams->IntegAlgId; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKeyLength = IntegrityAlgKeyLen; + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey = AllocateZeroPool (IntegrityAlgKeyLen); + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey, + OutputKey + 2 * EncryptAlgKeyLen + IntegrityAlgKeyLen, + IntegrityAlgKeyLen + ); + } + + IPSEC_DUMP_BUF ( + " >>> Local Encryption Key", + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey, + EncryptAlgKeyLen + ); + IPSEC_DUMP_BUF ( + " >>> Remote Encryption Key", + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey, + EncryptAlgKeyLen + ); + IPSEC_DUMP_BUF ( + " >>> Local Authentication Key", + ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey, + IntegrityAlgKeyLen + ); + IPSEC_DUMP_BUF ( + " >>> Remote Authentication Key", + ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey, + IntegrityAlgKeyLen + ); + + + +Exit: + if (EFI_ERROR (Status)) { + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey); + } + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey); + } + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey); + } + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey); + } + } + + if (OutputKey != NULL) { + FreePool (OutputKey); + } + + return EFI_SUCCESS; +} + +GLOBAL_REMOVE_IF_UNREFERENCED IKEV2_PACKET_HANDLER mIkev2Initial[][2] = { + { //PSK + { // IKEV2_INIT + Ikev2InitPskParser, + Ikev2InitPskGenerator + }, + { //IKEV2_AUTH + Ikev2AuthPskParser, + Ikev2AuthPskGenerator + } + }, + { // CERT + { // IKEV2_INIT + Ikev2InitCertParser, + Ikev2InitCertGenerator + }, + { // IKEV2_AUTH + Ikev2AuthCertParser, + Ikev2AuthCertGenerator + }, + }, +}; diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c b/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c new file mode 100644 index 0000000000..5b26ba1d02 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c @@ -0,0 +1,2787 @@ +/** @file + The Common operations used by IKE Exchange Process. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Utility.h" +#include "IpSecDebug.h" +#include "IkeService.h" +#include "IpSecConfigImpl.h" + +UINT16 mIkev2EncryptAlgorithmList[IKEV2_SUPPORT_ENCRYPT_ALGORITHM_NUM] = { + IKEV2_TRANSFORM_ID_ENCR_3DES, + IKEV2_TRANSFORM_ID_ENCR_AES_CBC, +}; + +UINT16 mIkev2PrfAlgorithmList[IKEV2_SUPPORT_PRF_ALGORITHM_NUM] = { + IKEV2_TRANSFORM_ID_PRF_HMAC_SHA1, +}; + +UINT16 mIkev2DhGroupAlgorithmList[IKEV2_SUPPORT_DH_ALGORITHM_NUM] = { + IKEV2_TRANSFORM_ID_DH_1024MODP, + IKEV2_TRANSFORM_ID_DH_2048MODP, +}; + +UINT16 mIkev2AuthAlgorithmList[IKEV2_SUPPORT_AUTH_ALGORITHM_NUM] = { + IKEV2_TRANSFORM_ID_AUTH_HMAC_SHA1_96, +}; + +/** + Allocate buffer for IKEV2_SA_SESSION and initialize it. + + @param[in] Private Pointer to IPSEC_PRIVATE_DATA. + @param[in] UdpService Pointer to IKE_UDP_SERVICE related to this IKE SA Session. + + @return Pointer to IKEV2_SA_SESSION or NULL. + +**/ +IKEV2_SA_SESSION * +Ikev2SaSessionAlloc ( + IN IPSEC_PRIVATE_DATA *Private, + IN IKE_UDP_SERVICE *UdpService + ) +{ + EFI_STATUS Status; + IKEV2_SESSION_COMMON *SessionCommon; + IKEV2_SA_SESSION *IkeSaSession; + + IkeSaSession = AllocateZeroPool (sizeof (IKEV2_SA_SESSION)); + if (IkeSaSession == NULL) { + return NULL; + } + + // + // Initialize the fields of IkeSaSession and its SessionCommon. + // + IkeSaSession->NCookie = NULL; + IkeSaSession->Signature = IKEV2_SA_SESSION_SIGNATURE; + IkeSaSession->InitiatorCookie = IkeGenerateCookie (); + IkeSaSession->ResponderCookie = 0; + // + // BUGBUG: Message ID starts from 2 is to match the OpenSwan requirement, but it + // might not match the IPv6 Logo. In its test specification, it mentions that + // the Message ID should start from zero after the IKE_SA_INIT exchange. + // + IkeSaSession->MessageId = 2; + SessionCommon = &IkeSaSession->SessionCommon; + SessionCommon->UdpService = UdpService; + SessionCommon->Private = Private; + SessionCommon->IkeSessionType = IkeSessionTypeIkeSa; + SessionCommon->IkeVer = 2; + SessionCommon->AfterEncodePayload = NULL; + SessionCommon->BeforeDecodePayload = NULL; + + // + // Create a resend notfiy event for retry. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ikev2ResendNotify, + SessionCommon, + &SessionCommon->TimeoutEvent + ); + + if (EFI_ERROR (Status)) { + FreePool (IkeSaSession); + return NULL; + } + + // + // Initialize the lists in IkeSaSession. + // + InitializeListHead (&IkeSaSession->ChildSaSessionList); + InitializeListHead (&IkeSaSession->ChildSaEstablishSessionList); + InitializeListHead (&IkeSaSession->InfoMIDList); + InitializeListHead (&IkeSaSession->DeleteSaList); + + return IkeSaSession; +} + +/** + Register the established IKEv2 SA into Private->Ikev2EstablishedList. If there is + IKEV2_SA_SESSION with same remote peer IP, remove the old one then register the + new one. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be registered. + @param[in] Private Pointer to IPSEC_PRAVATE_DATA. + +**/ +VOID +Ikev2SaSessionReg ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IPSEC_PRIVATE_DATA *Private + ) +{ + IKEV2_SESSION_COMMON *SessionCommon; + IKEV2_SA_SESSION *OldIkeSaSession; + EFI_STATUS Status; + UINT64 Lifetime; + + // + // Keep IKE SA exclusive to remote ip address. + // + SessionCommon = &IkeSaSession->SessionCommon; + OldIkeSaSession = Ikev2SaSessionRemove (&Private->Ikev2EstablishedList, &SessionCommon->RemotePeerIp); + if (OldIkeSaSession != NULL) { + // + // TODO: It should delete all child SAs if rekey the IKE SA. + // + Ikev2SaSessionFree (OldIkeSaSession); + } + + // + // Cleanup the fields of SessionCommon for processing. + // + Ikev2SessionCommonRefresh (SessionCommon); + + // + // Insert the ready IKE SA session into established list. + // + Ikev2SaSessionInsert (&Private->Ikev2EstablishedList, IkeSaSession, &SessionCommon->RemotePeerIp); + + // + // Create a notfiy event for the IKE SA life time counting. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ikev2LifetimeNotify, + SessionCommon, + &SessionCommon->TimeoutEvent + ); + if (EFI_ERROR(Status)){ + // + // If TimerEvent creation failed, the SA will be alive untill user disable it or + // receiving a Delete Payload from peer. + // + return; + } + + // + // Start to count the lifetime of the IKE SA. + // + if (IkeSaSession->Spd->Data->ProcessingPolicy->SaLifetime.HardLifetime == 0) { + Lifetime = IKE_SA_DEFAULT_LIFETIME; + } else { + Lifetime = IkeSaSession->Spd->Data->ProcessingPolicy->SaLifetime.HardLifetime; + } + + Status = gBS->SetTimer ( + SessionCommon->TimeoutEvent, + TimerRelative, + MultU64x32(Lifetime, 10000000) // ms->100ns + ); + if (EFI_ERROR(Status)){ + // + // If SetTimer failed, the SA will be alive untill user disable it or + // receiving a Delete Payload from peer. + // + return ; + } + + DEBUG (( + DEBUG_INFO, + "\n------IkeSa established and start to count down %d seconds lifetime\n", + Lifetime + )); + + return ; +} + +/** + Find a IKEV2_SA_SESSION by the remote peer IP. + + @param[in] SaSessionList SaSession List to be searched. + @param[in] RemotePeerIp Pointer to specified IP address. + + @return Pointer to IKEV2_SA_SESSION if find one or NULL. + +**/ +IKEV2_SA_SESSION * +Ikev2SaSessionLookup ( + IN LIST_ENTRY *SaSessionList, + IN EFI_IP_ADDRESS *RemotePeerIp + ) +{ + LIST_ENTRY *Entry; + IKEV2_SA_SESSION *IkeSaSession; + + NET_LIST_FOR_EACH (Entry, SaSessionList) { + IkeSaSession = IKEV2_SA_SESSION_BY_SESSION (Entry); + + if (CompareMem ( + &IkeSaSession->SessionCommon.RemotePeerIp, + RemotePeerIp, + sizeof (EFI_IP_ADDRESS) + ) == 0) { + + return IkeSaSession; + } + } + + return NULL; +} + +/** + Insert a IKE_SA_SESSION into IkeSaSession list. The IkeSaSession list is either + Private->Ikev2SaSession list or Private->Ikev2EstablishedList list. + + @param[in] SaSessionList Pointer to list to be inserted into. + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be inserted. + @param[in] RemotePeerIp Pointer to EFI_IP_ADDRESSS to indicate the + unique IKEV2_SA_SESSION. + +**/ +VOID +Ikev2SaSessionInsert ( + IN LIST_ENTRY *SaSessionList, + IN IKEV2_SA_SESSION *IkeSaSession, + IN EFI_IP_ADDRESS *RemotePeerIp + ) +{ + Ikev2SaSessionRemove (SaSessionList, RemotePeerIp); + InsertTailList (SaSessionList, &IkeSaSession->BySessionTable); +} + +/** + Remove the SA Session by Remote Peer IP. + + @param[in] SaSessionList Pointer to list to be searched. + @param[in] RemotePeerIp Pointer to EFI_IP_ADDRESS to use for SA Session search. + + @retval Pointer to IKEV2_SA_SESSION with the specified remote IP address or NULL. + +**/ +IKEV2_SA_SESSION * +Ikev2SaSessionRemove ( + IN LIST_ENTRY *SaSessionList, + IN EFI_IP_ADDRESS *RemotePeerIp + ) +{ + LIST_ENTRY *Entry; + IKEV2_SA_SESSION *IkeSaSession; + + NET_LIST_FOR_EACH (Entry, SaSessionList) { + IkeSaSession = IKEV2_SA_SESSION_BY_SESSION (Entry); + + if (CompareMem ( + &IkeSaSession->SessionCommon.RemotePeerIp, + RemotePeerIp, + sizeof (EFI_IP_ADDRESS) + ) == 0) { + + RemoveEntryList (Entry); + return IkeSaSession; + } + } + + return NULL; +} + +/** + Marking a SA session as on deleting. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION. + + @retval EFI_SUCCESS Find the related SA session and marked it. + +**/ +EFI_STATUS +Ikev2SaSessionOnDeleting ( + IN IKEV2_SA_SESSION *IkeSaSession + ) +{ + return EFI_SUCCESS; +} + +/** + Free specified Seession Common. The session common would belong to a IKE SA or + a Child SA. + + @param[in] SessionCommon Pointer to a Session Common. + +**/ +VOID +Ikev2SaSessionCommonFree ( + IN IKEV2_SESSION_COMMON *SessionCommon + ) +{ + + ASSERT (SessionCommon != NULL); + + if (SessionCommon->LastSentPacket != NULL) { + IkePacketFree (SessionCommon->LastSentPacket); + } + + if (SessionCommon->SaParams != NULL) { + FreePool (SessionCommon->SaParams); + } + if (SessionCommon->TimeoutEvent != NULL) { + gBS->CloseEvent (SessionCommon->TimeoutEvent); + } +} + +/** + After IKE/Child SA is estiblished, close the time event and free sent packet. + + @param[in] SessionCommon Pointer to a Session Common. + +**/ +VOID +Ikev2SessionCommonRefresh ( + IN IKEV2_SESSION_COMMON *SessionCommon + ) +{ + ASSERT (SessionCommon != NULL); + + gBS->CloseEvent (SessionCommon->TimeoutEvent); + SessionCommon->TimeoutEvent = NULL; + SessionCommon->TimeoutInterval = 0; + SessionCommon->RetryCount = 0; + if (SessionCommon->LastSentPacket != NULL) { + IkePacketFree (SessionCommon->LastSentPacket); + SessionCommon->LastSentPacket = NULL; + } + + return ; +} +/** + Free specified IKEV2 SA Session. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be freed. + +**/ +VOID +Ikev2SaSessionFree ( + IN IKEV2_SA_SESSION *IkeSaSession + ) +{ + IKEV2_SESSION_KEYS *IkeKeys; + LIST_ENTRY *Entry; + IKEV2_CHILD_SA_SESSION *ChildSa; + IKEV2_DH_BUFFER *DhBuffer; + + ASSERT (IkeSaSession != NULL); + + // + // Delete Common Session + // + Ikev2SaSessionCommonFree (&IkeSaSession->SessionCommon); + + // + // Delete ChildSaEstablish List and SAD + // + for (Entry = IkeSaSession->ChildSaEstablishSessionList.ForwardLink; + Entry != &IkeSaSession->ChildSaEstablishSessionList; + ) { + + ChildSa = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); + Entry = Entry->ForwardLink; + Ikev2ChildSaSilentDelete (ChildSa->IkeSaSession, ChildSa->LocalPeerSpi); + + } + + // + // Delete ChildSaSessionList + // + for ( Entry = IkeSaSession->ChildSaSessionList.ForwardLink; + Entry != &IkeSaSession->ChildSaSessionList; + ){ + ChildSa = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); + Entry = Entry->ForwardLink; + RemoveEntryList (Entry->BackLink); + Ikev2ChildSaSessionFree (ChildSa); + } + + // + // Delete DhBuffer and Keys + // + if (IkeSaSession->IkeKeys != NULL) { + IkeKeys = IkeSaSession->IkeKeys; + DhBuffer = IkeKeys->DhBuffer; + + // + // Delete DhBuffer + // + Ikev2DhBufferFree (DhBuffer); + + // + // Delete Keys + // + if (IkeKeys->SkAiKey != NULL) { + FreePool (IkeKeys->SkAiKey); + } + if (IkeKeys->SkArKey != NULL) { + FreePool (IkeKeys->SkArKey); + } + if (IkeKeys->SkdKey != NULL) { + FreePool (IkeKeys->SkdKey); + } + if (IkeKeys->SkEiKey != NULL) { + FreePool (IkeKeys->SkEiKey); + } + if (IkeKeys->SkErKey != NULL) { + FreePool (IkeKeys->SkErKey); + } + if (IkeKeys->SkPiKey != NULL) { + FreePool (IkeKeys->SkPiKey); + } + if (IkeKeys->SkPrKey != NULL) { + FreePool (IkeKeys->SkPrKey); + } + FreePool (IkeKeys); + } + + if (IkeSaSession->SaData != NULL) { + FreePool (IkeSaSession->SaData); + } + + if (IkeSaSession->NiBlock != NULL) { + FreePool (IkeSaSession->NiBlock); + } + + if (IkeSaSession->NrBlock != NULL) { + FreePool (IkeSaSession->NrBlock); + } + + if (IkeSaSession->NCookie != NULL) { + FreePool (IkeSaSession->NCookie); + } + + if (IkeSaSession->InitPacket != NULL) { + FreePool (IkeSaSession->InitPacket); + } + + if (IkeSaSession->RespPacket != NULL) { + FreePool (IkeSaSession->RespPacket); + } + + FreePool (IkeSaSession); + + return ; +} + +/** + Increase the MessageID in IkeSaSession. + + @param[in] IkeSaSession Pointer to a specified IKEV2_SA_SESSION. + +**/ +VOID +Ikev2SaSessionIncreaseMessageId ( + IN IKEV2_SA_SESSION *IkeSaSession + ) +{ + if (IkeSaSession->MessageId < 0xffffffff) { + IkeSaSession->MessageId ++; + } else { + // + // TODO: Trigger Rekey process. + // + } +} + +/** + Allocate memory for IKEV2 Child SA Session. + + @param[in] UdpService Pointer to IKE_UDP_SERVICE. + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to this Child SA + Session. + + @retval Pointer of a new created IKEV2 Child SA Session or NULL. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionAlloc ( + IN IKE_UDP_SERVICE *UdpService, + IN IKEV2_SA_SESSION *IkeSaSession + ) +{ + EFI_STATUS Status; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *ChildSaCommon; + IKEV2_SESSION_COMMON *SaCommon; + + ChildSaSession = AllocateZeroPool (sizeof (IKEV2_CHILD_SA_SESSION)); + if (ChildSaSession == NULL) { + return NULL; + } + + // + // Initialize the fields of ChildSaSession and its SessionCommon. + // + ChildSaSession->Signature = IKEV2_CHILD_SA_SESSION_SIGNATURE; + ChildSaSession->IkeSaSession = IkeSaSession; + ChildSaSession->MessageId = IkeSaSession->MessageId; + ChildSaSession->LocalPeerSpi = IkeGenerateSpi (); + ChildSaCommon = &ChildSaSession->SessionCommon; + ChildSaCommon->UdpService = UdpService; + ChildSaCommon->Private = IkeSaSession->SessionCommon.Private; + ChildSaCommon->IkeSessionType = IkeSessionTypeChildSa; + ChildSaCommon->IkeVer = 2; + ChildSaCommon->AfterEncodePayload = Ikev2ChildSaAfterEncodePayload; + ChildSaCommon->BeforeDecodePayload = Ikev2ChildSaBeforeDecodePayload; + SaCommon = &ChildSaSession->IkeSaSession->SessionCommon; + + // + // Create a resend notfiy event for retry. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ikev2ResendNotify, + ChildSaCommon, + &ChildSaCommon->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + FreePool (ChildSaSession); + return NULL; + } + + CopyMem (&ChildSaCommon->LocalPeerIp, &SaCommon->LocalPeerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&ChildSaCommon->RemotePeerIp, &SaCommon->RemotePeerIp, sizeof (EFI_IP_ADDRESS)); + + return ChildSaSession; +} + +/** + Register a established IKEv2 Child SA into IkeSaSession->ChildSaEstablishSessionList. + If the there is IKEV2_CHILD_SA_SESSION with same remote peer IP, remove the old one + then register the new one. + + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION to be registered. + @param[in] Private Pointer to IPSEC_PRAVATE_DATA. + +**/ +VOID +Ikev2ChildSaSessionReg ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession, + IN IPSEC_PRIVATE_DATA *Private + ) +{ + IKEV2_SESSION_COMMON *SessionCommon; + IKEV2_CHILD_SA_SESSION *OldChildSaSession; + IKEV2_SA_SESSION *IkeSaSession; + EFI_STATUS Status; + UINT64 Lifetime; + + // + // Keep the IKE SA exclusive. + // + SessionCommon = &ChildSaSession->SessionCommon; + IkeSaSession = ChildSaSession->IkeSaSession; + OldChildSaSession = Ikev2ChildSaSessionRemove ( + &IkeSaSession->ChildSaEstablishSessionList, + ChildSaSession->LocalPeerSpi, + IKEV2_ESTABLISHED_CHILDSA_LIST + ); + if (OldChildSaSession != NULL) { + // + // Free the old one. + // + Ikev2ChildSaSessionFree (OldChildSaSession); + } + + // + // Store the ready child SA into SAD. + // + Ikev2StoreSaData (ChildSaSession); + + // + // Cleanup the fields of SessionCommon for processing. + // + Ikev2SessionCommonRefresh (SessionCommon); + + // + // Insert the ready child SA session into established list. + // + Ikev2ChildSaSessionInsert (&IkeSaSession->ChildSaEstablishSessionList, ChildSaSession); + + // + // Create a Notify event for the IKE SA life time counting. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ikev2LifetimeNotify, + SessionCommon, + &SessionCommon->TimeoutEvent + ); + if (EFI_ERROR(Status)){ + return ; + } + + // + // Start to count the lifetime of the IKE SA. + // + if (ChildSaSession->Spd->Data->ProcessingPolicy->SaLifetime.HardLifetime != 0){ + Lifetime = ChildSaSession->Spd->Data->ProcessingPolicy->SaLifetime.HardLifetime; + } else { + Lifetime = CHILD_SA_DEFAULT_LIFETIME; + } + + Status = gBS->SetTimer ( + SessionCommon->TimeoutEvent, + TimerRelative, + MultU64x32(Lifetime, 10000000) // ms->100ns + ); + if (EFI_ERROR(Status)){ + return ; + } + + DEBUG (( + DEBUG_INFO, + "\n------ChildSa established and start to count down %d seconds lifetime\n", + Lifetime + )); + + return ; +} + +/** + Find the ChildSaSession by it's MessagId. + + @param[in] SaSessionList Pointer to a ChildSaSession List. + @param[in] Mid The messageId used to search ChildSaSession. + + @return Pointer to IKEV2_CHILD_SA_SESSION or NULL. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionLookupByMid ( + IN LIST_ENTRY *SaSessionList, + IN UINT32 Mid + ) +{ + LIST_ENTRY *Entry; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + + NET_LIST_FOR_EACH (Entry, SaSessionList) { + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); + + if (ChildSaSession->MessageId == Mid) { + return ChildSaSession; + } + } + return NULL; +} + +/** + This function find the Child SA by the specified SPI. + + This functin find a ChildSA session by searching the ChildSaSessionlist of + the input IKEV2_SA_SESSION by specified MessageID. + + @param[in] SaSessionList Pointer to List to be searched. + @param[in] Spi Specified SPI. + + @return Pointer to IKEV2_CHILD_SA_SESSION or NULL. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionLookupBySpi ( + IN LIST_ENTRY *SaSessionList, + IN UINT32 Spi + ) +{ + LIST_ENTRY *Entry; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + + NET_LIST_FOR_EACH (Entry, SaSessionList) { + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); + + if (ChildSaSession->RemotePeerSpi == Spi || ChildSaSession->LocalPeerSpi == Spi) { + return ChildSaSession; + } + } + + return NULL; +} + +/** + Insert a Child SA Session into the specified ChildSa list. + + @param[in] SaSessionList Pointer to list to be inserted in. + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION to be inserted. + +**/ +VOID +Ikev2ChildSaSessionInsert ( + IN LIST_ENTRY *SaSessionList, + IN IKEV2_CHILD_SA_SESSION *ChildSaSession + ) +{ + InsertTailList (SaSessionList, &ChildSaSession->ByIkeSa); +} + +/** + Remove the IKEV2_CHILD_SA_SESSION from IkeSaSessionList. + + @param[in] SaSessionList The SA Session List to be iterated. + @param[in] Spi Spi used to identified the IKEV2_CHILD_SA_SESSION. + @param[in] ListType The type of the List to indicate whether it is a + Established. + + @return The point to IKEV2_CHILD_SA_SESSION or NULL. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionRemove ( + IN LIST_ENTRY *SaSessionList, + IN UINT32 Spi, + IN UINT8 ListType + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SaSessionList) { + + if (ListType == IKEV2_ESTABLISHED_CHILDSA_LIST || ListType == IKEV2_ESTABLISHING_CHILDSA_LIST) { + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry); + } else if (ListType == IKEV2_DELET_CHILDSA_LIST) { + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_DEL_SA (Entry); + } else { + return NULL; + } + + if (ChildSaSession->RemotePeerSpi == Spi || ChildSaSession->LocalPeerSpi == Spi) { + RemoveEntryList (Entry); + return ChildSaSession; + } + } + + return NULL; +} + +/** + Mark a specified Child SA Session as on deleting. + + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. + + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +Ikev2ChildSaSessionOnDeleting ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession + ) +{ + return EFI_SUCCESS; +} + +/** + Free the memory located for the specified IKEV2_CHILD_SA_SESSION. + + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. + +**/ +VOID +Ikev2ChildSaSessionFree ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession + ) +{ + IKEV2_SESSION_COMMON *SessionCommon; + + SessionCommon = &ChildSaSession->SessionCommon; + if (ChildSaSession->SaData != NULL) { + FreePool (ChildSaSession->SaData); + } + + if (ChildSaSession->NiBlock != NULL) { + FreePool (ChildSaSession->NiBlock); + } + + if (ChildSaSession->NrBlock != NULL) { + FreePool (ChildSaSession->NrBlock); + } + + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.AuthKey); + } + + if (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.LocalPeerInfo.EspAlgoInfo.EncKey); + } + + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.AuthKey); + } + + if (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey != NULL) { + FreePool (ChildSaSession->ChildKeymats.RemotePeerInfo.EspAlgoInfo.EncKey); + } + + // + // Delete DhBuffer + // + Ikev2DhBufferFree (ChildSaSession->DhBuffer); + + // + // Delete SpdSelector + // + if (ChildSaSession->SpdSelector != NULL) { + if (ChildSaSession->SpdSelector->LocalAddress != NULL) { + FreePool (ChildSaSession->SpdSelector->LocalAddress); + } + if (ChildSaSession->SpdSelector->RemoteAddress != NULL) { + FreePool (ChildSaSession->SpdSelector->RemoteAddress); + } + FreePool (ChildSaSession->SpdSelector); + } + Ikev2SaSessionCommonFree (SessionCommon); + FreePool (ChildSaSession); + + return ; +} + +/** + Delete the specified established Child SA. + + This function delete the Child SA directly and don't send the Information Packet to + remote peer. + + @param[in] IkeSaSession Pointer to a IKE SA Session used to be searched for. + @param[in] Spi SPI used to find the Child SA. + + @retval EFI_NOT_FOUND Pointer of IKE SA Session is NULL. + @retval EFI_NOT_FOUND There is no specified Child SA related with the input + SPI under this IKE SA Session. + @retval EFI_SUCCESS Delete the Child SA successfully. + +**/ +EFI_STATUS +Ikev2ChildSaSilentDelete ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT32 Spi + ) +{ + EFI_STATUS Status; + EFI_IPSEC_CONFIG_SELECTOR *Selector; + UINTN SelectorSize; + BOOLEAN IsLocalFound; + BOOLEAN IsRemoteFound; + UINT32 LocalSpi; + UINT32 RemoteSpi; + IKEV2_CHILD_SA_SESSION *ChildSession; + EFI_IPSEC_CONFIG_SELECTOR *LocalSelector; + EFI_IPSEC_CONFIG_SELECTOR *RemoteSelector; + IPSEC_PRIVATE_DATA *Private; + + if (IkeSaSession == NULL) { + return EFI_NOT_FOUND; + } + + IsLocalFound = FALSE; + IsRemoteFound = FALSE; + ChildSession = NULL; + LocalSelector = NULL; + RemoteSelector = NULL; + + Private = IkeSaSession->SessionCommon.Private; + + // + // Remove the Established SA from ChildSaEstablishlist. + // + ChildSession = Ikev2ChildSaSessionRemove( + &(IkeSaSession->ChildSaEstablishSessionList), + Spi, + IKEV2_ESTABLISHED_CHILDSA_LIST + ); + if (ChildSession == NULL) { + return EFI_NOT_FOUND; + } + + LocalSpi = ChildSession->LocalPeerSpi; + RemoteSpi = ChildSession->RemotePeerSpi; + + SelectorSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR); + Selector = AllocateZeroPool (SelectorSize); + if (Selector == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + while (1) { + Status = EfiIpSecConfigGetNextSelector ( + &Private->IpSecConfig, + IPsecConfigDataTypeSad, + &SelectorSize, + Selector + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + FreePool (Selector); + + Selector = AllocateZeroPool (SelectorSize); + if (Selector == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + Status = EfiIpSecConfigGetNextSelector ( + &Private->IpSecConfig, + IPsecConfigDataTypeSad, + &SelectorSize, + Selector + ); + } + + if (EFI_ERROR (Status)) { + break; + } + + if (Selector->SaId.Spi == RemoteSpi) { + // + // SPI is unique. There is only one SAD whose SPI is + // same with RemoteSpi. + // + IsRemoteFound = TRUE; + RemoteSelector = AllocateZeroPool (SelectorSize); + if (RemoteSelector == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + CopyMem (RemoteSelector, Selector, SelectorSize); + } + + if (Selector->SaId.Spi == LocalSpi) { + // + // SPI is unique. There is only one SAD whose SPI is + // same with LocalSpi. + // + IsLocalFound = TRUE; + LocalSelector = AllocateZeroPool (SelectorSize); + if (LocalSelector == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + CopyMem (LocalSelector, Selector, SelectorSize); + } + } + // + // Delete SA from the Variable. + // + if (IsLocalFound) { + Status = EfiIpSecConfigSetData ( + &Private->IpSecConfig, + IPsecConfigDataTypeSad, + LocalSelector, + NULL, + NULL + ); + } + + if (IsRemoteFound) { + Status = EfiIpSecConfigSetData ( + &Private->IpSecConfig, + IPsecConfigDataTypeSad, + RemoteSelector, + NULL, + NULL + ); + + } + + DEBUG ( + (DEBUG_INFO, + "\n------IKEV2 deleted ChildSa(local spi, remote spi):(0x%x, 0x%x)------\n", + LocalSpi, + RemoteSpi) + ); + Ikev2ChildSaSessionFree (ChildSession); + + if (RemoteSelector != NULL) { + FreePool (RemoteSelector); + } + + if (LocalSelector != NULL) { + FreePool (LocalSelector); + } + + if (Selector != NULL) { + FreePool (Selector); + } + + return Status; +} + +/** + Free the specified DhBuffer. + + @param[in] DhBuffer Pointer to IKEV2_DH_BUFFER to be freed. + +**/ +VOID +Ikev2DhBufferFree ( + IKEV2_DH_BUFFER *DhBuffer +) +{ + if (DhBuffer != NULL) { + if (DhBuffer->GxBuffer != NULL) { + FreePool (DhBuffer->GxBuffer); + } + if (DhBuffer->GyBuffer != NULL) { + FreePool (DhBuffer->GyBuffer); + } + if (DhBuffer->GxyBuffer != NULL) { + FreePool (DhBuffer->GxyBuffer); + } + if (DhBuffer->DhContext != NULL) { + IpSecCryptoIoFreeDh (&DhBuffer->DhContext); + } + FreePool (DhBuffer); + } +} + +/** + This function is to parse a request IKE packet and return its request type. + The request type is one of IKE CHILD SA creation, IKE SA rekeying and + IKE CHILD SA rekeying. + + @param[in] IkePacket IKE packet to be prased. + + return the type of the IKE packet. + +**/ +IKEV2_CREATE_CHILD_REQUEST_TYPE +Ikev2ChildExchangeRequestType( + IN IKE_PACKET *IkePacket + ) +{ + BOOLEAN Flag; + LIST_ENTRY *Entry; + IKE_PAYLOAD *IkePayload; + + Flag = FALSE; + + NET_LIST_FOR_EACH (Entry, &(IkePacket)->PayloadList) { + IkePayload = IKE_PAYLOAD_BY_PACKET (Entry); + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_TS_INIT) { + // + // Packet with Ts Payload means it is for either CHILD_SA_CREATE or CHILD_SA_REKEY. + // + Flag = TRUE; + } + if (IkePayload->PayloadType == IKEV2_PAYLOAD_TYPE_NOTIFY) { + if (((IKEV2_NOTIFY*)IkePayload)->MessageType == IKEV2_NOTIFICATION_REKEY_SA) { + // + // If notify payload with REKEY_SA message type, the IkePacket is for + // rekeying Child SA. + // + return IkeRequestTypeRekeyChildSa; + } + } + }; + + if (!Flag){ + // + // The Create Child Exchange is for IKE SA rekeying. + // + return IkeRequestTypeRekeyIkeSa; + } else { + // + // If the Notify payloaad with transport mode message type, the IkePacket is + // for create Child SA. + // + return IkeRequestTypeCreateChildSa; + } +} + +/** + Associate a SPD selector to the Child SA Session. + + This function is called when the Child SA is not the first child SA of its + IKE SA. It associate a SPD to this Child SA. + + @param[in, out] ChildSaSession Pointer to the Child SA Session to be associated to + a SPD selector. + + @retval EFI_SUCCESS Associate one SPD selector to this Child SA Session successfully. + @retval EFI_NOT_FOUND Can't find the related SPD selector. + +**/ +EFI_STATUS +Ikev2ChildSaAssociateSpdEntry ( + IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession + ) +{ + IpSecVisitConfigData (IPsecConfigDataTypeSpd, Ikev2MatchSpdEntry, ChildSaSession); + if (ChildSaSession->Spd != NULL) { + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + + +/** + This function finds the SPI from Create Child SA Exchange Packet. + + @param[in] IkePacket Pointer to IKE_PACKET to be searched. + + @retval SPI number or 0 if it is not supported. + +**/ +UINT32 +Ikev2ChildExchangeRekeySpi ( + IN IKE_PACKET *IkePacket + ) +{ + // + // Not support yet. + // + return 0; +} + +/** + Validate the IKE header of received IKE packet. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to this IKE packet. + @param[in] IkeHdr Pointer to IKE header of received IKE packet. + + @retval TRUE If the IKE header is valid. + @retval FALSE If the IKE header is invalid. + +**/ +BOOLEAN +Ikev2ValidateHeader ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_HEADER *IkeHdr + ) +{ + + IKEV2_SESSION_STATE State; + + State = IkeSaSession->SessionCommon.State; + if (State == IkeStateInit) { + // + // For the IKE Initial Exchange, the MessagId should be zero. + // + if (IkeHdr->MessageId != 0) { + return FALSE; + } + } else { + if (State == IkeStateAuth) { + if (IkeHdr->MessageId != 1) { + return FALSE; + } + } + if (IkeHdr->InitiatorCookie != IkeSaSession->InitiatorCookie || + IkeHdr->ResponderCookie != IkeSaSession->ResponderCookie + ) { + // + // TODO: send notification INVALID-COOKIE + // + return FALSE; + } + } + + // + // Information Exchagne and Create Child Exchange can be started from each part. + // + if (IkeHdr->ExchangeType != IKEV2_EXCHANGE_TYPE_INFO && + IkeHdr->ExchangeType != IKEV2_EXCHANGE_TYPE_CREATE_CHILD + ) { + if (IkeSaSession->SessionCommon.IsInitiator) { + if (IkeHdr->InitiatorCookie != IkeSaSession->InitiatorCookie) { + // + // TODO: send notification INVALID-COOKIE + // + return FALSE; + } + if (IkeHdr->Flags != IKE_HEADER_FLAGS_RESPOND) { + return FALSE; + } + } else { + if (IkeHdr->Flags != IKE_HEADER_FLAGS_INIT) { + return FALSE; + } + } + } + + return TRUE; +} + +/** + Create and intialize IKEV2_SA_DATA for speicifed IKEV2_SESSION_COMMON. + + This function will be only called by the initiator. The responder's IKEV2_SA_DATA + will be generated during parsed the initiator packet. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to. + + @retval a Pointer to a new IKEV2_SA_DATA or NULL. + +**/ +IKEV2_SA_DATA * +Ikev2InitializeSaData ( + IN IKEV2_SESSION_COMMON *SessionCommon + ) +{ + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SA_DATA *SaData; + IKEV2_PROPOSAL_DATA *ProposalData; + IKEV2_TRANSFORM_DATA *TransformData; + IKE_SA_ATTRIBUTE *Attribute; + + ASSERT (SessionCommon != NULL); + // + // TODO: Remove the hard code of the support Alogrithm. Those data should be + // get from the SPD/PAD data. + // + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + SaData = AllocateZeroPool ( + sizeof (IKEV2_SA_DATA) + + sizeof (IKEV2_PROPOSAL_DATA) * 2 + + sizeof (IKEV2_TRANSFORM_DATA) * 4 * 2 + ); + } else { + SaData = AllocateZeroPool ( + sizeof (IKEV2_SA_DATA) + + sizeof (IKEV2_PROPOSAL_DATA) * 2 + + sizeof (IKEV2_TRANSFORM_DATA) * 3 * 2 + ); + } + if (SaData == NULL) { + return NULL; + } + + // + // First proposal payload: 3DES + SHA1 + DH + // + SaData->NumProposals = 2; + ProposalData = (IKEV2_PROPOSAL_DATA *) (SaData + 1); + ProposalData->ProposalIndex = 1; + + // + // If SA data for IKE_SA_INIT exchage, contains 4 transforms. If SA data for + // IKE_AUTH exchange contains 3 transforms. + // + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + ProposalData->NumTransforms = 4; + } else { + ProposalData->NumTransforms = 3; + } + + + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + ProposalData->ProtocolId = IPSEC_PROTO_ISAKMP; + } else { + ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); + ProposalData->ProtocolId = IPSEC_PROTO_IPSEC_ESP; + ProposalData->Spi = AllocateZeroPool (sizeof (ChildSaSession->LocalPeerSpi)); + if (ProposalData->Spi == NULL) { + FreePool (SaData); + return NULL; + } + + CopyMem ( + ProposalData->Spi, + &ChildSaSession->LocalPeerSpi, + sizeof(ChildSaSession->LocalPeerSpi) + ); + } + + // + // Set transform attribute for Encryption Algorithm - 3DES + // + TransformData = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1); + TransformData->TransformIndex = 0; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_ENCR; + TransformData->TransformId = IKEV2_TRANSFORM_ID_ENCR_3DES; + + // + // Set transform attribute for Integrity Algorithm - SHA1_96 + // + TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); + TransformData->TransformIndex = 1; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_INTEG; + TransformData->TransformId = IKEV2_TRANSFORM_ID_AUTH_HMAC_SHA1_96; + + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + // + // Set transform attribute for Pseduo-Random Function - HAMC_SHA1 + // + TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); + TransformData->TransformIndex = 2; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_PRF; + TransformData->TransformId = IKEV2_TRANSFORM_ID_PRF_HMAC_SHA1; + } + + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + // + // Set transform attribute for DH Group - DH 1024 + // + TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); + TransformData->TransformIndex = 3; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_DH; + TransformData->TransformId = IKEV2_TRANSFORM_ID_DH_1024MODP; + } else { + // + // Transform type for Extended Sequence Numbers. Currently not support Extended + // Sequence Number. + // + TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); + TransformData->TransformIndex = 2; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_ESN; + TransformData->TransformId = 0; + } + + // + // Second proposal payload: 3DES + SHA1 + DH + // + ProposalData = (IKEV2_PROPOSAL_DATA *) (TransformData + 1); + ProposalData->ProposalIndex = 2; + + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + ProposalData->ProtocolId = IPSEC_PROTO_ISAKMP; + ProposalData->NumTransforms = 4; + } else { + + ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); + ProposalData->ProtocolId = IPSEC_PROTO_IPSEC_ESP; + ProposalData->NumTransforms = 3; + ProposalData->Spi = AllocateZeroPool (sizeof (ChildSaSession->LocalPeerSpi)); + if (ProposalData->Spi == NULL) { + FreePool (((IKEV2_PROPOSAL_DATA *) (SaData + 1))->Spi); + FreePool (SaData); + return NULL; + } + + CopyMem ( + ProposalData->Spi, + &ChildSaSession->LocalPeerSpi, + sizeof(ChildSaSession->LocalPeerSpi) + ); + } + + // + // Set transform attribute for Encryption Algorithm - AES-CBC + // + TransformData = (IKEV2_TRANSFORM_DATA *) (ProposalData + 1); + TransformData->TransformIndex = 0; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_ENCR; + TransformData->TransformId = IKEV2_TRANSFORM_ID_ENCR_AES_CBC; + Attribute = &TransformData->Attribute; + Attribute->AttrType = IKEV2_ATTRIBUTE_TYPE_KEYLEN; + Attribute->Attr.AttrLength = (UINT16) (8 * IpSecGetEncryptKeyLength (IKEV2_TRANSFORM_ID_ENCR_AES_CBC)); + + // + // Set transform attribute for Integrity Algorithm - SHA1_96 + // + TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); + TransformData->TransformIndex = 1; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_INTEG; + TransformData->TransformId = IKEV2_TRANSFORM_ID_AUTH_HMAC_SHA1_96; + + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + // + // Set transform attribute for Pseduo-Random Function - HAMC_SHA1 + // + TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); + TransformData->TransformIndex = 2; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_PRF; + TransformData->TransformId = IKEV2_TRANSFORM_ID_PRF_HMAC_SHA1; + } + + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + // + // Set transform attrbiute for DH Group - DH-1024 + // + TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); + TransformData->TransformIndex = 3; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_DH; + TransformData->TransformId = IKEV2_TRANSFORM_ID_DH_1024MODP; + } else { + // + // Transform type for Extended Sequence Numbers. Currently not support Extended + // Sequence Number. + // + TransformData = (IKEV2_TRANSFORM_DATA *) (TransformData + 1); + TransformData->TransformIndex = 2; + TransformData->TransformType = IKEV2_TRANSFORM_TYPE_ESN; + TransformData->TransformId = 0; + } + + return SaData; +} + +/** + Store the SA into SAD. + + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. + +**/ +VOID +Ikev2StoreSaData ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession + ) +{ + EFI_STATUS Status; + EFI_IPSEC_SA_ID SaId; + EFI_IPSEC_SA_DATA2 SaData; + IKEV2_SESSION_COMMON *SessionCommon; + IPSEC_PRIVATE_DATA *Private; + UINT32 TempAddressCount; + EFI_IP_ADDRESS_INFO *TempAddressInfo; + + SessionCommon = &ChildSaSession->SessionCommon; + Private = SessionCommon->Private; + + ZeroMem (&SaId, sizeof (EFI_IPSEC_SA_ID)); + ZeroMem (&SaData, sizeof (EFI_IPSEC_SA_DATA2)); + + // + // Create a SpdSelector. In this implementation, one SPD represents + // 2 direction traffic, so in here, there needs to reverse the local address + // and remote address for Remote Peer's SA, then reverse again for the locate + // SA. + // + TempAddressCount = ChildSaSession->SpdSelector->LocalAddressCount; + TempAddressInfo = ChildSaSession->SpdSelector->LocalAddress; + + ChildSaSession->SpdSelector->LocalAddressCount = ChildSaSession->SpdSelector->RemoteAddressCount; + ChildSaSession->SpdSelector->LocalAddress = ChildSaSession->SpdSelector->RemoteAddress; + + ChildSaSession->SpdSelector->RemoteAddress = TempAddressInfo; + ChildSaSession->SpdSelector->RemoteAddressCount= TempAddressCount; + + // + // Set the SaId and SaData. + // + SaId.Spi = ChildSaSession->LocalPeerSpi; + SaId.Proto = EfiIPsecESP; + SaData.AntiReplayWindows = 16; + SaData.SNCount = 0; + SaData.Mode = ChildSaSession->Spd->Data->ProcessingPolicy->Mode; + + // + // If it is tunnel mode, should add the TunnelDest and TunnelSource for SaData. + // + if (SaData.Mode == EfiIPsecTunnel) { + CopyMem ( + &SaData.TunnelSourceAddress, + &ChildSaSession->Spd->Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress, + sizeof (EFI_IP_ADDRESS) + ); + CopyMem ( + &SaData.TunnelDestinationAddress, + &ChildSaSession->Spd->Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + + CopyMem (&SaId.DestAddress, &ChildSaSession->SessionCommon.LocalPeerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&SaData.AlgoInfo, &ChildSaSession->ChildKeymats.LocalPeerInfo, sizeof (EFI_IPSEC_ALGO_INFO)); + SaData.SpdSelector = ChildSaSession->SpdSelector; + + // + // Store the remote SA into SAD. + // + Status = EfiIpSecConfigSetData ( + &Private->IpSecConfig, + IPsecConfigDataTypeSad, + (EFI_IPSEC_CONFIG_SELECTOR *) &SaId, + &SaData, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Store the local SA into SAD. + // + ChildSaSession->SpdSelector->RemoteAddressCount = ChildSaSession->SpdSelector->LocalAddressCount; + ChildSaSession->SpdSelector->RemoteAddress = ChildSaSession->SpdSelector->LocalAddress; + + ChildSaSession->SpdSelector->LocalAddress = TempAddressInfo; + ChildSaSession->SpdSelector->LocalAddressCount = TempAddressCount; + + SaId.Spi = ChildSaSession->RemotePeerSpi; + + CopyMem (&SaId.DestAddress, &ChildSaSession->SessionCommon.RemotePeerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&SaData.AlgoInfo, &ChildSaSession->ChildKeymats.RemotePeerInfo, sizeof (EFI_IPSEC_ALGO_INFO)); + SaData.SpdSelector = ChildSaSession->SpdSelector; + + // + // If it is tunnel mode, should add the TunnelDest and TunnelSource for SaData. + // + if (SaData.Mode == EfiIPsecTunnel) { + CopyMem ( + &SaData.TunnelSourceAddress, + &ChildSaSession->Spd->Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress, + sizeof (EFI_IP_ADDRESS) + ); + CopyMem ( + &SaData.TunnelDestinationAddress, + &ChildSaSession->Spd->Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + + Status = EfiIpSecConfigSetData ( + &Private->IpSecConfig, + IPsecConfigDataTypeSad, + (EFI_IPSEC_CONFIG_SELECTOR *) &SaId, + &SaData, + NULL + ); + + ASSERT_EFI_ERROR (Status); +} + +/** + Call back function of the IKE life time is over. + + This function will mark the related IKE SA Session as deleting and trigger a + Information negotiation. + + @param[in] Event The signaled Event. + @param[in] Context Pointer to data passed by caller. + +**/ +VOID +EFIAPI +Ikev2LifetimeNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *SessionCommon; + + ASSERT (Context != NULL); + SessionCommon = (IKEV2_SESSION_COMMON *) Context; + + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + DEBUG (( + DEBUG_INFO, + "\n---IkeSa Lifetime is out(cookie_i, cookie_r):(0x%lx, 0x%lx)---\n", + IkeSaSession->InitiatorCookie, + IkeSaSession->ResponderCookie + )); + + // + // Change the IKE SA Session's State to IKE_STATE_SA_DELETING. + // + IKEV2_DUMP_STATE (IkeSaSession->SessionCommon.State, IkeStateSaDeleting); + IkeSaSession->SessionCommon.State = IkeStateSaDeleting; + + } else { + ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); + IkeSaSession = ChildSaSession->IkeSaSession; + + // + // Link the timeout child SA to the DeleteSaList. + // + InsertTailList (&IkeSaSession->DeleteSaList, &ChildSaSession->ByDelete); + + // + // Change the Child SA Session's State to IKE_STATE_SA_DELETING. + // + DEBUG (( + DEBUG_INFO, + "\n------ChildSa Lifetime is out(SPI):(0x%x)------\n", + ChildSaSession->LocalPeerSpi + )); + } + + // + // TODO: Send the delete info packet or delete silently + // + mIkev2Exchange.NegotiateInfo ((UINT8 *) IkeSaSession, NULL); +} + +/** + This function will be called if the TimeOut Event is signaled. + + @param[in] Event The signaled Event. + @param[in] Context The data passed by caller. + +**/ +VOID +EFIAPI +Ikev2ResendNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IPSEC_PRIVATE_DATA *Private; + IKEV2_SA_SESSION *IkeSaSession; + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *SessionCommon; + LIST_ENTRY *ChildSaEntry; + UINT8 Value; + EFI_STATUS Status; + + ASSERT (Context != NULL); + IkeSaSession = NULL; + ChildSaSession = NULL; + SessionCommon = (IKEV2_SESSION_COMMON *) Context; + Private = SessionCommon->Private; + + // + // Remove the SA session from the processing list if exceed the max retry. + // + if (SessionCommon->RetryCount > IKE_MAX_RETRY) { + if (SessionCommon->IkeSessionType == IkeSessionTypeIkeSa) { + IkeSaSession = IKEV2_SA_SESSION_FROM_COMMON (SessionCommon); + if (IkeSaSession->SessionCommon.State == IkeStateSaDeleting) { + + // + // If the IkeSaSession is initiator, delete all its Child SAs before removing IKE SA. + // If the IkesaSession is responder, all ChildSa has been remove in Ikev2HandleInfo(); + // + for (ChildSaEntry = IkeSaSession->ChildSaEstablishSessionList.ForwardLink; + ChildSaEntry != &IkeSaSession->ChildSaEstablishSessionList; + ) { + ChildSaSession = IKEV2_CHILD_SA_SESSION_BY_IKE_SA (ChildSaEntry); + // + // Move to next ChildSa Entry. + // + ChildSaEntry = ChildSaEntry->ForwardLink; + // + // Delete LocalSpi & RemoteSpi and remove the ChildSaSession from the + // EstablishedChildSaList. + // + Ikev2ChildSaSilentDelete (IkeSaSession, ChildSaSession->LocalPeerSpi); + } + + // + // If the IKE SA Delete Payload wasn't sent out successfully, Delete it from the EstablishedList. + // + Ikev2SaSessionRemove (&Private->Ikev2EstablishedList, &SessionCommon->RemotePeerIp); + + if (Private != NULL && Private->IsIPsecDisabling) { + // + // After all IKE SAs were deleted, set the IPSEC_STATUS_DISABLED value in + // IPsec status variable. + // + if (IsListEmpty (&Private->Ikev1EstablishedList) && IsListEmpty (&Private->Ikev2EstablishedList)) { + Value = IPSEC_STATUS_DISABLED; + Status = gRT->SetVariable ( + IPSECCONFIG_STATUS_NAME, + &gEfiIpSecConfigProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (Value), + &Value + ); + if (!EFI_ERROR (Status)) { + // + // Set the Disabled Flag in Private data. + // + Private->IpSec.DisabledFlag = TRUE; + Private->IsIPsecDisabling = FALSE; + } + } + } + } else { + Ikev2SaSessionRemove (&Private->Ikev2SessionList, &SessionCommon->RemotePeerIp); + } + Ikev2SaSessionFree (IkeSaSession); + + } else { + + // + // If the packet sent by Child SA. + // + ChildSaSession = IKEV2_CHILD_SA_SESSION_FROM_COMMON (SessionCommon); + IkeSaSession = ChildSaSession->IkeSaSession; + if (ChildSaSession->SessionCommon.State == IkeStateSaDeleting) { + + // + // Established Child SA should be remove from the SAD entry and + // DeleteList. The function of Ikev2DeleteChildSaSilent() will remove + // the childSA from the IkeSaSession->ChildSaEstablishedList. So there + // is no need to remove it here. + // + Ikev2ChildSaSilentDelete (IkeSaSession, ChildSaSession->LocalPeerSpi); + Ikev2ChildSaSessionRemove ( + &IkeSaSession->DeleteSaList, + ChildSaSession->LocalPeerSpi, + IKEV2_DELET_CHILDSA_LIST + ); + } else { + Ikev2ChildSaSessionRemove ( + &IkeSaSession->ChildSaSessionList, + ChildSaSession->LocalPeerSpi, + IKEV2_ESTABLISHING_CHILDSA_LIST + ); + } + + Ikev2ChildSaSessionFree (ChildSaSession); + } + return ; + } + + // + // Increase the retry count. + // + SessionCommon->RetryCount++; + DEBUG ((DEBUG_INFO, ">>>Resending the last packet ...\n")); + + // + // Resend the last packet. + // + Ikev2SendIkePacket ( + SessionCommon->UdpService, + (UINT8*)SessionCommon, + SessionCommon->LastSentPacket, + 0 + ); +} + +/** + Copy ChildSaSession->Spd->Selector to ChildSaSession->SpdSelector. + + ChildSaSession->SpdSelector stores the real Spdselector for its SA. Sometime, + the SpdSelector in ChildSaSession is more accurated or the scope is smaller + than the one in ChildSaSession->Spd, especially for the tunnel mode. + + @param[in, out] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION related to. + + @retval EFI_SUCCESS The operation complete successfully. + @retval EFI_OUT_OF_RESOURCES If the required resource can't be allocated. + +**/ +EFI_STATUS +Ikev2ChildSaSessionSpdSelectorCreate ( + IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (ChildSaSession->Spd != NULL && ChildSaSession->Spd->Selector != NULL) { + if (ChildSaSession->SpdSelector == NULL) { + ChildSaSession->SpdSelector = AllocateZeroPool (sizeof (EFI_IPSEC_SPD_SELECTOR)); + if (ChildSaSession->SpdSelector == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + } + CopyMem ( + ChildSaSession->SpdSelector, + ChildSaSession->Spd->Selector, + sizeof (EFI_IPSEC_SPD_SELECTOR) + ); + ChildSaSession->SpdSelector->RemoteAddress = AllocateCopyPool ( + ChildSaSession->Spd->Selector->RemoteAddressCount * + sizeof (EFI_IP_ADDRESS_INFO), + ChildSaSession->Spd->Selector->RemoteAddress + ); + if (ChildSaSession->SpdSelector->RemoteAddress == NULL) { + Status = EFI_OUT_OF_RESOURCES; + + FreePool (ChildSaSession->SpdSelector); + + return Status; + } + + ChildSaSession->SpdSelector->LocalAddress = AllocateCopyPool ( + ChildSaSession->Spd->Selector->LocalAddressCount * + sizeof (EFI_IP_ADDRESS_INFO), + ChildSaSession->Spd->Selector->LocalAddress + ); + if (ChildSaSession->SpdSelector->LocalAddress == NULL) { + Status = EFI_OUT_OF_RESOURCES; + + FreePool (ChildSaSession->SpdSelector->RemoteAddress); + + FreePool (ChildSaSession->SpdSelector); + + return Status; + } + + ChildSaSession->SpdSelector->RemoteAddressCount = ChildSaSession->Spd->Selector->RemoteAddressCount; + ChildSaSession->SpdSelector->LocalAddressCount = ChildSaSession->Spd->Selector->LocalAddressCount; + } + + return Status; +} + +/** + Generate a ChildSa Session and insert it into related IkeSaSession. + + @param[in] IkeSaSession Pointer to related IKEV2_SA_SESSION. + @param[in] UdpService Pointer to related IKE_UDP_SERVICE. + + @return pointer of IKEV2_CHILD_SA_SESSION. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionCreate ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_UDP_SERVICE *UdpService + ) +{ + IKEV2_CHILD_SA_SESSION *ChildSaSession; + IKEV2_SESSION_COMMON *ChildSaCommon; + + // + // Create a new ChildSaSession.Insert it into processing list and initiate the common parameters. + // + ChildSaSession = Ikev2ChildSaSessionAlloc (UdpService, IkeSaSession); + if (ChildSaSession == NULL) { + return NULL; + } + + // + // Set the specific parameters. + // + ChildSaSession->Spd = IkeSaSession->Spd; + ChildSaCommon = &ChildSaSession->SessionCommon; + ChildSaCommon->IsInitiator = IkeSaSession->SessionCommon.IsInitiator; + if (IkeSaSession->SessionCommon.State == IkeStateAuth) { + ChildSaCommon->State = IkeStateAuth; + IKEV2_DUMP_STATE (ChildSaCommon->State, IkeStateAuth); + } else { + ChildSaCommon->State = IkeStateCreateChild; + IKEV2_DUMP_STATE (ChildSaCommon->State, IkeStateCreateChild); + } + + // + // If SPD->Selector is not NULL, copy it to the ChildSaSession->SpdSelector. + // The ChildSaSession->SpdSelector might be changed after the traffic selector + // negoniation and it will be copied into the SAData after ChildSA established. + // + if (EFI_ERROR (Ikev2ChildSaSessionSpdSelectorCreate (ChildSaSession))) { + Ikev2ChildSaSessionFree (ChildSaSession); + return NULL; + } + + // + // Copy first NiBlock and NrBlock to ChildSa Session + // + ChildSaSession->NiBlock = AllocateZeroPool (IkeSaSession->NiBlkSize); + if (ChildSaSession->NiBlock == NULL) { + Ikev2ChildSaSessionFree (ChildSaSession); + return NULL; + } + + ChildSaSession->NiBlkSize = IkeSaSession->NiBlkSize; + CopyMem (ChildSaSession->NiBlock, IkeSaSession->NiBlock, IkeSaSession->NiBlkSize); + + ChildSaSession->NrBlock = AllocateZeroPool (IkeSaSession->NrBlkSize); + if (ChildSaSession->NrBlock == NULL) { + Ikev2ChildSaSessionFree (ChildSaSession); + return NULL; + } + + ChildSaSession->NrBlkSize = IkeSaSession->NrBlkSize; + CopyMem (ChildSaSession->NrBlock, IkeSaSession->NrBlock, IkeSaSession->NrBlkSize); + + // + // Only if the Create Child SA is called for the IKE_INIT Exchange and + // IkeSaSession is initiator (Only Initiator's SPD is not NULL), Set the + // Traffic Selectors related information here. + // + if (IkeSaSession->SessionCommon.State == IkeStateAuth && IkeSaSession->Spd != NULL) { + ChildSaSession->ProtoId = IkeSaSession->Spd->Selector->NextLayerProtocol; + ChildSaSession->LocalPort = IkeSaSession->Spd->Selector->LocalPort; + ChildSaSession->RemotePort = IkeSaSession->Spd->Selector->RemotePort; + } + + // + // Insert the new ChildSaSession into processing child SA list. + // + Ikev2ChildSaSessionInsert (&IkeSaSession->ChildSaSessionList, ChildSaSession); + return ChildSaSession; +} + +/** + Check if the SPD is related to the input Child SA Session. + + This function is the subfunction of Ikev1AssociateSpdEntry(). It is the call + back function of IpSecVisitConfigData(). + + + @param[in] Type Type of the input Config Selector. + @param[in] Selector Pointer to the Configure Selector to be checked. + @param[in] Data Pointer to the Configure Selector's Data passed + from the caller. + @param[in] SelectorSize The buffer size of Selector. + @param[in] DataSize The buffer size of the Data. + @param[in] Context The data passed from the caller. It is a Child + SA Session in this context. + + @retval EFI_SUCCESS The SPD Selector is not related to the Child SA Session. + @retval EFI_ABORTED The SPD Selector is related to the Child SA session and + set the ChildSaSession->Spd to point to this SPD Selector. + +**/ +EFI_STATUS +Ikev2MatchSpdEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE Type, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN UINTN SelectorSize, + IN UINTN DataSize, + IN VOID *Context + ) +{ + IKEV2_CHILD_SA_SESSION *ChildSaSession; + EFI_IPSEC_SPD_SELECTOR *SpdSelector; + EFI_IPSEC_SPD_DATA *SpdData; + BOOLEAN IsMatch; + UINT8 IpVersion; + + ASSERT (Type == IPsecConfigDataTypeSpd); + SpdData = (EFI_IPSEC_SPD_DATA *) Data; + // + // Bypass all non-protect SPD entry first + // + if (SpdData->Action != EfiIPsecActionProtect) { + return EFI_SUCCESS; + } + + ChildSaSession = (IKEV2_CHILD_SA_SESSION *) Context; + IpVersion = ChildSaSession->SessionCommon.UdpService->IpVersion; + SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) Selector; + IsMatch = TRUE; + + if (SpdSelector->NextLayerProtocol == EFI_IP_PROTO_UDP && + SpdSelector->LocalPort == IKE_DEFAULT_PORT && + SpdSelector->LocalPortRange == 0 && + SpdSelector->RemotePort == IKE_DEFAULT_PORT && + SpdSelector->RemotePortRange == 0 + ) { + // + // TODO: Skip IKE Policy here or set a SPD entry? + // + return EFI_SUCCESS; + } + + if (SpdSelector->NextLayerProtocol != EFI_IPSEC_ANY_PROTOCOL && + SpdSelector->NextLayerProtocol != ChildSaSession->ProtoId + ) { + IsMatch = FALSE; + } + + if (SpdSelector->LocalPort != EFI_IPSEC_ANY_PORT && SpdSelector->LocalPort != ChildSaSession->LocalPort) { + IsMatch = FALSE; + } + + if (SpdSelector->RemotePort != EFI_IPSEC_ANY_PORT && SpdSelector->RemotePort != ChildSaSession->RemotePort) { + IsMatch = FALSE; + } + + IsMatch = (BOOLEAN) (IsMatch && + IpSecMatchIpAddress ( + IpVersion, + &ChildSaSession->SessionCommon.LocalPeerIp, + SpdSelector->LocalAddress, + SpdSelector->LocalAddressCount + )); + + IsMatch = (BOOLEAN) (IsMatch && + IpSecMatchIpAddress ( + IpVersion, + &ChildSaSession->SessionCommon.RemotePeerIp, + SpdSelector->RemoteAddress, + SpdSelector->RemoteAddressCount + )); + + if (IsMatch) { + ChildSaSession->Spd = IkeSearchSpdEntry (SpdSelector); + return EFI_ABORTED; + } else { + return EFI_SUCCESS; + } +} + +/** + Check if the Algorithm ID is supported. + + @param[in] AlgorithmId The specified Algorithm ID. + @param[in] Type The type used to indicate the Algorithm is for Encrypt or + Authentication. + + @retval TRUE If the Algorithm ID is supported. + @retval FALSE If the Algorithm ID is not supported. + +**/ +BOOLEAN +Ikev2IsSupportAlg ( + IN UINT16 AlgorithmId, + IN UINT8 Type + ) +{ + UINT8 Index; + switch (Type) { + case IKE_ENCRYPT_TYPE : + for (Index = 0; Index < IKEV2_SUPPORT_ENCRYPT_ALGORITHM_NUM; Index++) { + if (mIkev2EncryptAlgorithmList[Index] == AlgorithmId) { + return TRUE; + } + } + break; + + case IKE_AUTH_TYPE : + for (Index = 0; Index < IKEV2_SUPPORT_AUTH_ALGORITHM_NUM; Index++) { + if (mIkev2AuthAlgorithmList[Index] == AlgorithmId) { + return TRUE; + } + } + break; + + case IKE_DH_TYPE : + for (Index = 0; Index < IKEV2_SUPPORT_DH_ALGORITHM_NUM; Index++) { + if (mIkev2DhGroupAlgorithmList[Index] == AlgorithmId) { + return TRUE; + } + } + break; + + case IKE_PRF_TYPE : + for (Index = 0; Index < IKEV2_SUPPORT_PRF_ALGORITHM_NUM; Index++) { + if (mIkev2PrfAlgorithmList[Index] == AlgorithmId) { + return TRUE; + } + } + } + return FALSE; +} + +/** + Get the preferred algorithm types from ProposalData. + + @param[in] ProposalData Pointer to related IKEV2_PROPOSAL_DATA. + @param[out] PreferEncryptAlgorithm Output of preferred encrypt algorithm. + @param[out] PreferIntegrityAlgorithm Output of preferred integrity algorithm. + @param[out] PreferPrfAlgorithm Output of preferred PRF algorithm. Only + for IKE SA. + @param[out] PreferDhGroup Output of preferred DH group. Only for + IKE SA. + @param[out] PreferEncryptKeylength Output of preferred encrypt key length + in bytes. + @param[out] IsSupportEsn Output of value about the Extented Sequence + Number is support or not. Only for Child SA. + @param[in] IsChildSa If it is ture, the ProposalData is for IKE + SA. Otherwise the proposalData is for Child SA. + +**/ +VOID +Ikev2ParseProposalData ( + IN IKEV2_PROPOSAL_DATA *ProposalData, + OUT UINT16 *PreferEncryptAlgorithm, + OUT UINT16 *PreferIntegrityAlgorithm, + OUT UINT16 *PreferPrfAlgorithm, + OUT UINT16 *PreferDhGroup, + OUT UINTN *PreferEncryptKeylength, + OUT BOOLEAN *IsSupportEsn, + IN BOOLEAN IsChildSa +) +{ + IKEV2_TRANSFORM_DATA *TransformData; + UINT8 TransformIndex; + + // + // Check input parameters. + // + if (ProposalData == NULL || + PreferEncryptAlgorithm == NULL || + PreferIntegrityAlgorithm == NULL || + PreferEncryptKeylength == NULL + ) { + return; + } + + if (IsChildSa) { + if (IsSupportEsn == NULL) { + return; + } + } else { + if (PreferPrfAlgorithm == NULL || PreferDhGroup == NULL) { + return; + } + } + + TransformData = (IKEV2_TRANSFORM_DATA *)(ProposalData + 1); + for (TransformIndex = 0; TransformIndex < ProposalData->NumTransforms; TransformIndex++) { + switch (TransformData->TransformType) { + // + // For IKE SA there are four algorithm types. Encryption Algorithm, Pseudo-random Function, + // Integrity Algorithm, Diffie-Hellman Group. For Child SA, there are three algorithm types. + // Encryption Algorithm, Integrity Algorithm, Extended Sequence Number. + // + case IKEV2_TRANSFORM_TYPE_ENCR: + if (*PreferEncryptAlgorithm == 0 && Ikev2IsSupportAlg (TransformData->TransformId, IKE_ENCRYPT_TYPE)) { + // + // Check the attribute value. According to RFC, only Keylength is support. + // + if (TransformData->Attribute.AttrType == IKEV2_ATTRIBUTE_TYPE_KEYLEN) { + // + // If the Keylength is not support, continue to check the next one. + // + if (IpSecGetEncryptKeyLength ((UINT8)TransformData->TransformId) != (UINTN)(TransformData->Attribute.Attr.AttrValue >> 3)){ + break; + } else { + *PreferEncryptKeylength = TransformData->Attribute.Attr.AttrValue; + } + } + *PreferEncryptAlgorithm = TransformData->TransformId; + } + break; + + case IKEV2_TRANSFORM_TYPE_PRF : + if (!IsChildSa) { + if (*PreferPrfAlgorithm == 0 && Ikev2IsSupportAlg (TransformData->TransformId, IKE_PRF_TYPE)) { + *PreferPrfAlgorithm = TransformData->TransformId; + } + } + break; + + case IKEV2_TRANSFORM_TYPE_INTEG : + if (*PreferIntegrityAlgorithm == 0 && Ikev2IsSupportAlg (TransformData->TransformId, IKE_AUTH_TYPE)) { + *PreferIntegrityAlgorithm = TransformData->TransformId; + } + break; + + case IKEV2_TRANSFORM_TYPE_DH : + if (!IsChildSa) { + if (*PreferDhGroup == 0 && Ikev2IsSupportAlg (TransformData->TransformId, IKE_DH_TYPE)) { + *PreferDhGroup = TransformData->TransformId; + } + } + break; + + case IKEV2_TRANSFORM_TYPE_ESN : + if (IsChildSa) { + if (TransformData->TransformId != 0) { + *IsSupportEsn = TRUE; + } + } + break; + + default: + break; + } + TransformData = (IKEV2_TRANSFORM_DATA *)(TransformData + 1); + } +} + +/** + Parse the received Initial Exchange Packet. + + This function parse the SA Payload and Key Payload to find out the cryptographic + suite for the further IKE negotiation and fill it into the IKE SA Session's + CommonSession->SaParams. + + @param[in, out] IkeSaSession Pointer to related IKEV2_SA_SESSION. + @param[in] SaPayload The received packet. + @param[in] Type The received packet IKE header flag. + + @retval TRUE If the SA proposal in Packet is acceptable. + @retval FALSE If the SA proposal in Packet is not acceptable. + +**/ +BOOLEAN +Ikev2SaParseSaPayload ( + IN OUT IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *SaPayload, + IN UINT8 Type + ) +{ + IKEV2_PROPOSAL_DATA *ProposalData; + UINT8 ProposalIndex; + UINT16 PreferEncryptAlgorithm; + UINT16 PreferIntegrityAlgorithm; + UINT16 PreferPrfAlgorithm; + UINT16 PreferDhGroup; + UINTN PreferEncryptKeylength; + UINT16 EncryptAlgorithm; + UINT16 IntegrityAlgorithm; + UINT16 PrfAlgorithm; + UINT16 DhGroup; + UINTN EncryptKeylength; + BOOLEAN IsMatch; + UINTN SaDataSize; + + PreferPrfAlgorithm = 0; + PreferIntegrityAlgorithm = 0; + PreferDhGroup = 0; + PreferEncryptAlgorithm = 0; + PreferEncryptKeylength = 0; + PrfAlgorithm = 0; + IntegrityAlgorithm = 0; + DhGroup = 0; + EncryptAlgorithm = 0; + EncryptKeylength = 0; + IsMatch = FALSE; + + if (Type == IKE_HEADER_FLAGS_INIT) { + ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *)SaPayload->PayloadBuf + 1); + for (ProposalIndex = 0; ProposalIndex < ((IKEV2_SA_DATA *)SaPayload->PayloadBuf)->NumProposals; ProposalIndex++) { + // + // Iterate each proposal to find the perfered one. + // + if (ProposalData->ProtocolId == IPSEC_PROTO_ISAKMP && ProposalData->NumTransforms >= 4) { + // + // Get the preferred algorithms. + // + Ikev2ParseProposalData ( + ProposalData, + &PreferEncryptAlgorithm, + &PreferIntegrityAlgorithm, + &PreferPrfAlgorithm, + &PreferDhGroup, + &PreferEncryptKeylength, + NULL, + FALSE + ); + + if (PreferEncryptAlgorithm != 0 && + PreferIntegrityAlgorithm != 0 && + PreferPrfAlgorithm != 0 && + PreferDhGroup != 0 + ) { + // + // Find the matched one. + // + IkeSaSession->SessionCommon.SaParams = AllocateZeroPool (sizeof (IKEV2_SA_PARAMS)); + if (IkeSaSession->SessionCommon.SaParams == NULL) { + return FALSE; + } + + IkeSaSession->SessionCommon.SaParams->EncAlgId = PreferEncryptAlgorithm; + IkeSaSession->SessionCommon.SaParams->EnckeyLen = PreferEncryptKeylength; + IkeSaSession->SessionCommon.SaParams->DhGroup = PreferDhGroup; + IkeSaSession->SessionCommon.SaParams->Prf = PreferPrfAlgorithm; + IkeSaSession->SessionCommon.SaParams->IntegAlgId = PreferIntegrityAlgorithm; + IkeSaSession->SessionCommon.PreferDhGroup = PreferDhGroup; + + // + // Save the matched one in IKEV2_SA_DATA for furthure calculation. + // + SaDataSize = sizeof (IKEV2_SA_DATA) + + sizeof (IKEV2_PROPOSAL_DATA) + + sizeof (IKEV2_TRANSFORM_DATA) * 4; + IkeSaSession->SaData = AllocateZeroPool (SaDataSize); + if (IkeSaSession->SaData == NULL) { + FreePool (IkeSaSession->SessionCommon.SaParams); + return FALSE; + } + + IkeSaSession->SaData->NumProposals = 1; + + // + // BUGBUG: Suppose the matched proposal only has 4 transforms. If + // The matched Proposal has more than 4 transforms means it contains + // one than one transform with same type. + // + CopyMem ( + (IKEV2_PROPOSAL_DATA *) (IkeSaSession->SaData + 1), + ProposalData, + SaDataSize - sizeof (IKEV2_SA_DATA) + ); + + ((IKEV2_PROPOSAL_DATA *) (IkeSaSession->SaData + 1))->ProposalIndex = 1; + + return TRUE; + } else { + PreferEncryptAlgorithm = 0; + PreferIntegrityAlgorithm = 0; + PreferPrfAlgorithm = 0; + PreferDhGroup = 0; + PreferEncryptKeylength = 0; + } + } + // + // Point to next Proposal. + // + ProposalData = (IKEV2_PROPOSAL_DATA*)((UINT8*)(ProposalData + 1) + + ProposalData->NumTransforms * sizeof (IKEV2_TRANSFORM_DATA)); + } + } else if (Type == IKE_HEADER_FLAGS_RESPOND) { + // + // First check the SA proposal's ProtoctolID and Transform Numbers. Since it is + // the responded SA proposal, suppose it only has one proposal and the transform Numbers + // is 4. + // + ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *) SaPayload->PayloadBuf + 1); + if (ProposalData->ProtocolId != IPSEC_PROTO_ISAKMP || ProposalData->NumTransforms != 4) { + return FALSE; + } + // + // Get the preferred algorithms. + // + Ikev2ParseProposalData ( + ProposalData, + &PreferEncryptAlgorithm, + &PreferIntegrityAlgorithm, + &PreferPrfAlgorithm, + &PreferDhGroup, + &PreferEncryptKeylength, + NULL, + FALSE + ); + // + // Check if the Sa proposal data from received packet is in the IkeSaSession->SaData. + // + ProposalData = (IKEV2_PROPOSAL_DATA *) (IkeSaSession->SaData + 1); + + for (ProposalIndex = 0; ProposalIndex < IkeSaSession->SaData->NumProposals && (!IsMatch); ProposalIndex++) { + Ikev2ParseProposalData ( + ProposalData, + &EncryptAlgorithm, + &IntegrityAlgorithm, + &PrfAlgorithm, + &DhGroup, + &EncryptKeylength, + NULL, + FALSE + ); + if (EncryptAlgorithm == PreferEncryptAlgorithm && + EncryptKeylength == PreferEncryptKeylength && + IntegrityAlgorithm == PreferIntegrityAlgorithm && + PrfAlgorithm == PreferPrfAlgorithm && + DhGroup == PreferDhGroup + ) { + IsMatch = TRUE; + } else { + EncryptAlgorithm = 0; + IntegrityAlgorithm = 0; + PrfAlgorithm = 0; + DhGroup = 0; + EncryptKeylength = 0; + } + + ProposalData = (IKEV2_PROPOSAL_DATA*)((UINT8*)(ProposalData + 1) + + ProposalData->NumTransforms * sizeof (IKEV2_TRANSFORM_DATA)); + } + + if (IsMatch) { + IkeSaSession->SessionCommon.SaParams = AllocateZeroPool (sizeof (IKEV2_SA_PARAMS)); + if (IkeSaSession->SessionCommon.SaParams == NULL) { + return FALSE; + } + + IkeSaSession->SessionCommon.SaParams->EncAlgId = PreferEncryptAlgorithm; + IkeSaSession->SessionCommon.SaParams->EnckeyLen = PreferEncryptKeylength; + IkeSaSession->SessionCommon.SaParams->DhGroup = PreferDhGroup; + IkeSaSession->SessionCommon.SaParams->Prf = PreferPrfAlgorithm; + IkeSaSession->SessionCommon.SaParams->IntegAlgId = PreferIntegrityAlgorithm; + IkeSaSession->SessionCommon.PreferDhGroup = PreferDhGroup; + + return TRUE; + } + } + + return FALSE; +} + +/** + Parse the received Authentication Exchange Packet. + + This function parse the SA Payload and Key Payload to find out the cryptographic + suite for the ESP and fill it into the Child SA Session's CommonSession->SaParams. + + @param[in, out] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION related to + this Authentication Exchange. + @param[in] SaPayload The received packet. + @param[in] Type The IKE header's flag of received packet . + + @retval TRUE If the SA proposal in Packet is acceptable. + @retval FALSE If the SA proposal in Packet is not acceptable. + +**/ +BOOLEAN +Ikev2ChildSaParseSaPayload ( + IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession, + IN IKE_PAYLOAD *SaPayload, + IN UINT8 Type + ) +{ + IKEV2_PROPOSAL_DATA *ProposalData; + UINT8 ProposalIndex; + UINT16 PreferEncryptAlgorithm; + UINT16 PreferIntegrityAlgorithm; + UINTN PreferEncryptKeylength; + BOOLEAN PreferIsSupportEsn; + UINT16 EncryptAlgorithm; + UINT16 IntegrityAlgorithm; + UINTN EncryptKeylength; + BOOLEAN IsSupportEsn; + BOOLEAN IsMatch; + UINTN SaDataSize; + + + PreferIntegrityAlgorithm = 0; + PreferEncryptAlgorithm = 0; + PreferEncryptKeylength = 0; + IntegrityAlgorithm = 0; + EncryptAlgorithm = 0; + EncryptKeylength = 0; + IsMatch = TRUE; + IsSupportEsn = FALSE; + PreferIsSupportEsn = FALSE; + + if (Type == IKE_HEADER_FLAGS_INIT) { + ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *) SaPayload->PayloadBuf + 1); + for (ProposalIndex = 0; ProposalIndex < ((IKEV2_SA_DATA *) SaPayload->PayloadBuf)->NumProposals; ProposalIndex++) { + // + // Iterate each proposal to find the preferred one. + // + if (ProposalData->ProtocolId == IPSEC_PROTO_IPSEC_ESP && ProposalData->NumTransforms >= 3) { + // + // Get the preferred algorithm. + // + Ikev2ParseProposalData ( + ProposalData, + &PreferEncryptAlgorithm, + &PreferIntegrityAlgorithm, + NULL, + NULL, + &PreferEncryptKeylength, + &IsSupportEsn, + TRUE + ); + // + // Don't support the ESN now. + // + if (PreferEncryptAlgorithm != 0 && + PreferIntegrityAlgorithm != 0 && + !IsSupportEsn + ) { + // + // Find the matched one. + // + ChildSaSession->SessionCommon.SaParams = AllocateZeroPool (sizeof (IKEV2_SA_PARAMS)); + if (ChildSaSession->SessionCommon.SaParams == NULL) { + return FALSE; + } + + ChildSaSession->SessionCommon.SaParams->EncAlgId = PreferEncryptAlgorithm; + ChildSaSession->SessionCommon.SaParams->EnckeyLen = PreferEncryptKeylength; + ChildSaSession->SessionCommon.SaParams->IntegAlgId = PreferIntegrityAlgorithm; + CopyMem (&ChildSaSession->RemotePeerSpi, ProposalData->Spi, sizeof (ChildSaSession->RemotePeerSpi)); + + // + // Save the matched one in IKEV2_SA_DATA for furthure calculation. + // + SaDataSize = sizeof (IKEV2_SA_DATA) + + sizeof (IKEV2_PROPOSAL_DATA) + + sizeof (IKEV2_TRANSFORM_DATA) * 4; + + ChildSaSession->SaData = AllocateZeroPool (SaDataSize); + if (ChildSaSession->SaData == NULL) { + FreePool (ChildSaSession->SessionCommon.SaParams); + return FALSE; + } + + ChildSaSession->SaData->NumProposals = 1; + + // + // BUGBUG: Suppose there are 4 transforms in the matched proposal. If + // the matched Proposal has more than 4 transforms that means there + // are more than one transform with same type. + // + CopyMem ( + (IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1), + ProposalData, + SaDataSize - sizeof (IKEV2_SA_DATA) + ); + + ((IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1))->ProposalIndex = 1; + + ((IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1))->Spi = AllocateCopyPool ( + sizeof (ChildSaSession->LocalPeerSpi), + &ChildSaSession->LocalPeerSpi + ); + if (((IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1))->Spi == NULL) { + FreePool (ChildSaSession->SessionCommon.SaParams); + + FreePool (ChildSaSession->SaData ); + + return FALSE; + } + + return TRUE; + + } else { + PreferEncryptAlgorithm = 0; + PreferIntegrityAlgorithm = 0; + IsSupportEsn = TRUE; + } + } + // + // Point to next Proposal + // + ProposalData = (IKEV2_PROPOSAL_DATA *)((UINT8 *)(ProposalData + 1) + + ProposalData->NumTransforms * sizeof (IKEV2_TRANSFORM_DATA)); + } + } else if (Type == IKE_HEADER_FLAGS_RESPOND) { + // + // First check the SA proposal's ProtoctolID and Transform Numbers. Since it is + // the responded SA proposal, suppose it only has one proposal and the transform Numbers + // is 3. + // + ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *)SaPayload->PayloadBuf + 1); + if (ProposalData->ProtocolId != IPSEC_PROTO_IPSEC_ESP || ProposalData->NumTransforms != 3) { + return FALSE; + } + // + // Get the preferred algorithms. + // + Ikev2ParseProposalData ( + ProposalData, + &PreferEncryptAlgorithm, + &PreferIntegrityAlgorithm, + NULL, + NULL, + &PreferEncryptKeylength, + &PreferIsSupportEsn, + TRUE + ); + + ProposalData = (IKEV2_PROPOSAL_DATA *) (ChildSaSession->SaData + 1); + + for (ProposalIndex = 0; ProposalIndex < ChildSaSession->SaData->NumProposals && (!IsMatch); ProposalIndex++) { + Ikev2ParseProposalData ( + ProposalData, + &EncryptAlgorithm, + &IntegrityAlgorithm, + NULL, + NULL, + &EncryptKeylength, + &IsSupportEsn, + TRUE + ); + if (EncryptAlgorithm == PreferEncryptAlgorithm && + EncryptKeylength == PreferEncryptKeylength && + IntegrityAlgorithm == PreferIntegrityAlgorithm && + IsSupportEsn == PreferIsSupportEsn + ) { + IsMatch = TRUE; + } else { + PreferEncryptAlgorithm = 0; + PreferIntegrityAlgorithm = 0; + IsSupportEsn = TRUE; + } + ProposalData = (IKEV2_PROPOSAL_DATA*)((UINT8*)(ProposalData + 1) + + ProposalData->NumTransforms * sizeof (IKEV2_TRANSFORM_DATA)); + } + + ProposalData = (IKEV2_PROPOSAL_DATA *)((IKEV2_SA_DATA *)SaPayload->PayloadBuf + 1); + if (IsMatch) { + ChildSaSession->SessionCommon.SaParams = AllocateZeroPool (sizeof (IKEV2_SA_PARAMS)); + if (ChildSaSession->SessionCommon.SaParams == NULL) { + return FALSE; + } + + ChildSaSession->SessionCommon.SaParams->EncAlgId = PreferEncryptAlgorithm; + ChildSaSession->SessionCommon.SaParams->EnckeyLen = PreferEncryptKeylength; + ChildSaSession->SessionCommon.SaParams->IntegAlgId = PreferIntegrityAlgorithm; + CopyMem (&ChildSaSession->RemotePeerSpi, ProposalData->Spi, sizeof (ChildSaSession->RemotePeerSpi)); + + return TRUE; + } + } + return FALSE; +} + +/** + Generate Key buffer from fragments. + + If the digest length of specified HashAlgId is larger than or equal with the + required output key length, derive the key directly. Otherwise, Key Material + needs to be PRF-based concatenation according to 2.13 of RFC 4306: + prf+ (K,S) = T1 | T2 | T3 | T4 | ..., T1 = prf (K, S | 0x01), + T2 = prf (K, T1 | S | 0x02), T3 = prf (K, T2 | S | 0x03),T4 = prf (K, T3 | S | 0x04) + then derive the key from this key material. + + @param[in] HashAlgId The Hash Algorithm ID used to generate key. + @param[in] HashKey Pointer to a key buffer which contains hash key. + @param[in] HashKeyLength The length of HashKey in bytes. + @param[in, out] OutputKey Pointer to buffer which is used to receive the + output key. + @param[in] OutputKeyLength The length of OutPutKey buffer. + @param[in] Fragments Pointer to the data to be used to generate key. + @param[in] NumFragments The numbers of the Fragement. + + @retval EFI_SUCCESS The operation complete successfully. + @retval EFI_INVALID_PARAMETER If NumFragments is zero. + @retval EFI_OUT_OF_RESOURCES If the required resource can't be allocated. + @retval Others The operation is failed. + +**/ +EFI_STATUS +Ikev2SaGenerateKey ( + IN UINT8 HashAlgId, + IN UINT8 *HashKey, + IN UINTN HashKeyLength, + IN OUT UINT8 *OutputKey, + IN UINTN OutputKeyLength, + IN PRF_DATA_FRAGMENT *Fragments, + IN UINTN NumFragments + ) +{ + EFI_STATUS Status; + PRF_DATA_FRAGMENT LocalFragments[3]; + UINT8 *Digest; + UINTN DigestSize; + UINTN Round; + UINTN Index; + UINTN AuthKeyLength; + UINTN FragmentsSize; + UINT8 TailData; + + Status = EFI_SUCCESS; + + if (NumFragments == 0) { + return EFI_INVALID_PARAMETER; + } + + LocalFragments[0].Data = NULL; + LocalFragments[1].Data = NULL; + LocalFragments[2].Data = NULL; + + AuthKeyLength = IpSecGetHmacDigestLength (HashAlgId); + DigestSize = AuthKeyLength; + Digest = AllocateZeroPool (AuthKeyLength); + + if (Digest == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // If the required output key length is less than the digest size, + // copy the digest into OutputKey. + // + if (OutputKeyLength <= DigestSize) { + Status = IpSecCryptoIoHmac ( + HashAlgId, + HashKey, + HashKeyLength, + (HASH_DATA_FRAGMENT *) Fragments, + NumFragments, + Digest, + DigestSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + CopyMem (OutputKey, Digest, OutputKeyLength); + goto Exit; + } + + // + //Otherwise, Key Material need to be PRF-based concatenation according to 2.13 + //of RFC 4306: prf+ (K,S) = T1 | T2 | T3 | T4 | ..., T1 = prf (K, S | 0x01), + //T2 = prf (K, T1 | S | 0x02), T3 = prf (K, T2 | S | 0x03),T4 = prf (K, T3 | S | 0x04) + //then derive the key from this key material. + // + FragmentsSize = 0; + for (Index = 0; Index < NumFragments; Index++) { + FragmentsSize = FragmentsSize + Fragments[Index].DataSize; + } + + LocalFragments[1].Data = AllocateZeroPool (FragmentsSize); + if (LocalFragments[1].Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + LocalFragments[1].DataSize = FragmentsSize; + + // + // Copy all input fragments into LocalFragments[1]; + // + FragmentsSize = 0; + for (Index = 0; Index < NumFragments; Index++) { + CopyMem ( + LocalFragments[1].Data + FragmentsSize, + Fragments[Index].Data, + Fragments[Index].DataSize + ); + FragmentsSize = FragmentsSize + Fragments[Index].DataSize; + } + + // + // Prepare 0x01 as the first tail data. + // + TailData = 0x01; + LocalFragments[2].Data = &TailData; + LocalFragments[2].DataSize = sizeof (TailData); + // + // Allocate buffer for the first fragment + // + LocalFragments[0].Data = AllocateZeroPool (AuthKeyLength); + if (LocalFragments[0].Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + LocalFragments[0].DataSize = AuthKeyLength; + + Round = (OutputKeyLength - 1) / AuthKeyLength + 1; + for (Index = 0; Index < Round; Index++) { + Status = IpSecCryptoIoHmac ( + HashAlgId, + HashKey, + HashKeyLength, + (HASH_DATA_FRAGMENT *)(Index == 0 ? &LocalFragments[1] : LocalFragments), + Index == 0 ? 2 : 3, + Digest, + DigestSize + ); + if (EFI_ERROR(Status)) { + goto Exit; + } + CopyMem ( + LocalFragments[0].Data, + Digest, + DigestSize + ); + if (OutputKeyLength > DigestSize * (Index + 1)) { + CopyMem ( + OutputKey + Index * DigestSize, + Digest, + DigestSize + ); + LocalFragments[0].DataSize = DigestSize; + TailData ++; + } else { + // + // The last round + // + CopyMem ( + OutputKey + Index * DigestSize, + Digest, + OutputKeyLength - Index * DigestSize + ); + } + } + +Exit: + // + // Only First and second Framgement Data need to be freed. + // + for (Index = 0 ; Index < 2; Index++) { + if (LocalFragments[Index].Data != NULL) { + FreePool (LocalFragments[Index].Data); + } + } + if (Digest != NULL) { + FreePool (Digest); + } + return Status; +} + diff --git a/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h b/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h new file mode 100644 index 0000000000..319b6cb32c --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h @@ -0,0 +1,1134 @@ +/** @file + The interfaces of IKE/Child session operations and payload related operations + used by IKE Exchange Process. + + Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IKE_V2_UTILITY_H_ +#define _IKE_V2_UTILITY_H_ + +#include "Ikev2.h" +#include "IkeCommon.h" +#include "IpSecCryptIo.h" + +#include + +#define IKEV2_SUPPORT_ENCRYPT_ALGORITHM_NUM 2 +#define IKEV2_SUPPORT_PRF_ALGORITHM_NUM 1 +#define IKEV2_SUPPORT_DH_ALGORITHM_NUM 2 +#define IKEV2_SUPPORT_AUTH_ALGORITHM_NUM 1 + +/** + Allocate buffer for IKEV2_SA_SESSION and initialize it. + + @param[in] Private Pointer to IPSEC_PRIVATE_DATA. + @param[in] UdpService Pointer to IKE_UDP_SERVICE related to this IKE SA Session. + + @return Pointer to IKEV2_SA_SESSION. + +**/ +IKEV2_SA_SESSION * +Ikev2SaSessionAlloc ( + IN IPSEC_PRIVATE_DATA *Private, + IN IKE_UDP_SERVICE *UdpService + ); + +/** + Register Establish IKEv2 SA into Private->Ikev2EstablishedList. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be registered. + @param[in] Private Pointer to IPSEC_PRAVATE_DATA. + +**/ +VOID +Ikev2SaSessionReg ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IPSEC_PRIVATE_DATA *Private + ); + +/** + Find a IKEV2_SA_SESSION by the remote peer IP. + + @param[in] SaSessionList SaSession List to be searched. + @param[in] RemotePeerIp Pointer to specified IP address. + + @return Pointer to IKEV2_SA_SESSION if find one or NULL. + +**/ +IKEV2_SA_SESSION * +Ikev2SaSessionLookup ( + IN LIST_ENTRY *SaSessionList, + IN EFI_IP_ADDRESS *RemotePeerIp + ); + +/** + Insert a IKE_SA_SESSION into IkeSaSession list. The IkeSaSession list is either + Private->Ikev2SaSession list or Private->Ikev2EstablishedList list. + + @param[in] SaSessionList Pointer to list to be inserted into. + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be inserted. + @param[in] RemotePeerIp Pointer to EFI_IP_ADDRESSS to indicate the + unique IKEV2_SA_SESSION. + +**/ +VOID +Ikev2SaSessionInsert ( + IN LIST_ENTRY *SaSessionList, + IN IKEV2_SA_SESSION *IkeSaSession, + IN EFI_IP_ADDRESS *RemotePeerIp + ); + +/** + Remove the SA Session by Remote Peer IP. + + @param[in] SaSessionList Pointer to list to be searched. + @param[in] RemotePeerIp Pointer to EFI_IP_ADDRESS to use for SA Session search. + + @retval Pointer to IKEV2_SA_SESSION with the specified remote IP address. + +**/ +IKEV2_SA_SESSION * +Ikev2SaSessionRemove ( + IN LIST_ENTRY *SaSessionList, + IN EFI_IP_ADDRESS *RemotePeerIp + ); + + +/** + Marking a SA session as on deleting. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION. + + @retval EFI_SUCCESS Find the related SA session and marked it. + +**/ +EFI_STATUS +Ikev2SaSessionOnDeleting ( + IN IKEV2_SA_SESSION *IkeSaSession + ); + +/** + After IKE/Child SA is estiblished, close the time event and free sent packet. + + @param[in] SessionCommon Pointer to a Session Common. + +**/ +VOID +Ikev2SessionCommonRefresh ( + IN IKEV2_SESSION_COMMON *SessionCommon + ); + +/** + Free specified IKEV2 SA Session. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION to be freed. + +**/ +VOID +Ikev2SaSessionFree ( + IN IKEV2_SA_SESSION *IkeSaSession + ); + +/** + Free specified Seession Common. The session common would belong to a IKE SA or + a Child SA. + + @param[in] SessionCommon Pointer to a Session Common. + +**/ +VOID +Ikev2SaSessionCommonFree ( + IN IKEV2_SESSION_COMMON *SessionCommon + ); + +/** + Increase the MessageID in IkeSaSession. + + @param[in] IkeSaSession Pointer to a specified IKEV2_SA_SESSION. + +**/ +VOID +Ikev2SaSessionIncreaseMessageId ( + IN IKEV2_SA_SESSION *IkeSaSession + ); + +/** + Allocate Momery for IKEV2 Child SA Session. + + @param[in] UdpService Pointer to IKE_UDP_SERVICE. + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to this Child SA + Session. + + @retval Pointer of a new created IKEV2 Child SA Session. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionAlloc ( + IN IKE_UDP_SERVICE *UdpService, + IN IKEV2_SA_SESSION *IkeSaSession + ); + +/** + Register a established IKEv2 Child SA into IkeSaSession->ChildSaEstablishSessionList. + If the there is IKEV2_CHILD_SA_SESSION with same remote peer IP, remove the old one + then register the new one. + + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION to be registered. + @param[in] Private Pointer to IPSEC_PRAVATE_DATA. + +**/ +VOID +Ikev2ChildSaSessionReg ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession, + IN IPSEC_PRIVATE_DATA *Private + ); + +/** + This function find the Child SA by the specified Spi. + + This functin find a ChildSA session by searching the ChildSaSessionlist of + the input IKEV2_SA_SESSION by specified MessageID. + + @param[in] SaSessionList Pointer to List to be searched. + @param[in] Spi Specified SPI. + + @return Pointer to IKEV2_CHILD_SA_SESSION. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionLookupBySpi ( + IN LIST_ENTRY *SaSessionList, + IN UINT32 Spi + ); + +/** + Find the ChildSaSession by it's MessagId. + + @param[in] SaSessionList Pointer to a ChildSaSession List. + @param[in] Mid The messageId used to search ChildSaSession. + + @return Pointer to IKEV2_CHILD_SA_SESSION. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionLookupByMid ( + IN LIST_ENTRY *SaSessionList, + IN UINT32 Mid + ); + +/** + Insert a Child SA Session into the specified ChildSa list.. + + @param[in] SaSessionList Pointer to list to be inserted in. + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION to be inserted. + +**/ +VOID +Ikev2ChildSaSessionInsert ( + IN LIST_ENTRY *SaSessionList, + IN IKEV2_CHILD_SA_SESSION *ChildSaSession + ); + +/** + Remove the IKEV2_CHILD_SA_SESSION from IkeSaSessionList. + + @param[in] SaSessionList The SA Session List to be iterated. + @param[in] Spi Spi used to identify the IKEV2_CHILD_SA_SESSION. + @param[in] ListType The type of the List to indicate whether it is a + Established. + + @return The point to IKEV2_CHILD_SA_SESSION. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionRemove ( + IN LIST_ENTRY *SaSessionList, + IN UINT32 Spi, + IN UINT8 ListType + ); + +/** + Mark a specified Child SA Session as on deleting. + + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. + + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +Ikev2ChildSaSessionOnDeleting ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession + ); + +/** + Free the memory located for the specified IKEV2_CHILD_SA_SESSION. + + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. + +**/ +VOID +Ikev2ChildSaSessionFree ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession + ); + +/** + Free the specified DhBuffer. + + @param[in] DhBuffer Pointer to IKEV2_DH_BUFFER to be freed. + +**/ +VOID +Ikev2DhBufferFree ( + IN IKEV2_DH_BUFFER *DhBuffer + ); + +/** + Delete the specified established Child SA. + + This function delete the Child SA directly and dont send the Information Packet to + remote peer. + + @param[in] IkeSaSession Pointer to a IKE SA Session used to be searched for. + @param[in] Spi SPI used to find the Child SA. + + @retval EFI_NOT_FOUND Pointer of IKE SA Session is NULL. + @retval EFI_NOT_FOUND There is no specified Child SA related with the input + SPI under this IKE SA Session. + @retval EFI_SUCCESS Delete the Child SA successfully. + +**/ +EFI_STATUS +Ikev2ChildSaSilentDelete ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT32 Spi + ); + +/** + This function is to parse a request IKE packet and return its request type. + The request type is one of IKE CHILD SA creation, IKE SA rekeying and + IKE CHILD SA rekeying. + + @param[in] IkePacket IKE packet to be prased. + + return the type of the IKE packet. + +**/ +IKEV2_CREATE_CHILD_REQUEST_TYPE +Ikev2ChildExchangeRequestType( + IN IKE_PACKET *IkePacket + ); + +/** + This function finds the SPI from Create Child Sa Exchange Packet. + + @param[in] IkePacket Pointer to IKE_PACKET to be searched. + + @retval SPI number. + +**/ +UINT32 +Ikev2ChildExchangeRekeySpi( + IN IKE_PACKET *IkePacket + ); + + +/** + Associate a SPD selector to the Child SA Session. + + This function is called when the Child SA is not the first child SA of its + IKE SA. It associate a SPD to this Child SA. + + @param[in, out] ChildSaSession Pointer to the Child SA Session to be associated to + a SPD selector. + + @retval EFI_SUCCESS Associate one SPD selector to this Child SA Session successfully. + @retval EFI_NOT_FOUND Can't find the related SPD selector. + +**/ +EFI_STATUS +Ikev2ChildSaAssociateSpdEntry ( + IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession + ); + +/** + Validate the IKE header of received IKE packet. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to this IKE packet. + @param[in] IkeHdr Pointer to IKE header of received IKE packet. + + @retval TRUE If the IKE header is valid. + @retval FALSE If the IKE header is invalid. + +**/ +BOOLEAN +Ikev2ValidateHeader ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_HEADER *IkeHdr + ); + +/** + Create and intialize IKEV2_SA_DATA for speicifed IKEV2_SESSION_COMMON. + + This function will be only called by the initiator. The responder's IKEV2_SA_DATA + will be generated during parsed the initiator packet. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to. + + @retval a Pointer to a new IKEV2_SA_DATA or NULL. + +**/ +IKEV2_SA_DATA * +Ikev2InitializeSaData ( + IN IKEV2_SESSION_COMMON *SessionCommon + ); + +/** + Store the SA into SAD. + + @param[in] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION. + +**/ +VOID +Ikev2StoreSaData ( + IN IKEV2_CHILD_SA_SESSION *ChildSaSession + ); + +/** + Routine process before the payload decoding. + + @param[in] SessionCommon Pointer to ChildSa SessionCommon. + @param[in] PayloadBuf Pointer to the payload. + @param[in] PayloadSize Size of PayloadBuf in byte. + @param[in] PayloadType Type of Payload. + +**/ +VOID +Ikev2ChildSaBeforeDecodePayload ( + IN UINT8 *SessionCommon, + IN UINT8 *PayloadBuf, + IN UINTN PayloadSize, + IN UINT8 PayloadType + ); + +/** + Routine Process after the encode payload. + + @param[in] SessionCommon Pointer to ChildSa SessionCommon. + @param[in] PayloadBuf Pointer to the payload. + @param[in] PayloadSize Size of PayloadBuf in byte. + @param[in] PayloadType Type of Payload. + +**/ +VOID +Ikev2ChildSaAfterEncodePayload ( + IN UINT8 *SessionCommon, + IN UINT8 *PayloadBuf, + IN UINTN PayloadSize, + IN UINT8 PayloadType + ); + +/** + Generate Ikev2 SA payload according to SessionSaData + + @param[in] SessionSaData The data used in SA payload. + @param[in] NextPayload The payload type presented in NextPayload field of + SA Payload header. + @param[in] Type The SA type. It MUST be neither (1) for IKE_SA or + (2) for CHILD_SA or (3) for INFO. + + @retval a Pointer to SA IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateSaPayload ( + IN IKEV2_SA_DATA *SessionSaData, + IN UINT8 NextPayload, + IN IKE_SESSION_TYPE Type + ); + +/** + Generate a ID payload. + + @param[in] CommonSession Pointer to IKEV2_SESSION_COMMON related to ID payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + + @retval Pointer to ID IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateIdPayload ( + IN IKEV2_SESSION_COMMON *CommonSession, + IN UINT8 NextPayload + ); + +/** + Generate a ID payload. + + @param[in] CommonSession Pointer to IKEV2_SESSION_COMMON related to ID payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + @param[in] InCert Pointer to the Certificate which distinguished name + will be added into the Id payload. + @param[in] CertSize Size of the Certificate. + + @retval Pointer to ID IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCertIdPayload ( + IN IKEV2_SESSION_COMMON *CommonSession, + IN UINT8 NextPayload, + IN UINT8 *InCert, + IN UINTN CertSize + ); + +/** + Generate a Nonce payload contenting the input parameter NonceBuf. + + @param[in] NonceBuf The nonce buffer content the whole Nonce payload block + except the payload header. + @param[in] NonceSize The buffer size of the NonceBuf + @param[in] NextPayload The payload type presented in the NextPayload field + of Nonce Payload header. + + @retval Pointer to Nonce IKE paload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateNoncePayload ( + IN UINT8 *NonceBuf, + IN UINTN NonceSize, + IN UINT8 NextPayload + ); + +/** + Generate the Notify payload. + + Since the structure of Notify payload which defined in RFC 4306 is simple, so + there is no internal data structure for Notify payload. This function generate + Notify payload defined in RFC 4306, but all the fields in this payload are still + in host order and need call Ikev2EncodePayload() to convert those fields from + the host order to network order beforing sending it. + + @param[in] ProtocolId The protocol type ID. For IKE_SA it MUST be one (1). + For IPsec SAs it MUST be neither (2) for AH or (3) + for ESP. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Notify payload. + @param[in] SpiSize Size of the SPI in SPI size field of the Notify Payload. + @param[in] MessageType The message type in NotifyMessageType field of the + Notify Payload. + @param[in] SpiBuf Pointer to buffer contains the SPI value. + @param[in] NotifyData Pointer to buffer contains the notification data. + @param[in] NotifyDataSize The size of NotifyData in bytes. + + + @retval Pointer to IKE Notify Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateNotifyPayload ( + IN UINT8 ProtocolId, + IN UINT8 NextPayload, + IN UINT8 SpiSize, + IN UINT16 MessageType, + IN UINT8 *SpiBuf, + IN UINT8 *NotifyData, + IN UINTN NotifyDataSize + ); + +/** + Generate the Delete payload. + + Since the structure of Delete payload which defined in RFC 4306 is simple, + there is no internal data structure for Delete payload. This function generate + Delete payload defined in RFC 4306, but all the fields in this payload are still + in host order and need call Ikev2EncodePayload() to convert those fields from + the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used of Delete payload generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] SpiSize Size of the SPI in SPI size field of the Delete Payload. + @param[in] SpiNum Number of SPI in NumofSPIs field of the Delete Payload. + @param[in] SpiBuf Pointer to buffer contains the SPI value. + + @retval Pointer to IKE Delete Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateDeletePayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 SpiSize, + IN UINT16 SpiNum, + IN UINT8 *SpiBuf + ); + +/** + Generate the Configuration payload. + + This function generates a configuration payload defined in RFC 4306, but all the + fields in this payload are still in host order and need call Ikev2EncodePayload() + to convert those fields from the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used for Delete payload + generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] CfgType The attribute type in the Configuration attribute. + + @retval Pointer to IKE CP Payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCpPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 CfgType + ); + +/** + Generate a Authentication Payload. + + This function is used for both Authentication generation and verification. When the + IsVerify is TRUE, it create a Auth Data for verification. This function choose the + related IKE_SA_INIT Message for Auth data creation according to the IKE Session's type + and the value of IsVerify parameter. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to. + @param[in] IdPayload Pointer to the ID payload to be used for Authentication + payload generation. + @param[in] NextPayload The type filled into the Authentication Payload next + payload field. + @param[in] IsVerify If it is TURE, the Authentication payload is used for + verification. + + @return pointer to IKE Authentication payload for pre-shard key method. + +**/ +IKE_PAYLOAD * +Ikev2PskGenerateAuthPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *IdPayload, + IN UINT8 NextPayload, + IN BOOLEAN IsVerify + ); + +/** + Generate a Authentication Payload for Certificate Auth method. + + This function has two functions. One is creating a local Authentication + Payload for sending and other is creating the remote Authentication data + for verification when the IsVerify is TURE. + + @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to. + @param[in] IdPayload Pointer to the ID payload to be used for Authentication + payload generation. + @param[in] NextPayload The type filled into the Authentication Payload + next payload field. + @param[in] IsVerify If it is TURE, the Authentication payload is used + for verification. + @param[in] UefiPrivateKey Pointer to the UEFI private key. Ignore it when + verify the authenticate payload. + @param[in] UefiPrivateKeyLen The size of UefiPrivateKey in bytes. Ignore it + when verify the authenticate payload. + @param[in] UefiKeyPwd Pointer to the password of UEFI private key. + Ignore it when verify the authenticate payload. + @param[in] UefiKeyPwdLen The size of UefiKeyPwd in bytes.Ignore it when + verify the authenticate payload. + + @return pointer to IKE Authentication payload for certification method. + +**/ +IKE_PAYLOAD * +Ikev2CertGenerateAuthPayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *IdPayload, + IN UINT8 NextPayload, + IN BOOLEAN IsVerify, + IN UINT8 *UefiPrivateKey, + IN UINTN UefiPrivateKeyLen, + IN UINT8 *UefiKeyPwd, + IN UINTN UefiKeyPwdLen + ); + +/** + Generate TS payload. + + This function generates TSi or TSr payload according to type of next payload. + If the next payload is Responder TS, gereate TSi Payload. Otherwise, generate + TSr payload + + @param[in] ChildSa Pointer to IKEV2_CHILD_SA_SESSION related to this TS payload. + @param[in] NextPayload The payload type presented in the NextPayload field + of ID Payload header. + @param[in] IsTunnel It indicates that if the Ts Payload is after the CP payload. + If yes, it means the Tsi and Tsr payload should be with + Max port range and address range and protocol is marked + as zero. + + @retval Pointer to Ts IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateTsPayload ( + IN IKEV2_CHILD_SA_SESSION *ChildSa, + IN UINT8 NextPayload, + IN BOOLEAN IsTunnel + ); + +/** + Parser the Notify Cookie payload. + + This function parses the Notify Cookie payload.If the Notify ProtocolId is not + IPSEC_PROTO_ISAKMP or if the SpiSize is not zero or if the MessageType is not + the COOKIE, return EFI_INVALID_PARAMETER. + + @param[in] IkeNCookie Pointer to the IKE_PAYLOAD which contians the + Notify Cookie payload. + the Notify payload. + @param[in, out] IkeSaSession Pointer to the relevant IKE SA Session. + + @retval EFI_SUCCESS The Notify Cookie Payload is valid. + @retval EFI_INVALID_PARAMETER The Notify Cookie Payload is invalid. + @retval EFI_OUT_OF_RESOURCE The required resource can't be allocated. + +**/ +EFI_STATUS +Ikev2ParserNotifyCookiePayload ( + IN IKE_PAYLOAD *IkeNCookie, + IN OUT IKEV2_SA_SESSION *IkeSaSession + ); + +/** + Generate the Certificate payload or Certificate Request Payload. + + Since the Certificate Payload structure is same with Certificate Request Payload, + the only difference is that one contains the Certificate Data, other contains + the acceptable certificateion CA. This function generate Certificate payload + or Certificate Request Payload defined in RFC 4306, but all the fields + in the payload are still in host order and need call Ikev2EncodePayload() + to convert those fields from the host order to network order beforing sending it. + + @param[in] IkeSaSession Pointer to IKE SA Session to be used of Delete payload + generation. + @param[in] NextPayload The next paylaod type in NextPayload field of + the Delete payload. + @param[in] Certificate Pointer of buffer contains the certification data. + @param[in] CertificateLen The length of Certificate in byte. + @param[in] EncodeType Specified the Certificate Encodeing which is defined + in RFC 4306. + @param[in] IsRequest To indicate create Certificate Payload or Certificate + Request Payload. If it is TURE, create Certificate + Request Payload. Otherwise, create Certificate Payload. + + @retval a Pointer to IKE Payload whose payload buffer containing the Certificate + payload or Certificated Request payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateCertificatePayload ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload, + IN UINT8 *Certificate, + IN UINTN CertificateLen, + IN UINT8 EncodeType, + IN BOOLEAN IsRequest + ); + +/** + General interface of payload encoding. + + This function encode the internal data structure into payload which + is defined in RFC 4306. The IkePayload->PayloadBuf used to store both the input + payload and converted payload. Only the SA payload use the interal structure + to store the attribute. Other payload use structure which is same with the RFC + defined, for this kind payloads just do host order to network order change of + some fields. + + @param[in] SessionCommon Pointer to IKE Session Common used to encode the payload. + @param[in, out] IkePayload Pointer to IKE payload to be encode as input, and + store the encoded result as output. + + @retval EFI_INVALID_PARAMETER Meet error when encode the SA payload. + @retval EFI_SUCCESS Encode successfully. + +**/ +EFI_STATUS +Ikev2EncodePayload ( + IN UINT8 *SessionCommon, + IN OUT IKE_PAYLOAD *IkePayload + ); + +/** + The general interface of decode Payload. + + This function convert the received Payload into internal structure. + + @param[in] SessionCommon Pointer to IKE Session Common to use for decoding. + @param[in, out] IkePayload Pointer to IKE payload to be decode as input, and + store the decoded result as output. + + @retval EFI_INVALID_PARAMETER Meet error when decode the SA payload. + @retval EFI_SUCCESS Decode successfully. + +**/ +EFI_STATUS +Ikev2DecodePayload ( + IN UINT8 *SessionCommon, + IN OUT IKE_PAYLOAD *IkePayload + ); + +/** + Decrypt IKE packet. + + This function decrpt the Encrypted IKE packet and put the result into IkePacket->PayloadBuf. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON containing + some parameter used during decrypting. + @param[in, out] IkePacket Point to IKE_PACKET to be decrypted as input, + and the decrypted reslult as output. + @param[in, out] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_INVALID_PARAMETER If the IKE packet length is zero or the + IKE packet length is not Algorithm Block Size + alignment. + @retval EFI_SUCCESS Decrypt IKE packet successfully. + +**/ +EFI_STATUS +Ikev2DecryptPacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN OUT UINTN IkeType + ); + +/** + Encrypt IKE packet. + + This function encrypt IKE packet before sending it. The Encrypted IKE packet + is put in to IKEV2 Encrypted Payload. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the IKE packet. + @param[in, out] IkePacket Pointer to IKE packet to be encrypted. + + @retval EFI_SUCCESS Operation is successful. + @retval Others OPeration is failed. + +**/ +EFI_STATUS +Ikev2EncryptPacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket + ); + +/** + Encode the IKE packet. + + This function put all Payloads into one payload then encrypt it if needed. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON containing + some parameter used during IKE packet encoding. + @param[in, out] IkePacket Pointer to IKE_PACKET to be encoded as input, + and the encoded reslult as output. + @param[in] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS Encode IKE packet successfully. + @retval Otherwise Encode IKE packet failed. + +**/ +EFI_STATUS +Ikev2EncodePacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN UINTN IkeType + ); + +/** + Decode the IKE packet. + + This function first decrypts the IKE packet if needed , then separats the whole + IKE packet from the IkePacket->PayloadBuf into IkePacket payload list. + + @param[in] SessionCommon Pointer to IKEV1_SESSION_COMMON containing + some parameter used by IKE packet decoding. + @param[in, out] IkePacket The IKE Packet to be decoded on input, and + the decoded result on return. + @param[in] IkeType The type of IKE. IKE_SA_TYPE, IKE_INFO_TYPE and + IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS The IKE packet is decoded successfull. + @retval Otherwise The IKE packet decoding is failed. + +**/ +EFI_STATUS +Ikev2DecodePacket ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN OUT IKE_PACKET *IkePacket, + IN UINTN IkeType + ); + +/** + Save some useful payloads after accepting the Packet. + + @param[in] SessionCommon Pointer to IKEV2_SESSION_COMMON related to the operation. + @param[in] IkePacket Pointer to received IkePacet. + @param[in] IkeType The type used to indicate it is in IkeSa or ChildSa or Info + exchange. + +**/ +VOID +Ikev2OnPacketAccepted ( + IN IKEV2_SESSION_COMMON *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINT8 IkeType + ); + +/** + Send out IKEV2 packet. + + @param[in] IkeUdpService Pointer to IKE_UDP_SERVICE used to send the IKE packet. + @param[in] SessionCommon Pointer to IKEV1_SESSION_COMMON related to the IKE packet. + @param[in] IkePacket Pointer to IKE_PACKET to be sent out. + @param[in] IkeType The type of IKE to point what's kind of the IKE + packet is to be sent out. IKE_SA_TYPE, IKE_INFO_TYPE + and IKE_CHILD_TYPE are supportted. + + @retval EFI_SUCCESS The operation complete successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +Ikev2SendIkePacket ( + IN IKE_UDP_SERVICE *IkeUdpService, + IN UINT8 *SessionCommon, + IN IKE_PACKET *IkePacket, + IN UINTN IkeType + ); + +/** + Callback function for the IKE life time is over. + + This function will mark the related IKE SA Session as deleting and trigger a + Information negotiation. + + @param[in] Event The time out event. + @param[in] Context Pointer to data passed by caller. + +**/ +VOID +EFIAPI +Ikev2LifetimeNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + This function will be called if the TimeOut Event is signaled. + + @param[in] Event The signaled Event. + @param[in] Context The data passed by caller. + +**/ +VOID +EFIAPI +Ikev2ResendNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Generate a Key Exchange payload according to the DH group type and save the + public Key into IkeSaSession IkeKey field. + + @param[in, out] IkeSaSession Pointer of the IKE_SA_SESSION. + @param[in] NextPayload The payload type presented in the NextPayload field of Key + Exchange Payload header. + + @retval Pointer to Key IKE payload. + +**/ +IKE_PAYLOAD * +Ikev2GenerateKePayload ( + IN OUT IKEV2_SA_SESSION *IkeSaSession, + IN UINT8 NextPayload + ); + +/** + Check if the SPD is related to the input Child SA Session. + + This function is the subfunction of Ikev1AssociateSpdEntry(). It is the call + back function of IpSecVisitConfigData(). + + + @param[in] Type Type of the input Config Selector. + @param[in] Selector Pointer to the Configure Selector to be checked. + @param[in] Data Pointer to the Configure Selector's Data passed + from the caller. + @param[in] SelectorSize The buffer size of Selector. + @param[in] DataSize The buffer size of the Data. + @param[in] Context The data passed from the caller. It is a Child + SA Session in this context. + + @retval EFI_SUCCESS The SPD Selector is not related to the Child SA Session. + @retval EFI_ABORTED The SPD Selector is related to the Child SA session and + set the ChildSaSession->Spd to point to this SPD Selector. + +**/ +EFI_STATUS +Ikev2MatchSpdEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE Type, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN UINTN SelectorSize, + IN UINTN DataSize, + IN VOID *Context + ); + +/** + Check if the Algorithm ID is supported. + + @param[in] AlgorithmId The specified Algorithm ID. + @param[in] Type The type used to indicate the Algorithm is for Encrypt or + Authentication. + + @retval TRUE If the Algorithm ID is supported. + @retval FALSE If the Algorithm ID is not supported. + +**/ +BOOLEAN +Ikev2IsSupportAlg ( + IN UINT16 AlgorithmId, + IN UINT8 Type + ); + +/** + Generate a ChildSa Session and insert it into related IkeSaSession. + + @param[in] IkeSaSession Pointer to related IKEV2_SA_SESSION. + @param[in] UdpService Pointer to related IKE_UDP_SERVICE. + + @return pointer of IKEV2_CHILD_SA_SESSION. + +**/ +IKEV2_CHILD_SA_SESSION * +Ikev2ChildSaSessionCreate ( + IN IKEV2_SA_SESSION *IkeSaSession, + IN IKE_UDP_SERVICE *UdpService + ) ; + +/** + Parse the received Initial Exchange Packet. + + This function parse the SA Payload and Key Payload to find out the cryptographic + suite for the further IKE negotiation and fill it into the IKE SA Session's + CommonSession->SaParams. + + @param[in, out] IkeSaSession Pointer to related IKEV2_SA_SESSION. + @param[in] SaPayload The received packet. + @param[in] Type The received packet IKE header flag. + + @retval TRUE If the SA proposal in Packet is acceptable. + @retval FALSE If the SA proposal in Packet is not acceptable. + +**/ +BOOLEAN +Ikev2SaParseSaPayload ( + IN OUT IKEV2_SA_SESSION *IkeSaSession, + IN IKE_PAYLOAD *SaPayload, + IN UINT8 Type + ); + +/** + Parse the received Authentication Exchange Packet. + + This function parse the SA Payload and Key Payload to find out the cryptographic + suite for the ESP and fill it into the Child SA Session's CommonSession->SaParams. + + @param[in, out] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION related to + this Authentication Exchange. + @param[in] SaPayload The received packet. + @param[in] Type The IKE header's flag of received packet . + + @retval TRUE If the SA proposal in Packet is acceptable. + @retval FALSE If the SA proposal in Packet is not acceptable. + +**/ +BOOLEAN +Ikev2ChildSaParseSaPayload ( + IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession, + IN IKE_PAYLOAD *SaPayload, + IN UINT8 Type + ); + +/** + Generate Key buffer from fragments. + + If the digest length of specified HashAlgId is larger than or equal with the + required output key length, derive the key directly. Otherwise, Key Material + needs to be PRF-based concatenation according to 2.13 of RFC 4306: + prf+ (K,S) = T1 | T2 | T3 | T4 | ..., T1 = prf (K, S | 0x01), + T2 = prf (K, T1 | S | 0x02), T3 = prf (K, T2 | S | 0x03),T4 = prf (K, T3 | S | 0x04) + then derive the key from this key material. + + @param[in] HashAlgId The Hash Algorithm ID used to generate key. + @param[in] HashKey Pointer to a key buffer which contains hash key. + @param[in] HashKeyLength The length of HashKey in bytes. + @param[in, out] OutputKey Pointer to buffer which is used to receive the + output key. + @param[in] OutputKeyLength The length of OutPutKey buffer. + @param[in] Fragments Pointer to the data to be used to generate key. + @param[in] NumFragments The numbers of the Fragement. + + @retval EFI_SUCCESS The operation complete successfully. + @retval EFI_INVALID_PARAMETER If NumFragments is zero. + @retval EFI_OUT_OF_RESOURCES If the required resource can't be allocated. + @retval Others The operation is failed. + +**/ +EFI_STATUS +Ikev2SaGenerateKey ( + IN UINT8 HashAlgId, + IN UINT8 *HashKey, + IN UINTN HashKeyLength, + IN OUT UINT8 *OutputKey, + IN UINTN OutputKeyLength, + IN PRF_DATA_FRAGMENT *Fragments, + IN UINTN NumFragments + ); + +/** + Copy ChildSaSession->Spd->Selector to ChildSaSession->SpdSelector. + + ChildSaSession->SpdSelector stores the real Spdselector for its SA. Sometime, + the SpdSelector in ChildSaSession is more accurated or the scope is smaller + than the one in ChildSaSession->Spd, especially for the tunnel mode. + + @param[in, out] ChildSaSession Pointer to IKEV2_CHILD_SA_SESSION related to. + + @retval EFI_SUCCESS The operation complete successfully. + @retval EFI_OUT_OF_RESOURCES If the required resource can't be allocated. + +**/ +EFI_STATUS +Ikev2ChildSaSessionSpdSelectorCreate ( + IN OUT IKEV2_CHILD_SA_SESSION *ChildSaSession + ); + +extern IKE_ALG_GUID_INFO mIPsecEncrAlgInfo[]; +#endif + diff --git a/Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.c b/Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.c new file mode 100644 index 0000000000..911d3e3236 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.c @@ -0,0 +1,3138 @@ +/** @file + The implementation of IPSEC_CONFIG_PROTOCOL. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecConfigImpl.h" +#include "IpSecDebug.h" + +LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum]; +BOOLEAN mSetBySelf = FALSE; + +// +// Common CompareSelector routine entry for SPD/SAD/PAD. +// +IPSEC_COMPARE_SELECTOR mCompareSelector[] = { + (IPSEC_COMPARE_SELECTOR) CompareSpdSelector, + (IPSEC_COMPARE_SELECTOR) CompareSaId, + (IPSEC_COMPARE_SELECTOR) ComparePadId +}; + +// +// Common IsZeroSelector routine entry for SPD/SAD/PAD. +// +IPSEC_IS_ZERO_SELECTOR mIsZeroSelector[] = { + (IPSEC_IS_ZERO_SELECTOR) IsZeroSpdSelector, + (IPSEC_IS_ZERO_SELECTOR) IsZeroSaId, + (IPSEC_IS_ZERO_SELECTOR) IsZeroPadId +}; + +// +// Common DuplicateSelector routine entry for SPD/SAD/PAD. +// +IPSEC_DUPLICATE_SELECTOR mDuplicateSelector[] = { + (IPSEC_DUPLICATE_SELECTOR) DuplicateSpdSelector, + (IPSEC_DUPLICATE_SELECTOR) DuplicateSaId, + (IPSEC_DUPLICATE_SELECTOR) DuplicatePadId +}; + +// +// Common FixPolicyEntry routine entry for SPD/SAD/PAD. +// +IPSEC_FIX_POLICY_ENTRY mFixPolicyEntry[] = { + (IPSEC_FIX_POLICY_ENTRY) FixSpdEntry, + (IPSEC_FIX_POLICY_ENTRY) FixSadEntry, + (IPSEC_FIX_POLICY_ENTRY) FixPadEntry +}; + +// +// Common UnfixPolicyEntry routine entry for SPD/SAD/PAD. +// +IPSEC_FIX_POLICY_ENTRY mUnfixPolicyEntry[] = { + (IPSEC_FIX_POLICY_ENTRY) UnfixSpdEntry, + (IPSEC_FIX_POLICY_ENTRY) UnfixSadEntry, + (IPSEC_FIX_POLICY_ENTRY) UnfixPadEntry +}; + +// +// Common SetPolicyEntry routine entry for SPD/SAD/PAD. +// +IPSEC_SET_POLICY_ENTRY mSetPolicyEntry[] = { + (IPSEC_SET_POLICY_ENTRY) SetSpdEntry, + (IPSEC_SET_POLICY_ENTRY) SetSadEntry, + (IPSEC_SET_POLICY_ENTRY) SetPadEntry +}; + +// +// Common GetPolicyEntry routine entry for SPD/SAD/PAD. +// +IPSEC_GET_POLICY_ENTRY mGetPolicyEntry[] = { + (IPSEC_GET_POLICY_ENTRY) GetSpdEntry, + (IPSEC_GET_POLICY_ENTRY) GetSadEntry, + (IPSEC_GET_POLICY_ENTRY) GetPadEntry +}; + +// +// Routine entry for IpSecConfig protocol. +// +EFI_IPSEC_CONFIG_PROTOCOL mIpSecConfigInstance = { + EfiIpSecConfigSetData, + EfiIpSecConfigGetData, + EfiIpSecConfigGetNextSelector, + EfiIpSecConfigRegisterNotify, + EfiIpSecConfigUnregisterNotify +}; + +/** + Get the all IPSec configuration variables and store those variables + to the internal data structure. + + This founction is called by IpSecConfigInitialize() that is to intialize the + IPsecConfiguration Protocol. + + @param[in] Private Point to IPSEC_PRIVATE_DATA. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + @retval EFI_SUCCESS Restore the IPsec Configuration successfully. + @retval others Other errors is found during the variable getting. + +**/ +EFI_STATUS +IpSecConfigRestore ( + IN IPSEC_PRIVATE_DATA *Private + ); + +/** + Check if the specified EFI_IP_ADDRESS_INFO is in EFI_IP_ADDRESS_INFO list. + + @param[in] AddressInfo Pointer of IP_ADDRESS_INFO to be search in AddressInfo list. + @param[in] AddressInfoList A list that contains IP_ADDRESS_INFOs. + @param[in] AddressCount Point out how many IP_ADDRESS_INFO in the list. + + @retval TRUE The specified AddressInfo is in the AddressInfoList. + @retval FALSE The specified AddressInfo is not in the AddressInfoList. + +**/ +BOOLEAN +IsInAddressInfoList( + IN EFI_IP_ADDRESS_INFO *AddressInfo, + IN EFI_IP_ADDRESS_INFO *AddressInfoList, + IN UINT32 AddressCount + ) +{ + UINT8 Index; + EFI_IP_ADDRESS ZeroAddress; + + ZeroMem(&ZeroAddress, sizeof (EFI_IP_ADDRESS)); + + // + // Zero Address means any address is matched. + // + if (AddressCount == 1) { + if (CompareMem ( + &AddressInfoList[0].Address, + &ZeroAddress, + sizeof (EFI_IP_ADDRESS) + ) == 0) { + return TRUE; + } + } + for (Index = 0; Index < AddressCount ; Index++) { + if (CompareMem ( + AddressInfo, + &AddressInfoList[Index].Address, + sizeof (EFI_IP_ADDRESS) + ) == 0 && + AddressInfo->PrefixLength == AddressInfoList[Index].PrefixLength + ) { + return TRUE; + } + } + return FALSE; +} + +/** + Compare two SPD Selectors. + + Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/ + NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the + Local Addresses and remote Addresses. + + @param[in] Selector1 Pointer of first SPD Selector. + @param[in] Selector2 Pointer of second SPD Selector. + + @retval TRUE This two Selector have the same value in above fields. + @retval FALSE Not all above fields have the same value in these two Selectors. + +**/ +BOOLEAN +CompareSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ) +{ + EFI_IPSEC_SPD_SELECTOR *SpdSel1; + EFI_IPSEC_SPD_SELECTOR *SpdSel2; + BOOLEAN IsMatch; + UINTN Index; + + SpdSel1 = &Selector1->SpdSelector; + SpdSel2 = &Selector2->SpdSelector; + IsMatch = TRUE; + + // + // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/ + // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the + // two Spdselectors. Since the SPD supports two directions, it needs to + // compare two directions. + // + if ((SpdSel1->LocalAddressCount != SpdSel2->LocalAddressCount && + SpdSel1->LocalAddressCount != SpdSel2->RemoteAddressCount) || + (SpdSel1->RemoteAddressCount != SpdSel2->RemoteAddressCount && + SpdSel1->RemoteAddressCount != SpdSel2->LocalAddressCount) || + SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol || + SpdSel1->LocalPort != SpdSel2->LocalPort || + SpdSel1->LocalPortRange != SpdSel2->LocalPortRange || + SpdSel1->RemotePort != SpdSel2->RemotePort || + SpdSel1->RemotePortRange != SpdSel2->RemotePortRange + ) { + IsMatch = FALSE; + return IsMatch; + } + + // + // Compare the all LocalAddress fields in the two Spdselectors. + // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare + // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return + // TRUE. + // + for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->LocalAddress[Index], + SpdSel2->LocalAddress, + SpdSel2->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel2->LocalAddress[Index], + SpdSel1->LocalAddress, + SpdSel1->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->RemoteAddress[Index], + SpdSel2->RemoteAddress, + SpdSel2->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel2->RemoteAddress[Index], + SpdSel1->RemoteAddress, + SpdSel1->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + // + // Finish the one direction compare. If it is matched, return; otherwise, + // compare the other direction. + // + if (IsMatch) { + return IsMatch; + } + // + // Secondly, the SpdSel1->LocalAddress doesn't equal to SpdSel2->LocalAddress and + // SpdSel1->RemoteAddress doesn't equal to SpdSel2->RemoteAddress. Try to compare + // the RemoteAddress to LocalAddress. + // + IsMatch = TRUE; + for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->RemoteAddress[Index], + SpdSel2->LocalAddress, + SpdSel2->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel2->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel2->RemoteAddress[Index], + SpdSel1->LocalAddress, + SpdSel1->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->LocalAddress[Index], + SpdSel2->RemoteAddress, + SpdSel2->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + if (IsMatch) { + for (Index = 0; Index < SpdSel2->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel2->LocalAddress[Index], + SpdSel1->RemoteAddress, + SpdSel1->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + return IsMatch; +} + +/** + Find if the two SPD Selectors has subordinative. + + Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/ + NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the + Local Addresses and remote Addresses. + + @param[in] Selector1 Pointer of first SPD Selector. + @param[in] Selector2 Pointer of second SPD Selector. + + @retval TRUE The first SPD Selector is subordinate Selector of second SPD Selector. + @retval FALSE The first SPD Selector is not subordinate Selector of second + SPD Selector. + +**/ +BOOLEAN +IsSubSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ) +{ + EFI_IPSEC_SPD_SELECTOR *SpdSel1; + EFI_IPSEC_SPD_SELECTOR *SpdSel2; + BOOLEAN IsMatch; + UINTN Index; + + SpdSel1 = &Selector1->SpdSelector; + SpdSel2 = &Selector2->SpdSelector; + IsMatch = TRUE; + + // + // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/ + // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the + // two Spdselectors. Since the SPD supports two directions, it needs to + // compare two directions. + // + if (SpdSel1->LocalAddressCount > SpdSel2->LocalAddressCount || + SpdSel1->RemoteAddressCount > SpdSel2->RemoteAddressCount || + (SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol && SpdSel2->NextLayerProtocol != 0xffff) || + (SpdSel1->LocalPort > SpdSel2->LocalPort && SpdSel2->LocalPort != 0)|| + (SpdSel1->LocalPortRange > SpdSel2->LocalPortRange && SpdSel1->LocalPort != 0)|| + (SpdSel1->RemotePort > SpdSel2->RemotePort && SpdSel2->RemotePort != 0) || + (SpdSel1->RemotePortRange > SpdSel2->RemotePortRange && SpdSel2->RemotePort != 0) + ) { + IsMatch = FALSE; + } + + // + // Compare the all LocalAddress fields in the two Spdselectors. + // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare + // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return + // TRUE. + // + if (IsMatch) { + for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->LocalAddress[Index], + SpdSel2->LocalAddress, + SpdSel2->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + + if (IsMatch) { + for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->RemoteAddress[Index], + SpdSel2->RemoteAddress, + SpdSel2->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + } + if (IsMatch) { + return IsMatch; + } + + // + // + // The SPD selector in SPD entry is two way. + // + // Compare the LocalAddressCount/RemoteAddressCount/NextLayerProtocol/ + // LocalPort/LocalPortRange/RemotePort/RemotePortRange fields in the + // two Spdselectors. Since the SPD supports two directions, it needs to + // compare two directions. + // + IsMatch = TRUE; + if (SpdSel1->LocalAddressCount > SpdSel2->RemoteAddressCount || + SpdSel1->RemoteAddressCount > SpdSel2->LocalAddressCount || + (SpdSel1->NextLayerProtocol != SpdSel2->NextLayerProtocol && SpdSel2->NextLayerProtocol != 0xffff) || + (SpdSel1->LocalPort > SpdSel2->RemotePort && SpdSel2->RemotePort != 0)|| + (SpdSel1->LocalPortRange > SpdSel2->RemotePortRange && SpdSel1->RemotePort != 0)|| + (SpdSel1->RemotePort > SpdSel2->LocalPort && SpdSel2->LocalPort != 0) || + (SpdSel1->RemotePortRange > SpdSel2->LocalPortRange && SpdSel2->LocalPort != 0) + ) { + IsMatch = FALSE; + return IsMatch; + } + + // + // Compare the all LocalAddress fields in the two Spdselectors. + // First, SpdSel1->LocalAddress to SpdSel2->LocalAddress && Compare + // SpdSel1->RemoteAddress to SpdSel2->RemoteAddress. If all match, return + // TRUE. + // + for (Index = 0; Index < SpdSel1->LocalAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->LocalAddress[Index], + SpdSel2->RemoteAddress, + SpdSel2->RemoteAddressCount + )) { + IsMatch = FALSE; + break; + } + } + + if (IsMatch) { + for (Index = 0; Index < SpdSel1->RemoteAddressCount; Index++) { + if (!IsInAddressInfoList ( + &SpdSel1->RemoteAddress[Index], + SpdSel2->LocalAddress, + SpdSel2->LocalAddressCount + )) { + IsMatch = FALSE; + break; + } + } + } + return IsMatch; + +} + +/** + Compare two SA IDs. + + @param[in] Selector1 Pointer of first SA ID. + @param[in] Selector2 Pointer of second SA ID. + + @retval TRUE This two Selectors have the same SA ID. + @retval FALSE This two Selecotrs don't have the same SA ID. + +**/ +BOOLEAN +CompareSaId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ) +{ + EFI_IPSEC_SA_ID *SaId1; + EFI_IPSEC_SA_ID *SaId2; + BOOLEAN IsMatch; + + SaId1 = &Selector1->SaId; + SaId2 = &Selector2->SaId; + IsMatch = TRUE; + + if (CompareMem (SaId1, SaId2, sizeof (EFI_IPSEC_SA_ID)) != 0) { + IsMatch = FALSE; + } + + return IsMatch; +} + +/** + Compare two PAD IDs. + + @param[in] Selector1 Pointer of first PAD ID. + @param[in] Selector2 Pointer of second PAD ID. + + @retval TRUE This two Selectors have the same PAD ID. + @retval FALSE This two Selecotrs don't have the same PAD ID. + +**/ +BOOLEAN +ComparePadId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ) +{ + EFI_IPSEC_PAD_ID *PadId1; + EFI_IPSEC_PAD_ID *PadId2; + BOOLEAN IsMatch; + + PadId1 = &Selector1->PadId; + PadId2 = &Selector2->PadId; + IsMatch = TRUE; + + // + // Compare the PeerIdValid fields in PadId. + // + if (PadId1->PeerIdValid != PadId2->PeerIdValid) { + IsMatch = FALSE; + } + // + // Compare the PeerId fields in PadId if PeerIdValid is true. + // + if (IsMatch && + PadId1->PeerIdValid && + AsciiStriCmp ((CONST CHAR8 *) PadId1->Id.PeerId, (CONST CHAR8 *) PadId2->Id.PeerId) != 0 + ) { + IsMatch = FALSE; + } + // + // Compare the IpAddress fields in PadId if PeerIdValid is false. + // + if (IsMatch && + !PadId1->PeerIdValid && + (PadId1->Id.IpAddress.PrefixLength != PadId2->Id.IpAddress.PrefixLength || + CompareMem (&PadId1->Id.IpAddress.Address, &PadId2->Id.IpAddress.Address, sizeof (EFI_IP_ADDRESS)) != 0) + ) { + IsMatch = FALSE; + } + + return IsMatch; +} + +/** + Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount + fields. + + @param[in] Selector Pointer of the SPD Selector. + + @retval TRUE If the SPD Selector is Zero. + @retval FALSE If the SPD Selector is not Zero. + +**/ +BOOLEAN +IsZeroSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ) +{ + EFI_IPSEC_SPD_SELECTOR *SpdSel; + BOOLEAN IsZero; + + SpdSel = &Selector->SpdSelector; + IsZero = FALSE; + + if (SpdSel->LocalAddressCount == 0 && SpdSel->RemoteAddressCount == 0) { + IsZero = TRUE; + } + + return IsZero; +} + +/** + Check if the SA ID is Zero by its DestAddress. + + @param[in] Selector Pointer of the SA ID. + + @retval TRUE If the SA ID is Zero. + @retval FALSE If the SA ID is not Zero. + +**/ +BOOLEAN +IsZeroSaId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ) +{ + BOOLEAN IsZero; + EFI_IPSEC_CONFIG_SELECTOR ZeroSelector; + + IsZero = FALSE; + + ZeroMem (&ZeroSelector, sizeof (EFI_IPSEC_CONFIG_SELECTOR)); + + if (CompareMem (&ZeroSelector, Selector, sizeof (EFI_IPSEC_CONFIG_SELECTOR)) == 0) { + IsZero = TRUE; + } + + return IsZero; +} + +/** + Check if the PAD ID is Zero. + + @param[in] Selector Pointer of the PAD ID. + + @retval TRUE If the PAD ID is Zero. + @retval FALSE If the PAD ID is not Zero. + +**/ +BOOLEAN +IsZeroPadId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ) +{ + EFI_IPSEC_PAD_ID *PadId; + EFI_IPSEC_PAD_ID ZeroId; + BOOLEAN IsZero; + + PadId = &Selector->PadId; + IsZero = FALSE; + + ZeroMem (&ZeroId, sizeof (EFI_IPSEC_PAD_ID)); + + if (CompareMem (PadId, &ZeroId, sizeof (EFI_IPSEC_PAD_ID)) == 0) { + IsZero = TRUE; + } + + return IsZero; +} + +/** + Copy Source SPD Selector to the Destination SPD Selector. + + @param[in, out] DstSel Pointer of Destination SPD Selector. + @param[in] SrcSel Pointer of Source SPD Selector. + @param[in, out] Size The size of the Destination SPD Selector. If it + not NULL and its value less than the size of + Source SPD Selector, the value of Source SPD + Selector's size will be passed to caller by this + parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source SPD Selector is NULL + @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of the Source SPD Selector. + @retval EFI_SUCCESS Copy Source SPD Selector to the Destination SPD + Selector successfully. + +**/ +EFI_STATUS +DuplicateSpdSelector ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ) +{ + EFI_IPSEC_SPD_SELECTOR *Dst; + EFI_IPSEC_SPD_SELECTOR *Src; + + Dst = &DstSel->SpdSelector; + Src = &SrcSel->SpdSelector; + + if (Dst == NULL || Src == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Size != NULL && (*Size) < SIZE_OF_SPD_SELECTOR (Src)) { + *Size = SIZE_OF_SPD_SELECTOR (Src); + return EFI_BUFFER_TOO_SMALL; + } + // + // Copy the base structure of SPD selector. + // + CopyMem (Dst, Src, sizeof (EFI_IPSEC_SPD_SELECTOR)); + + // + // Copy the local address array of SPD selector. + // + Dst->LocalAddress = (EFI_IP_ADDRESS_INFO *) (Dst + 1); + CopyMem ( + Dst->LocalAddress, + Src->LocalAddress, + sizeof (EFI_IP_ADDRESS_INFO) * Dst->LocalAddressCount + ); + + // + // Copy the remote address array of SPD selector. + // + Dst->RemoteAddress = Dst->LocalAddress + Dst->LocalAddressCount; + CopyMem ( + Dst->RemoteAddress, + Src->RemoteAddress, + sizeof (EFI_IP_ADDRESS_INFO) * Dst->RemoteAddressCount + ); + + return EFI_SUCCESS; +} + +/** + Copy Source SA ID to the Destination SA ID. + + @param[in, out] DstSel Pointer of Destination SA ID. + @param[in] SrcSel Pointer of Source SA ID. + @param[in, out] Size The size of the Destination SA ID. If it + not NULL and its value less than the size of + Source SA ID, the value of Source SA ID's size + will be passed to caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source SA ID is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source SA ID. + @retval EFI_SUCCESS Copy Source SA ID to the Destination SA ID successfully. + +**/ +EFI_STATUS +DuplicateSaId ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ) +{ + EFI_IPSEC_SA_ID *Dst; + EFI_IPSEC_SA_ID *Src; + + Dst = &DstSel->SaId; + Src = &SrcSel->SaId; + + if (Dst == NULL || Src == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Size != NULL && *Size < sizeof (EFI_IPSEC_SA_ID)) { + *Size = sizeof (EFI_IPSEC_SA_ID); + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (Dst, Src, sizeof (EFI_IPSEC_SA_ID)); + + return EFI_SUCCESS; +} + +/** + Copy Source PAD ID to the Destination PAD ID. + + @param[in, out] DstSel Pointer of Destination PAD ID. + @param[in] SrcSel Pointer of Source PAD ID. + @param[in, out] Size The size of the Destination PAD ID. If it + not NULL and its value less than the size of + Source PAD ID, the value of Source PAD ID's size + will be passed to caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source PAD ID is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source PAD ID . + @retval EFI_SUCCESS Copy Source PAD ID to the Destination PAD ID successfully. + +**/ +EFI_STATUS +DuplicatePadId ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ) +{ + EFI_IPSEC_PAD_ID *Dst; + EFI_IPSEC_PAD_ID *Src; + + Dst = &DstSel->PadId; + Src = &SrcSel->PadId; + + if (Dst == NULL || Src == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Size != NULL && *Size < sizeof (EFI_IPSEC_PAD_ID)) { + *Size = sizeof (EFI_IPSEC_PAD_ID); + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (Dst, Src, sizeof (EFI_IPSEC_PAD_ID)); + + return EFI_SUCCESS; +} + +/** + Fix the value of some members of SPD Selector. + + This function is called by IpSecCopyPolicyEntry()which copy the Policy + Entry into the Variable. Since some members in SPD Selector are pointers, + a physical address to relative address convertion is required before copying + this SPD entry into the variable. + + @param[in] Selector Pointer of SPD Selector. + @param[in, out] Data Pointer of SPD Data. + +**/ +VOID +FixSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *Selector, + IN OUT EFI_IPSEC_SPD_DATA *Data + ) +{ + // + // It assumes that all ref buffers in SPD selector and data are + // stored in the continous memory and close to the base structure. + // + FIX_REF_BUF_ADDR (Selector->LocalAddress, Selector); + FIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector); + + if (Data->ProcessingPolicy != NULL) { + if (Data->ProcessingPolicy->TunnelOption != NULL) { + FIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data); + } + + FIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data); + } + +} + +/** + Fix the value of some members of SA ID. + + This function is called by IpSecCopyPolicyEntry()which copy the Policy + Entry into the Variable. Since some members in SA ID are pointers, + a physical address to relative address conversion is required before copying + this SAD into the variable. + + @param[in] SaId Pointer of SA ID + @param[in, out] Data Pointer of SA Data. + +**/ +VOID +FixSadEntry ( + IN EFI_IPSEC_SA_ID *SaId, + IN OUT EFI_IPSEC_SA_DATA2 *Data + ) +{ + // + // It assumes that all ref buffers in SAD selector and data are + // stored in the continous memory and close to the base structure. + // + if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data); + } + + if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + FIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data); + } + + if (Data->SpdSelector != NULL) { + if (Data->SpdSelector->LocalAddress != NULL) { + FIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data); + } + + FIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data); + FIX_REF_BUF_ADDR (Data->SpdSelector, Data); + } + +} + +/** + Fix the value of some members of PAD ID. + + This function is called by IpSecCopyPolicyEntry()which copy the Policy + Entry into the Variable. Since some members in PAD ID are pointers, + a physical address to relative address conversion is required before copying + this PAD into the variable. + + @param[in] PadId Pointer of PAD ID. + @param[in, out] Data Pointer of PAD Data. + +**/ +VOID +FixPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN OUT EFI_IPSEC_PAD_DATA *Data + ) +{ + // + // It assumes that all ref buffers in pad selector and data are + // stored in the continous memory and close to the base structure. + // + if (Data->AuthData != NULL) { + FIX_REF_BUF_ADDR (Data->AuthData, Data); + } + + if (Data->RevocationData != NULL) { + FIX_REF_BUF_ADDR (Data->RevocationData, Data); + } + +} + +/** + Recover the value of some members of SPD Selector. + + This function is corresponding to FixSpdEntry(). It recovers the value of members + of SPD Selector that are fixed by FixSpdEntry(). + + @param[in, out] Selector Pointer of SPD Selector. + @param[in, out] Data Pointer of SPD Data. + +**/ +VOID +UnfixSpdEntry ( + IN OUT EFI_IPSEC_SPD_SELECTOR *Selector, + IN OUT EFI_IPSEC_SPD_DATA *Data + ) +{ + // + // It assumes that all ref buffers in SPD selector and data are + // stored in the continous memory and close to the base structure. + // + UNFIX_REF_BUF_ADDR (Selector->LocalAddress, Selector); + UNFIX_REF_BUF_ADDR (Selector->RemoteAddress, Selector); + + if (Data->ProcessingPolicy != NULL) { + UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy, Data); + if (Data->ProcessingPolicy->TunnelOption != NULL) { + UNFIX_REF_BUF_ADDR (Data->ProcessingPolicy->TunnelOption, Data); + } + } + +} + +/** + Recover the value of some members of SA ID. + + This function is corresponding to FixSadEntry(). It recovers the value of members + of SAD ID that are fixed by FixSadEntry(). + + @param[in, out] SaId Pointer of SAD ID. + @param[in, out] Data Pointer of SAD Data. + +**/ +VOID +UnfixSadEntry ( + IN OUT EFI_IPSEC_SA_ID *SaId, + IN OUT EFI_IPSEC_SA_DATA2 *Data + ) +{ + // + // It assumes that all ref buffers in SAD selector and data are + // stored in the continous memory and close to the base structure. + // + if (Data->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.AuthKey, Data); + } + + if (SaId->Proto == EfiIPsecESP && Data->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + UNFIX_REF_BUF_ADDR (Data->AlgoInfo.EspAlgoInfo.EncKey, Data); + } + + if (Data->SpdSelector != NULL) { + UNFIX_REF_BUF_ADDR (Data->SpdSelector, Data); + if (Data->SpdSelector->LocalAddress != NULL) { + UNFIX_REF_BUF_ADDR (Data->SpdSelector->LocalAddress, Data); + } + + UNFIX_REF_BUF_ADDR (Data->SpdSelector->RemoteAddress, Data); + } + +} + +/** + Recover the value of some members of PAD ID. + + This function is corresponding to FixPadEntry(). It recovers the value of members + of PAD ID that are fixed by FixPadEntry(). + + @param[in] PadId Pointer of PAD ID. + @param[in, out] Data Pointer of PAD Data. + +**/ +VOID +UnfixPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN OUT EFI_IPSEC_PAD_DATA *Data + ) +{ + // + // It assumes that all ref buffers in pad selector and data are + // stored in the continous memory and close to the base structure. + // + if (Data->AuthData != NULL) { + UNFIX_REF_BUF_ADDR (Data->AuthData, Data); + } + + if (Data->RevocationData != NULL) { + UNFIX_REF_BUF_ADDR (Data->RevocationData, Data); + } + +} + +/** + Set the security policy information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_SPD_DATA. + @param[in] Context Pointer to one entry selector that describes + the expected position the new data entry will + be added. If Context is NULL, the new entry will + be appended the end of database. + + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - Selector is not NULL and its LocalAddress + is NULL or its RemoteAddress is NULL. + - Data is not NULL and its Action is Protected + and its plolicy is NULL. + - Data is not NULL, its Action is not protected, + and its policy is not NULL. + - The Action of Data is Protected, its policy + mode is Tunnel, and its tunnel option is NULL. + - The Action of Data is protected and its policy + mode is not Tunnel and it tunnel option is not NULL. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetSpdEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ) +{ + EFI_IPSEC_SPD_SELECTOR *SpdSel; + EFI_IPSEC_SPD_DATA *SpdData; + EFI_IPSEC_SPD_SELECTOR *InsertBefore; + LIST_ENTRY *SpdList; + LIST_ENTRY *SadList; + LIST_ENTRY *SpdSas; + LIST_ENTRY *EntryInsertBefore; + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + LIST_ENTRY *NextEntry; + IPSEC_SPD_ENTRY *SpdEntry; + IPSEC_SAD_ENTRY *SadEntry; + UINTN SpdEntrySize; + UINTN Index; + + SpdSel = (Selector == NULL) ? NULL : &Selector->SpdSelector; + SpdData = (Data == NULL) ? NULL : (EFI_IPSEC_SPD_DATA *) Data; + InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SpdSelector; + SpdList = &mConfigData[IPsecConfigDataTypeSpd]; + + if (SpdSel != NULL) { + if (SpdSel->LocalAddress == NULL || SpdSel->RemoteAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + } + + if (SpdData != NULL) { + if ((SpdData->Action == EfiIPsecActionProtect && SpdData->ProcessingPolicy == NULL) || + (SpdData->Action != EfiIPsecActionProtect && SpdData->ProcessingPolicy != NULL) + ) { + return EFI_INVALID_PARAMETER; + } + + if (SpdData->Action == EfiIPsecActionProtect) { + if ((SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption == NULL) || + (SpdData->ProcessingPolicy->Mode != EfiIPsecTunnel && SpdData->ProcessingPolicy->TunnelOption != NULL) + ) { + return EFI_INVALID_PARAMETER; + } + } + } + // + // The default behavior is to insert the node ahead of the header. + // + EntryInsertBefore = SpdList; + + // + // Remove the existed SPD entry. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SpdList) { + + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + + if (SpdSel == NULL || + CompareSpdSelector ((EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel) + ) { + // + // Record the existed entry position to keep the original order. + // + EntryInsertBefore = SpdEntry->List.ForwardLink; + RemoveEntryList (&SpdEntry->List); + + // + // Update the reverse ref of SAD entry in the SPD.sas list. + // + SpdSas = &SpdEntry->Data->Sas; + + // + // TODO: Deleted the related SAs. + // + NET_LIST_FOR_EACH (Entry2, SpdSas) { + SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry2); + SadEntry->Data->SpdEntry = NULL; + } + + // + // Free the existed SPD entry + // + FreePool (SpdEntry); + } + } + // + // Return success here if only want to remove the SPD entry. + // + if (SpdData == NULL || SpdSel == NULL) { + return EFI_SUCCESS; + } + // + // Search the appointed entry position if InsertBefore is not NULL. + // + if (InsertBefore != NULL) { + + NET_LIST_FOR_EACH (Entry, SpdList) { + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + + if (CompareSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, + (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore + )) { + EntryInsertBefore = Entry; + break; + } + } + } + + // + // Do Padding for the different Arch. + // + SpdEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_SPD_ENTRY)); + SpdEntrySize = ALIGN_VARIABLE (SpdEntrySize + (UINTN)SIZE_OF_SPD_SELECTOR (SpdSel)); + SpdEntrySize += IpSecGetSizeOfEfiSpdData (SpdData); + + SpdEntry = AllocateZeroPool (SpdEntrySize); + + if (SpdEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Fix the address of Selector and Data buffer and copy them, which is + // continous memory and close to the base structure of SPD entry. + // + SpdEntry->Selector = (EFI_IPSEC_SPD_SELECTOR *) ALIGN_POINTER ((SpdEntry + 1), sizeof (UINTN)); + SpdEntry->Data = (IPSEC_SPD_DATA *) ALIGN_POINTER ( + ((UINT8 *) SpdEntry->Selector + SIZE_OF_SPD_SELECTOR (SpdSel)), + sizeof (UINTN) + ); + + DuplicateSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector, + (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel, + NULL + ); + + CopyMem ( + SpdEntry->Data->Name, + SpdData->Name, + sizeof (SpdData->Name) + ); + SpdEntry->Data->PackageFlag = SpdData->PackageFlag; + SpdEntry->Data->TrafficDirection = SpdData->TrafficDirection; + SpdEntry->Data->Action = SpdData->Action; + + // + // Fix the address of ProcessingPolicy and copy it if need, which is continous + // memory and close to the base structure of SAD data. + // + if (SpdData->Action != EfiIPsecActionProtect) { + SpdEntry->Data->ProcessingPolicy = NULL; + } else { + SpdEntry->Data->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ALIGN_POINTER ( + SpdEntry->Data + 1, + sizeof (UINTN) + ); + IpSecDuplicateProcessPolicy (SpdEntry->Data->ProcessingPolicy, SpdData->ProcessingPolicy); + } + // + // Update the sas list of the new SPD entry. + // + InitializeListHead (&SpdEntry->Data->Sas); + + SadList = &mConfigData[IPsecConfigDataTypeSad]; + + NET_LIST_FOR_EACH (Entry, SadList) { + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + + for (Index = 0; Index < SpdData->SaIdCount; Index++) { + + if (CompareSaId ( + (EFI_IPSEC_CONFIG_SELECTOR *) &SpdData->SaId[Index], + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id + )) { + if (SadEntry->Data->SpdEntry != NULL) { + RemoveEntryList (&SadEntry->BySpd); + } + InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd); + SadEntry->Data->SpdEntry = SpdEntry; + } + } + } + // + // Insert the new SPD entry. + // + InsertTailList (EntryInsertBefore, &SpdEntry->List); + + return EFI_SUCCESS; +} + +/** + Set the security association information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_SA_DATA. + @param[in] Context Pointer to one entry selector which describes + the expected position the new data entry will + be added. If Context is NULL,the new entry will + be appended the end of database. + + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetSadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ) +{ + IPSEC_SAD_ENTRY *SadEntry; + IPSEC_SPD_ENTRY *SpdEntry; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + LIST_ENTRY *SadList; + LIST_ENTRY *SpdList; + EFI_IPSEC_SA_ID *SaId; + EFI_IPSEC_SA_DATA2 *SaData; + EFI_IPSEC_SA_ID *InsertBefore; + LIST_ENTRY *EntryInsertBefore; + UINTN SadEntrySize; + + SaId = (Selector == NULL) ? NULL : &Selector->SaId; + SaData = (Data == NULL) ? NULL : (EFI_IPSEC_SA_DATA2 *) Data; + InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->SaId; + SadList = &mConfigData[IPsecConfigDataTypeSad]; + + // + // The default behavior is to insert the node ahead of the header. + // + EntryInsertBefore = SadList; + + // + // Remove the existed SAD entry. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, SadList) { + + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + + if (SaId == NULL || + CompareSaId ( + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id, + (EFI_IPSEC_CONFIG_SELECTOR *) SaId + )) { + // + // Record the existed entry position to keep the original order. + // + EntryInsertBefore = SadEntry->List.ForwardLink; + + // + // Update the related SAD.byspd field. + // + if (SadEntry->Data->SpdEntry != NULL) { + RemoveEntryList (&SadEntry->BySpd); + } + + RemoveEntryList (&SadEntry->List); + FreePool (SadEntry); + } + } + // + // Return success here if only want to remove the SAD entry + // + if (SaData == NULL || SaId == NULL) { + return EFI_SUCCESS; + } + // + // Search the appointed entry position if InsertBefore is not NULL. + // + if (InsertBefore != NULL) { + + NET_LIST_FOR_EACH (Entry, SadList) { + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + + if (CompareSaId ( + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id, + (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore + )) { + EntryInsertBefore = Entry; + break; + } + } + } + + // + // Do Padding for different Arch. + // + SadEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_SAD_ENTRY)); + SadEntrySize = ALIGN_VARIABLE (SadEntrySize + sizeof (EFI_IPSEC_SA_ID)); + SadEntrySize = ALIGN_VARIABLE (SadEntrySize + sizeof (IPSEC_SAD_DATA)); + + if (SaId->Proto == EfiIPsecAH) { + SadEntrySize += SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength; + } else { + SadEntrySize = ALIGN_VARIABLE (SadEntrySize + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength); + SadEntrySize += ALIGN_VARIABLE (SaData->AlgoInfo.EspAlgoInfo.EncKeyLength); + } + + if (SaData->SpdSelector != NULL) { + SadEntrySize += SadEntrySize + (UINTN)SIZE_OF_SPD_SELECTOR (SaData->SpdSelector); + } + SadEntry = AllocateZeroPool (SadEntrySize); + + if (SadEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Fix the address of Id and Data buffer and copy them, which is + // continous memory and close to the base structure of SAD entry. + // + SadEntry->Id = (EFI_IPSEC_SA_ID *) ALIGN_POINTER ((SadEntry + 1), sizeof (UINTN)); + SadEntry->Data = (IPSEC_SAD_DATA *) ALIGN_POINTER ((SadEntry->Id + 1), sizeof (UINTN)); + + CopyMem (SadEntry->Id, SaId, sizeof (EFI_IPSEC_SA_ID)); + + SadEntry->Data->Mode = SaData->Mode; + SadEntry->Data->SequenceNumber = SaData->SNCount; + SadEntry->Data->AntiReplayWindowSize = SaData->AntiReplayWindows; + + ZeroMem ( + &SadEntry->Data->AntiReplayBitmap, + sizeof (SadEntry->Data->AntiReplayBitmap) + ); + + ZeroMem ( + &SadEntry->Data->AlgoInfo, + sizeof (EFI_IPSEC_ALGO_INFO) + ); + + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId = SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId; + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength = SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength; + + if (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) { + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SadEntry->Data + 1), sizeof (UINTN)); + CopyMem ( + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, + SaData->AlgoInfo.EspAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength + ); + } + + if (SaId->Proto == EfiIPsecESP) { + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId = SaData->AlgoInfo.EspAlgoInfo.EncAlgoId; + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength = SaData->AlgoInfo.EspAlgoInfo.EncKeyLength; + + if (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) { + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (SadEntry->Data + 1) + + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength), + sizeof (UINTN) + ); + CopyMem ( + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey, + SaData->AlgoInfo.EspAlgoInfo.EncKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength + ); + } + } + + CopyMem ( + &SadEntry->Data->SaLifetime, + &SaData->SaLifetime, + sizeof (EFI_IPSEC_SA_LIFETIME) + ); + + SadEntry->Data->PathMTU = SaData->PathMTU; + SadEntry->Data->SpdSelector = NULL; + SadEntry->Data->ESNEnabled = FALSE; + SadEntry->Data->ManualSet = SaData->ManualSet; + + // + // Copy Tunnel Source/Destination Address + // + if (SaData->Mode == EfiIPsecTunnel) { + CopyMem ( + &SadEntry->Data->TunnelDestAddress, + &SaData->TunnelDestinationAddress, + sizeof (EFI_IP_ADDRESS) + ); + CopyMem ( + &SadEntry->Data->TunnelSourceAddress, + &SaData->TunnelSourceAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + // + // Update the spd.sas list of the spd entry specified by SAD selector + // + SpdList = &mConfigData[IPsecConfigDataTypeSpd]; + + for (Entry = SpdList->ForwardLink; Entry != SpdList && SaData->SpdSelector != NULL; Entry = Entry->ForwardLink) { + + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + if (IsSubSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector, + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector + ) && SpdEntry->Data->Action == EfiIPsecActionProtect) { + SadEntry->Data->SpdEntry = SpdEntry; + SadEntry->Data->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *)((UINT8 *)SadEntry + + SadEntrySize - + (UINTN)SIZE_OF_SPD_SELECTOR (SaData->SpdSelector) + ); + DuplicateSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdSelector, + (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector, + NULL + ); + InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd); + } + } + // + // Insert the new SAD entry. + // + InsertTailList (EntryInsertBefore, &SadEntry->List); + + return EFI_SUCCESS; +} + +/** + Set the peer authorization configuration information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_PAD_DATA. + @param[in] Context Pointer to one entry selector that describes + the expected position the new data entry will + be added. If Context is NULL, the new entry will + be appended the end of database. + + @retval EFI_OUT_OF_RESOURCES The required system resources could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetPadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ) +{ + IPSEC_PAD_ENTRY *PadEntry; + EFI_IPSEC_PAD_ID *PadId; + EFI_IPSEC_PAD_DATA *PadData; + LIST_ENTRY *PadList; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + EFI_IPSEC_PAD_ID *InsertBefore; + LIST_ENTRY *EntryInsertBefore; + UINTN PadEntrySize; + + PadId = (Selector == NULL) ? NULL : &Selector->PadId; + PadData = (Data == NULL) ? NULL : (EFI_IPSEC_PAD_DATA *) Data; + InsertBefore = (Context == NULL) ? NULL : &((EFI_IPSEC_CONFIG_SELECTOR *) Context)->PadId; + PadList = &mConfigData[IPsecConfigDataTypePad]; + + // + // The default behavior is to insert the node ahead of the header. + // + EntryInsertBefore = PadList; + + // + // Remove the existed pad entry. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, PadList) { + + PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry); + + if (PadId == NULL || + ComparePadId ((EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id, (EFI_IPSEC_CONFIG_SELECTOR *) PadId) + ) { + // + // Record the existed entry position to keep the original order. + // + EntryInsertBefore = PadEntry->List.ForwardLink; + RemoveEntryList (&PadEntry->List); + + FreePool (PadEntry); + } + } + // + // Return success here if only want to remove the pad entry + // + if (PadData == NULL || PadId == NULL) { + return EFI_SUCCESS; + } + // + // Search the appointed entry position if InsertBefore is not NULL. + // + if (InsertBefore != NULL) { + + NET_LIST_FOR_EACH (Entry, PadList) { + PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry); + + if (ComparePadId ( + (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id, + (EFI_IPSEC_CONFIG_SELECTOR *) InsertBefore + )) { + EntryInsertBefore = Entry; + break; + } + } + } + + // + // Do PADDING for different arch. + // + PadEntrySize = ALIGN_VARIABLE (sizeof (IPSEC_PAD_ENTRY)); + PadEntrySize = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_ID)); + PadEntrySize = ALIGN_VARIABLE (PadEntrySize + sizeof (EFI_IPSEC_PAD_DATA)); + PadEntrySize = ALIGN_VARIABLE (PadEntrySize + (PadData->AuthData != NULL ? PadData->AuthDataSize : 0)); + PadEntrySize += PadData->RevocationData != NULL ? PadData->RevocationDataSize : 0; + + PadEntry = AllocateZeroPool (PadEntrySize); + + if (PadEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Fix the address of Id and Data buffer and copy them, which is + // continous memory and close to the base structure of pad entry. + // + PadEntry->Id = (EFI_IPSEC_PAD_ID *) ALIGN_POINTER ((PadEntry + 1), sizeof (UINTN)); + PadEntry->Data = (EFI_IPSEC_PAD_DATA *) ALIGN_POINTER ((PadEntry->Id + 1), sizeof (UINTN)); + + CopyMem (PadEntry->Id, PadId, sizeof (EFI_IPSEC_PAD_ID)); + + PadEntry->Data->AuthProtocol = PadData->AuthProtocol; + PadEntry->Data->AuthMethod = PadData->AuthMethod; + PadEntry->Data->IkeIdFlag = PadData->IkeIdFlag; + + if (PadData->AuthData != NULL) { + PadEntry->Data->AuthDataSize = PadData->AuthDataSize; + PadEntry->Data->AuthData = (VOID *) ALIGN_POINTER (PadEntry->Data + 1, sizeof (UINTN)); + CopyMem ( + PadEntry->Data->AuthData, + PadData->AuthData, + PadData->AuthDataSize + ); + } else { + PadEntry->Data->AuthDataSize = 0; + PadEntry->Data->AuthData = NULL; + } + + if (PadData->RevocationData != NULL) { + PadEntry->Data->RevocationDataSize = PadData->RevocationDataSize; + PadEntry->Data->RevocationData = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (PadEntry->Data + 1) + PadData->AuthDataSize), + sizeof (UINTN) + ); + CopyMem ( + PadEntry->Data->RevocationData, + PadData->RevocationData, + PadData->RevocationDataSize + ); + } else { + PadEntry->Data->RevocationDataSize = 0; + PadEntry->Data->RevocationData = NULL; + } + // + // Insert the new pad entry. + // + InsertTailList (EntryInsertBefore, &PadEntry->List); + + return EFI_SUCCESS; +} + +/** + This function lookup the data entry from IPsec SPD. Return the configuration + value of the specified SPD Entry. + + @param[in] Selector Pointer to an entry selector which is an identifier + of the SPD entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetSpdEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + IPSEC_SPD_ENTRY *SpdEntry; + IPSEC_SAD_ENTRY *SadEntry; + EFI_IPSEC_SPD_SELECTOR *SpdSel; + EFI_IPSEC_SPD_DATA *SpdData; + LIST_ENTRY *SpdList; + LIST_ENTRY *SpdSas; + LIST_ENTRY *Entry; + UINTN RequiredSize; + + SpdSel = &Selector->SpdSelector; + SpdData = (EFI_IPSEC_SPD_DATA *) Data; + SpdList = &mConfigData[IPsecConfigDataTypeSpd]; + + NET_LIST_FOR_EACH (Entry, SpdList) { + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + + // + // Find the required SPD entry + // + if (CompareSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SpdSel, + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector + )) { + + RequiredSize = IpSecGetSizeOfSpdData (SpdEntry->Data); + if (*DataSize < RequiredSize) { + *DataSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + if (SpdData == NULL) { + return EFI_INVALID_PARAMETER; + } + + *DataSize = RequiredSize; + + // + // Extract and fill all SaId array from the SPD.sas list + // + SpdSas = &SpdEntry->Data->Sas; + SpdData->SaIdCount = 0; + + NET_LIST_FOR_EACH (Entry, SpdSas) { + SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry); + CopyMem ( + &SpdData->SaId[SpdData->SaIdCount++], + SadEntry->Id, + sizeof (EFI_IPSEC_SA_ID) + ); + } + // + // Fill the other fields in SPD data. + // + CopyMem (SpdData->Name, SpdEntry->Data->Name, sizeof (SpdData->Name)); + + SpdData->PackageFlag = SpdEntry->Data->PackageFlag; + SpdData->TrafficDirection = SpdEntry->Data->TrafficDirection; + SpdData->Action = SpdEntry->Data->Action; + + if (SpdData->Action != EfiIPsecActionProtect) { + SpdData->ProcessingPolicy = NULL; + } else { + SpdData->ProcessingPolicy = (EFI_IPSEC_PROCESS_POLICY *) ((UINT8 *) SpdData + sizeof (EFI_IPSEC_SPD_DATA) + (SpdData->SaIdCount - 1) * sizeof (EFI_IPSEC_SA_ID)); + + IpSecDuplicateProcessPolicy ( + SpdData->ProcessingPolicy, + SpdEntry->Data->ProcessingPolicy + ); + } + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + This function lookup the data entry from IPsec SAD. Return the configuration + value of the specified SAD Entry. + + @param[in] Selector Pointer to an entry selector which is an identifier + of the SAD entry. + @param[in, out] DataSize On output, the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetSadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + IPSEC_SAD_ENTRY *SadEntry; + LIST_ENTRY *Entry; + LIST_ENTRY *SadList; + EFI_IPSEC_SA_ID *SaId; + EFI_IPSEC_SA_DATA2 *SaData; + UINTN RequiredSize; + + SaId = &Selector->SaId; + SaData = (EFI_IPSEC_SA_DATA2 *) Data; + SadList = &mConfigData[IPsecConfigDataTypeSad]; + + NET_LIST_FOR_EACH (Entry, SadList) { + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + + // + // Find the required SAD entry. + // + if (CompareSaId ( + (EFI_IPSEC_CONFIG_SELECTOR *) SaId, + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Id + )) { + // + // Calculate the required size of the SAD entry. + // Data Layout is follows: + // |EFI_IPSEC_SA_DATA + // |AuthKey + // |EncryptKey (Optional) + // |SpdSelector (Optional) + // + RequiredSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_SA_DATA2)); + + if (SaId->Proto == EfiIPsecAH) { + RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength); + } else { + RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength); + RequiredSize = ALIGN_VARIABLE (RequiredSize + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength); + } + + if (SadEntry->Data->SpdSelector != NULL) { + RequiredSize += SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdSelector); + } + + if (*DataSize < RequiredSize) { + *DataSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Fill the data fields of SAD entry. + // + *DataSize = RequiredSize; + SaData->Mode = SadEntry->Data->Mode; + SaData->SNCount = SadEntry->Data->SequenceNumber; + SaData->AntiReplayWindows = SadEntry->Data->AntiReplayWindowSize; + + CopyMem ( + &SaData->SaLifetime, + &SadEntry->Data->SaLifetime, + sizeof (EFI_IPSEC_SA_LIFETIME) + ); + + ZeroMem ( + &SaData->AlgoInfo, + sizeof (EFI_IPSEC_ALGO_INFO) + ); + + if (SaId->Proto == EfiIPsecAH) { + // + // Copy AH alogrithm INFO to SaData + // + SaData->AlgoInfo.AhAlgoInfo.AuthAlgoId = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthAlgoId; + SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKeyLength; + if (SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength != 0) { + SaData->AlgoInfo.AhAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN)); + CopyMem ( + SaData->AlgoInfo.AhAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.AhAlgoInfo.AuthKey, + SaData->AlgoInfo.AhAlgoInfo.AuthKeyLength + ); + } + } else if (SaId->Proto == EfiIPsecESP) { + // + // Copy ESP alogrithem INFO to SaData + // + SaData->AlgoInfo.EspAlgoInfo.AuthAlgoId = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId; + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength; + if (SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength != 0) { + SaData->AlgoInfo.EspAlgoInfo.AuthKey = (VOID *) ALIGN_POINTER ((SaData + 1), sizeof (UINTN)); + CopyMem ( + SaData->AlgoInfo.EspAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength + ); + } + + SaData->AlgoInfo.EspAlgoInfo.EncAlgoId = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId; + SaData->AlgoInfo.EspAlgoInfo.EncKeyLength = SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength; + + if (SaData->AlgoInfo.EspAlgoInfo.EncKeyLength != 0) { + SaData->AlgoInfo.EspAlgoInfo.EncKey = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (SaData + 1) + + SaData->AlgoInfo.EspAlgoInfo.AuthKeyLength), + sizeof (UINTN) + ); + CopyMem ( + SaData->AlgoInfo.EspAlgoInfo.EncKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey, + SaData->AlgoInfo.EspAlgoInfo.EncKeyLength + ); + } + } + + SaData->PathMTU = SadEntry->Data->PathMTU; + + // + // Fill Tunnel Address if it is Tunnel Mode + // + if (SadEntry->Data->Mode == EfiIPsecTunnel) { + CopyMem ( + &SaData->TunnelDestinationAddress, + &SadEntry->Data->TunnelDestAddress, + sizeof (EFI_IP_ADDRESS) + ); + CopyMem ( + &SaData->TunnelSourceAddress, + &SadEntry->Data->TunnelSourceAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + // + // Fill the spd selector field of SAD data + // + if (SadEntry->Data->SpdSelector != NULL) { + + SaData->SpdSelector = (EFI_IPSEC_SPD_SELECTOR *) ( + (UINT8 *)SaData + + RequiredSize - + SIZE_OF_SPD_SELECTOR (SadEntry->Data->SpdSelector) + ); + + DuplicateSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SaData->SpdSelector, + (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdSelector, + NULL + ); + + } else { + + SaData->SpdSelector = NULL; + } + + SaData->ManualSet = SadEntry->Data->ManualSet; + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + This function lookup the data entry from IPsec PAD. Return the configuration + value of the specified PAD Entry. + + @param[in] Selector Pointer to an entry selector which is an identifier + of the PAD entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetPadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + IPSEC_PAD_ENTRY *PadEntry; + LIST_ENTRY *PadList; + LIST_ENTRY *Entry; + EFI_IPSEC_PAD_ID *PadId; + EFI_IPSEC_PAD_DATA *PadData; + UINTN RequiredSize; + + PadId = &Selector->PadId; + PadData = (EFI_IPSEC_PAD_DATA *) Data; + PadList = &mConfigData[IPsecConfigDataTypePad]; + + NET_LIST_FOR_EACH (Entry, PadList) { + PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry); + + // + // Find the required pad entry. + // + if (ComparePadId ( + (EFI_IPSEC_CONFIG_SELECTOR *) PadId, + (EFI_IPSEC_CONFIG_SELECTOR *) PadEntry->Id + )) { + // + // Calculate the required size of the pad entry. + // + RequiredSize = ALIGN_VARIABLE (sizeof (EFI_IPSEC_PAD_DATA)); + RequiredSize = ALIGN_VARIABLE (RequiredSize + PadEntry->Data->AuthDataSize); + RequiredSize += PadEntry->Data->RevocationDataSize; + + if (*DataSize < RequiredSize) { + *DataSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + // + // Fill the data fields of pad entry + // + *DataSize = RequiredSize; + PadData->AuthProtocol = PadEntry->Data->AuthProtocol; + PadData->AuthMethod = PadEntry->Data->AuthMethod; + PadData->IkeIdFlag = PadEntry->Data->IkeIdFlag; + + // + // Copy Authentication data. + // + if (PadEntry->Data->AuthData != NULL) { + + PadData->AuthDataSize = PadEntry->Data->AuthDataSize; + PadData->AuthData = (VOID *) ALIGN_POINTER ((PadData + 1), sizeof (UINTN)); + CopyMem ( + PadData->AuthData, + PadEntry->Data->AuthData, + PadData->AuthDataSize + ); + } else { + + PadData->AuthDataSize = 0; + PadData->AuthData = NULL; + } + // + // Copy Revocation Data. + // + if (PadEntry->Data->RevocationData != NULL) { + + PadData->RevocationDataSize = PadEntry->Data->RevocationDataSize; + PadData->RevocationData = (VOID *) ALIGN_POINTER ( + ((UINT8 *) (PadData + 1) + PadData->AuthDataSize), + sizeof (UINTN) + ); + CopyMem ( + PadData->RevocationData, + PadEntry->Data->RevocationData, + PadData->RevocationDataSize + ); + } else { + + PadData->RevocationDataSize = 0; + PadData->RevocationData = NULL; + } + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Copy Source Process Policy to the Destination Process Policy. + + @param[in] Dst Pointer to the Source Process Policy. + @param[in] Src Pointer to the Destination Process Policy. + +**/ +VOID +IpSecDuplicateProcessPolicy ( + IN EFI_IPSEC_PROCESS_POLICY *Dst, + IN EFI_IPSEC_PROCESS_POLICY *Src + ) +{ + // + // Firstly copy the structure content itself. + // + CopyMem (Dst, Src, sizeof (EFI_IPSEC_PROCESS_POLICY)); + + // + // Recursively copy the tunnel option if needed. + // + if (Dst->Mode != EfiIPsecTunnel) { + ASSERT (Dst->TunnelOption == NULL); + } else { + Dst->TunnelOption = (EFI_IPSEC_TUNNEL_OPTION *) ALIGN_POINTER ((Dst + 1), sizeof (UINTN)); + CopyMem ( + Dst->TunnelOption, + Src->TunnelOption, + sizeof (EFI_IPSEC_TUNNEL_OPTION) + ); + } +} + +/** + Calculate the a whole size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed + to by the pointer members. + + @param[in] SpdData Pointer to a specified EFI_IPSEC_SPD_DATA. + + @return the whole size the specified EFI_IPSEC_SPD_DATA. + +**/ +UINTN +IpSecGetSizeOfEfiSpdData ( + IN EFI_IPSEC_SPD_DATA *SpdData + ) +{ + UINTN Size; + + Size = ALIGN_VARIABLE (sizeof (IPSEC_SPD_DATA)); + + if (SpdData->Action == EfiIPsecActionProtect) { + Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_PROCESS_POLICY)); + + if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) { + Size = ALIGN_VARIABLE (Size + sizeof (EFI_IPSEC_TUNNEL_OPTION)); + } + } + + return Size; +} + +/** + Calculate the a whole size of IPSEC_SPD_DATA which includes the buffer size pointed + to by the pointer members and the buffer size used by the Sa List. + + @param[in] SpdData Pointer to the specified IPSEC_SPD_DATA. + + @return the whole size of IPSEC_SPD_DATA. + +**/ +UINTN +IpSecGetSizeOfSpdData ( + IN IPSEC_SPD_DATA *SpdData + ) +{ + UINTN Size; + LIST_ENTRY *Link; + + Size = sizeof (EFI_IPSEC_SPD_DATA) - sizeof (EFI_IPSEC_SA_ID); + + if (SpdData->Action == EfiIPsecActionProtect) { + Size += sizeof (EFI_IPSEC_PROCESS_POLICY); + + if (SpdData->ProcessingPolicy->Mode == EfiIPsecTunnel) { + Size += sizeof (EFI_IPSEC_TUNNEL_OPTION); + } + } + + NET_LIST_FOR_EACH (Link, &SpdData->Sas) { + Size += sizeof (EFI_IPSEC_SA_ID); + } + + return Size; +} + +/** + Get the IPsec Variable. + + Get the all variables which start with the string contained in VaraiableName. + Since all IPsec related variable store in continual space, those kinds of + variable can be searched by the EfiGetNextVariableName. Those variables also are + returned in a continual buffer. + + @param[in] VariableName Pointer to a specified Variable Name. + @param[in] VendorGuid Pointer to a specified Vendor Guid. + @param[in] Attributes Point to memory location to return the attributes + of variable. If the point is NULL, the parameter + would be ignored. + @param[in, out] DataSize As input, point to the maximum size of return + Data-Buffer. As output, point to the actual + size of the returned Data-Buffer. + @param[in] Data Point to return Data-Buffer. + + @retval EFI_ABORTED If the Variable size which contained in the variable + structure doesn't match the variable size obtained + from the EFIGetVariable. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has + been updated with the size needed to complete the request. + @retval EFI_SUCCESS The function completed successfully. + @retval others Other errors found during the variable getting. +**/ +EFI_STATUS +IpSecGetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 *Attributes, OPTIONAL + IN OUT UINTN *DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + EFI_GUID VendorGuidI; + UINTN VariableNameLength; + CHAR16 *VariableNameI; + UINTN VariableNameISize; + UINTN VariableNameISizeNew; + UINTN VariableIndex; + UINTN VariableCount; + IP_SEC_VARIABLE_INFO IpSecVariableInfo; + UINTN DataSizeI; + + // + // The variable name constructor is "VariableName + Info/0001/0002/... + NULL". + // So the varialbe name is like "VariableNameInfo", "VariableName0001", ... + // "VariableNameNULL". + // + VariableNameLength = StrLen (VariableName); + VariableNameISize = (VariableNameLength + 5) * sizeof (CHAR16); + VariableNameI = AllocateZeroPool (VariableNameISize); + if (VariableNameI == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Construct the varible name of ipsecconfig meta data. + // + UnicodeSPrint (VariableNameI, VariableNameISize, L"%s%s", VariableName, L"Info"); + + DataSizeI = sizeof (IpSecVariableInfo); + + Status = gRT->GetVariable ( + VariableNameI, + VendorGuid, + Attributes, + &DataSizeI, + &IpSecVariableInfo + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (*DataSize < IpSecVariableInfo.VariableSize) { + *DataSize = IpSecVariableInfo.VariableSize; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + + VariableCount = IpSecVariableInfo.VariableCount; + VariableNameI[0] = L'\0'; + + while (VariableCount != 0) { + // + // Get the variable name one by one in the variable database. + // + VariableNameISizeNew = VariableNameISize; + Status = gRT->GetNextVariableName ( + &VariableNameISizeNew, + VariableNameI, + &VendorGuidI + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + VariableNameI = ReallocatePool ( + VariableNameISize, + VariableNameISizeNew, + VariableNameI + ); + if (VariableNameI == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + VariableNameISize = VariableNameISizeNew; + + Status = gRT->GetNextVariableName ( + &VariableNameISizeNew, + VariableNameI, + &VendorGuidI + ); + } + + if (EFI_ERROR (Status)) { + break; + } + // + // Check whether the current variable is the required "ipsecconfig". + // + if (StrnCmp (VariableNameI, VariableName, VariableNameLength) == 0 || + CompareGuid (VendorGuid, &VendorGuidI) + ) { + // + // Parse the variable count of the current ipsecconfig data. + // + VariableIndex = StrDecimalToUintn (VariableNameI + VariableNameLength); + if (VariableIndex!= 0 && VariableIndex <= IpSecVariableInfo.VariableCount) { + // + // Get the variable size of the current ipsecconfig data. + // + DataSizeI = 0; + Status = gRT->GetVariable ( + VariableNameI, + VendorGuid, + Attributes, + &DataSizeI, + NULL + ); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + // + // Validate the variable count and variable size. + // + if (VariableIndex != IpSecVariableInfo.VariableCount) { + // + // If the varaibe is not the last one, its size should be the max + // size of the single variable. + // + if (DataSizeI != IpSecVariableInfo.SingleVariableSize) { + return EFI_ABORTED; + } + } else { + if (DataSizeI != IpSecVariableInfo.VariableSize % IpSecVariableInfo.SingleVariableSize) { + return EFI_ABORTED; + } + } + // + // Get the variable data of the current ipsecconfig data and + // store it into user buffer continously. + // + Status = gRT->GetVariable ( + VariableNameI, + VendorGuid, + Attributes, + &DataSizeI, + (UINT8 *) Data + (VariableIndex - 1) * IpSecVariableInfo.SingleVariableSize + ); + ASSERT_EFI_ERROR (Status); + VariableCount--; + } + } + } + // + // The VariableCount in "VariableNameInfo" varaible should have the correct + // numbers of variables which name starts with VariableName. + // + if (VariableCount != 0) { + Status = EFI_ABORTED; + } + +ON_EXIT: + if (VariableNameI != NULL) { + FreePool (VariableNameI); + } + return Status; +} + +/** + Set the IPsec variables. + + Set all IPsec variables which start with the specified variable name. Those variables + are set one by one. + + @param[in] VariableName The name of the vendor's variable. It is a + Null-Terminated Unicode String. + @param[in] VendorGuid Unify identifier for vendor. + @param[in] Attributes Point to memory location to return the attributes of + variable. If the point is NULL, the parameter would be ignored. + @param[in] DataSize The size in bytes of Data-Buffer. + @param[in] Data Points to the content of the variable. + + @retval EFI_SUCCESS The firmware successfully stored the variable and its data, as + defined by the Attributes. + @retval others Storing the variables failed. + +**/ +EFI_STATUS +IpSecSetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + CHAR16 *VariableNameI; + UINTN VariableNameSize; + UINTN VariableIndex; + IP_SEC_VARIABLE_INFO IpSecVariableInfo; + UINT64 MaximumVariableStorageSize; + UINT64 RemainingVariableStorageSize; + UINT64 MaximumVariableSize; + + Status = gRT->QueryVariableInfo ( + Attributes, + &MaximumVariableStorageSize, + &RemainingVariableStorageSize, + &MaximumVariableSize + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // "VariableName + Info/0001/0002/... + NULL" + // + VariableNameSize = (StrLen (VariableName) + 5) * sizeof (CHAR16); + VariableNameI = AllocateZeroPool (VariableNameSize); + + if (VariableNameI == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + // + // Construct the variable of ipsecconfig general information. Like the total + // numbers of the Ipsecconfig variables, the total size of all ipsecconfig variables. + // + UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%s", VariableName, L"Info"); + MaximumVariableSize -= VariableNameSize; + + IpSecVariableInfo.VariableCount = (UINT32) ((DataSize + (UINTN) MaximumVariableSize - 1) / (UINTN) MaximumVariableSize); + IpSecVariableInfo.VariableSize = (UINT32) DataSize; + IpSecVariableInfo.SingleVariableSize = (UINT32) MaximumVariableSize; + + // + // Set the variable of ipsecconfig general information. + // + Status = gRT->SetVariable ( + VariableNameI, + VendorGuid, + Attributes, + sizeof (IpSecVariableInfo), + &IpSecVariableInfo + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error set ipsecconfig meta data with %r\n", Status)); + goto ON_EXIT; + } + + for (VariableIndex = 0; VariableIndex < IpSecVariableInfo.VariableCount; VariableIndex++) { + // + // Construct and set the variable of ipsecconfig data one by one. + // The index of variable name begin from 0001, and the varaible name + // likes "VariableName0001", "VaraiableName0002".... + // + UnicodeSPrint (VariableNameI, VariableNameSize, L"%s%04d", VariableName, VariableIndex + 1); + Status = gRT->SetVariable ( + VariableNameI, + VendorGuid, + Attributes, + (VariableIndex == IpSecVariableInfo.VariableCount - 1) ? + (DataSize % (UINTN) MaximumVariableSize) : + (UINTN) MaximumVariableSize, + (UINT8 *) Data + VariableIndex * (UINTN) MaximumVariableSize + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Error set ipsecconfig variable data with %r\n", Status)); + goto ON_EXIT; + } + } + +ON_EXIT: + if (VariableNameI != NULL) { + FreePool (VariableNameI); + } + + return Status; +} + +/** + Return the configuration value for the EFI IPsec driver. + + This function lookup the data entry from IPsec database or IKEv2 configuration + information. The expected data type and unique identification are described in + DataType and Selector parameters. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to retrieve. + @param[in] Selector Pointer to an entry selector that is an identifier of the IPsec + configuration data entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec configuration data. + The type of the data buffer associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - Selector is NULL. + - DataSize is NULL. + - Data is NULL and *DataSize is not zero + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigGetData ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + if (This == NULL || Selector == NULL || DataSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*DataSize != 0 && Data == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= IPsecConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + return mGetPolicyEntry[DataType](Selector, DataSize, Data); +} + +/** + Set the security association, security policy and peer authorization configuration + information for the EFI IPsec driver. + + This function is used to set the IPsec configuration information of type DataType for + the EFI IPsec driver. + The IPsec configuration data has a unique selector/identifier separately to identify + a data entry. The selector structure depends on DataType's definition. + Using SetData() with a Data of NULL causes the IPsec configuration data entry identified + by DataType and Selector to be deleted. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to be set. + @param[in] Selector Pointer to an entry selector on operated configuration data + specified by DataType. A NULL Selector causes the entire + specified-type configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure of the data buffer is + associated with the DataType. + @param[in] InsertBefore Pointer to one entry selector which describes the expected + position the new data entry will be added. If InsertBefore is NULL, + the new entry will be appended to the end of the database. + + @retval EFI_SUCCESS The specified configuration entry data was set successfully. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigSetData ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN EFI_IPSEC_CONFIG_SELECTOR *InsertBefore OPTIONAL + ) +{ + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= IPsecConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + Status = mSetPolicyEntry[DataType](Selector, Data, InsertBefore); + + if (!EFI_ERROR (Status) && !mSetBySelf) { + // + // Save the updated config data into variable. + // + IpSecConfigSave (); + } + + return Status; +} + +/** + Enumerates the current selector for IPsec configuration data entry. + + This function is called multiple times to retrieve the entry Selector in IPsec + configuration database. On each call to GetNextSelector(), the next entry + Selector are retrieved into the output interface. + + If the entire IPsec configuration database has been iterated, the error + EFI_NOT_FOUND is returned. + If the Selector buffer is too small for the next Selector copy, an + EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect + the size of buffer needed. + + On the initial call to GetNextSelector() to start the IPsec configuration database + search, a pointer to the buffer with all zero value is passed in Selector. Calls + to SetData() between calls to GetNextSelector may produce unpredictable results. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of IPsec configuration data to retrieve. + @param[in, out] SelectorSize The size of the Selector buffer. + @param[in, out] Selector On input, supplies the pointer to last Selector that was + returned by GetNextSelector(). + On output, returns one copy of the current entry Selector + of a given DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - SelectorSize is NULL. + - Selector is NULL. + @retval EFI_NOT_FOUND The next configuration data entry was not found. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_BUFFER_TOO_SMALL The SelectorSize is too small for the result. This parameter + has been updated with the size needed to complete the search + request. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigGetNextSelector ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN OUT UINTN *SelectorSize, + IN OUT EFI_IPSEC_CONFIG_SELECTOR *Selector + ) +{ + LIST_ENTRY *Link; + IPSEC_COMMON_POLICY_ENTRY *CommonEntry; + BOOLEAN IsFound; + + if (This == NULL || Selector == NULL || SelectorSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= IPsecConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + IsFound = FALSE; + + NET_LIST_FOR_EACH (Link, &mConfigData[DataType]) { + CommonEntry = BASE_CR (Link, IPSEC_COMMON_POLICY_ENTRY, List); + + if (IsFound || (BOOLEAN)(mIsZeroSelector[DataType](Selector))) { + // + // If found the appointed entry, then duplicate the next one and return, + // or if the appointed entry is zero, then return the first one directly. + // + return mDuplicateSelector[DataType](Selector, CommonEntry->Selector, SelectorSize); + } else { + // + // Set the flag if find the appointed entry. + // + IsFound = mCompareSelector[DataType](Selector, CommonEntry->Selector); + } + } + + return EFI_NOT_FOUND; +} + +/** + Register an event that is to be signaled whenever a configuration process on the + specified IPsec configuration information is done. + + The register function is not surpport now and always returns EFI_UNSUPPORTED. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to be registered the event for. + @param[in] Event The event to be registered. + + @retval EFI_SUCCESS The event is registered successfully. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_ACCESS_DENIED The Event is already registered for the DataType. + @retval EFI_UNSUPPORTED The notify registration is unsupported, or the specified + DataType is not supported. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigRegisterNotify ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Remove the specified event that was previously registered on the specified IPsec + configuration data. + + This function is not support now and alwasy return EFI_UNSUPPORTED. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The configuration data type to remove the registered event for. + @param[in] Event The event to be unregistered. + + @retval EFI_SUCCESS The event was removed successfully. + @retval EFI_NOT_FOUND The Event specified by DataType could not be found in the + database. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_UNSUPPORTED The notify registration is unsupported, or the specified + DataType is not supported. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigUnregisterNotify ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Copy whole data in specified EFI_SIPEC_CONFIG_SELECTOR and the Data to a buffer. + + This function is a caller defined function, and it is called by the IpSecVisitConfigData(). + The orignal caller is IpSecConfigSave(), which calls the IpsecVisitConfigData() to + copy all types of IPsec Config datas into one buffer and store this buffer into firmware in + the form of several variables. + + @param[in] Type A specified IPSEC_CONFIG_DATA_TYPE. + @param[in] Selector Points to a EFI_IPSEC_CONFIG_SELECTOR to be copied + to the buffer. + @param[in] Data Points to data to be copied to the buffer. The + Data type is related to the Type. + @param[in] SelectorSize The size of the Selector. + @param[in] DataSize The size of the Data. + @param[in, out] Buffer The buffer to store the Selector and Data. + + @retval EFI_SUCCESS Copy the Selector and Data to a buffer successfully. + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + +**/ +EFI_STATUS +IpSecCopyPolicyEntry ( + IN EFI_IPSEC_CONFIG_DATA_TYPE Type, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN UINTN SelectorSize, + IN UINTN DataSize, + IN OUT IPSEC_VARIABLE_BUFFER *Buffer + ) +{ + IPSEC_VAR_ITEM_HEADER SelectorHeader; + IPSEC_VAR_ITEM_HEADER DataHeader; + UINTN EntrySize; + UINT8 *TempPoint; + + if (Type == IPsecConfigDataTypeSad) { + // + // Don't save automatically-generated SA entry into variable. + // + if (((EFI_IPSEC_SA_DATA2 *) Data)->ManualSet == FALSE) { + return EFI_SUCCESS; + } + } + // + // Increase the capacity size of the buffer if needed. + // + EntrySize = ALIGN_VARIABLE (sizeof (SelectorHeader)); + EntrySize = ALIGN_VARIABLE (EntrySize + SelectorSize); + EntrySize = ALIGN_VARIABLE (EntrySize + sizeof (SelectorHeader)); + EntrySize = ALIGN_VARIABLE (EntrySize + DataSize); + + //EntrySize = SelectorSize + DataSize + 2 * sizeof (SelectorHeader); + if (Buffer->Capacity - Buffer->Size < EntrySize) { + // + // Calculate the required buffer + // + Buffer->Capacity += EntrySize; + TempPoint = AllocatePool (Buffer->Capacity); + + if (TempPoint == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Copy the old Buffer to new buffer and free the old one. + // + CopyMem (TempPoint, Buffer->Ptr, Buffer->Size); + FreePool (Buffer->Ptr); + + Buffer->Ptr = TempPoint; + } + + mFixPolicyEntry[Type](Selector, Data); + + // + // Fill the selector header and copy it into buffer. + // + SelectorHeader.Type = (UINT8) (Type | IPSEC_VAR_ITEM_HEADER_LOGO_BIT); + SelectorHeader.Size = (UINT16) SelectorSize; + + CopyMem ( + Buffer->Ptr + Buffer->Size, + &SelectorHeader, + sizeof (SelectorHeader) + ); + Buffer->Size = ALIGN_VARIABLE (Buffer->Size + sizeof (SelectorHeader)); + + // + // Copy the selector into buffer. + // + CopyMem ( + Buffer->Ptr + Buffer->Size, + Selector, + SelectorSize + ); + Buffer->Size = ALIGN_VARIABLE (Buffer->Size + SelectorSize); + + // + // Fill the data header and copy it into buffer. + // + DataHeader.Type = (UINT8) Type; + DataHeader.Size = (UINT16) DataSize; + + CopyMem ( + Buffer->Ptr + Buffer->Size, + &DataHeader, + sizeof (DataHeader) + ); + Buffer->Size = ALIGN_VARIABLE (Buffer->Size + sizeof (DataHeader)); + // + // Copy the data into buffer. + // + CopyMem ( + Buffer->Ptr + Buffer->Size, + Data, + DataSize + ); + Buffer->Size = ALIGN_VARIABLE (Buffer->Size + DataSize); + + mUnfixPolicyEntry[Type](Selector, Data); + + return EFI_SUCCESS; +} + +/** + Visit all IPsec Configurations of specified Type and call the caller defined + interface. + + @param[in] DataType The specified IPsec Config Data Type. + @param[in] Routine The function defined by the caller. + @param[in] Context The data passed to the Routine. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated + @retval EFI_SUCCESS This function completed successfully. + +**/ +EFI_STATUS +IpSecVisitConfigData ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN IPSEC_COPY_POLICY_ENTRY Routine, + IN VOID *Context + ) +{ + EFI_STATUS GetNextStatus; + EFI_STATUS GetDataStatus; + EFI_STATUS RoutineStatus; + EFI_IPSEC_CONFIG_SELECTOR *Selector; + VOID *Data; + UINTN SelectorSize; + UINTN DataSize; + UINTN SelectorBufferSize; + UINTN DataBufferSize; + BOOLEAN FirstGetNext; + + FirstGetNext = TRUE; + DataBufferSize = 0; + Data = NULL; + SelectorBufferSize = sizeof (EFI_IPSEC_CONFIG_SELECTOR); + Selector = AllocateZeroPool (SelectorBufferSize); + + if (Selector == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + while (TRUE) { + // + // Get the real size of the selector. + // + SelectorSize = SelectorBufferSize; + GetNextStatus = EfiIpSecConfigGetNextSelector ( + &mIpSecConfigInstance, + DataType, + &SelectorSize, + Selector + ); + if (GetNextStatus == EFI_BUFFER_TOO_SMALL) { + FreePool (Selector); + SelectorBufferSize = SelectorSize; + // + // Allocate zero pool for the first selector, while store the last + // selector content for the other selectors. + // + if (FirstGetNext) { + Selector = AllocateZeroPool (SelectorBufferSize); + } else { + Selector = AllocateCopyPool (SelectorBufferSize, Selector); + } + + if (Selector == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Get the content of the selector. + // + GetNextStatus = EfiIpSecConfigGetNextSelector ( + &mIpSecConfigInstance, + DataType, + &SelectorSize, + Selector + ); + } + + if (EFI_ERROR (GetNextStatus)) { + break; + } + + FirstGetNext = FALSE; + + // + // Get the real size of the policy entry according to the selector. + // + DataSize = DataBufferSize; + GetDataStatus = EfiIpSecConfigGetData ( + &mIpSecConfigInstance, + DataType, + Selector, + &DataSize, + Data + ); + if (GetDataStatus == EFI_BUFFER_TOO_SMALL) { + if (Data != NULL) { + FreePool (Data); + } + + DataBufferSize = DataSize; + Data = AllocateZeroPool (DataBufferSize); + + if (Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Get the content of the policy entry according to the selector. + // + GetDataStatus = EfiIpSecConfigGetData ( + &mIpSecConfigInstance, + DataType, + Selector, + &DataSize, + Data + ); + } + + if (EFI_ERROR (GetDataStatus)) { + break; + } + // + // Prepare the buffer of updated policy entry, which is stored in + // the continous memory, and then save into variable later. + // + RoutineStatus = Routine ( + DataType, + Selector, + Data, + SelectorSize, + DataSize, + Context + ); + if (EFI_ERROR (RoutineStatus)) { + break; + } + } + + if (Data != NULL) { + FreePool (Data); + } + + if (Selector != NULL) { + FreePool (Selector); + } + + return EFI_SUCCESS; +} + +/** + This function is the subfunction of EFIIpSecConfigSetData. + + This function call IpSecSetVaraible to set the IPsec Configuration into the firmware. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + @retval EFI_SUCCESS Saved the configration successfully. + @retval Others Other errors were found while obtaining the variable. + +**/ +EFI_STATUS +IpSecConfigSave ( + VOID + ) +{ + IPSEC_VARIABLE_BUFFER Buffer; + EFI_STATUS Status; + EFI_IPSEC_CONFIG_DATA_TYPE Type; + + Buffer.Size = 0; + Buffer.Capacity = IPSEC_DEFAULT_VARIABLE_SIZE; + Buffer.Ptr = AllocateZeroPool (Buffer.Capacity); + + if (Buffer.Ptr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // For each policy database, prepare the contious buffer to save into variable. + // + for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) { + IpSecVisitConfigData ( + Type, + (IPSEC_COPY_POLICY_ENTRY) IpSecCopyPolicyEntry, + &Buffer + ); + } + // + // Save the updated policy database into variable. + // + Status = IpSecSetVariable ( + IPSECCONFIG_VARIABLE_NAME, + &gEfiIpSecConfigProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + Buffer.Size, + Buffer.Ptr + ); + + FreePool (Buffer.Ptr); + + return Status; +} + +/** + Get the all IPSec configuration variables and store those variables + to the internal data structure. + + This founction is called by IpSecConfigInitialize() which is to intialize the + IPsecConfiguration Protocol. + + @param[in] Private Point to IPSEC_PRIVATE_DATA. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated + @retval EFI_SUCCESS Restore the IPsec Configuration successfully. + @retval others Other errors is found while obtaining the variable. + +**/ +EFI_STATUS +IpSecConfigRestore ( + IN IPSEC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + UINT8 *Buffer; + IPSEC_VAR_ITEM_HEADER *Header; + UINT8 *Ptr; + EFI_IPSEC_CONFIG_SELECTOR *Selector; + EFI_IPSEC_CONFIG_DATA_TYPE Type; + VOID *Data; + UINT8 Value; + UINTN Size; + + Value = 0; + Size = sizeof (Value); + BufferSize = 0; + Buffer = NULL; + + Status = gRT->GetVariable ( + IPSECCONFIG_STATUS_NAME, + &gEfiIpSecConfigProtocolGuid, + NULL, + &Size, + &Value + ); + + if (!EFI_ERROR (Status) && Value == IPSEC_STATUS_ENABLED) { + Private->IpSec.DisabledFlag = FALSE; + } + // + // Get the real size of policy database in variable. + // + Status = IpSecGetVariable ( + IPSECCONFIG_VARIABLE_NAME, + &gEfiIpSecConfigProtocolGuid, + NULL, + &BufferSize, + Buffer + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Get the content of policy database in variable. + // + Status = IpSecGetVariable ( + IPSECCONFIG_VARIABLE_NAME, + &gEfiIpSecConfigProtocolGuid, + NULL, + &BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + for (Ptr = Buffer; Ptr < Buffer + BufferSize;) { + + Header = (IPSEC_VAR_ITEM_HEADER *) Ptr; + Type = (EFI_IPSEC_CONFIG_DATA_TYPE) (Header->Type & IPSEC_VAR_ITEM_HEADER_CONTENT_BIT); + ASSERT (((Header->Type & 0x80) == IPSEC_VAR_ITEM_HEADER_LOGO_BIT) && (Type < IPsecConfigDataTypeMaximum)); + + Selector = (EFI_IPSEC_CONFIG_SELECTOR *) ALIGN_POINTER (Header + 1, sizeof (UINTN)); + Header = (IPSEC_VAR_ITEM_HEADER *) ALIGN_POINTER ( + (UINT8 *) Selector + Header->Size, + sizeof (UINTN) + ); + ASSERT (Header->Type == Type); + + Data = ALIGN_POINTER (Header + 1, sizeof (UINTN)); + + mUnfixPolicyEntry[Type](Selector, Data); + + // + // Update each policy entry according to the content in variable. + // + mSetBySelf = TRUE; + Status = EfiIpSecConfigSetData ( + &Private->IpSecConfig, + Type, + Selector, + Data, + NULL + ); + mSetBySelf = FALSE; + + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + Ptr = ALIGN_POINTER ((UINT8 *) Data + Header->Size, sizeof (UINTN)); + } + + FreePool (Buffer); + } + + return EFI_SUCCESS; +} + +/** + Install and Initialize IPsecConfig protocol + + @param[in, out] Private Pointer to IPSEC_PRIVATE_DATA. After this function finish, + the pointer of IPsecConfig Protocol implementation will copy + into its IPsecConfig member. + + @retval EFI_SUCCESS Initialized the IPsecConfig Protocol successfully. + @retval Others Initializing the IPsecConfig Protocol failed. +**/ +EFI_STATUS +IpSecConfigInitialize ( + IN OUT IPSEC_PRIVATE_DATA *Private + ) +{ + EFI_IPSEC_CONFIG_DATA_TYPE Type; + + CopyMem ( + &Private->IpSecConfig, + &mIpSecConfigInstance, + sizeof (EFI_IPSEC_CONFIG_PROTOCOL) + ); + + // + // Initialize the list head of policy database. + // + for (Type = IPsecConfigDataTypeSpd; Type < IPsecConfigDataTypeMaximum; Type++) { + InitializeListHead (&mConfigData[Type]); + } + // + // Restore the content of policy database according to the variable. + // + IpSecConfigRestore (Private); + + return gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiIpSecConfigProtocolGuid, + &Private->IpSecConfig, + NULL + ); +} diff --git a/Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.h b/Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.h new file mode 100644 index 0000000000..3e365dae4a --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.h @@ -0,0 +1,955 @@ +/** @file + Definitions related to IPSEC_CONFIG_PROTOCOL implementations. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IPSEC_CONFIG_IMPL_H_ +#define _IPSEC_CONFIG_IMPL_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "IpSecImpl.h" + +#define EFI_IPSEC_ANY_PROTOCOL 0xFFFF +#define EFI_IPSEC_ANY_PORT 0 + +#define IPSEC_VAR_ITEM_HEADER_LOGO_BIT 0x80 +#define IPSEC_VAR_ITEM_HEADER_CONTENT_BIT 0x7F + +#define IPSECCONFIG_VARIABLE_NAME L"IpSecConfig" +#define IPSECCONFIG_STATUS_NAME L"IpSecStatus" + +#define SIZE_OF_SPD_SELECTOR(x) (UINTN) (sizeof (EFI_IPSEC_SPD_SELECTOR) \ + + sizeof (EFI_IP_ADDRESS_INFO) * ((x)->LocalAddressCount + (x)->RemoteAddressCount)) + +#define FIX_REF_BUF_ADDR(addr, base) addr = (VOID *) ((UINTN) (addr) - (UINTN) (base)) +#define UNFIX_REF_BUF_ADDR(addr, base) addr = (VOID *) ((UINTN) (addr) + (UINTN) (base)) + +// +// The data structure used to store the genernall information of IPsec configuration. +// +typedef struct { + UINT32 VariableCount; // the total number of the IPsecConfig variables. + UINT32 VariableSize; // The total size of all IpsecConfig variables. + UINT32 SingleVariableSize; // The max size of single variable +} IP_SEC_VARIABLE_INFO; + +typedef struct { + EFI_IPSEC_CONFIG_SELECTOR *Selector; + VOID *Data; + LIST_ENTRY List; +} IPSEC_COMMON_POLICY_ENTRY; + +typedef struct { + UINT8 *Ptr; + UINTN Size; + UINTN Capacity; +} IPSEC_VARIABLE_BUFFER; + +#pragma pack(1) +typedef struct { + UINT8 Type; + UINT16 Size; +} IPSEC_VAR_ITEM_HEADER; +#pragma pack() + +/** + The prototype of Copy Source Selector to the Destination Selector. + + @param[in, out] DstSel Pointer of Destination Selector. It would be + SPD Selector, or SAD Selector or PAD Selector. + @param[in] SrcSel Pointer of Source Selector. It would be + SPD Selector, or SAD Selector or PAD Selector. + @param[in, out] Size The size of the Destination Selector. If it + is not NULL and its value is less than the size of + Source Selector, the value of Source Selector's + size will be passed to the caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source Selector is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of Source Selector. + @retval EFI_SUCCESS Copy Source Selector to the Destination + Selector successfully. + +**/ +typedef +EFI_STATUS +(*IPSEC_DUPLICATE_SELECTOR) ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ); + +/** + It is prototype of compare two Selectors. The Selector would be SPD Selector, + or SAD Selector, or PAD selector. + + @param[in] Selector1 Pointer of the first Selector. + @param[in] Selector2 Pointer of the second Selector. + + @retval TRUE These two Selectors have the same value in certain fields. + @retval FALSE Not all fields have the same value in these two Selectors. + +**/ +typedef +BOOLEAN +(*IPSEC_COMPARE_SELECTOR) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + +/** + The prototype of a function to check if the Selector is Zero by its certain fields. + + @param[in] Selector Pointer of the Selector. + + @retval TRUE If the Selector is Zero. + @retval FALSE If the Selector is not Zero. + +**/ +typedef +BOOLEAN +(*IPSEC_IS_ZERO_SELECTOR) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + The prototype of a function to fix the value of particular members of the Selector. + + @param[in] Selector Pointer of Selector. + @param[in] Data Pointer of Data. + +**/ +typedef +VOID +(*IPSEC_FIX_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data + ); + +/** + It is prototype function to define a routine function by the caller of IpSecVisitConfigData(). + + @param[in] Type A specified IPSEC_CONFIG_DATA_TYPE. + @param[in] Selector Points to EFI_IPSEC_CONFIG_SELECTOR to be copied + to the buffer. + @param[in] Data Points to data to be copied to the buffer. The + Data type is related to the Type. + @param[in] SelectorSize The size of the Selector. + @param[in] DataSize The size of the Data. + @param[in, out] Buffer The buffer to store the Selector and Data. + + @retval EFI_SUCCESS Copied the Selector and Data to a buffer successfully. + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + +**/ +typedef +EFI_STATUS +(*IPSEC_COPY_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_DATA_TYPE Type, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN UINTN SelectorSize, + IN UINTN DataSize, + IN OUT VOID *Context + ); + +/** + Set the security policy information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. + @param[in] Context Pointer to one entry selector that describes + the expected position the new data entry will + be added. If Context is NULL, the new entry will + be appended to the end of the database. + + @retval EFI_INVALID_PARAMETER Certain Parameters are not correct. The Parameter + requiring a check depends on the Selector type. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +typedef +EFI_STATUS +(*IPSEC_SET_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ); + +/** + A prototype function definition to lookup the data entry from IPsec. Return the configuration + value of the specified Entry. + + @param[in] Selector Pointer to an entry selector that is an identifier + of the entry. + @param[in, out] DataSize On output, the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +typedef +EFI_STATUS +(*IPSEC_GET_POLICY_ENTRY) ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + IN VOID *Data + ); + +/** + Compare two SPD Selectors. + + Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/ + NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the + Local Addresses and remote Addresses. + + @param[in] Selector1 Pointer of the first SPD Selector. + @param[in] Selector2 Pointer of the second SPD Selector. + + @retval TRUE These two Selectors have the same value in above fields. + @retval FALSE Not all of the above fields have the same value in these two Selectors. + +**/ +BOOLEAN +CompareSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + + +/** + Visit all IPsec Configurations of specified Type and call the caller defined + interface. + + @param[in] DataType The specified IPsec Config Data Type. + @param[in] Routine The function caller defined. + @param[in] Context The data passed to the Routine. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + @retval EFI_SUCCESS This function complete successfully. + +**/ +EFI_STATUS +IpSecVisitConfigData ( + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN IPSEC_COPY_POLICY_ENTRY Routine, + IN VOID *Context + ); + + +/** + This function is the subfunction of the EFIIpSecConfigSetData. + + This function call IpSecSetVaraible to set the IPsec Configuration into the firmware. + + @retval EFI_OUT_OF_RESOURCES The required system resource could not be allocated. + @retval EFI_SUCCESS Saved the configration successfully. + @retval Others Other errors were found while obtaining the variable. + +**/ +EFI_STATUS +IpSecConfigSave ( + VOID + ); + +/** + Initialize IPsecConfig protocol + + @param[in, out] Private Pointer to IPSEC_PRIVATE_DATA. After this function finish, + the pointer of IPsecConfig Protocol implementation will copy + into its IPsecConfig member. + + @retval EFI_SUCCESS Initialized the IPsecConfig Protocol successfully. + @retval Others Initializing the IPsecConfig Protocol failed. + +**/ +EFI_STATUS +IpSecConfigInitialize ( + IN OUT IPSEC_PRIVATE_DATA *Private + ); + +/** + Calculate the entire size of EFI_IPSEC_SPD_DATA, which includes the buffer size pointed + by the pointer members. + + @param[in] SpdData Pointer to a specified EFI_IPSEC_SPD_DATA. + + @return The entire size of the specified EFI_IPSEC_SPD_DATA. + +**/ +UINTN +IpSecGetSizeOfEfiSpdData ( + IN EFI_IPSEC_SPD_DATA *SpdData + ); + +/** + Calculate the a entire size of IPSEC_SPD_DATA, which includes the buffer size pointed + by the pointer members and the buffer size used by Sa List. + + @param[in] SpdData Pointer to the specified IPSEC_SPD_DATA. + + @return The entire size of IPSEC_SPD_DATA. + +**/ +UINTN +IpSecGetSizeOfSpdData ( + IN IPSEC_SPD_DATA *SpdData + ); + +/** + Copy Source Process Policy to the Destination Process Policy. + + @param[in] Dst Pointer to the Source Process Policy. + @param[in] Src Pointer to the Destination Process Policy. + +**/ +VOID +IpSecDuplicateProcessPolicy ( + IN EFI_IPSEC_PROCESS_POLICY *Dst, + IN EFI_IPSEC_PROCESS_POLICY *Src + ); + +/** + Find if the two SPD Selectors has subordinative. + + Compare two SPD Selector by the fields of LocalAddressCount/RemoteAddressCount/ + NextLayerProtocol/LocalPort/LocalPortRange/RemotePort/RemotePortRange and the + Local Addresses and remote Addresses. + + @param[in] Selector1 Pointer of first SPD Selector. + @param[in] Selector2 Pointer of second SPD Selector. + + @retval TRUE The first SPD Selector is subordinate Selector of second SPD Selector. + @retval FALSE The first SPD Selector is not subordinate Selector of second + SPD Selector. + +**/ +BOOLEAN +IsSubSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + +/** + Compare two SA IDs. + + @param[in] Selector1 Pointer of the first SA ID. + @param[in] Selector2 Pointer of the second SA ID. + + @retval TRUE This two Selectors have the same SA ID. + @retval FALSE This two Selecotrs don't have the same SA ID. + +**/ +BOOLEAN +CompareSaId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + +/** + Compare two PAD IDs. + + @param[in] Selector1 Pointer of the first PAD ID. + @param[in] Selector2 Pointer of the second PAD ID. + + @retval TRUE This two Selectors have the same PAD ID. + @retval FALSE This two Selecotrs don't have the same PAD ID. + +**/ +BOOLEAN +ComparePadId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector1, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector2 + ); + +/** + Check if the SPD Selector is Zero by its LocalAddressCount and RemoteAddressCount + fields. + + @param[in] Selector Pointer of the SPD Selector. + + @retval TRUE If the SPD Selector is Zero. + @retval FALSE If the SPD Selector is not Zero. + +**/ +BOOLEAN +IsZeroSpdSelector ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + Check if the SA ID is Zero by its DestAddress. + + @param[in] Selector Pointer of the SA ID. + + @retval TRUE If the SA ID is Zero. + @retval FALSE If the SA ID is not Zero. + +**/ +BOOLEAN +IsZeroSaId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + Check if the PAD ID is Zero. + + @param[in] Selector Pointer of the PAD ID. + + @retval TRUE If the PAD ID is Zero. + @retval FALSE If the PAD ID is not Zero. + +**/ +BOOLEAN +IsZeroPadId ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + Copy Source SPD Selector to the Destination SPD Selector. + + @param[in, out] DstSel Pointer of Destination SPD Selector. + @param[in] SrcSel Pointer of Source SPD Selector. + @param[in, out] Size The size of the Destination SPD Selector. If + it is not NULL and its value is less than the + size of Source SPD Selector, the value of + Source SPD Selector's size will be passed to + the caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source SPD Selector is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size is less than size of Source SPD Selector. + @retval EFI_SUCCESS Copy Source SPD Selector to the Destination SPD + Selector successfully. + +**/ +EFI_STATUS +DuplicateSpdSelector ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ); + +/** + Copy Source SA ID to the Destination SA ID. + + @param[in, out] DstSel Pointer of the Destination SA ID. + @param[in] SrcSel Pointer of the Source SA ID. + @param[in, out] Size The size of the Destination SA ID. If it + not NULL, and its value is less than the size of + Source SA ID, the value of Source SA ID's size + will be passed to the caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source SA ID is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source SA ID. + @retval EFI_SUCCESS Copied Source SA ID to the Destination SA ID successfully. + +**/ +EFI_STATUS +DuplicateSaId ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ); + +/** + Copy Source PAD ID to the Destination PAD ID. + + @param[in, out] DstSel Pointer of Destination PAD ID. + @param[in] SrcSel Pointer of Source PAD ID. + @param[in, out] Size The size of the Destination PAD ID. If it + not NULL, and its value less than the size of + Source PAD ID, the value of Source PAD ID's size + will be passed to the caller by this parameter. + + @retval EFI_INVALID_PARAMETER If the Destination or Source PAD ID is NULL. + @retval EFI_BUFFER_TOO_SMALL If the input Size less than size of source PAD ID. + @retval EFI_SUCCESS Copied Source PAD ID to the Destination PAD ID successfully. + +**/ +EFI_STATUS +DuplicatePadId ( + IN OUT EFI_IPSEC_CONFIG_SELECTOR *DstSel, + IN EFI_IPSEC_CONFIG_SELECTOR *SrcSel, + IN OUT UINTN *Size + ); + +/** + Fix the value of some members of the SPD Selector. + + This function is called by IpSecCopyPolicyEntry(), which copies the Policy + Entry into the Variable. Since some members in SPD Selector are pointers, + a physical address to relative address conversion is required before copying + this SPD entry into the variable. + + @param[in] Selector Pointer of SPD Selector. + @param[in, out] Data Pointer of SPD Data. + +**/ +VOID +FixSpdEntry ( + IN EFI_IPSEC_SPD_SELECTOR *Selector, + IN OUT EFI_IPSEC_SPD_DATA *Data + ); + +/** + Fix the value of some members of SA ID. + + This function is called by IpSecCopyPolicyEntry(), which copies the Policy + Entry into the Variable. Since some members in SA ID are pointers, + a physical address to relative address conversion is required before copying + this SAD into the variable. + + @param[in] SaId Pointer of SA ID. + @param[in, out] Data Pointer of SA Data. + +**/ +VOID +FixSadEntry ( + IN EFI_IPSEC_SA_ID *SaId, + IN OUT EFI_IPSEC_SA_DATA2 *Data + ); + +/** + Fix the value of some members of PAD ID. + + This function is called by IpSecCopyPolicyEntry(), which copy the Policy + Entry into the Variable. Since some members in PAD ID are pointers, + a physical address to relative address conversion is required before copying + this PAD into the variable. + + @param[in] PadId Pointer of PAD ID. + @param[in, out] Data Pointer of PAD Data. + +**/ +VOID +FixPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN OUT EFI_IPSEC_PAD_DATA *Data + ); + +/** + Recover the value of some members of SPD Selector. + + This function is corresponding to FixSpdEntry(). It recovers the value of members + of SPD Selector which fix by the FixSpdEntry(). + + @param[in, out] Selector Pointer of SPD Selector. + @param[in, out] Data Pointer of SPD Data. + +**/ +VOID +UnfixSpdEntry ( + IN OUT EFI_IPSEC_SPD_SELECTOR *Selector, + IN OUT EFI_IPSEC_SPD_DATA *Data + ); + + +/** + Recover the value of some members of SA ID. + + This function is corresponding to FixSadEntry(). It recovers the value of members + of SAD ID which fix by the FixSadEntry(). + + @param[in, out] SaId Pointer of SAD ID + @param[in, out] Data Pointer of SAD Data. + +**/ +VOID +UnfixSadEntry ( + IN OUT EFI_IPSEC_SA_ID *SaId, + IN OUT EFI_IPSEC_SA_DATA2 *Data + ); + +/** + Recover the value of some members of PAD ID. + + This function is corresponding to FixPadEntry(). It recovers the value of members + of PAD ID which fix by the FixPadEntry(). + + @param[in] PadId Pointer of PAD ID + @param[in, out] Data Pointer of PAD Data. + +**/ +VOID +UnfixPadEntry ( + IN EFI_IPSEC_PAD_ID *PadId, + IN OUT EFI_IPSEC_PAD_DATA *Data + ); + +/** + Set the security policy information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_SPD_DATA. + @param[in] Context Pointer to one entry selector that describes + the expected position the new data entry will + be added. If Context is NULL,the new entry will + be appended the end of database. + + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - Selector is not NULL and its LocalAddress + is NULL or its RemoteAddress is NULL. + - Data is not NULL, its Action is Protected, + and its policy is NULL. + - Data is not NULL and its Action is not protected + and its policy is not NULL. + - The Action of Data is Protected, its policy + mode is Tunnel, and its tunnel option is NULL. + - The Action of Data is protected, its policy + mode is not Tunnel, and it tunnel option is not NULL. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetSpdEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ); + +/** + Set the security association information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_SA_DATA. + @param[in] Context Pointer to one entry selector which describes + the expected position the new data entry will + be added. If Context is NULL,the new entry will + be appended to the end of database. + + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetSadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ); + +/** + Set the peer authorization configuration information for the EFI IPsec driver. + + The IPsec configuration data has a unique selector/identifier separately to + identify a data entry. + + @param[in] Selector Pointer to an entry selector on operated + configuration data specified by DataType. + A NULL Selector causes the entire specified-type + configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure + of the data buffer should be EFI_IPSEC_PAD_DATA. + @param[in] Context Pointer to one entry selector that describes + the expected position where the new data entry will + be added. If Context is NULL, the new entry will + be appended the end of database. + + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +EFI_STATUS +SetPadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN VOID *Context OPTIONAL + ); + +/** + This function looks up the data entry from IPsec SPD, and returns the configuration + value of the specified SPD Entry. + + @param[in] Selector Pointer to an entry selector which is an identifier + of the SPD entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. The type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER Data is NULL and *DataSize is not zero. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetSpdEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + This function looks up the data entry from IPsec SAD and returns the configuration + value of the specified SAD Entry. + + @param[in] Selector Pointer to an entry selector that is an identifier + of the SAD entry. + @param[in, out] DataSize On output, the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. This type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetSadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + This function looks up the data entry from IPsec PADand returns the configuration + value of the specified PAD Entry. + + @param[in] Selector Pointer to an entry selector that is an identifier + of the PAD entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec + configuration data. This type of the data buffer + is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +GetPadEntry ( + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + Return the configuration value for the EFI IPsec driver. + + This function lookup the data entry from IPsec database or IKEv2 configuration + information. The expected data type and unique identification are described in + DataType and Selector parameters. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to retrieve. + @param[in] Selector Pointer to an entry selector that is an identifier of the IPsec + configuration data entry. + @param[in, out] DataSize On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the IPsec configuration data. + The type of the data buffer is associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - Selector is NULL. + - DataSize is NULL. + - Data is NULL and *DataSize is not zero + @retval EFI_NOT_FOUND The configuration data specified by Selector is not found. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. DataSize has been + updated with the size needed to complete the request. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigGetData ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + Set the security association, security policy and peer authorization configuration + information for the EFI IPsec driver. + + This function is used to set the IPsec configuration information of type DataType for + the EFI IPsec driver. + The IPsec configuration data has a unique selector/identifier separately to identify + a data entry. The selector structure depends on DataType's definition. + Using SetData() with a Data of NULL causes the IPsec configuration data entry identified + by DataType and Selector to be deleted. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to be set. + @param[in] Selector Pointer to an entry selector on operated configuration data + specified by DataType. A NULL Selector causes the entire + specified-type configuration information to be flushed. + @param[in] Data The data buffer to be set. The structure of the data buffer is + associated with the DataType. + @param[in] InsertBefore Pointer to one entry selector which describes the expected + position the new data entry will be added. If InsertBefore is NULL, + the new entry will be appended the end of database. + + @retval EFI_SUCCESS The specified configuration entry data was set successfully. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_OUT_OF_RESOURCED The required system resource could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigSetData ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_IPSEC_CONFIG_SELECTOR *Selector, + IN VOID *Data, + IN EFI_IPSEC_CONFIG_SELECTOR *InsertBefore OPTIONAL + ); + +/** + Enumerates the current selector for IPsec configuration data entry. + + This function is called multiple times to retrieve the entry Selector in IPsec + configuration database. On each call to GetNextSelector(), the next entry + Selector are retrieved into the output interface. + + If the entire IPsec configuration database has been iterated, the error + EFI_NOT_FOUND is returned. + If the Selector buffer is too small for the next Selector copy, an + EFI_BUFFER_TOO_SMALL error is returned, and SelectorSize is updated to reflect + the size of buffer needed. + + On the initial call to GetNextSelector() to start the IPsec configuration database + search, a pointer to the buffer with all zero value is passed in Selector. Calls + to SetData() between calls to GetNextSelector may produce unpredictable results. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of IPsec configuration data to retrieve. + @param[in, out] SelectorSize The size of the Selector buffer. + @param[in, out] Selector On input, supplies the pointer to last Selector that was + returned by GetNextSelector(). + On output, returns one copy of the current entry Selector + of a given DataType. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - SelectorSize is NULL. + - Selector is NULL. + @retval EFI_NOT_FOUND The next configuration data entry was not found. + @retval EFI_UNSUPPORTED The specified DataType is not supported. + @retval EFI_BUFFER_TOO_SMALL The SelectorSize is too small for the result. This parameter + has been updated with the size needed to complete the search + request. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigGetNextSelector ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN OUT UINTN *SelectorSize, + IN OUT EFI_IPSEC_CONFIG_SELECTOR *Selector + ); + +/** + Register an event that is to be signaled whenever a configuration process on the + specified IPsec configuration information is done. + + The register function is not surpport now and always returns EFI_UNSUPPORTED. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to be registered the event for. + @param[in] Event The event to be registered. + + @retval EFI_SUCCESS The event is registered successfully. + @retval EFI_INVALID_PARAMETER This is NULL, or Event is NULL. + @retval EFI_ACCESS_DENIED The Event is already registered for the DataType. + @retval EFI_UNSUPPORTED The notify registration unsupported, or the specified + DataType is not supported. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigRegisterNotify ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ); + + +/** + Remove the specified event that was previously registered on the specified IPsec + configuration data. + + This function is not supported now and always returns EFI_UNSUPPORTED. + + @param[in] This Pointer to the EFI_IPSEC_CONFIG_PROTOCOL instance. + @param[in] DataType The configuration data type to remove the registered event for. + @param[in] Event The event to be unregistered. + + @retval EFI_SUCCESS The event was removed successfully. + @retval EFI_NOT_FOUND The Event specified by DataType could not be found in the + database. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_UNSUPPORTED The notify registration unsupported or the specified + DataType is not supported. + +**/ +EFI_STATUS +EFIAPI +EfiIpSecConfigUnregisterNotify ( + IN EFI_IPSEC_CONFIG_PROTOCOL *This, + IN EFI_IPSEC_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ); + +extern LIST_ENTRY mConfigData[IPsecConfigDataTypeMaximum]; + +#endif diff --git a/Core/NetworkPkg/IpSecDxe/IpSecCryptIo.c b/Core/NetworkPkg/IpSecDxe/IpSecCryptIo.c new file mode 100644 index 0000000000..dca44231be --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecCryptIo.c @@ -0,0 +1,1021 @@ +/** @file + Common interfaces to call Security library. + + Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecCryptIo.h" +// +// The informations for the supported Encrypt/Decrpt Alogrithm. +// +GLOBAL_REMOVE_IF_UNREFERENCED ENCRYPT_ALGORITHM mIpsecEncryptAlgorithmList[IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE] = { + {IKE_EALG_NULL, 0, 0, 1, NULL, NULL, NULL, NULL}, + {IKE_EALG_NONE, 0, 0, 1, NULL, NULL, NULL, NULL}, + {IKE_EALG_3DESCBC, 24, 8, 8, TdesGetContextSize, TdesInit, TdesCbcEncrypt, TdesCbcDecrypt}, + {IKE_EALG_AESCBC, 16, 16, 16, AesGetContextSize, AesInit, AesCbcEncrypt, AesCbcDecrypt} +}; + +// +// The informations for the supported Authentication algorithm +// +GLOBAL_REMOVE_IF_UNREFERENCED AUTH_ALGORITHM mIpsecAuthAlgorithmList[IPSEC_AUTH_ALGORITHM_LIST_SIZE] = { + {IKE_AALG_NONE, 0, 0, 0, NULL, NULL, NULL, NULL}, + {IKE_AALG_NULL, 0, 0, 0, NULL, NULL, NULL, NULL}, + {IKE_AALG_SHA1HMAC, 20, 12, 64, HmacSha1GetContextSize, HmacSha1Init, HmacSha1Update, HmacSha1Final} +}; + +// +// The information for the supported Hash aglorithm +// +GLOBAL_REMOVE_IF_UNREFERENCED HASH_ALGORITHM mIpsecHashAlgorithmList[IPSEC_HASH_ALGORITHM_LIST_SIZE] = { + {IKE_AALG_NONE, 0, 0, 0, NULL, NULL, NULL, NULL}, + {IKE_AALG_NULL, 0, 0, 0, NULL, NULL, NULL, NULL}, + {IKE_AALG_SHA1HMAC, 20, 12, 64, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final} +}; + +BOOLEAN mInitialRandomSeed = FALSE; + +/** + Get the block size of specified encryption alogrithm. + + @param[in] AlgorithmId The encryption algorithm ID. + + @return The value of block size. + +**/ +UINTN +IpSecGetEncryptBlockSize ( + IN UINT8 AlgorithmId + ) +{ + UINT8 Index; + + for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) { + if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) { + return mIpsecEncryptAlgorithmList[Index].BlockSize; + } + } + + return (UINTN) -1; +} + +/** + Get the key length of the specified encryption alogrithm. + + @param[in] AlgorithmId The encryption algorithm ID. + + @return The value of key length. + +**/ +UINTN +IpSecGetEncryptKeyLength ( + IN UINT8 AlgorithmId + ) +{ + UINT8 Index; + + for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) { + if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) { + return mIpsecEncryptAlgorithmList[Index].KeyLength; + } + } + + return (UINTN) -1; +} + +/** + Get the IV size of the specified encryption alogrithm. + + @param[in] AlgorithmId The encryption algorithm ID. + + @return The value of IV size. + +**/ +UINTN +IpSecGetEncryptIvLength ( + IN UINT8 AlgorithmId + ) +{ + UINT8 Index; + + for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) { + if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) { + return mIpsecEncryptAlgorithmList[Index].IvLength; + } + } + + return (UINTN) -1; +} + +/** + Get the HMAC digest length by the specified Algorithm ID. + + @param[in] AlgorithmId The specified Alogrithm ID. + + @return The digest length of the specified Authentication Algorithm ID. + +**/ +UINTN +IpSecGetHmacDigestLength ( + IN UINT8 AlgorithmId + ) +{ + UINT8 Index; + + for (Index = 0; Index < IPSEC_AUTH_ALGORITHM_LIST_SIZE; Index++) { + if (mIpsecAuthAlgorithmList[Index].AlgorithmId == AlgorithmId) { + // + // Return the Digest Length of the Algorithm. + // + return mIpsecAuthAlgorithmList[Index].DigestLength; + } + } + + return 0; +} + +/** + Get the ICV size of the specified Authenticaion alogrithm. + + @param[in] AlgorithmId The Authentication algorithm ID. + + @return The value of ICV size. + +**/ +UINTN +IpSecGetIcvLength ( + IN UINT8 AlgorithmId + ) +{ + UINT8 Index; + + for (Index = 0; Index < IPSEC_AUTH_ALGORITHM_LIST_SIZE; Index++) { + if (AlgorithmId == mIpsecAuthAlgorithmList[Index].AlgorithmId) { + return mIpsecAuthAlgorithmList[Index].IcvLength; + } + } + + return (UINTN) -1; +} + +/** + Generate a random data for IV. If the IvSize is zero, not needed to create + IV and return EFI_SUCCESS. + + @param[in] IvBuffer The pointer of the IV buffer. + @param[in] IvSize The IV size in bytes. + + @retval EFI_SUCCESS Create a random data for IV. + +**/ +EFI_STATUS +IpSecGenerateIv ( + IN UINT8 *IvBuffer, + IN UINTN IvSize + ) +{ + if (IvSize != 0) { + return IpSecCryptoIoGenerateRandomBytes (IvBuffer, IvSize); + } + + return EFI_SUCCESS; +} + +/** + Get index of the specified encryption alogrithm from the mIpsecEncryptAlgorithemList. + + @param[in] AlgorithmId The encryption algorithm ID. + + @return the index. + +**/ +UINTN +IpSecGetIndexFromEncList ( + IN UINT8 AlgorithmId + ) +{ + UINT8 Index; + + for (Index = 0; Index < IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE; Index++) { + if (AlgorithmId == mIpsecEncryptAlgorithmList[Index].AlgorithmId) { + return Index; + } + } + + return (UINTN) -1; +} + +/** + Get index of the specified encryption alogrithm from the mIpsecAuthAlgorithemList. + + @param[in] AlgorithmId The encryption algorithm ID. + + @return the index. + +**/ +UINTN +IpSecGetIndexFromAuthList ( + IN UINT8 AlgorithmId + ) +{ + UINT8 Index; + + for (Index = 0; Index < IPSEC_AUTH_ALGORITHM_LIST_SIZE; Index++) { + if (AlgorithmId == mIpsecAuthAlgorithmList[Index].AlgorithmId) { + // + // The BlockSize is same with IvSize. + // + return Index; + } + } + + return (UINTN) -1; +} + +/** + Encrypt the buffer. + + This function calls relevant encryption interface from CryptoLib according to + the input alogrithm ID. The InData should be multiple of block size. This function + doesn't perform the padding. If it has the Ivec data, the length of it should be + same with the block size. The block size is different from the different algorithm. + + @param[in] AlgorithmId The Alogrithem identification defined in RFC. + @param[in] Key Pointer to the buffer containing encrypting key. + @param[in] KeyBits The length of the key in bits. + @param[in] Ivec Point to the buffer containning the Initializeion + Vector (IV) data. + @param[in] InData Point to the buffer containing the data to be + encrypted. + @param[in] InDataLength The length of InData in Bytes. + @param[out] OutData Point to the buffer that receives the encryption + output. + + @retval EFI_UNSUPPORTED The input Algorithm is not supported. + @retval EFI_OUT_OF_RESOURCE The required resource can't be allocated. + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +IpSecCryptoIoEncrypt ( + IN CONST UINT8 AlgorithmId, + IN CONST UINT8 *Key, + IN CONST UINTN KeyBits, + IN CONST UINT8 *Ivec, OPTIONAL + IN UINT8 *InData, + IN UINTN InDataLength, + OUT UINT8 *OutData + ) +{ + UINTN Index; + UINTN ContextSize; + UINT8 *Context; + EFI_STATUS Status; + + Status = EFI_UNSUPPORTED; + + switch (AlgorithmId) { + + case IKE_EALG_NULL: + case IKE_EALG_NONE: + CopyMem (OutData, InData, InDataLength); + return EFI_SUCCESS; + + case IKE_EALG_3DESCBC: + case IKE_EALG_AESCBC: + Index = IpSecGetIndexFromEncList (AlgorithmId); + if (Index == -1) { + return Status; + } + // + // Get Context Size + // + ContextSize = mIpsecEncryptAlgorithmList[Index].CipherGetContextSize (); + Context = AllocateZeroPool (ContextSize); + + if (Context == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Initiate Context + // + if (mIpsecEncryptAlgorithmList[Index].CipherInitiate (Context, Key, KeyBits)) { + if (mIpsecEncryptAlgorithmList[Index].CipherEncrypt (Context, InData, InDataLength, Ivec, OutData)) { + Status = EFI_SUCCESS; + } + } + break; + + default: + return Status; + + } + + if (Context != NULL) { + FreePool (Context); + } + + return Status; +} + +/** + Decrypts the buffer. + + This function calls relevant Decryption interface from CryptoLib according to + the input alogrithm ID. The InData should be multiple of block size. This function + doesn't perform the padding. If it has the Ivec data, the length of it should be + same with the block size. The block size is different from the different algorithm. + + @param[in] AlgorithmId The Alogrithem identification defined in RFC. + @param[in] Key Pointer to the buffer containing encrypting key. + @param[in] KeyBits The length of the key in bits. + @param[in] Ivec Point to the buffer containning the Initializeion + Vector (IV) data. + @param[in] InData Point to the buffer containing the data to be + decrypted. + @param[in] InDataLength The length of InData in Bytes. + @param[out] OutData Pointer to the buffer that receives the decryption + output. + + @retval EFI_UNSUPPORTED The input Algorithm is not supported. + @retval EFI_OUT_OF_RESOURCE The required resource can't be allocated. + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +IpSecCryptoIoDecrypt ( + IN CONST UINT8 AlgorithmId, + IN CONST UINT8 *Key, + IN CONST UINTN KeyBits, + IN CONST UINT8 *Ivec, OPTIONAL + IN UINT8 *InData, + IN UINTN InDataLength, + OUT UINT8 *OutData + ) +{ + UINTN Index; + UINTN ContextSize; + UINT8 *Context; + EFI_STATUS Status; + + Status = EFI_UNSUPPORTED; + + switch (AlgorithmId) { + + case IKE_EALG_NULL: + case IKE_EALG_NONE: + CopyMem (OutData, InData, InDataLength); + return EFI_SUCCESS; + + case IKE_EALG_3DESCBC: + case IKE_EALG_AESCBC: + Index = IpSecGetIndexFromEncList(AlgorithmId); + if (Index == -1) { + return Status; + } + + // + // Get Context Size + // + ContextSize = mIpsecEncryptAlgorithmList[Index].CipherGetContextSize(); + Context = AllocateZeroPool (ContextSize); + if (Context == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initiate Context + // + if (mIpsecEncryptAlgorithmList[Index].CipherInitiate (Context, Key, KeyBits)) { + if (mIpsecEncryptAlgorithmList[Index].CipherDecrypt (Context, InData, InDataLength, Ivec, OutData)) { + Status = EFI_SUCCESS; + } + } + break; + + default: + return Status; + } + + if (Context != NULL) { + FreePool (Context); + } + + return Status; +} + +/** + Digests the Payload with key and store the result into the OutData. + + This function calls relevant Hmac interface from CryptoLib according to + the input alogrithm ID. It computes all datas from InDataFragment and output + the result into the OutData buffer. If the OutDataSize is larger than the related + HMAC alogrithm output size, return EFI_INVALID_PARAMETER. + + @param[in] AlgorithmId The authentication Identification. + @param[in] Key Pointer of the authentication key. + @param[in] KeyLength The length of the Key in bytes. + @param[in] InDataFragment The list contains all data to be authenticated. + @param[in] FragmentCount The size of the InDataFragment. + @param[out] OutData For in, the buffer to receive the output data. + For out, the buffer contains the authenticated data. + @param[in] OutDataSize The size of the buffer of OutData. + + @retval EFI_UNSUPPORTED If the AuthAlg is not in the support list. + @retval EFI_INVALID_PARAMETER The OutData buffer size is larger than algorithm digest size. + @retval EFI_SUCCESS Authenticate the payload successfully. + @retval otherwise Authentication of the payload fails. + +**/ +EFI_STATUS +IpSecCryptoIoHmac ( + IN CONST UINT8 AlgorithmId, + IN CONST UINT8 *Key, + IN UINTN KeyLength, + IN HASH_DATA_FRAGMENT *InDataFragment, + IN UINTN FragmentCount, + OUT UINT8 *OutData, + IN UINTN OutDataSize + ) +{ + UINTN ContextSize; + UINTN Index; + UINT8 FragmentIndex; + UINT8 *HashContext; + EFI_STATUS Status; + UINT8 *OutHashData; + UINTN OutHashSize; + + Status = EFI_UNSUPPORTED; + OutHashData = NULL; + + OutHashSize = IpSecGetHmacDigestLength (AlgorithmId); + // + // If the expected hash data size is larger than the related Hash algorithm + // output length, return EFI_INVALID_PARAMETER. + // + if (OutDataSize > OutHashSize) { + return EFI_INVALID_PARAMETER; + } + OutHashData = AllocatePool (OutHashSize); + + if (OutHashData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + switch (AlgorithmId) { + + case IKE_AALG_NONE : + case IKE_AALG_NULL : + return EFI_SUCCESS; + + case IKE_AALG_SHA1HMAC: + Index = IpSecGetIndexFromAuthList (AlgorithmId); + if (Index == -1) { + return Status; + } + + // + // Get Context Size + // + ContextSize = mIpsecAuthAlgorithmList[Index].HmacGetContextSize(); + HashContext = AllocateZeroPool (ContextSize); + + if (HashContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Initiate HMAC context and hash the input data. + // + if (mIpsecAuthAlgorithmList[Index].HmacInitiate(HashContext, Key, KeyLength)) { + for (FragmentIndex = 0; FragmentIndex < FragmentCount; FragmentIndex++) { + if (!mIpsecAuthAlgorithmList[Index].HmacUpdate ( + HashContext, + InDataFragment[FragmentIndex].Data, + InDataFragment[FragmentIndex].DataSize + ) + ) { + goto Exit; + } + } + if (mIpsecAuthAlgorithmList[Index].HmacFinal (HashContext, OutHashData)) { + // + // In some cases, like the Icv computing, the Icv size might be less than + // the key length size, so copy the part of hash data to the OutData. + // + CopyMem (OutData, OutHashData, OutDataSize); + Status = EFI_SUCCESS; + } + + goto Exit; + } + + default: + return Status; + } + +Exit: + if (HashContext != NULL) { + FreePool (HashContext); + } + if (OutHashData != NULL) { + FreePool (OutHashData); + } + + return Status; +} + +/** + Digests the Payload and store the result into the OutData. + + This function calls relevant Hash interface from CryptoLib according to + the input alogrithm ID. It computes all datas from InDataFragment and output + the result into the OutData buffer. If the OutDataSize is larger than the related + Hash alogrithm output size, return EFI_INVALID_PARAMETER. + + @param[in] AlgorithmId The authentication Identification. + @param[in] InDataFragment A list contains all data to be authenticated. + @param[in] FragmentCount The size of the InDataFragment. + @param[out] OutData For in, the buffer to receive the output data. + For out, the buffer contains the authenticated data. + @param[in] OutDataSize The size of the buffer of OutData. + + @retval EFI_UNSUPPORTED If the AuthAlg is not in the support list. + @retval EFI_SUCCESS Authenticated the payload successfully. + @retval EFI_INVALID_PARAMETER If the OutDataSize is larger than the related Hash + algorithm could handle. + @retval otherwise Authentication of the payload failed. + +**/ +EFI_STATUS +IpSecCryptoIoHash ( + IN CONST UINT8 AlgorithmId, + IN HASH_DATA_FRAGMENT *InDataFragment, + IN UINTN FragmentCount, + OUT UINT8 *OutData, + IN UINTN OutDataSize + ) +{ + UINTN ContextSize; + UINTN Index; + UINT8 FragmentIndex; + UINT8 *HashContext; + EFI_STATUS Status; + UINT8 *OutHashData; + UINTN OutHashSize; + + Status = EFI_UNSUPPORTED; + OutHashData = NULL; + + OutHashSize = IpSecGetHmacDigestLength (AlgorithmId); + // + // If the expected hash data size is larger than the related Hash algorithm + // output length, return EFI_INVALID_PARAMETER. + // + if (OutDataSize > OutHashSize) { + return EFI_INVALID_PARAMETER; + } + OutHashData = AllocatePool (OutHashSize); + if (OutHashData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + switch (AlgorithmId) { + + case IKE_AALG_NONE: + case IKE_AALG_NULL: + return EFI_SUCCESS; + + case IKE_AALG_SHA1HMAC: + Index = IpSecGetIndexFromAuthList (AlgorithmId); + if (Index == -1) { + return Status; + } + // + // Get Context Size + // + ContextSize = mIpsecHashAlgorithmList[Index].HashGetContextSize(); + HashContext = AllocateZeroPool (ContextSize); + if (HashContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Initiate Hash context and hash the input data. + // + if (mIpsecHashAlgorithmList[Index].HashInitiate(HashContext)) { + for (FragmentIndex = 0; FragmentIndex < FragmentCount; FragmentIndex++) { + if (!mIpsecHashAlgorithmList[Index].HashUpdate ( + HashContext, + InDataFragment[FragmentIndex].Data, + InDataFragment[FragmentIndex].DataSize + ) + ) { + goto Exit; + } + } + if (mIpsecHashAlgorithmList[Index].HashFinal (HashContext, OutHashData)) { + // + // In some cases, like the Icv computing, the Icv size might be less than + // the key length size, so copy the part of hash data to the OutData. + // + CopyMem (OutData, OutHashData, OutDataSize); + Status = EFI_SUCCESS; + } + + goto Exit; + } + + default: + return Status; + } + +Exit: + if (HashContext != NULL) { + FreePool (HashContext); + } + if (OutHashData != NULL) { + FreePool (OutHashData); + } + + return Status; +} + +/** + Generates the Diffie-Hellman public key. + + This function first initiate a DHContext, then call the DhSetParameter() to set + the prime and primelenght, at end call the DhGenerateKey() to generates random + secret exponent, and computes the public key. The output returned via parameter + PublicKey and PublicKeySize. DH context is updated accordingly. If the PublicKey + buffer is too small to hold the public key, EFI_INVALID_PARAMETER is returned + and PublicKeySize is set to the required buffer size to obtain the public key. + + @param[in, out] DhContext Pointer to the DH context. + @param[in] Generator Vlaue of generator. + @param[in] PrimeLength Length in bits of prime to be generated. + @param[in] Prime Pointer to the buffer to receive the generated + prime number. + @param[out] PublicKey Pointer to the buffer to receive generated public key. + @param[in, out] PublicKeySize For in, the size of PublicKey buffer in bytes. + For out, the size of data returned in PublicKey + buffer in bytes. + + @retval EFI_SUCCESS The operation perfoms successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +IpSecCryptoIoDhGetPublicKey ( + IN OUT UINT8 **DhContext, + IN UINTN Generator, + IN UINTN PrimeLength, + IN CONST UINT8 *Prime, + OUT UINT8 *PublicKey, + IN OUT UINTN *PublicKeySize + ) +{ + EFI_STATUS Status; + + *DhContext = DhNew (); + ASSERT (*DhContext != NULL); + if (!DhSetParameter (*DhContext, Generator, PrimeLength, Prime)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + if (!DhGenerateKey (*DhContext, PublicKey, PublicKeySize)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + return EFI_SUCCESS; + +Exit: + if (*DhContext != NULL) { + DhFree (*DhContext); + DhContext = NULL; + } + + return Status; +} + +/** + Generates exchanged common key. + + Given peer's public key, this function computes the exchanged common key, based + on its own context including value of prime modulus and random secret exponent. + + @param[in, out] DhContext Pointer to the DH context. + @param[in] PeerPublicKey Pointer to the peer's Public Key. + @param[in] PeerPublicKeySize Size of peer's public key in bytes. + @param[out] Key Pointer to the buffer to receive generated key. + @param[in, out] KeySize For in, the size of Key buffer in bytes. + For out, the size of data returned in Key + buffer in bytes. + + @retval EFI_SUCCESS The operation perfoms successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +IpSecCryptoIoDhComputeKey ( + IN OUT UINT8 *DhContext, + IN CONST UINT8 *PeerPublicKey, + IN UINTN PeerPublicKeySize, + OUT UINT8 *Key, + IN OUT UINTN *KeySize + ) +{ + if (!DhComputeKey (DhContext, PeerPublicKey, PeerPublicKeySize, Key, KeySize)) { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Releases the DH context. If DhContext is NULL, return EFI_INVALID_PARAMETER. + + @param[in, out] DhContext Pointer to the DH context to be freed. + + @retval EFI_SUCCESS The operation perfoms successfully. + @retval EFI_INVALID_PARAMETER The DhContext is NULL. + +**/ +EFI_STATUS +IpSecCryptoIoFreeDh ( + IN OUT UINT8 **DhContext + ) +{ + if (*DhContext == NULL) { + return EFI_INVALID_PARAMETER; + } + + DhFree (*DhContext); + return EFI_SUCCESS; +} + +/** + Generates random numbers of specified size. + + If the Random Generator wasn't initiated, initiate it first, then call RandomBytes. + + @param[out] OutBuffer Pointer to buffer to receive random value. + @param[in] Bytes Size of randome bytes to generate. + + @retval EFI_SUCCESS The operation perfoms successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +IpSecCryptoIoGenerateRandomBytes ( + OUT UINT8* OutBuffer, + IN UINTN Bytes + ) +{ + if (!mInitialRandomSeed) { + RandomSeed (NULL, 0); + mInitialRandomSeed = TRUE; + } + if (RandomBytes (OutBuffer, Bytes)) { + return EFI_SUCCESS; + } else { + return EFI_INVALID_PARAMETER; + } +} + +/** + Authenticate data with the certificate. + + @param[in] InData Pointer to the Data to be signed. + @param[in] InDataSize InData size in bytes. + @param[in] PrivateKey Pointer to the private key. + @param[in] PrivateKeySize The size of Private Key in bytes. + @param[in] KeyPassWord Pointer to the password for retrieving private key. + @param[in] KeyPwdSize The size of Key Password in bytes. + @param[out] OutData The pointer to the signed data. + @param[in, out] OutDataSize Pointer to contain the size of out data. + +**/ +VOID +IpSecCryptoIoAuthDataWithCertificate ( + IN UINT8 *InData, + IN UINTN InDataSize, + IN UINT8 *PrivateKey, + IN UINTN PrivateKeySize, + IN UINT8 *KeyPassWord, + IN UINTN KeyPwdSize, + OUT UINT8 **OutData, + IN OUT UINTN *OutDataSize + ) +{ + UINT8 *RsaContext; + UINT8 *Signature; + UINTN SigSize; + + SigSize = 0; + RsaContext = NULL; + + // + // Retrieve RSA Private Key from password-protected PEM data + // + RsaGetPrivateKeyFromPem ( + (CONST UINT8 *)PrivateKey, + PrivateKeySize, + (CONST CHAR8 *)KeyPassWord, + (VOID **) &RsaContext + ); + if (RsaContext == NULL) { + return; + } + + // + // Sign data + // + Signature = NULL; + if (!RsaPkcs1Sign (RsaContext, InData, InDataSize, Signature, &SigSize)) { + Signature = AllocateZeroPool (SigSize); + } else { + return; + } + + RsaPkcs1Sign (RsaContext, InData, InDataSize, Signature, &SigSize); + + *OutData = Signature; + *OutDataSize = SigSize; + + if (RsaContext != NULL) { + RsaFree (RsaContext); + } +} + +/** + Verify the singed data with the public key which is contained in a certificate. + + @param[in] InCert Pointer to the Certificate which contains the + public key. + @param[in] CertLen The size of Certificate in bytes. + @param[in] InCa Pointer to the CA certificate + @param[in] CaLen The size of CA certificate in bytes. + @param[in] InData Pointer to octect message hash to be checked. + @param[in] InDataSize Size of the message hash in bytes. + @param[in] Singnature The pointer to the RSA PKCS1-V1_5 signature to be verifed. + @param[in] SigSize Size of signature in bytes. + + @retval TRUE Valid signature encoded in PKCS1-v1_5. + @retval FALSE Invalid signature or invalid RSA context. + +**/ +BOOLEAN +IpSecCryptoIoVerifySignDataByCertificate ( + IN UINT8 *InCert, + IN UINTN CertLen, + IN UINT8 *InCa, + IN UINTN CaLen, + IN UINT8 *InData, + IN UINTN InDataSize, + IN UINT8 *Singnature, + IN UINTN SigSize + ) +{ + UINT8 *RsaContext; + BOOLEAN Status; + + // + // Create the RSA Context + // + RsaContext = RsaNew (); + if (RsaContext == NULL) { + return FALSE; + } + + // + // Verify the validity of X509 Certificate + // + if (!X509VerifyCert (InCert, CertLen, InCa, CaLen)) { + return FALSE; + } + + // + // Retrieve the RSA public Key from Certificate + // + RsaGetPublicKeyFromX509 ((CONST UINT8 *)InCert, CertLen, (VOID **)&RsaContext); + + // + // Verify data + // + Status = RsaPkcs1Verify (RsaContext, InData, InDataSize, Singnature, SigSize); + + if (RsaContext != NULL) { + RsaFree (RsaContext); + } + + return Status; +} + +/** + Retrieves the RSA Public Key from one X509 certificate (DER format only). + + @param[in] InCert Pointer to the certificate. + @param[in] CertLen The size of the certificate in bytes. + @param[out] PublicKey Pointer to the retrieved public key. + @param[out] PublicKeyLen Size of Public Key in bytes. + + @retval EFI_SUCCESS Successfully get the public Key. + @retval EFI_INVALID_PARAMETER The certificate is malformed. + +**/ +EFI_STATUS +IpSecCryptoIoGetPublicKeyFromCert ( + IN UINT8 *InCert, + IN UINTN CertLen, + OUT UINT8 **PublicKey, + OUT UINTN *PublicKeyLen + ) +{ + UINT8 *RsaContext; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + // + // Create the RSA Context + // + RsaContext = RsaNew (); + + // + // Retrieve the RSA public key from CA Certificate + // + if (!RsaGetPublicKeyFromX509 ((CONST UINT8 *)InCert, CertLen, (VOID **) &RsaContext)) { + Status = EFI_INVALID_PARAMETER; + goto EXIT; + } + + *PublicKeyLen = 0; + + RsaGetKey (RsaContext, RsaKeyN, NULL, PublicKeyLen); + + *PublicKey = AllocateZeroPool (*PublicKeyLen); + if (*PublicKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + if (!RsaGetKey (RsaContext, RsaKeyN, *PublicKey, PublicKeyLen)) { + Status = EFI_INVALID_PARAMETER; + } + +EXIT: + if (RsaContext != NULL) { + RsaFree (RsaContext); + } + + return Status; +} + +/** + Retrieves the subject name from one X509 certificate (DER format only). + + @param[in] InCert Pointer to the X509 certificate. + @param[in] CertSize The size of the X509 certificate in bytes. + @param[out] CertSubject Pointer to the retrieved certificate subject. + @param[out] SubjectSize The size of Certificate Subject in bytes. + + @retval EFI_SUCCESS Retrieved the certificate subject successfully. + @retval EFI_INVALID_PARAMETER The certificate is malformed. + +**/ +EFI_STATUS +IpSecCryptoIoGetSubjectFromCert ( + IN UINT8 *InCert, + IN UINTN CertSize, + OUT UINT8 **CertSubject, + OUT UINTN *SubjectSize + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + *SubjectSize = 0; + X509GetSubjectName (InCert, CertSize, *CertSubject, SubjectSize); + + *CertSubject = AllocateZeroPool (*SubjectSize); + if (!X509GetSubjectName (InCert, CertSize, *CertSubject, SubjectSize)) { + Status = EFI_INVALID_PARAMETER; + } + + return Status; +} diff --git a/Core/NetworkPkg/IpSecDxe/IpSecCryptIo.h b/Core/NetworkPkg/IpSecDxe/IpSecCryptIo.h new file mode 100644 index 0000000000..0e106af0f0 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecCryptIo.h @@ -0,0 +1,827 @@ +/** @file + Definitions related to the Cryptographic Operations in IPsec. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef _EFI_IPSEC_CRYPTIO_H_ +#define _EFI_IPSEC_CRYPTIO_H_ + +#include +#include +#include +#include +#include + +#include "IpSecImpl.h" +#include "IkeCommon.h" + +#define IPSEC_ENCRYPT_ALGORITHM_LIST_SIZE 4 +#define IPSEC_AUTH_ALGORITHM_LIST_SIZE 3 +#define IPSEC_HASH_ALGORITHM_LIST_SIZE 3 + +/// +/// Authentication Algorithm Definition +/// The number value definition is aligned to IANA assignment +/// +#define IKE_AALG_NONE 0x00 +#define IKE_AALG_SHA1HMAC 0x02 +#define IKE_AALG_NULL 0xFB + +/// +/// Encryption Algorithm Definition +/// The number value definition is aligned to IANA assignment +/// +#define IKE_EALG_NONE 0x00 +#define IKE_EALG_3DESCBC 0x03 +#define IKE_EALG_NULL 0x0B +#define IKE_EALG_AESCBC 0x0C + +/** + Prototype of HMAC GetContextSize. + + Retrieves the size, in bytes, of the context buffer required. + + @return The size, in bytes, of the context buffer required. + +**/ +typedef +UINTN +(EFIAPI *CRYPTO_HMAC_GETCONTEXTSIZE)( + VOID + ); + +/** + Prototype of HMAC Operation Initiating. + + Initialization with a new context. + + @param[out] Context Input Context. + @param[in] Key Pointer to the key for HMAC. + @param[in] KeySize The length of the Key in bytes. + + @retval TRUE Initialization Successfully. + +**/ +typedef +BOOLEAN +(EFIAPI *CRYPTO_HMAC_INIT)( + OUT VOID *Context, + IN CONST UINT8 *Key, + IN UINTN KeySize + ); + +/** + Prototype of HMAC update. + HMAC update operation. Continue an HMAC message digest operation, processing + another message block, and updating the HMAC context. + + If Context is NULL, then ASSERT(). + If Data is NULL, then ASSERT(). + + @param[in,out] Context The Specified Context. + @param[in,out] Data The Input Data to be digested. + @param[in] DataLength The length, in bytes, of Data. + + @retval TRUE Update data successfully. + @retval FALSE The Context has been finalized. + +**/ +typedef +BOOLEAN +(EFIAPI *CRYPTO_HMAC_UPDATE)( + IN OUT VOID *Context, + IN CONST VOID *Data, + IN UINTN DataLength + ); + +/** + Prototype of HMAC finallization. + Terminate a HMAC message digest operation and output the message digest. + + If Context is NULL, then ASSERT(). + If HashValue is NULL, then ASSERT(). + + @param[in,out] Context The specified Context. + @param[out] HmacValue Pointer to a 16-byte message digest output buffer. + + @retval TRUE Finalized successfully. + +**/ +typedef +BOOLEAN +(EFIAPI *CRYPTO_HMAC_FINAL)( + IN OUT VOID *Context, + OUT UINT8 *HmacValue + ); + +/** + Prototype of Block Cipher GetContextSize. + + Retrieves the size, in bytes, of the context buffer required. + + @return The size, in bytes, of the context buffer required. + +**/ +typedef +UINTN +(EFIAPI *CRYPTO_CIPHER_GETCONTEXTSIZE)( + VOID + ); + +/** + Prototype of Block Cipher initiation. + Intializes the user-supplied key as the specifed context (key materials) for both + encryption and decryption operations. + + If Context is NULL, then ASSERT(). + If Key is NULL, then generate random key for usage. + + @param[in,out] Context The specified Context. + @param[in] Key User-supplied cipher key. + @param[in] KeyBits Key length in bits. + + @retval TRUE Block Cipher Initialization was successful. + +**/ +typedef +BOOLEAN +(EFIAPI *CRYPTO_CIPHER_INIT)( + IN OUT VOID *Context, + IN CONST UINT8 *Key, + IN UINTN KeyBits + ); + +/** + Prototype of Cipher encryption. + Encrypts plaintext message with the specified cipher. + + If Context is NULL, then ASSERT(). + if InData is NULL, then ASSERT(). + If Size of input data is not multiple of Cipher algorithm related block size, + then ASSERT(). + + @param[in] Context The specified Context. + @param[in] InData The input plaintext data to be encrypted. + @param[in] InputSize The size of input data. + @param[in] Ivec Pointer to Initial Vector data for encryption. + @param[out] OutData The resultant encrypted ciphertext. + + @retval TRUE Encryption successful. + +**/ +typedef +BOOLEAN +(EFIAPI *CRYPTO_CIPHER_ENCRYPT)( + IN VOID *Context, + IN CONST UINT8 *InData, + IN UINTN InputSize, + IN CONST UINT8 *Ivec, + OUT UINT8 *OutData + ); + +/** + Prototype of Cipher decryption. + Decrypts cipher message with specified cipher. + + If Context is NULL, then ASSERT(). + if InData is NULL, then ASSERT(). + If Size of input data is not a multiple of a certaion block size , then ASSERT(). + + @param[in] Context The specified Context. + @param[in] InData The input ciphertext data to be decrypted. + @param[in] InputSize The InData size. + @param[in] Ivec Pointer to the Initial Vector data for decryption. + @param[out] OutData The resultant decrypted plaintext. + + @retval TRUE Decryption successful. + +**/ +typedef +BOOLEAN +(EFIAPI *CRYPTO_CIPHER_DECRYPT)( + IN VOID *Context, + IN CONST UINT8 *InData, + IN UINTN InputSize, + IN CONST UINT8 *Ivec, + OUT UINT8 *OutData + ); + +/** + Prototype of Hash ContextSize. + + Retrieves the size, in bytes, of the context buffer required for specified hash operations. + + @return The size, in bytes, of the context buffer required for certain hash operations. + +**/ +typedef +UINTN +(EFIAPI *CRYPTO_HASH_GETCONTEXTSIZE)( + VOID + ); + +/** + Prototype of Hash Initiate. + + Initializes user-supplied memory pointed by Context as specified hash context for + subsequent use. + + If Context is NULL, then ASSERT(). + + @param[out] Context Pointer to specified context being initialized. + + @retval TRUE context initialization succeeded. + @retval FALSE context initialization failed. + +**/ +typedef +BOOLEAN +(EFIAPI *CRYPTO_HASH_INIT)( + OUT VOID *Context + ); + +/** + Prototype of Hash Update + + Digests the input data and updates hash context. + + This function performs digest on a data buffer of the specified size. + It can be called multiple times to compute the digest of long or discontinuous data streams. + Context should be already correctly intialized by HashInit(), and should not be finalized + by HashFinal(). Behavior with invalid context is undefined. + + If Context is NULL, then ASSERT(). + + @param[in, out] Context Pointer to the specified context. + @param[in] Data Pointer to the buffer containing the data to be hashed. + @param[in] DataSize Size of Data buffer in bytes. + + @retval TRUE data digest succeeded. + @retval FALSE data digest failed. + +**/ +typedef +BOOLEAN +(EFIAPI *CRYPTO_HASH_UPDATE)( + IN OUT VOID *Context, + IN CONST VOID *Data, + IN UINTN DataSize + ); + +/** + Prototype of Hash Finalization. + + Completes computation of the digest value. + + This function completes hash computation and retrieves the digest value into + the specified memory. After this function has been called, the context cannot + be used again. + context should be already correctly intialized by HashInit(), and should not be + finalized by HashFinal(). Behavior with invalid context is undefined. + + If Context is NULL, then ASSERT(). + If HashValue is NULL, then ASSERT(). + + @param[in, out] Context Pointer to the specified context. + @param[out] HashValue Pointer to a buffer that receives the digest + value. + + @retval TRUE digest computation succeeded. + @retval FALSE digest computation failed. + +**/ +typedef +BOOLEAN +(EFIAPI *CRYPTO_HASH_FINAL)( + IN OUT VOID *Context, + OUT UINT8 *HashValue + ); + +// +// The struct used to store the information and operation of Block Cipher algorithm. +// +typedef struct _ENCRYPT_ALGORITHM { + // + // The ID of the Algorithm + // + UINT8 AlgorithmId; + // + // The Key length of the Algorithm + // + UINTN KeyLength; + // + // Iv Size of the Algorithm + // + UINTN IvLength; + // + // The Block Size of the Algorithm + // + UINTN BlockSize; + // + // The Function pointer of GetContextSize. + // + CRYPTO_CIPHER_GETCONTEXTSIZE CipherGetContextSize; + // + // The Function pointer of Cipher initiation. + // + CRYPTO_CIPHER_INIT CipherInitiate; + // + // The Function pointer of Cipher Encryption. + // + CRYPTO_CIPHER_ENCRYPT CipherEncrypt; + // + // The Function pointer of Cipher Decrption. + // + CRYPTO_CIPHER_DECRYPT CipherDecrypt; +} ENCRYPT_ALGORITHM; + +// +// The struct used to store the information and operation of Autahentication algorithm. +// +typedef struct _AUTH_ALGORITHM { + // + // ID of the Algorithm + // + UINT8 AlgorithmId; + // + // The Key length of the Algorithm + // + UINTN DigestLength; + // + // The ICV length of the Algorithm + // + UINTN IcvLength; + // + // The block size of the Algorithm + // + UINTN BlockSize; + // + // The function pointer of GetContextSize. + // + CRYPTO_HMAC_GETCONTEXTSIZE HmacGetContextSize; + // + // The function pointer of Initiation + // + CRYPTO_HMAC_INIT HmacInitiate; + // + // The function pointer of HMAC Update. + // + CRYPTO_HMAC_UPDATE HmacUpdate; + // + // The fucntion pointer of HMAC Final + // + CRYPTO_HMAC_FINAL HmacFinal; +} AUTH_ALGORITHM; + +// +// The struct used to store the informatino and operation of Hash algorithm. +// +typedef struct _HASH_ALGORITHM { + // + // ID of the Algorithm + // + UINT8 AlgorithmId; + // + // The Key length of the Algorithm + // + UINTN DigestLength; + // + // The ICV length of the Algorithm + // + UINTN IcvLength; + // + // The block size of the Algorithm + // + UINTN BlockSize; + // + // The function pointer of GetContextSize + // + CRYPTO_HASH_GETCONTEXTSIZE HashGetContextSize; + // + // The function pointer of Initiation + // + CRYPTO_HASH_INIT HashInitiate; + // + // The function pointer of Hash Update + // + CRYPTO_HASH_UPDATE HashUpdate; + // + // The fucntion pointer of Hash Final + // + CRYPTO_HASH_FINAL HashFinal; +} HASH_ALGORITHM; + +/** + Get the IV size of specified encryption alogrithm. + + @param[in] AlgorithmId The encryption algorithm ID. + + @return The value of IV size. + +**/ +UINTN +IpSecGetEncryptIvLength ( + IN UINT8 AlgorithmId + ); + +/** + Get the block size of specified encryption alogrithm. + + @param[in] AlgorithmId The encryption algorithm ID. + + @return The value of block size. + +**/ +UINTN +IpSecGetEncryptBlockSize ( + IN UINT8 AlgorithmId + ); + +/** + Get the required key length of the specified encryption alogrithm. + + @param[in] AlgorithmId The encryption algorithm ID. + + @return The value of key length. + +**/ +UINTN +IpSecGetEncryptKeyLength ( + IN UINT8 AlgorithmId + ); + +/** + Get the ICV size of the specified Authenticaion alogrithm. + + @param[in] AlgorithmId The Authentication algorithm ID. + + @return The value of ICV size. + +**/ +UINTN +IpSecGetIcvLength ( + IN UINT8 AlgorithmId + ); + +/** + Get the HMAC digest length by the specified Algorithm ID. + + @param[in] AlgorithmId The specified Alogrithm ID. + + @return The digest length of the specified Authentication Algorithm ID. + +**/ +UINTN +IpSecGetHmacDigestLength ( + IN UINT8 AlgorithmId + ); + +/** + Generate a random data for IV. If the IvSize is zero, not needed to create + IV and return EFI_SUCCESS. + + @param[in] IvBuffer The pointer of the IV buffer. + @param[in] IvSize The IV size in bytes. + + @retval EFI_SUCCESS Create random data for IV. + +**/ +EFI_STATUS +IpSecGenerateIv ( + IN UINT8 *IvBuffer, + IN UINTN IvSize + ); + +/** + Encrypt the buffer. + + This function calls relevant encryption interface from CryptoLib according to + the input alogrithm ID. The InData should be multiple of block size. This function + doesn't perform the padding. If it has the Ivec data, the length of it should be + same with the block size. The block size is different from the different algorithm. + + @param[in] AlgorithmId The Alogrithem identification defined in RFC. + @param[in] Key Pointer to the buffer containing encrypting key. + @param[in] KeyBits The length of the key in bits. + @param[in] Ivec Point to the buffer containning the Initializeion + Vector (IV) data. + @param[in] InData Point to the buffer containing the data to be + encrypted. + @param[in] InDataLength The length of InData in Bytes. + @param[out] OutData Point to the buffer that receives the encryption + output. + + @retval EFI_UNSUPPORTED The input Algorithm is not supported. + @retval EFI_OUT_OF_RESOURCE The required resource can't be allocated. + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +IpSecCryptoIoEncrypt ( + IN CONST UINT8 AlgorithmId, + IN CONST UINT8 *Key, + IN CONST UINTN KeyBits, + IN CONST UINT8 *Ivec, OPTIONAL + IN UINT8 *InData, + IN UINTN InDataLength, + OUT UINT8 *OutData + ); + +/** + Decrypts the buffer. + + This function calls relevant Decryption interface from CryptoLib according to + the input alogrithm ID. The InData should be multiple of block size. This function + doesn't perform the padding. If it has the Ivec data, the length of it should be + same with the block size. The block size is different from the different algorithm. + + @param[in] AlgorithmId The Alogrithem identification defined in RFC. + @param[in] Key Pointer to the buffer containing encrypting key. + @param[in] KeyBits The length of the key in bits. + @param[in] Ivec Point to the buffer containning the Initializeion + Vector (IV) data. + @param[in] InData Point to the buffer containing the data to be + decrypted. + @param[in] InDataLength The length of InData in Bytes. + @param[out] OutData Pointer to the buffer that receives the decryption + output. + + @retval EFI_UNSUPPORTED The input Algorithm is not supported. + @retval EFI_OUT_OF_RESOURCE The required resource can't be allocated. + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +IpSecCryptoIoDecrypt ( + IN CONST UINT8 AlgorithmId, + IN CONST UINT8 *Key, + IN CONST UINTN KeyBits, + IN CONST UINT8 *Ivec, OPTIONAL + IN UINT8 *InData, + IN UINTN InDataLength, + OUT UINT8 *OutData + ); + +/** + Digests the Payload with key and store the result into the OutData. + + This function calls relevant Hmac interface from CryptoLib according to + the input alogrithm ID. It computes all datas from InDataFragment and output + the result into the OutData buffer. If the OutDataSize is larger than the related + HMAC alogrithm output size, return EFI_INVALID_PARAMETER. + + @param[in] AlgorithmId The authentication Identification. + @param[in] Key Pointer of the authentication key. + @param[in] KeyLength The length of the Key in bytes. + @param[in] InDataFragment The list contains all data to be authenticated. + @param[in] FragmentCount The size of the InDataFragment. + @param[out] OutData For in, the buffer to receive the output data. + For out, the buffer contains the authenticated data. + @param[in] OutDataSize The size of the buffer of OutData. + + @retval EFI_UNSUPPORTED If the AuthAlg is not in the support list. + @retval EFI_INVALID_PARAMETER The OutData buffer size is larger than algorithm digest size. + @retval EFI_SUCCESS Authenticate the payload successfully. + @retval otherwise Authentication of the payload fails. + +**/ +EFI_STATUS +IpSecCryptoIoHmac ( + IN CONST UINT8 AlgorithmId, + IN CONST UINT8 *Key, + IN UINTN KeyLength, + IN HASH_DATA_FRAGMENT *InDataFragment, + IN UINTN FragmentCount, + OUT UINT8 *OutData, + IN UINTN OutDataSize + ); + +/** + Digests the Payload and store the result into the OutData. + + This function calls relevant Hash interface from CryptoLib according to + the input alogrithm ID. It computes all datas from InDataFragment and output + the result into the OutData buffer. If the OutDataSize is larger than the related + Hash alogrithm output size, return EFI_INVALID_PARAMETER. + + @param[in] AlgorithmId The authentication Identification. + @param[in] InDataFragment A list contains all data to be authenticated. + @param[in] FragmentCount The size of the InDataFragment. + @param[out] OutData For in, the buffer to receive the output data. + For out, the buffer contains the authenticated data. + @param[in] OutDataSize The size of the buffer of OutData. + + @retval EFI_UNSUPPORTED If the AuthAlg is not in the support list. + @retval EFI_SUCCESS Authenticated the payload successfully. + @retval EFI_INVALID_PARAMETER If the OutDataSize is larger than the related Hash + algorithm could handle. + @retval otherwise Authentication of the payload failed. + +**/ +EFI_STATUS +IpSecCryptoIoHash ( + IN CONST UINT8 AlgorithmId, + IN HASH_DATA_FRAGMENT *InDataFragment, + IN UINTN FragmentCount, + OUT UINT8 *OutData, + IN UINTN OutDataSize + ); + +/** + Generates the Diffie-Hellman public key. + + This function first initiate a DHContext, then call the DhSetParameter() to set + the prime and primelenght, at end call the DhGenerateKey() to generates random + secret exponent, and computes the public key. The output returned via parameter + PublicKey and PublicKeySize. DH context is updated accordingly. If the PublicKey + buffer is too small to hold the public key, EFI_INVALID_PARAMETER is returned + and PublicKeySize is set to the required buffer size to obtain the public key. + + @param[in, out] DhContext Pointer to the DH context. + @param[in] Generator Vlaue of generator. + @param[in] PrimeLength Length in bits of prime to be generated. + @param[in] Prime Pointer to the buffer to receive the generated + prime number. + @param[out] PublicKey Pointer to the buffer to receive generated public key. + @param[in, out] PublicKeySize For in, the size of PublicKey buffer in bytes. + For out, the size of data returned in PublicKey + buffer in bytes. + + @retval EFI_SUCCESS The operation perfoms successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +IpSecCryptoIoDhGetPublicKey ( + IN OUT UINT8 **DhContext, + IN UINTN Generator, + IN UINTN PrimeLength, + IN CONST UINT8 *Prime, + OUT UINT8 *PublicKey, + IN OUT UINTN *PublicKeySize + ); + +/** + Generates exchanged common key. + + Given peer's public key, this function computes the exchanged common key, based + on its own context including value of prime modulus and random secret exponent. + + @param[in, out] DhContext Pointer to the DH context. + @param[in] PeerPublicKey Pointer to the peer's Public Key. + @param[in] PeerPublicKeySize Size of peer's public key in bytes. + @param[out] Key Pointer to the buffer to receive generated key. + @param[in, out] KeySize For in, the size of Key buffer in bytes. + For out, the size of data returned in Key + buffer in bytes. + + @retval EFI_SUCCESS The operation perfoms successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +IpSecCryptoIoDhComputeKey ( + IN OUT UINT8 *DhContext, + IN CONST UINT8 *PeerPublicKey, + IN UINTN PeerPublicKeySize, + OUT UINT8 *Key, + IN OUT UINTN *KeySize + ); + +/** + Releases the DH context. If DhContext is NULL, return EFI_INVALID_PARAMETER. + + @param[in, out] DhContext Pointer to the DH context to be freed. + + @retval EFI_SUCCESS The operation perfoms successfully. + @retval EFI_INVALID_PARAMETER The DhContext is NULL. + +**/ +EFI_STATUS +IpSecCryptoIoFreeDh ( + IN OUT UINT8 **DhContext + ); + +/** + Generates random numbers of specified size. + + If the Random Generator wasn't initiated, initiate it first, then call RandomBytes. + + @param[out] OutBuffer Pointer to buffer to receive random value. + @param[in] Bytes Size of randome bytes to generate. + + @retval EFI_SUCCESS The operation perfoms successfully. + @retval Otherwise The operation is failed. + +**/ +EFI_STATUS +IpSecCryptoIoGenerateRandomBytes ( + OUT UINT8* OutBuffer, + IN UINTN Bytes + ); + +/** + Authenticate data with the certificate. + + @param[in] InData Pointer to the Data to be signed. + @param[in] InDataSize InData size in bytes. + @param[in] PrivateKey Pointer to the private key. + @param[in] PrivateKeySize The size of Private Key in bytes. + @param[in] KeyPassWord Pointer to the password for retrieving private key. + @param[in] KeyPwdSize The size of Key Password in bytes. + @param[out] OutData The pointer to the signed data. + @param[in, out] OutDataSize Pointer to contain the size of out data. + +**/ +VOID +IpSecCryptoIoAuthDataWithCertificate ( + IN UINT8 *InData, + IN UINTN InDataSize, + IN UINT8 *PrivateKey, + IN UINTN PrivateKeySize, + IN UINT8 *KeyPassWord, + IN UINTN KeyPwdSize, + OUT UINT8 **OutData, + IN OUT UINTN *OutDataSize + ); + +/** + Verify the singed data with the public key which is contained in a certificate. + + @param[in] InCert Pointer to the Certificate which contains the + public key. + @param[in] CertLen The size of Certificate in bytes. + @param[in] InCa Pointer to the CA certificate + @param[in] CaLen The size of CA certificate in bytes. + @param[in] InData Pointer to octect message hash to be checked. + @param[in] InDataSize Size of the message hash in bytes. + @param[in] Singnature The pointer to the RSA PKCS1-V1_5 signature to be verifed. + @param[in] SigSize Size of signature in bytes. + + @retval TRUE Valid signature encoded in PKCS1-v1_5. + @retval FALSE Invalid signature or invalid RSA context. + +**/ +BOOLEAN +IpSecCryptoIoVerifySignDataByCertificate ( + IN UINT8 *InCert, + IN UINTN CertLen, + IN UINT8 *InCa, + IN UINTN CaLen, + IN UINT8 *InData, + IN UINTN InDataSize, + IN UINT8 *Singnature, + IN UINTN SigSize + ); + +/** + Retrieves the RSA Public Key from one X509 certificate (DER format only). + + @param[in] InCert Pointer to the certificate. + @param[in] CertLen The size of the certificate in bytes. + @param[out] PublicKey Pointer to the retrieved public key. + @param[out] PublicKeyLen Size of Public Key in bytes. + + @retval EFI_SUCCESS Successfully get the public Key. + @retval EFI_INVALID_PARAMETER The CA certificate is malformed. + +**/ +EFI_STATUS +IpSecCryptoIoGetPublicKeyFromCert ( + IN UINT8 *InCert, + IN UINTN CertLen, + OUT UINT8 **PublicKey, + OUT UINTN *PublicKeyLen + ); + +/** + Retrieves the subject name from one X509 certificate (DER format only). + + @param[in] InCert Pointer to the X509 certificate. + @param[in] CertSize The size of the X509 certificate in bytes. + @param[out] CertSubject Pointer to the retrieved certificate subject. + @param[out] SubjectSize The size of Certificate Subject in bytes. + + @retval EFI_SUCCESS Retrieved the certificate subject successfully. + @retval EFI_INVALID_PARAMETER The certificate is malformed. + +**/ +EFI_STATUS +IpSecCryptoIoGetSubjectFromCert ( + IN UINT8 *InCert, + IN UINTN CertSize, + OUT UINT8 **CertSubject, + OUT UINTN *SubjectSize + ); + +#endif + diff --git a/Core/NetworkPkg/IpSecDxe/IpSecDebug.c b/Core/NetworkPkg/IpSecDxe/IpSecDebug.c new file mode 100644 index 0000000000..636e775969 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecDebug.c @@ -0,0 +1,334 @@ +/** @file + The Interfaces of IPsec debug information printing. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecImpl.h" +#include "IpSecDebug.h" + +// +// The print title for IKEv1 variety phase. +// +CHAR8 *mIkev1StateStr[IKE_STATE_NUM] = { + "IKEv1_MAIN_1", + "IKEv1_MAIN_2", + "IKEv1_MAIN_3", + "IKEv1_MAIN_ESTABLISHED", + "IKEv1_QUICK_1", + "IKEv1_QUICK_2", + "IKEv1_QUICK_ESTABLISHED" +}; + +// +// The print title for IKEv2 variety phase. +// +CHAR8 *mIkev2StateStr[IKE_STATE_NUM] = { + "IKEv2_STATE_INIT", + "IKEv2_STATE_AUTH", + "IKEv2_STATE_SA_ESTABLISH", + "IKEv2_STATE_CREATE_CHILD", + "IKEv2_STATE_SA_REKEYING", + "IKEv2_STATE_CHILD_SA_ESTABLISHED", + "IKEv2_STATE_SA_DELETING" +}; + +// +// The print title for IKEv1 variety Exchagne. +// +CHAR8 *mExchangeStr[] = { + "IKEv1 Main Exchange", + "IKEv1 Info Exchange", + "IKEv1 Quick Exchange", + "IKEv2 Initial Exchange", + "IKEv2 Auth Exchange", + "IKEv2 Create Child Exchange", + "IKEv2 Info Exchange", + "IKE Unknow Exchange" +}; + +// +// The print title for IKEv1 variety Payload. +// +CHAR8 *mIkev1PayloadStr[] = { + "IKEv1 None Payload", + "IKEv1 SA Payload", + "IKEv1 Proposal Payload", + "IKEv1 Transform Payload", + "IKEv1 KE Payload", + "IKEv1 ID Payload", + "IKEv1 Certificate Payload", + "IKEv1 Certificate Request Payload", + "IKEv1 Hash Payload", + "IKEv1 Signature Payload", + "IKEv1 Nonce Payload", + "IKEv1 Notify Payload", + "IKEv1 Delete Payload", + "IKEv1 Vendor Payload" +}; + +// +// The print title for IKEv2 variety Payload. +// +CHAR8* mIkev2PayloadStr[] = { + "IKEv2 SA Payload", + "IKEv2 Key Payload", + "IKEv2 Identity Initial Payload", + "IKEv2 Identity Respond Payload", + "IKEv2 Certificate Payload", + "IKEv2 Certificate Request Payload", + "IKEv2 Auth Payload", + "IKEv2 Nonce Payload", + "IKEv2 Notify Payload", + "IKEv2 Delet Payload", + "IKEv2 Vendor Payload", + "IKEv2 Traffic Selector Initiator Payload", + "IKEv2 Traffic Selector Respond Payload", + "IKEv2 Encrypt Payload", + "IKEv2 Configuration Payload", + "IKEv2 Extensible Authentication Payload" +}; + +/** + Print the IP address. + + @param[in] Level Debug print error level. Pass to DEBUG(). + @param[in] Ip Point to a specified IP address. + @param[in] IpVersion The IP Version. + +**/ +VOID +IpSecDumpAddress ( + IN UINTN Level, + IN EFI_IP_ADDRESS *Ip, + IN UINT8 IpVersion + ) +{ + if (IpVersion == IP_VERSION_6) { + DEBUG ( + (Level, + "%x%x:%x%x:%x%x:%x%x", + Ip->v6.Addr[0], + Ip->v6.Addr[1], + Ip->v6.Addr[2], + Ip->v6.Addr[3], + Ip->v6.Addr[4], + Ip->v6.Addr[5], + Ip->v6.Addr[6], + Ip->v6.Addr[7]) + ); + DEBUG ( + (Level, + ":%x%x:%x%x:%x%x:%x%x\n", + Ip->v6.Addr[8], + Ip->v6.Addr[9], + Ip->v6.Addr[10], + Ip->v6.Addr[11], + Ip->v6.Addr[12], + Ip->v6.Addr[13], + Ip->v6.Addr[14], + Ip->v6.Addr[15]) + ); + } else { + DEBUG ( + (Level, + "%d.%d.%d.%d\n", + Ip->v4.Addr[0], + Ip->v4.Addr[1], + Ip->v4.Addr[2], + Ip->v4.Addr[3]) + ); + } + +} + +/** + Print IKE Current states. + + @param[in] Previous The Previous state of IKE. + @param[in] Current The current state of IKE. + @param[in] IkeVersion The version of IKE. + +**/ +VOID +IkeDumpState ( + IN UINT32 Previous, + IN UINT32 Current, + IN UINT8 IkeVersion + ) +{ + if (Previous >= IKE_STATE_NUM || Current >= IKE_STATE_NUM) { + return; + } + + if (Previous == Current) { + if (IkeVersion == 1) { + DEBUG ((DEBUG_INFO, "\n****Current state is %a\n", mIkev1StateStr[Previous])); + } else if (IkeVersion == 2) { + DEBUG ((DEBUG_INFO, "\n****Current state is %a\n", mIkev2StateStr[Previous])); + } + } else { + if (IkeVersion == 1) { + DEBUG ((DEBUG_INFO, "\n****Change state from %a to %a\n", mIkev1StateStr[Previous], mIkev1StateStr[Current])); + } else { + DEBUG ((DEBUG_INFO, "\n****Change state from %a to %a\n", mIkev2StateStr[Previous], mIkev2StateStr[Current])); + } + } +} + +/** + Print the IKE Packet. + + @param[in] Packet Point to IKE packet to be printed. + @param[in] Direction Point to the IKE packet is inbound or outbound. + @param[in] IpVersion Specified IP Version. + +**/ +VOID +IpSecDumpPacket ( + IN IKE_PACKET *Packet, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN UINT8 IpVersion + ) +{ + CHAR8 *TypeStr; + UINTN PacketSize; + UINT64 InitCookie; + UINT64 RespCookie; + + ASSERT (Packet != NULL); + + PacketSize = Packet->PayloadTotalSize + sizeof (IKE_HEADER); + InitCookie = (Direction == EfiIPsecOutBound) ? HTONLL (Packet->Header->InitiatorCookie) : Packet->Header->InitiatorCookie; + RespCookie = (Direction == EfiIPsecOutBound) ? HTONLL (Packet->Header->ResponderCookie) : Packet->Header->ResponderCookie; + + switch (Packet->Header->ExchangeType) { + case IKE_XCG_TYPE_IDENTITY_PROTECT: + TypeStr = mExchangeStr[0]; + break; + + case IKE_XCG_TYPE_INFO: + TypeStr = mExchangeStr[1]; + break; + + case IKE_XCG_TYPE_QM: + TypeStr = mExchangeStr[2]; + break; + + case IKE_XCG_TYPE_SA_INIT: + TypeStr = mExchangeStr[3]; + break; + + case IKE_XCG_TYPE_AUTH: + TypeStr = mExchangeStr[4]; + break; + + case IKE_XCG_TYPE_CREATE_CHILD_SA: + TypeStr = mExchangeStr[5]; + break; + + case IKE_XCG_TYPE_INFO2: + TypeStr = mExchangeStr[6]; + break; + + default: + TypeStr = mExchangeStr[7]; + break; + } + + if (Direction == EfiIPsecOutBound) { + DEBUG ((DEBUG_INFO, "\n>>>Sending %d bytes %a to ", PacketSize, TypeStr)); + } else { + DEBUG ((DEBUG_INFO, "\n>>>Receiving %d bytes %a from ", PacketSize, TypeStr)); + } + + IpSecDumpAddress (DEBUG_INFO, &Packet->RemotePeerIp, IpVersion); + + DEBUG ((DEBUG_INFO, " InitiatorCookie:0x%lx ResponderCookie:0x%lx\n", InitCookie, RespCookie)); + DEBUG ( + (DEBUG_INFO, + " Version: 0x%x Flags:0x%x ExchangeType:0x%x\n", + Packet->Header->Version, + Packet->Header->Flags, + Packet->Header->ExchangeType) + ); + DEBUG ( + (DEBUG_INFO, + " MessageId:0x%x NextPayload:0x%x\n", + Packet->Header->MessageId, + Packet->Header->NextPayload) + ); + +} + +/** + Print the IKE Paylolad. + + @param[in] IkePayload Point to payload to be printed. + @param[in] IkeVersion The specified version of IKE. + +**/ +VOID +IpSecDumpPayload ( + IN IKE_PAYLOAD *IkePayload, + IN UINT8 IkeVersion + ) +{ + if (IkeVersion == 1) { + DEBUG ((DEBUG_INFO, "+%a\n", mIkev1PayloadStr[IkePayload->PayloadType])); + } else { + // + // For IKEV2 the first Payload type is started from 33. + // + DEBUG ((DEBUG_INFO, "+%a\n", mIkev2PayloadStr[IkePayload->PayloadType - 33])); + } + IpSecDumpBuf ("Payload data", IkePayload->PayloadBuf, IkePayload->PayloadSize); +} + +/** + Print the buffer in form of Hex. + + @param[in] Title The strings to be printed before the data of the buffer. + @param[in] Data Points to buffer to be printed. + @param[in] DataSize The size of the buffer to be printed. + +**/ +VOID +IpSecDumpBuf ( + IN CHAR8 *Title, + IN UINT8 *Data, + IN UINTN DataSize + ) +{ + UINTN Index; + UINTN DataIndex; + UINTN BytesRemaining; + UINTN BytesToPrint; + + DataIndex = 0; + BytesRemaining = DataSize; + + DEBUG ((DEBUG_INFO, "==%a %d bytes==\n", Title, DataSize)); + + while (BytesRemaining > 0) { + + BytesToPrint = (BytesRemaining > IPSEC_DEBUG_BYTE_PER_LINE) ? IPSEC_DEBUG_BYTE_PER_LINE : BytesRemaining; + + for (Index = 0; Index < BytesToPrint; Index++) { + DEBUG ((DEBUG_INFO, " 0x%02x,", Data[DataIndex++])); + } + + DEBUG ((DEBUG_INFO, "\n")); + BytesRemaining -= BytesToPrint; + } + +} diff --git a/Core/NetworkPkg/IpSecDxe/IpSecDebug.h b/Core/NetworkPkg/IpSecDxe/IpSecDebug.h new file mode 100644 index 0000000000..16bbcd53c6 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecDebug.h @@ -0,0 +1,107 @@ +/** @file + The definition of functions and MACROs used for IPsec debug information printting. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#ifndef _EFI_IPSEC_DEBUG_H_ +#define _EFI_IPSEC_DEBUG_H_ + +#include "IkeCommon.h" +#include "IkePacket.h" + +#define IPSEC_DUMP_ADDRESS(Level, Ip, Version) IpSecDumpAddress (Level, Ip, Version) +#define IKEV1_DUMP_STATE(Previous, Current) IkeDumpState (Previous, Current, 1) +#define IKEV2_DUMP_STATE(Previous, Current) IkeDumpState (Previous, Current, 2) +#define IPSEC_DUMP_PACKET(Packet, Direction, IpVersion) IpSecDumpPacket (Packet, Direction, IpVersion) +#define IPSEC_DUMP_PAYLOAD(IkePayload) IpSecDumpPayload (IkePayload, 1) +#define IKEV2_DUMP_PAYLOAD(IkePayload) IpSecDumpPayload (IkePayload, 2) +#define IPSEC_DUMP_BUF(Title, Data, DataSize) IpSecDumpBuf (Title, Data, DataSize) + +#define IPSEC_DEBUG_BYTE_PER_LINE 8 +#define IKE_STATE_NUM 7 + + + +/** + Print the IP address. + + @param[in] Level Debug print error level. Pass to DEBUG(). + @param[in] Ip Point to specified IP address. + @param[in] IpVersion The IP Version. + +**/ +VOID +IpSecDumpAddress ( + IN UINTN Level, + IN EFI_IP_ADDRESS *Ip, + IN UINT8 IpVersion + ); + +/** + Print IKE Current states. + + @param[in] Previous The Previous state of IKE. + @param[in] Current The current state of IKE. + @param[in] IkeVersion The version of IKE. + +**/ +VOID +IkeDumpState ( + IN UINT32 Previous, + IN UINT32 Current, + IN UINT8 IkeVersion + ); + +/** + Print the IKE Packet. + + @param[in] Packet Point to IKE packet to be printed. + @param[in] Direction Point to the IKE packet is inbound or outbound. + @param[in] IpVersion Specified IP Version. + +**/ +VOID +IpSecDumpPacket ( + IN IKE_PACKET *Packet, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN UINT8 IpVersion + ); + +/** + Print the IKE Paylolad. + + @param[in] IkePayload Point to payload to be printed. + @param[in] IkeVersion The specified version of IKE. + +**/ +VOID +IpSecDumpPayload ( + IN IKE_PAYLOAD *IkePayload, + IN UINT8 IkeVersion + ); + +/** + Print the buffer in form of Hex. + + @param[in] Title The strings to be printed before the data of the buffer. + @param[in] Data Point to buffer to be printed. + @param[in] DataSize The size of the buffer to be printed. + +**/ +VOID +IpSecDumpBuf ( + IN CHAR8 *Title, + IN UINT8 *Data, + IN UINTN DataSize + ); + +#endif diff --git a/Core/NetworkPkg/IpSecDxe/IpSecDriver.c b/Core/NetworkPkg/IpSecDxe/IpSecDriver.c new file mode 100644 index 0000000000..d8282b5e2f --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecDriver.c @@ -0,0 +1,665 @@ +/** @file + Driver Binding Protocol for IPsec Driver. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include "IpSecConfigImpl.h" +#include "IkeService.h" +#include "IpSecDebug.h" + +/** + Test to see if this driver supports ControllerHandle. This is the worker function + for IpSec4(6)DriverbindingSupported. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCES This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +IpSecSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *UdpServiceBindingGuid; + + if (IpVersion == IP_VERSION_4) { + UdpServiceBindingGuid = &gEfiUdp4ServiceBindingProtocolGuid; + } else { + UdpServiceBindingGuid = &gEfiUdp6ServiceBindingProtocolGuid; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + UdpServiceBindingGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + return EFI_SUCCESS; +} + +/** + Start this driver on ControllerHandle. This is the worker function + for IpSec4(6)DriverbindingStart. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCES This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval EFI_DEVICE_ERROR The device could not be started due to a device error. + Currently not implemented. + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +IpSecStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_IPSEC2_PROTOCOL *IpSec; + EFI_STATUS Status; + IPSEC_PRIVATE_DATA *Private; + + // + // Ipsec protocol should be installed when load image. + // + Status = gBS->LocateProtocol (&gEfiIpSec2ProtocolGuid, NULL, (VOID **) &IpSec); + + if (EFI_ERROR (Status)) { + return Status; + } + + Private = IPSEC_PRIVATE_DATA_FROM_IPSEC (IpSec); + + if (IpVersion == IP_VERSION_4) { + // + // Try to open a udp4 io for input. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + Status = IkeOpenInputUdp4 (Private, ControllerHandle, This->DriverBindingHandle); + } + } else { + // + // Try to open a udp6 io for input. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + Status = IkeOpenInputUdp6 (Private, ControllerHandle, This->DriverBindingHandle); + } + } + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; +} + +/** + Stop this driver on ControllerHandle. This is the worker function + for IpSec4(6)DriverbindingStop. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of a device to stop the driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCES This driver removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +IpSecStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer, + IN UINT8 IpVersion + ) +{ + EFI_IPSEC2_PROTOCOL *IpSec; + EFI_STATUS Status; + IPSEC_PRIVATE_DATA *Private; + IKE_UDP_SERVICE *UdpSrv; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IKEV2_SA_SESSION *Ikev2SaSession; + + // + // Locate ipsec protocol to get private data. + // + Status = gBS->LocateProtocol (&gEfiIpSec2ProtocolGuid, NULL, (VOID **) &IpSec); + + if (EFI_ERROR (Status)) { + return Status; + } + + Private = IPSEC_PRIVATE_DATA_FROM_IPSEC (IpSec); + + // + // The SAs are shared by both IP4 and IP6 stack. So we skip the cleanup + // and leave the SAs unchanged if the other IP stack is still running. + // + if ((IpVersion == IP_VERSION_4 && Private->Udp6Num ==0) || + (IpVersion == IP_VERSION_6 && Private->Udp4Num ==0)) { + // + // If IKEv2 SAs are under establishing, delete it directly. + // + if (!IsListEmpty (&Private->Ikev2SessionList)) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Private->Ikev2SessionList) { + Ikev2SaSession = IKEV2_SA_SESSION_BY_SESSION (Entry); + RemoveEntryList (&Ikev2SaSession->BySessionTable); + Ikev2SaSessionFree (Ikev2SaSession); + } + } + + // + // Delete established IKEv2 SAs. + // + if (!IsListEmpty (&Private->Ikev2EstablishedList)) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Private->Ikev2EstablishedList) { + Ikev2SaSession = IKEV2_SA_SESSION_BY_SESSION (Entry); + RemoveEntryList (&Ikev2SaSession->BySessionTable); + Ikev2SaSessionFree (Ikev2SaSession); + } + } + } + + if (IpVersion == IP_VERSION_4) { + // + // If has udp4 io opened on the controller, close and free it. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Private->Udp4List) { + + UdpSrv = IPSEC_UDP_SERVICE_FROM_LIST (Entry); + // + // Find the right udp service which installed on the appointed nic handle. + // + if (UdpSrv->Input != NULL && ControllerHandle == UdpSrv->Input->UdpHandle) { + UdpIoFreeIo (UdpSrv->Input); + UdpSrv->Input = NULL; + } + + if (UdpSrv->Output != NULL && ControllerHandle == UdpSrv->Output->UdpHandle) { + UdpIoFreeIo (UdpSrv->Output); + UdpSrv->Output = NULL; + } + + if (UdpSrv->Input == NULL && UdpSrv->Output == NULL) { + RemoveEntryList (&UdpSrv->List); + FreePool (UdpSrv); + ASSERT (Private->Udp4Num > 0); + Private->Udp4Num--; + } + } + } else { + // + // If has udp6 io opened on the controller, close and free it. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Private->Udp6List) { + + UdpSrv = IPSEC_UDP_SERVICE_FROM_LIST (Entry); + // + // Find the right udp service which installed on the appointed nic handle. + // + if (UdpSrv->Input != NULL && ControllerHandle == UdpSrv->Input->UdpHandle) { + UdpIoFreeIo (UdpSrv->Input); + UdpSrv->Input = NULL; + } + + if (UdpSrv->Output != NULL && ControllerHandle == UdpSrv->Output->UdpHandle) { + UdpIoFreeIo (UdpSrv->Output); + UdpSrv->Output = NULL; + } + + if (UdpSrv->Input == NULL && UdpSrv->Output == NULL) { + RemoveEntryList (&UdpSrv->List); + FreePool (UdpSrv); + ASSERT (Private->Udp6Num > 0); + Private->Udp6Num--; + } + } + } + + return EFI_SUCCESS; +} + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +IpSec4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return IpSecSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval EFI_DEVICE_ERROR The device could not be started due to a device error. + Currently not implemented. + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +IpSec4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return IpSecStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Stop this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of a device to stop the driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCES This driver removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +IpSec4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return IpSecStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_4 + ); +} + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +IpSec6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return IpSecSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); +} + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval EFI_DEVICE_ERROR The device could not be started due to a device error. + Currently not implemented. + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +IpSec6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return IpSecStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); +} + +/** + Stop this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of a device to stop the driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCES This driver removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +IpSec6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return IpSecStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_6 + ); +} + +EFI_DRIVER_BINDING_PROTOCOL gIpSec4DriverBinding = { + IpSec4DriverBindingSupported, + IpSec4DriverBindingStart, + IpSec4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gIpSec6DriverBinding = { + IpSec6DriverBindingSupported, + IpSec6DriverBindingStart, + IpSec6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +/** + This is a callback function when the mIpSecInstance.DisabledEvent is signaled. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +IpSecCleanupAllSa ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IPSEC_PRIVATE_DATA *Private; + Private = (IPSEC_PRIVATE_DATA *) Context; + Private->IsIPsecDisabling = TRUE; + IkeDeleteAllSas (Private, TRUE); +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for IPsec driver which installs the driver binding, + component name protocol, IPsec Config protcolon, and IPsec protocol in + its ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_ALREADY_STARTED The IPsec driver has been already loaded. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The operation is failed. + +**/ +EFI_STATUS +EFIAPI +IpSecDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + IPSEC_PRIVATE_DATA *Private; + EFI_IPSEC2_PROTOCOL *IpSec; + + // + // Check whether ipsec protocol has already been installed. + // + Status = gBS->LocateProtocol (&gEfiIpSec2ProtocolGuid, NULL, (VOID **) &IpSec); + + if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "_ModuleEntryPoint: IpSec has been already loaded\n")); + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + } + + Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, (VOID **) &mDpc); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to locate EfiDpcProtocol\n")); + goto ON_EXIT; + } + + Private = AllocateZeroPool (sizeof (IPSEC_PRIVATE_DATA)); + + if (Private == NULL) { + DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to allocate private data\n")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + // + // Create disable event to cleanup all SA when ipsec disabled by user. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + IpSecCleanupAllSa, + Private, + &mIpSecInstance.DisabledEvent + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to create disable event\n")); + goto ON_FREE_PRIVATE; + } + + Private->Signature = IPSEC_PRIVATE_DATA_SIGNATURE; + Private->ImageHandle = ImageHandle; + CopyMem (&Private->IpSec, &mIpSecInstance, sizeof (EFI_IPSEC2_PROTOCOL)); + + // + // Initilize Private's members. Thess members is used for IKE. + // + InitializeListHead (&Private->Udp4List); + InitializeListHead (&Private->Udp6List); + InitializeListHead (&Private->Ikev1SessionList); + InitializeListHead (&Private->Ikev1EstablishedList); + InitializeListHead (&Private->Ikev2SessionList); + InitializeListHead (&Private->Ikev2EstablishedList); + + RandomSeed (NULL, 0); + // + // Initialize the ipsec config data and restore it from variable. + // + Status = IpSecConfigInitialize (Private); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "_ModuleEntryPoint: Failed to initialize IpSecConfig\n")); + goto ON_CLOSE_EVENT; + } + // + // Install ipsec protocol which is used by ip driver to process ipsec header. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiIpSec2ProtocolGuid, + &Private->IpSec, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_UNINSTALL_CONFIG; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gIpSec4DriverBinding, + ImageHandle, + &gIpSecComponentName, + &gIpSecComponentName2 + ); + if (EFI_ERROR (Status)) { + goto ON_UNINSTALL_IPSEC; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gIpSec6DriverBinding, + NULL, + &gIpSecComponentName, + &gIpSecComponentName2 + ); + if (EFI_ERROR (Status)) { + goto ON_UNINSTALL_IPSEC4_DB; + } + + return Status; + +ON_UNINSTALL_IPSEC4_DB: + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gIpSec4DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gIpSecComponentName2, + &gEfiComponentNameProtocolGuid, + &gIpSecComponentName, + NULL + ); + +ON_UNINSTALL_IPSEC: + gBS->UninstallProtocolInterface ( + Private->Handle, + &gEfiIpSec2ProtocolGuid, + &Private->IpSec + ); +ON_UNINSTALL_CONFIG: + gBS->UninstallProtocolInterface ( + Private->Handle, + &gEfiIpSecConfigProtocolGuid, + &Private->IpSecConfig + ); +ON_CLOSE_EVENT: + gBS->CloseEvent (mIpSecInstance.DisabledEvent); + mIpSecInstance.DisabledEvent = NULL; +ON_FREE_PRIVATE: + FreePool (Private); +ON_EXIT: + return Status; +} + diff --git a/Core/NetworkPkg/IpSecDxe/IpSecDxe.inf b/Core/NetworkPkg/IpSecDxe/IpSecDxe.inf new file mode 100644 index 0000000000..583305b4f8 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecDxe.inf @@ -0,0 +1,110 @@ +## @file +# Packet-level security for IP datagram. +# +# This driver provides EFI IPsec2 Protocol which is used to abstract the ability +# to deal with the individual packets sent and received by the host and provide +# packet-level security for IP datagram. It provides the IP packet protection via +# ESP and it supports IKEv2 for key negotiation. +# +# Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = IpSecDxe + FILE_GUID = EE8367C0-A1D6-4565-8F89-EF628547B722 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = IpSecDriverEntryPoint + MODULE_UNI_FILE = IpSecDxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + IpSecConfigImpl.c + IpSecConfigImpl.h + IpSecCryptIo.h + IpSecCryptIo.c + IpSecDebug.h + ComponentName.c + IkeCommon.h + IpSecImpl.c + IkeService.c + Ike.h + IkePacket.h + IkePacket.c + IpSecDebug.c + IpSecMain.c + IpSecDriver.c + IkeCommon.c + IetfConstants.c + IpSecImpl.h + IkeService.h + Ikev2/Ikev2.h + Ikev2/Payload.h + Ikev2/Utility.h + Ikev2/Utility.c + Ikev2/Sa.c + Ikev2/ChildSa.c + Ikev2/Info.c + Ikev2/Payload.c + Ikev2/Exchange.c + + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + NetworkPkg/NetworkPkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + DebugLib + PrintLib + BaseCryptLib + DpcLib + UdpIoLib + NetLib + PcdLib + +[Protocols] + gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiUdp4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiUdp4ProtocolGuid ## SOMETIMES_CONSUMES + gEfiUdp6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiUdp6ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIpSecConfigProtocolGuid ## PRODUCES + gEfiIpSec2ProtocolGuid ## PRODUCES + +[Pcd] + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecCertificateEnabled ## SOMETIMES_CONSUMES + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCaFile ## SOMETIMES_CONSUMES + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCaFileSize ## SOMETIMES_CONSUMES + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCertificate ## SOMETIMES_CONSUMES + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCertificateSize ## SOMETIMES_CONSUMES + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCertificateKey ## SOMETIMES_CONSUMES + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCertificateKeySize ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + IpSecDxeExtra.uni diff --git a/Core/NetworkPkg/IpSecDxe/IpSecDxe.uni b/Core/NetworkPkg/IpSecDxe/IpSecDxe.uni new file mode 100644 index 0000000000..b3e5673d6f Binary files /dev/null and b/Core/NetworkPkg/IpSecDxe/IpSecDxe.uni differ diff --git a/Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni b/Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni new file mode 100644 index 0000000000..864b7f8f97 Binary files /dev/null and b/Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni differ diff --git a/Core/NetworkPkg/IpSecDxe/IpSecImpl.c b/Core/NetworkPkg/IpSecDxe/IpSecImpl.c new file mode 100644 index 0000000000..00f8ed1cb6 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecImpl.c @@ -0,0 +1,2184 @@ +/** @file + The implementation of IPsec. + + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecImpl.h" +#include "IkeService.h" +#include "IpSecDebug.h" +#include "IpSecCryptIo.h" +#include "IpSecConfigImpl.h" + +/** + Check if the specified Address is the Valid Address Range. + + This function checks if the bytes after prefixed length are all Zero in this + Address. This Address is supposed to point to a range address. That means it + should gives the correct prefixed address and the bytes outside the prefixed are + zero. + + @param[in] IpVersion The IP version. + @param[in] Address Points to EFI_IP_ADDRESS to be checked. + @param[in] PrefixLength The PrefixeLength of this address. + + @retval TRUE The address is a vaild address range. + @retval FALSE The address is not a vaild address range. + +**/ +BOOLEAN +IpSecValidAddressRange ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *Address, + IN UINT8 PrefixLength + ) +{ + UINT8 Div; + UINT8 Mod; + UINT8 Mask; + UINT8 AddrLen; + UINT8 *Addr; + EFI_IP_ADDRESS ZeroAddr; + + if (PrefixLength == 0) { + return TRUE; + } + + AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128); + + if (AddrLen <= PrefixLength) { + return FALSE; + } + + Div = (UINT8) (PrefixLength / 8); + Mod = (UINT8) (PrefixLength % 8); + Addr = (UINT8 *) Address; + ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS)); + + // + // Check whether the mod part of host scope is zero or not. + // + if (Mod > 0) { + Mask = (UINT8) (0xFF << (8 - Mod)); + + if ((Addr[Div] | Mask) != Mask) { + return FALSE; + } + + Div++; + } + // + // Check whether the div part of host scope is zero or not. + // + if (CompareMem ( + &Addr[Div], + &ZeroAddr, + sizeof (EFI_IP_ADDRESS) - Div + ) != 0) { + return FALSE; + } + + return TRUE; +} + +/** + Extrct the Address Range from a Address. + + This function keep the prefix address and zero other part address. + + @param[in] Address Point to a specified address. + @param[in] PrefixLength The prefix length. + @param[out] Range Contain the return Address Range. + +**/ +VOID +IpSecExtractAddressRange ( + IN EFI_IP_ADDRESS *Address, + IN UINT8 PrefixLength, + OUT EFI_IP_ADDRESS *Range + ) +{ + UINT8 Div; + UINT8 Mod; + UINT8 Mask; + UINT8 *Addr; + + if (PrefixLength == 0) { + return ; + } + + Div = (UINT8) (PrefixLength / 8); + Mod = (UINT8) (PrefixLength % 8); + Addr = (UINT8 *) Range; + + CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS)); + + // + // Zero the mod part of host scope. + // + if (Mod > 0) { + Mask = (UINT8) (0xFF << (8 - Mod)); + Addr[Div] = (UINT8) (Addr[Div] & Mask); + Div++; + } + // + // Zero the div part of host scope. + // + ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div); + +} + +/** + Checks if the IP Address in the address range of AddressInfos specified. + + @param[in] IpVersion The IP version. + @param[in] IpAddr Point to EFI_IP_ADDRESS to be check. + @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check + the IP Address is matched. + @param[in] AddressCount The total numbers of the AddressInfo. + + @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified. + @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified. + +**/ +BOOLEAN +IpSecMatchIpAddress ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *IpAddr, + IN EFI_IP_ADDRESS_INFO *AddressInfo, + IN UINT32 AddressCount + ) +{ + EFI_IP_ADDRESS Range; + UINT32 Index; + BOOLEAN IsMatch; + + IsMatch = FALSE; + + for (Index = 0; Index < AddressCount; Index++) { + // + // Check whether the target address is in the address range + // if it's a valid range of address. + // + if (IpSecValidAddressRange ( + IpVersion, + &AddressInfo[Index].Address, + AddressInfo[Index].PrefixLength + )) { + // + // Get the range of the target address belongs to. + // + ZeroMem (&Range, sizeof (EFI_IP_ADDRESS)); + IpSecExtractAddressRange ( + IpAddr, + AddressInfo[Index].PrefixLength, + &Range + ); + + if (CompareMem ( + &Range, + &AddressInfo[Index].Address, + sizeof (EFI_IP_ADDRESS) + ) == 0) { + // + // The target address is in the address range. + // + IsMatch = TRUE; + break; + } + } + + if (CompareMem ( + IpAddr, + &AddressInfo[Index].Address, + sizeof (EFI_IP_ADDRESS) + ) == 0) { + // + // The target address is exact same as the address. + // + IsMatch = TRUE; + break; + } + } + return IsMatch; +} + +/** + Check if the specified Protocol and Prot is supported by the specified SPD Entry. + + This function is the subfunction of IPsecLookUpSpdEntry() that is used to + check if the sent/received IKE packet has the related SPD entry support. + + @param[in] Protocol The Protocol to be checked. + @param[in] IpPayload Point to IP Payload to be check. + @param[in] SpdProtocol The Protocol supported by SPD. + @param[in] SpdLocalPort The Local Port in SPD. + @param[in] SpdRemotePort The Remote Port in SPD. + @param[in] IsOutbound Flag to indicate the is for IKE Packet sending or recieving. + + @retval TRUE The Protocol and Port are supported by the SPD Entry. + @retval FALSE The Protocol and Port are not supported by the SPD Entry. + +**/ +BOOLEAN +IpSecMatchNextLayerProtocol ( + IN UINT8 Protocol, + IN UINT8 *IpPayload, + IN UINT16 SpdProtocol, + IN UINT16 SpdLocalPort, + IN UINT16 SpdRemotePort, + IN BOOLEAN IsOutbound + ) +{ + BOOLEAN IsMatch; + + if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) { + return TRUE; + } + + IsMatch = FALSE; + + if (SpdProtocol == Protocol) { + switch (Protocol) { + case EFI_IP_PROTO_UDP: + case EFI_IP_PROTO_TCP: + // + // For udp and tcp, (0, 0) means no need to check local and remote + // port. The payload is passed from upper level, which means it should + // be in network order. + // + IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0); + IsMatch = (BOOLEAN) (IsMatch || + (IsOutbound && + (BOOLEAN)( + NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort && + NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort + ) + )); + + IsMatch = (BOOLEAN) (IsMatch || + (!IsOutbound && + (BOOLEAN)( + NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort && + NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort + ) + )); + break; + + case EFI_IP_PROTO_ICMP: + // + // For icmpv4, type code is replaced with local port and remote port, + // and (0, 0) means no need to check. + // + IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0); + IsMatch = (BOOLEAN) (IsMatch || + (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort && + ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort + ) + ); + break; + + case IP6_ICMP: + // + // For icmpv6, type code is replaced with local port and remote port, + // and (0, 0) means no need to check. + // + IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0); + + IsMatch = (BOOLEAN) (IsMatch || + (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort && + ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort + ) + ); + break; + + default: + IsMatch = TRUE; + break; + } + } + + return IsMatch; +} + +/** + Find the SAD through a specified SPD's SAD list. + + @param[in] SadList SAD list related to a specified SPD entry. + @param[in] DestAddress The destination address used to find the SAD entry. + @param[in] IpVersion The IP version. Ip4 or Ip6. + + @return The pointer to a certain SAD entry. + +**/ +IPSEC_SAD_ENTRY * +IpSecLookupSadBySpd ( + IN LIST_ENTRY *SadList, + IN EFI_IP_ADDRESS *DestAddress, + IN UINT8 IpVersion + ) +{ + LIST_ENTRY *Entry; + IPSEC_SAD_ENTRY *SadEntry; + + NET_LIST_FOR_EACH (Entry, SadList) { + + SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry); + // + // Find the right SAD entry which contains the appointed dest address. + // + if (IpSecMatchIpAddress ( + IpVersion, + DestAddress, + SadEntry->Data->SpdSelector->RemoteAddress, + SadEntry->Data->SpdSelector->RemoteAddressCount + )){ + return SadEntry; + } + } + + return NULL; +} + +/** + Find the SAD through whole SAD list. + + @param[in] Spi The SPI used to search the SAD entry. + @param[in] DestAddress The destination used to search the SAD entry. + @param[in] IpVersion The IP version. Ip4 or Ip6. + + @return the pointer to a certain SAD entry. + +**/ +IPSEC_SAD_ENTRY * +IpSecLookupSadBySpi ( + IN UINT32 Spi, + IN EFI_IP_ADDRESS *DestAddress, + IN UINT8 IpVersion + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *SadList; + IPSEC_SAD_ENTRY *SadEntry; + + SadList = &mConfigData[IPsecConfigDataTypeSad]; + + NET_LIST_FOR_EACH (Entry, SadList) { + + SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry); + + // + // Find the right SAD entry which contain the appointed spi and dest addr. + // + if (SadEntry->Id->Spi == Spi) { + if (SadEntry->Data->Mode == EfiIPsecTunnel) { + if (CompareMem ( + &DestAddress, + &SadEntry->Data->TunnelDestAddress, + sizeof (EFI_IP_ADDRESS) + )) { + return SadEntry; + } + } else { + if (SadEntry->Data->SpdSelector != NULL && + IpSecMatchIpAddress ( + IpVersion, + DestAddress, + SadEntry->Data->SpdSelector->RemoteAddress, + SadEntry->Data->SpdSelector->RemoteAddressCount + ) + ) { + return SadEntry; + } + } + } + } + return NULL; +} + +/** + Look up if there is existing SAD entry for specified IP packet sending. + + This function is called by the IPsecProcess when there is some IP packet needed to + send out. This function checks if there is an existing SAD entry that can be serviced + to this IP packet sending. If no existing SAD entry could be used, this + function will invoke an IPsec Key Exchange Negotiation. + + @param[in] Private Points to private data. + @param[in] NicHandle Points to a NIC handle. + @param[in] IpVersion The version of IP. + @param[in] IpHead The IP Header of packet to be sent out. + @param[in] IpPayload The IP Payload to be sent out. + @param[in] OldLastHead The Last protocol of the IP packet. + @param[in] SpdEntry Points to a related SPD entry. + @param[out] SadEntry Contains the Point of a related SAD entry. + + @retval EFI_DEVICE_ERROR One of following conditions is TRUE: + - If don't find related UDP service. + - Sequence Number is used up. + - Extension Sequence Number is used up. + @retval EFI_NOT_READY No existing SAD entry could be used. + @retval EFI_SUCCESS Find the related SAD entry. + +**/ +EFI_STATUS +IpSecLookupSadEntry ( + IN IPSEC_PRIVATE_DATA *Private, + IN EFI_HANDLE NicHandle, + IN UINT8 IpVersion, + IN VOID *IpHead, + IN UINT8 *IpPayload, + IN UINT8 OldLastHead, + IN IPSEC_SPD_ENTRY *SpdEntry, + OUT IPSEC_SAD_ENTRY **SadEntry + ) +{ + IKE_UDP_SERVICE *UdpService; + IPSEC_SAD_ENTRY *Entry; + IPSEC_SAD_DATA *Data; + EFI_IP_ADDRESS DestIp; + UINT32 SeqNum32; + + *SadEntry = NULL; + UdpService = IkeLookupUdp (Private, NicHandle, IpVersion); + + if (UdpService == NULL) { + return EFI_DEVICE_ERROR; + } + // + // Parse the destination address from ip header. + // + ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS)); + if (IpVersion == IP_VERSION_4) { + CopyMem ( + &DestIp, + &((IP4_HEAD *) IpHead)->Dst, + sizeof (IP4_ADDR) + ); + } else { + CopyMem ( + &DestIp, + &((EFI_IP6_HEADER *) IpHead)->DestinationAddress, + sizeof (EFI_IP_ADDRESS) + ); + } + + // + // Find the SAD entry in the spd.sas list according to the dest address. + // + Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp, IpVersion); + + if (Entry == NULL) { + if (OldLastHead != IP6_ICMP || + (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST) + ) { + // + // Start ike negotiation process except the request packet of ping. + // + if (SpdEntry->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) { + IkeNegotiate ( + UdpService, + SpdEntry, + &SpdEntry->Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress + ); + } else { + IkeNegotiate ( + UdpService, + SpdEntry, + &DestIp + ); + } + + } + + return EFI_NOT_READY; + } + + Data = Entry->Data; + + if (!Data->ManualSet) { + if (Data->ESNEnabled) { + // + // Validate the 64bit sn number if 64bit sn enabled. + // + if ((UINT64) (Data->SequenceNumber + 1) == 0) { + // + // TODO: Re-negotiate SA + // + return EFI_DEVICE_ERROR; + } + } else { + // + // Validate the 32bit sn number if 64bit sn disabled. + // + SeqNum32 = (UINT32) Data->SequenceNumber; + if ((UINT32) (SeqNum32 + 1) == 0) { + // + // TODO: Re-negotiate SA + // + return EFI_DEVICE_ERROR; + } + } + } + + *SadEntry = Entry; + + return EFI_SUCCESS; +} + +/** + Find a PAD entry according to a remote IP address. + + @param[in] IpVersion The version of IP. + @param[in] IpAddr Points to remote IP address. + + @return the pointer of related PAD entry. + +**/ +IPSEC_PAD_ENTRY * +IpSecLookupPadEntry ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *IpAddr + ) +{ + LIST_ENTRY *PadList; + LIST_ENTRY *Entry; + EFI_IP_ADDRESS_INFO *IpAddrInfo; + IPSEC_PAD_ENTRY *PadEntry; + + PadList = &mConfigData[IPsecConfigDataTypePad]; + + for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) { + + PadEntry = IPSEC_PAD_ENTRY_FROM_LIST (Entry); + IpAddrInfo = &PadEntry->Id->Id.IpAddress; + // + // Find the right pad entry which contain the appointed dest addr. + // + if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) { + return PadEntry; + } + } + + return NULL; +} + +/** + Check if the specified IP packet can be serviced by this SPD entry. + + @param[in] SpdEntry Point to SPD entry. + @param[in] IpVersion Version of IP. + @param[in] IpHead Point to IP header. + @param[in] IpPayload Point to IP payload. + @param[in] Protocol The Last protocol of IP packet. + @param[in] IsOutbound Traffic direction. + @param[out] Action The support action of SPD entry. + + @retval EFI_SUCCESS Find the related SPD. + @retval EFI_NOT_FOUND Not find the related SPD entry; + +**/ +EFI_STATUS +IpSecLookupSpdEntry ( + IN IPSEC_SPD_ENTRY *SpdEntry, + IN UINT8 IpVersion, + IN VOID *IpHead, + IN UINT8 *IpPayload, + IN UINT8 Protocol, + IN BOOLEAN IsOutbound, + OUT EFI_IPSEC_ACTION *Action + ) +{ + EFI_IPSEC_SPD_SELECTOR *SpdSel; + IP4_HEAD *Ip4; + EFI_IP6_HEADER *Ip6; + EFI_IP_ADDRESS SrcAddr; + EFI_IP_ADDRESS DstAddr; + BOOLEAN SpdMatch; + + ASSERT (SpdEntry != NULL); + SpdSel = SpdEntry->Selector; + Ip4 = (IP4_HEAD *) IpHead; + Ip6 = (EFI_IP6_HEADER *) IpHead; + + ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS)); + + // + // Parse the source and destination address from ip header. + // + if (IpVersion == IP_VERSION_4) { + CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR)); + CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR)); + } else { + CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)); + } + // + // Check the local and remote addresses for outbound traffic + // + SpdMatch = (BOOLEAN)(IsOutbound && + IpSecMatchIpAddress ( + IpVersion, + &SrcAddr, + SpdSel->LocalAddress, + SpdSel->LocalAddressCount + ) && + IpSecMatchIpAddress ( + IpVersion, + &DstAddr, + SpdSel->RemoteAddress, + SpdSel->RemoteAddressCount + ) + ); + + // + // Check the local and remote addresses for inbound traffic + // + SpdMatch = (BOOLEAN) (SpdMatch || + (!IsOutbound && + IpSecMatchIpAddress ( + IpVersion, + &DstAddr, + SpdSel->LocalAddress, + SpdSel->LocalAddressCount + ) && + IpSecMatchIpAddress ( + IpVersion, + &SrcAddr, + SpdSel->RemoteAddress, + SpdSel->RemoteAddressCount + ) + )); + + // + // Check the next layer protocol and local and remote ports. + // + SpdMatch = (BOOLEAN) (SpdMatch && + IpSecMatchNextLayerProtocol ( + Protocol, + IpPayload, + SpdSel->NextLayerProtocol, + SpdSel->LocalPort, + SpdSel->RemotePort, + IsOutbound + ) + ); + + if (SpdMatch) { + // + // Find the right SPD entry if match the 5 key elements. + // + *Action = SpdEntry->Data->Action; + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +/** + The call back function of NetbufFromExt. + + @param[in] Arg The argument passed from the caller. + +**/ +VOID +EFIAPI +IpSecOnRecyclePacket ( + IN VOID *Arg + ) +{ +} + +/** + This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP + is released. + + @param[in] Event The related event. + @param[in] Context The data passed by the caller. + +**/ +VOID +EFIAPI +IpSecRecycleCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IPSEC_RECYCLE_CONTEXT *RecycleContext; + + RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context; + + if (RecycleContext->FragmentTable != NULL) { + FreePool (RecycleContext->FragmentTable); + } + + if (RecycleContext->PayloadBuffer != NULL) { + FreePool (RecycleContext->PayloadBuffer); + } + + FreePool (RecycleContext); + gBS->CloseEvent (Event); + +} + +/** + Calculate the extension hader of IP. The return length only doesn't contain + the fixed IP header length. + + @param[in] IpHead Points to an IP head to be calculated. + @param[in] LastHead Points to the last header of the IP header. + + @return The length of the extension header. + +**/ +UINT16 +IpSecGetPlainExtHeadSize ( + IN VOID *IpHead, + IN UINT8 *LastHead + ) +{ + UINT16 Size; + + Size = (UINT16) (LastHead - (UINT8 *) IpHead); + + if (Size > sizeof (EFI_IP6_HEADER)) { + // + // * (LastHead+1) point the last header's length but not include the first + // 8 octers, so this formluation add 8 at the end. + // + Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8); + } else { + Size = 0; + } + + return Size; +} + +/** + Verify if the Authentication payload is correct. + + @param[in] EspBuffer Points to the ESP wrapped buffer. + @param[in] EspSize The size of the ESP wrapped buffer. + @param[in] SadEntry The related SAD entry to store the authentication + algorithm key. + @param[in] IcvSize The length of ICV. + + @retval EFI_SUCCESS The authentication data is correct. + @retval EFI_ACCESS_DENIED The authentication data is not correct. + +**/ +EFI_STATUS +IpSecEspAuthVerifyPayload ( + IN UINT8 *EspBuffer, + IN UINTN EspSize, + IN IPSEC_SAD_ENTRY *SadEntry, + IN UINTN IcvSize + ) +{ + EFI_STATUS Status; + UINTN AuthSize; + UINT8 IcvBuffer[12]; + HASH_DATA_FRAGMENT HashFragment[1]; + + // + // Calculate the size of authentication payload. + // + AuthSize = EspSize - IcvSize; + + // + // Calculate the icv buffer and size of the payload. + // + HashFragment[0].Data = EspBuffer; + HashFragment[0].DataSize = AuthSize; + + Status = IpSecCryptoIoHmac ( + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength, + HashFragment, + 1, + IcvBuffer, + IcvSize + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Compare the calculated icv and the appended original icv. + // + if (CompareMem (EspBuffer + AuthSize, IcvBuffer, IcvSize) == 0) { + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_ERROR, "Error auth verify payload\n")); + return EFI_ACCESS_DENIED; +} + +/** + Search the related SAD entry by the input . + + @param[in] IpHead The pointer to IP header. + @param[in] IpVersion The version of IP (IP4 or IP6). + @param[in] Spi The SPI used to search the related SAD entry. + + + @retval NULL Not find the related SAD entry. + @retval IPSEC_SAD_ENTRY Return the related SAD entry. + +**/ +IPSEC_SAD_ENTRY * +IpSecFoundSadFromInboundPacket ( + UINT8 *IpHead, + UINT8 IpVersion, + UINT32 Spi + ) +{ + EFI_IP_ADDRESS DestIp; + + // + // Parse destination address from ip header. + // + ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS)); + if (IpVersion == IP_VERSION_4) { + CopyMem ( + &DestIp, + &((IP4_HEAD *) IpHead)->Dst, + sizeof (IP4_ADDR) + ); + } else { + CopyMem ( + &DestIp, + &((EFI_IP6_HEADER *) IpHead)->DestinationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } + + // + // Lookup SAD entry according to the spi and dest address. + // + return IpSecLookupSadBySpi (Spi, &DestIp, IpVersion); +} + +/** + Validate the IP6 extension header format for both the packets we received + and that we will transmit. + + @param[in] NextHeader The next header field in IPv6 basic header. + @param[in] ExtHdrs The first bye of the option. + @param[in] ExtHdrsLen The length of the whole option. + @param[out] LastHeader The pointer of NextHeader of the last extension + header processed by IP6. + @param[out] RealExtsLen The length of extension headers processed by IP6 layer. + This is an optional parameter that may be NULL. + + @retval TRUE The option is properly formated. + @retval FALSE The option is malformated. + +**/ +BOOLEAN +IpSecIsIp6ExtsValid ( + IN UINT8 *NextHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + OUT UINT8 **LastHeader, + OUT UINT32 *RealExtsLen OPTIONAL + ) +{ + UINT32 Pointer; + UINT8 *Option; + UINT8 OptionLen; + UINT8 CountD; + UINT8 CountF; + UINT8 CountA; + + if (RealExtsLen != NULL) { + *RealExtsLen = 0; + } + + *LastHeader = NextHeader; + + if (ExtHdrs == NULL && ExtHdrsLen == 0) { + return TRUE; + } + + if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) { + return FALSE; + } + + Pointer = 0; + CountD = 0; + CountF = 0; + CountA = 0; + + while (Pointer <= ExtHdrsLen) { + + switch (*NextHeader) { + case IP6_HOP_BY_HOP: + if (Pointer != 0) { + return FALSE; + } + + // + // Fall through + // + case IP6_DESTINATION: + if (*NextHeader == IP6_DESTINATION) { + CountD++; + } + + if (CountD > 2) { + return FALSE; + } + + NextHeader = ExtHdrs + Pointer; + + Pointer++; + Option = ExtHdrs + Pointer; + OptionLen = (UINT8) ((*Option + 1) * 8 - 2); + Option++; + Pointer++; + + Pointer = Pointer + OptionLen; + break; + + case IP6_FRAGMENT: + if (++CountF > 1) { + return FALSE; + } + // + // RFC2402, AH header should after fragment header. + // + if (CountA > 1) { + return FALSE; + } + + NextHeader = ExtHdrs + Pointer; + Pointer = Pointer + 8; + break; + + case IP6_AH: + if (++CountA > 1) { + return FALSE; + } + + Option = ExtHdrs + Pointer; + NextHeader = Option; + Option++; + // + // RFC2402, Payload length is specified in 32-bit words, minus "2". + // + OptionLen = (UINT8) ((*Option + 2) * 4); + Pointer = Pointer + OptionLen; + break; + + default: + *LastHeader = NextHeader; + if (RealExtsLen != NULL) { + *RealExtsLen = Pointer; + } + + return TRUE; + } + } + + *LastHeader = NextHeader; + + if (RealExtsLen != NULL) { + *RealExtsLen = Pointer; + } + + return TRUE; +} + +/** + The actual entry to process the tunnel header and inner header for tunnel mode + outbound traffic. + + This function is the subfunction of IpSecEspInboundPacket(). It change the destination + Ip address to the station address and recalculate the uplayyer's checksum. + + + @param[in, out] IpHead Points to the IP header containing the ESP header + to be trimed on input, and without ESP header + on return. + @param[in] IpPayload The decrypted Ip payload. It start from the inner + header. + @param[in] IpVersion The version of IP. + @param[in] SadData Pointer of the relevant SAD. + @param[in, out] LastHead The Last Header in IP header on return. + +**/ +VOID +IpSecTunnelInboundPacket ( + IN OUT UINT8 *IpHead, + IN UINT8 *IpPayload, + IN UINT8 IpVersion, + IN IPSEC_SAD_DATA *SadData, + IN OUT UINT8 *LastHead + ) +{ + EFI_UDP_HEADER *UdpHeader; + TCP_HEAD *TcpHeader; + UINT16 *Checksum; + UINT16 PseudoChecksum; + UINT16 PacketChecksum; + UINT32 OptionLen; + IP6_ICMP_HEAD *Icmp6Head; + + Checksum = NULL; + + if (IpVersion == IP_VERSION_4) { + // + // Zero OutIP header use this to indicate the input packet is under + // IPsec Tunnel protected. + // + ZeroMem ( + (IP4_HEAD *)IpHead, + sizeof (IP4_HEAD) + ); + CopyMem ( + &((IP4_HEAD *)IpPayload)->Dst, + &SadData->TunnelDestAddress.v4, + sizeof (EFI_IPv4_ADDRESS) + ); + + // + // Recalculate IpHeader Checksum + // + if (((IP4_HEAD *)(IpPayload))->Checksum != 0 ) { + ((IP4_HEAD *)(IpPayload))->Checksum = 0; + ((IP4_HEAD *)(IpPayload))->Checksum = (UINT16) (~NetblockChecksum ( + (UINT8 *)IpPayload, + ((IP4_HEAD *)IpPayload)->HeadLen << 2 + )); + + + } + + // + // Recalcualte PseudoChecksum + // + switch (((IP4_HEAD *)IpPayload)->Protocol) { + case EFI_IP_PROTO_UDP : + UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2)); + Checksum = & UdpHeader->Checksum; + *Checksum = 0; + break; + + case EFI_IP_PROTO_TCP: + TcpHeader = (TCP_HEAD *) ((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2)); + Checksum = &TcpHeader->Checksum; + *Checksum = 0; + break; + + default: + break; + } + PacketChecksum = NetblockChecksum ( + (UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2), + NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2) + ); + PseudoChecksum = NetPseudoHeadChecksum ( + ((IP4_HEAD *)IpPayload)->Src, + ((IP4_HEAD *)IpPayload)->Dst, + ((IP4_HEAD *)IpPayload)->Protocol, + 0 + ); + + if (Checksum != NULL) { + *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum); + *Checksum = (UINT16) ~(NetAddChecksum (*Checksum, HTONS((UINT16)(NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2))))); + } + }else { + // + // Zero OutIP header use this to indicate the input packet is under + // IPsec Tunnel protected. + // + ZeroMem ( + IpHead, + sizeof (EFI_IP6_HEADER) + ); + CopyMem ( + &((EFI_IP6_HEADER*)IpPayload)->DestinationAddress, + &SadData->TunnelDestAddress.v6, + sizeof (EFI_IPv6_ADDRESS) + ); + + // + // Get the Extension Header and Header length. + // + IpSecIsIp6ExtsValid ( + &((EFI_IP6_HEADER *)IpPayload)->NextHeader, + IpPayload + sizeof (EFI_IP6_HEADER), + ((EFI_IP6_HEADER *)IpPayload)->PayloadLength, + &LastHead, + &OptionLen + ); + + // + // Recalcualte PseudoChecksum + // + switch (*LastHead) { + case EFI_IP_PROTO_UDP: + UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen); + Checksum = &UdpHeader->Checksum; + *Checksum = 0; + break; + + case EFI_IP_PROTO_TCP: + TcpHeader = (TCP_HEAD *)(IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen); + Checksum = &TcpHeader->Checksum; + *Checksum = 0; + break; + + case IP6_ICMP: + Icmp6Head = (IP6_ICMP_HEAD *) (IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen); + Checksum = &Icmp6Head->Checksum; + *Checksum = 0; + break; + } + PacketChecksum = NetblockChecksum ( + IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen, + NTOHS(((EFI_IP6_HEADER *)IpPayload)->PayloadLength) - OptionLen + ); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &((EFI_IP6_HEADER *)IpPayload)->SourceAddress, + &((EFI_IP6_HEADER *)IpPayload)->DestinationAddress, + *LastHead, + 0 + ); + + if (Checksum != NULL) { + *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum); + *Checksum = (UINT16) ~(NetAddChecksum ( + *Checksum, + HTONS ((UINT16)((NTOHS (((EFI_IP6_HEADER *)(IpPayload))->PayloadLength)) - OptionLen)) + )); + } + } +} + +/** + The actual entry to create inner header for tunnel mode inbound traffic. + + This function is the subfunction of IpSecEspOutboundPacket(). It create + the sending packet by encrypting its payload and inserting ESP header in the orginal + IP header, then return the IpHeader and IPsec protected Fragmentable. + + @param[in, out] IpHead Points to IP header containing the orginal IP header + to be processed on input, and inserted ESP header + on return. + @param[in] IpVersion The version of IP. + @param[in] SadData The related SAD data. + @param[in, out] LastHead The Last Header in IP header. + @param[in] OptionsBuffer Pointer to the options buffer. + @param[in] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments to be protected by + IPsec on input, and with IPsec protected + on return. + @param[in] FragmentCount The number of fragments. + +**/ +UINT8 * +IpSecTunnelOutboundPacket ( + IN OUT UINT8 *IpHead, + IN UINT8 IpVersion, + IN IPSEC_SAD_DATA *SadData, + IN OUT UINT8 *LastHead, + IN VOID **OptionsBuffer, + IN UINT32 *OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN UINT32 *FragmentCount + ) +{ + UINT8 *InnerHead; + NET_BUF *Packet; + UINT16 PacketChecksum; + UINT16 *Checksum; + UINT16 PseudoChecksum; + IP6_ICMP_HEAD *IcmpHead; + + Checksum = NULL; + if (OptionsLength == NULL) { + return NULL; + } + + if (IpVersion == IP_VERSION_4) { + InnerHead = AllocateZeroPool (sizeof (IP4_HEAD) + *OptionsLength); + if (InnerHead == NULL) { + return NULL; + } + + CopyMem ( + InnerHead, + IpHead, + sizeof (IP4_HEAD) + ); + CopyMem ( + InnerHead + sizeof (IP4_HEAD), + *OptionsBuffer, + *OptionsLength + ); + } else { + InnerHead = AllocateZeroPool (sizeof (EFI_IP6_HEADER) + *OptionsLength); + if (InnerHead == NULL) { + return NULL; + } + + CopyMem ( + InnerHead, + IpHead, + sizeof (EFI_IP6_HEADER) + ); + CopyMem ( + InnerHead + sizeof (EFI_IP6_HEADER), + *OptionsBuffer, + *OptionsLength + ); + } + if (OptionsBuffer != NULL) { + if (*OptionsLength != 0) { + + *OptionsBuffer = NULL; + *OptionsLength = 0; + } + } + + // + // 2. Reassamlbe Fragment into Packet + // + Packet = NetbufFromExt ( + (NET_FRAGMENT *)(*FragmentTable), + *FragmentCount, + 0, + 0, + IpSecOnRecyclePacket, + NULL + ); + if (Packet == NULL) { + FreePool (InnerHead); + return NULL; + } + + // + // 3. Check the Last Header, if it is TCP, UDP or ICMP recalcualate its pesudo + // CheckSum. + // + switch (*LastHead) { + case EFI_IP_PROTO_UDP: + Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, 0); + ASSERT (Packet->Udp != NULL); + Checksum = &Packet->Udp->Checksum; + *Checksum = 0; + break; + + case EFI_IP_PROTO_TCP: + Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, 0); + ASSERT (Packet->Tcp != NULL); + Checksum = &Packet->Tcp->Checksum; + *Checksum = 0; + break; + + case IP6_ICMP: + IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL); + ASSERT (IcmpHead != NULL); + Checksum = &IcmpHead->Checksum; + *Checksum = 0; + break; + + default: + break; + } + + PacketChecksum = NetbufChecksum (Packet); + + if (IpVersion == IP_VERSION_4) { + // + // Replace the source address of Inner Header. + // + CopyMem ( + &((IP4_HEAD *)InnerHead)->Src, + &SadData->SpdSelector->LocalAddress[0].Address.v4, + sizeof (EFI_IPv4_ADDRESS) + ); + + PacketChecksum = NetbufChecksum (Packet); + PseudoChecksum = NetPseudoHeadChecksum ( + ((IP4_HEAD *)InnerHead)->Src, + ((IP4_HEAD *)InnerHead)->Dst, + *LastHead, + 0 + ); + + } else { + // + // Replace the source address of Inner Header. + // + CopyMem ( + &((EFI_IP6_HEADER *)InnerHead)->SourceAddress, + &(SadData->SpdSelector->LocalAddress[0].Address.v6), + sizeof (EFI_IPv6_ADDRESS) + ); + PacketChecksum = NetbufChecksum (Packet); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &((EFI_IP6_HEADER *)InnerHead)->SourceAddress, + &((EFI_IP6_HEADER *)InnerHead)->DestinationAddress, + *LastHead, + 0 + ); + + } + if (Checksum != NULL) { + *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum); + *Checksum = (UINT16) ~(NetAddChecksum ((UINT16)*Checksum, HTONS ((UINT16) Packet->TotalSize))); + } + + if (Packet != NULL) { + NetbufFree (Packet); + } + return InnerHead; +} + +/** + The actual entry to relative function processes the inbound traffic of ESP header. + + This function is the subfunction of IpSecProtectInboundPacket(). It checks the + received packet security property and trim the ESP header and then returns without + an IPsec protected IP Header and FramgmentTable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Points to the IP header containing the ESP header + to be trimed on input, and without ESP header + on return. + @param[out] LastHead The Last Header in IP header on return. + @param[in, out] OptionsBuffer Pointer to the options buffer. + @param[in, out] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments in the form of IPsec + protected on input, and without IPsec protected + on return. + @param[in, out] FragmentCount The number of fragments. + @param[out] SpdSelector Pointer to contain the address of SPD selector on return. + @param[out] RecycleEvent The event for recycling of resources. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_ACCESS_DENIED One or more following conditions is TRUE: + - ESP header was not found or mal-format. + - The related SAD entry was not found. + - The related SAD entry does not support the ESP protocol. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + +**/ +EFI_STATUS +IpSecEspInboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + OUT UINT8 *LastHead, + IN OUT VOID **OptionsBuffer, + IN OUT UINT32 *OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN OUT UINT32 *FragmentCount, + OUT EFI_IPSEC_SPD_SELECTOR **SpdSelector, + OUT EFI_EVENT *RecycleEvent + ) +{ + EFI_STATUS Status; + NET_BUF *Payload; + UINTN EspSize; + UINTN IvSize; + UINTN BlockSize; + UINTN MiscSize; + UINTN PlainPayloadSize; + UINTN PaddingSize; + UINTN IcvSize; + UINT8 *ProcessBuffer; + EFI_ESP_HEADER *EspHeader; + EFI_ESP_TAIL *EspTail; + EFI_IPSEC_SA_ID *SaId; + IPSEC_SAD_DATA *SadData; + IPSEC_SAD_ENTRY *SadEntry; + IPSEC_RECYCLE_CONTEXT *RecycleContext; + UINT8 NextHeader; + UINT16 IpSecHeadSize; + UINT8 *InnerHead; + + Status = EFI_SUCCESS; + Payload = NULL; + ProcessBuffer = NULL; + RecycleContext = NULL; + *RecycleEvent = NULL; + PlainPayloadSize = 0; + NextHeader = 0; + + // + // Build netbuf from fragment table first. + // + Payload = NetbufFromExt ( + (NET_FRAGMENT *) *FragmentTable, + *FragmentCount, + 0, + sizeof (EFI_ESP_HEADER), + IpSecOnRecyclePacket, + NULL + ); + if (Payload == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Get the esp size and esp header from netbuf. + // + EspSize = Payload->TotalSize; + EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL); + + if (EspHeader == NULL) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + // + // Parse destination address from ip header and found the related SAD Entry. + // + SadEntry = IpSecFoundSadFromInboundPacket ( + IpHead, + IpVersion, + NTOHL (EspHeader->Spi) + ); + + if (SadEntry == NULL) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + SaId = SadEntry->Id; + SadData = SadEntry->Data; + + // + // Only support esp protocol currently. + // + if (SaId->Proto != EfiIPsecESP) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + if (!SadData->ManualSet) { + // + // TODO: Check SA lifetime and sequence number + // + } + + // + // Allocate buffer for decryption and authentication. + // + ProcessBuffer = AllocateZeroPool (EspSize); + if (ProcessBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer); + + // + // Get the IcvSize for authentication and BlockSize/IvSize for Decryption. + // + IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId); + IvSize = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId); + BlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId); + + // + // Make sure the ESP packet is not mal-formt. + // 1. Check whether the Espsize is larger than ESP header + IvSize + EspTail + IcvSize. + // 2. Check whether the left payload size is multiple of IvSize. + // + MiscSize = sizeof (EFI_ESP_HEADER) + IvSize + IcvSize; + if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL))) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + if ((EspSize - MiscSize) % BlockSize != 0) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + // + // Authenticate the ESP packet. + // + if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + Status = IpSecEspAuthVerifyPayload ( + ProcessBuffer, + EspSize, + SadEntry, + IcvSize + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + // + // Decrypt the payload by the SAD entry if it has decrypt key. + // + if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + Status = IpSecCryptoIoDecrypt ( + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId, + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3, + ProcessBuffer + sizeof (EFI_ESP_HEADER), + ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize, + EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize, + ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + // + // Parse EspTail and compute the plain payload size. + // + EspTail = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL)); + PaddingSize = EspTail->PaddingLength; + NextHeader = EspTail->NextHeader; + + if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL) + PaddingSize)) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + PlainPayloadSize = EspSize - MiscSize - sizeof (EFI_ESP_TAIL) - PaddingSize; + + // + // TODO: handle anti-replay window + // + // + // Decryption and authentication with esp has been done, so it's time to + // reload the new packet, create recycle event and fixup ip header. + // + RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT)); + if (RecycleContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IpSecRecycleCallback, + RecycleContext, + RecycleEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // The caller will take responsible to handle the original fragment table + // + *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA)); + if (*FragmentTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + RecycleContext->PayloadBuffer = ProcessBuffer; + RecycleContext->FragmentTable = *FragmentTable; + + // + // If Tunnel, recalculate upper-layyer PesudoCheckSum and trim the out + // + if (SadData->Mode == EfiIPsecTunnel) { + InnerHead = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize; + IpSecTunnelInboundPacket ( + IpHead, + InnerHead, + IpVersion, + SadData, + LastHead + ); + + if (IpVersion == IP_VERSION_4) { + (*FragmentTable)[0].FragmentBuffer = InnerHead ; + (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize; + + }else { + (*FragmentTable)[0].FragmentBuffer = InnerHead; + (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize; + } + } else { + (*FragmentTable)[0].FragmentBuffer = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize; + (*FragmentTable)[0].FragmentLength = (UINT32) PlainPayloadSize; + } + + *FragmentCount = 1; + + // + // Update the total length field in ip header since processed by esp. + // + if (SadData->Mode != EfiIPsecTunnel) { + if (IpVersion == IP_VERSION_4) { + ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + PlainPayloadSize)); + } else { + IpSecHeadSize = IpSecGetPlainExtHeadSize (IpHead, LastHead); + ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize)); + } + // + // Update the next layer field in ip header since esp header inserted. + // + *LastHead = NextHeader; + } + + + // + // Update the SPD association of the SAD entry. + // + *SpdSelector = SadData->SpdSelector; + +ON_EXIT: + if (Payload != NULL) { + NetbufFree (Payload); + } + + if (EFI_ERROR (Status)) { + if (ProcessBuffer != NULL) { + FreePool (ProcessBuffer); + } + + if (RecycleContext != NULL) { + FreePool (RecycleContext); + } + + if (*RecycleEvent != NULL) { + gBS->CloseEvent (*RecycleEvent); + } + } + + return Status; +} + +/** + The actual entry to the relative function processes the output traffic using the ESP protocol. + + This function is the subfunction of IpSecProtectOutboundPacket(). It protected + the sending packet by encrypting its payload and inserting ESP header in the orginal + IP header, then return the IpHeader and IPsec protected Fragmentable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Points to IP header containing the orginal IP header + to be processed on input, and inserted ESP header + on return. + @param[in, out] LastHead The Last Header in IP header. + @param[in, out] OptionsBuffer Pointer to the options buffer. + @param[in, out] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments to be protected by + IPsec on input, and with IPsec protected + on return. + @param[in, out] FragmentCount The number of fragments. + @param[in] SadEntry The related SAD entry. + @param[out] RecycleEvent The event for recycling of resources. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_OUT_OF_RESOURCES The required system resources can't be allocated. + +**/ +EFI_STATUS +IpSecEspOutboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN OUT UINT8 *LastHead, + IN OUT VOID **OptionsBuffer, + IN OUT UINT32 *OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN OUT UINT32 *FragmentCount, + IN IPSEC_SAD_ENTRY *SadEntry, + OUT EFI_EVENT *RecycleEvent + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_IPSEC_SA_ID *SaId; + IPSEC_SAD_DATA *SadData; + IPSEC_RECYCLE_CONTEXT *RecycleContext; + UINT8 *ProcessBuffer; + UINTN BytesCopied; + INTN EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4 + UINTN EspSize; // Total size of esp wrapped ip payload + UINTN IvSize; // Size of IV, optional, might be 0 + UINTN PlainPayloadSize;// Original IP payload size + UINTN PaddingSize; // Size of padding + UINTN EncryptSize; // Size of data to be encrypted, start after IV and + // stop before ICV + UINTN IcvSize; // Size of ICV, optional, might be 0 + UINT8 *RestOfPayload; // Start of Payload after IV + UINT8 *Padding; // Start address of padding + EFI_ESP_HEADER *EspHeader; // Start address of ESP frame + EFI_ESP_TAIL *EspTail; // Address behind padding + UINT8 *InnerHead; + HASH_DATA_FRAGMENT HashFragment[1]; + + Status = EFI_ACCESS_DENIED; + SaId = SadEntry->Id; + SadData = SadEntry->Data; + ProcessBuffer = NULL; + RecycleContext = NULL; + *RecycleEvent = NULL; + InnerHead = NULL; + + if (!SadData->ManualSet && + SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL && + SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL + ) { + // + // Invalid manual SAD entry configuration. + // + goto ON_EXIT; + } + + // + // Create OutHeader according to Inner Header + // + if (SadData->Mode == EfiIPsecTunnel) { + InnerHead = IpSecTunnelOutboundPacket ( + IpHead, + IpVersion, + SadData, + LastHead, + OptionsBuffer, + OptionsLength, + FragmentTable, + FragmentCount + ); + + if (InnerHead == NULL) { + return EFI_INVALID_PARAMETER; + } + + } + + // + // Calculate enctrypt block size, need iv by default and 4 bytes alignment. + // + EncryptBlockSize = 4; + + if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + EncryptBlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId); + + if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) { + goto ON_EXIT; + } + } + + // + // Calculate the plain payload size accroding to the fragment table. + // + PlainPayloadSize = 0; + for (Index = 0; Index < *FragmentCount; Index++) { + PlainPayloadSize += (*FragmentTable)[Index].FragmentLength; + } + + // + // Add IPHeader size for Tunnel Mode + // + if (SadData->Mode == EfiIPsecTunnel) { + if (IpVersion == IP_VERSION_4) { + PlainPayloadSize += sizeof (IP4_HEAD); + } else { + PlainPayloadSize += sizeof (EFI_IP6_HEADER); + } + // + // OPtions should be encryption into it + // + PlainPayloadSize += *OptionsLength; + } + + + // + // Calculate icv size, optional by default and 4 bytes alignment. + // + IcvSize = 0; + if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId); + if (IcvSize % 4 != 0) { + goto ON_EXIT; + } + } + + // + // Calcuate the total size of esp wrapped ip payload. + // + IvSize = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId); + EncryptSize = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize; + PaddingSize = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL); + EspSize = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize; + + ProcessBuffer = AllocateZeroPool (EspSize); + if (ProcessBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Calculate esp header and esp tail including header, payload and padding. + // + EspHeader = (EFI_ESP_HEADER *) ProcessBuffer; + RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize; + Padding = RestOfPayload + PlainPayloadSize; + EspTail = (EFI_ESP_TAIL *) (Padding + PaddingSize); + + // + // Fill the sn and spi fields in esp header. + // + EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1); + //EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber); + EspHeader->Spi = HTONL (SaId->Spi); + + // + // Copy the rest of payload (after iv) from the original fragment buffer. + // + BytesCopied = 0; + + // + // For Tunnel Mode + // + if (SadData->Mode == EfiIPsecTunnel) { + if (IpVersion == IP_VERSION_4) { + // + // HeadLen, Total Length + // + ((IP4_HEAD *)InnerHead)->HeadLen = (UINT8) ((sizeof (IP4_HEAD) + *OptionsLength) >> 2); + ((IP4_HEAD *)InnerHead)->TotalLen = HTONS ((UINT16) PlainPayloadSize); + ((IP4_HEAD *)InnerHead)->Checksum = 0; + ((IP4_HEAD *)InnerHead)->Checksum = (UINT16) (~NetblockChecksum ( + (UINT8 *)InnerHead, + sizeof(IP4_HEAD) + )); + CopyMem ( + RestOfPayload + BytesCopied, + InnerHead, + sizeof (IP4_HEAD) + *OptionsLength + ); + BytesCopied += sizeof (IP4_HEAD) + *OptionsLength; + + } else { + ((EFI_IP6_HEADER *)InnerHead)->PayloadLength = HTONS ((UINT16) (PlainPayloadSize - sizeof (EFI_IP6_HEADER))); + CopyMem ( + RestOfPayload + BytesCopied, + InnerHead, + sizeof (EFI_IP6_HEADER) + *OptionsLength + ); + BytesCopied += sizeof (EFI_IP6_HEADER) + *OptionsLength; + } + } + + for (Index = 0; Index < *FragmentCount; Index++) { + CopyMem ( + (RestOfPayload + BytesCopied), + (*FragmentTable)[Index].FragmentBuffer, + (*FragmentTable)[Index].FragmentLength + ); + BytesCopied += (*FragmentTable)[Index].FragmentLength; + } + // + // Fill the padding buffer by natural number sequence. + // + for (Index = 0; Index < PaddingSize; Index++) { + Padding[Index] = (UINT8) (Index + 1); + } + // + // Fill the padding length and next header fields in esp tail. + // + EspTail->PaddingLength = (UINT8) PaddingSize; + EspTail->NextHeader = *LastHead; + + // + // Fill the next header for Tunnel mode. + // + if (SadData->Mode == EfiIPsecTunnel) { + if (IpVersion == IP_VERSION_4) { + EspTail->NextHeader = 4; + } else { + EspTail->NextHeader = 41; + } + } + + // + // Generate iv at random by crypt library. + // + Status = IpSecGenerateIv ( + (UINT8 *) (EspHeader + 1), + IvSize + ); + + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Encryption the payload (after iv) by the SAD entry if has encrypt key. + // + if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) { + Status = IpSecCryptoIoEncrypt ( + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId, + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3, + (UINT8 *)(EspHeader + 1), + RestOfPayload, + EncryptSize, + RestOfPayload + ); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + // + // Authenticate the esp wrapped buffer by the SAD entry if it has auth key. + // + if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) { + + HashFragment[0].Data = ProcessBuffer; + HashFragment[0].DataSize = EspSize - IcvSize; + Status = IpSecCryptoIoHmac ( + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey, + SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength, + HashFragment, + 1, + ProcessBuffer + EspSize - IcvSize, + IcvSize + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + // + // Encryption and authentication with esp has been done, so it's time to + // reload the new packet, create recycle event and fixup ip header. + // + RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT)); + if (RecycleContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IpSecRecycleCallback, + RecycleContext, + RecycleEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + // + // Caller take responsible to handle the original fragment table. + // + *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA)); + if (*FragmentTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + RecycleContext->FragmentTable = *FragmentTable; + RecycleContext->PayloadBuffer = ProcessBuffer; + (*FragmentTable)[0].FragmentBuffer = ProcessBuffer; + (*FragmentTable)[0].FragmentLength = (UINT32) EspSize; + *FragmentCount = 1; + + // + // Update the total length field in ip header since processed by esp. + // + if (IpVersion == IP_VERSION_4) { + ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + EspSize)); + } else { + ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize); + } + + // + // If tunnel mode, it should change the outer Ip header with tunnel source address + // and destination tunnel address. + // + if (SadData->Mode == EfiIPsecTunnel) { + if (IpVersion == IP_VERSION_4) { + CopyMem ( + &((IP4_HEAD *) IpHead)->Src, + &SadData->TunnelSourceAddress.v4, + sizeof (EFI_IPv4_ADDRESS) + ); + CopyMem ( + &((IP4_HEAD *) IpHead)->Dst, + &SadData->TunnelDestAddress.v4, + sizeof (EFI_IPv4_ADDRESS) + ); + } else { + CopyMem ( + &((EFI_IP6_HEADER *) IpHead)->SourceAddress, + &SadData->TunnelSourceAddress.v6, + sizeof (EFI_IPv6_ADDRESS) + ); + CopyMem ( + &((EFI_IP6_HEADER *) IpHead)->DestinationAddress, + &SadData->TunnelDestAddress.v6, + sizeof (EFI_IPv6_ADDRESS) + ); + } + } + + // + // Update the next layer field in ip header since esp header inserted. + // + *LastHead = IPSEC_ESP_PROTOCOL; + + // + // Increase the sn number in SAD entry according to rfc4303. + // + SadData->SequenceNumber++; + +ON_EXIT: + if (EFI_ERROR (Status)) { + if (ProcessBuffer != NULL) { + FreePool (ProcessBuffer); + } + + if (RecycleContext != NULL) { + FreePool (RecycleContext); + } + + if (*RecycleEvent != NULL) { + gBS->CloseEvent (*RecycleEvent); + } + } + + return Status; +} + +/** + This function processes the inbound traffic with IPsec. + + It checks the received packet security property, trims the ESP/AH header, and then + returns without an IPsec protected IP Header and FragmentTable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Points to IP header containing the ESP/AH header + to be trimed on input, and without ESP/AH header + on return. + @param[in, out] LastHead The Last Header in IP header on return. + @param[in, out] OptionsBuffer Pointer to the options buffer. + @param[in, out] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments in form of IPsec + protected on input, and without IPsec protected + on return. + @param[in, out] FragmentCount The number of fragments. + @param[out] SpdEntry Pointer to contain the address of SPD entry on return. + @param[out] RecycleEvent The event for recycling of resources. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_UNSUPPORTED The IPSEC protocol is not supported. + +**/ +EFI_STATUS +IpSecProtectInboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN OUT UINT8 *LastHead, + IN OUT VOID **OptionsBuffer, + IN OUT UINT32 *OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN OUT UINT32 *FragmentCount, + OUT EFI_IPSEC_SPD_SELECTOR **SpdEntry, + OUT EFI_EVENT *RecycleEvent + ) +{ + if (*LastHead == IPSEC_ESP_PROTOCOL) { + // + // Process the esp ipsec header of the inbound traffic. + // + return IpSecEspInboundPacket ( + IpVersion, + IpHead, + LastHead, + OptionsBuffer, + OptionsLength, + FragmentTable, + FragmentCount, + SpdEntry, + RecycleEvent + ); + } + // + // The other protocols are not supported. + // + return EFI_UNSUPPORTED; +} + +/** + This fucntion processes the output traffic with IPsec. + + It protected the sending packet by encrypting it payload and inserting ESP/AH header + in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Point to IP header containing the orginal IP header + to be processed on input, and inserted ESP/AH header + on return. + @param[in, out] LastHead The Last Header in IP header. + @param[in, out] OptionsBuffer Pointer to the options buffer. + @param[in, out] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments to be protected by + IPsec on input, and with IPsec protected + on return. + @param[in, out] FragmentCount Number of fragments. + @param[in] SadEntry Related SAD entry. + @param[out] RecycleEvent Event for recycling of resources. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported. + +**/ +EFI_STATUS +IpSecProtectOutboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN OUT UINT8 *LastHead, + IN OUT VOID **OptionsBuffer, + IN OUT UINT32 *OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN OUT UINT32 *FragmentCount, + IN IPSEC_SAD_ENTRY *SadEntry, + OUT EFI_EVENT *RecycleEvent + ) +{ + if (SadEntry->Id->Proto == EfiIPsecESP) { + // + // Process the esp ipsec header of the outbound traffic. + // + return IpSecEspOutboundPacket ( + IpVersion, + IpHead, + LastHead, + OptionsBuffer, + OptionsLength, + FragmentTable, + FragmentCount, + SadEntry, + RecycleEvent + ); + } + // + // The other protocols are not supported. + // + return EFI_UNSUPPORTED; +} diff --git a/Core/NetworkPkg/IpSecDxe/IpSecImpl.h b/Core/NetworkPkg/IpSecDxe/IpSecImpl.h new file mode 100644 index 0000000000..89597bdc80 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecImpl.h @@ -0,0 +1,390 @@ +/** @file + The definitions related to IPsec protocol implementation. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _IP_SEC_IMPL_H_ +#define _IP_SEC_IMPL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _IPSEC_PRIVATE_DATA IPSEC_PRIVATE_DATA; +typedef struct _IPSEC_SPD_ENTRY IPSEC_SPD_ENTRY; +typedef struct _IPSEC_PAD_ENTRY IPSEC_PAD_ENTRY; +typedef struct _IPSEC_SPD_DATA IPSEC_SPD_DATA; + +#define IPSEC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('I', 'P', 'S', 'E') + +#define IPSEC_PRIVATE_DATA_FROM_IPSEC(a) CR (a, IPSEC_PRIVATE_DATA, IpSec, IPSEC_PRIVATE_DATA_SIGNATURE) +#define IPSEC_PRIVATE_DATA_FROM_UDP4LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp4List, IPSEC_PRIVATE_DATA_SIGNATURE) +#define IPSEC_PRIVATE_DATA_FROM_UDP6LIST(a) CR (a, IPSEC_PRIVATE_DATA, Udp6List, IPSEC_PRIVATE_DATA_SIGNATURE) +#define IPSEC_UDP_SERVICE_FROM_LIST(a) BASE_CR (a, IKE_UDP_SERVICE, List) +#define IPSEC_SPD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_SPD_ENTRY, List) +#define IPSEC_SAD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_SAD_ENTRY, List) +#define IPSEC_PAD_ENTRY_FROM_LIST(a) BASE_CR (a, IPSEC_PAD_ENTRY, List) +#define IPSEC_SAD_ENTRY_FROM_SPD(a) BASE_CR (a, IPSEC_SAD_ENTRY, BySpd) + +#define IPSEC_STATUS_DISABLED 0 +#define IPSEC_STATUS_ENABLED 1 +#define IPSEC_ESP_PROTOCOL 50 +#define IPSEC_AH_PROTOCOL 51 +#define IPSEC_DEFAULT_VARIABLE_SIZE 0x100 + +// +// Internal Structure Definition +// +#pragma pack(1) +typedef struct _EFI_AH_HEADER { + UINT8 NextHeader; + UINT8 PayloadLen; + UINT16 Reserved; + UINT32 Spi; + UINT32 SequenceNumber; +} EFI_AH_HEADER; + +typedef struct _EFI_ESP_HEADER { + UINT32 Spi; + UINT32 SequenceNumber; +} EFI_ESP_HEADER; + +typedef struct _EFI_ESP_TAIL { + UINT8 PaddingLength; + UINT8 NextHeader; +} EFI_ESP_TAIL; +#pragma pack() + +struct _IPSEC_SPD_DATA { + CHAR16 Name[100]; + UINT32 PackageFlag; + EFI_IPSEC_TRAFFIC_DIR TrafficDirection; + EFI_IPSEC_ACTION Action; + EFI_IPSEC_PROCESS_POLICY *ProcessingPolicy; + LIST_ENTRY Sas; +}; + +struct _IPSEC_SPD_ENTRY { + EFI_IPSEC_SPD_SELECTOR *Selector; + IPSEC_SPD_DATA *Data; + LIST_ENTRY List; +}; + +typedef struct _IPSEC_SAD_DATA { + EFI_IPSEC_MODE Mode; + UINT64 SequenceNumber; + UINT8 AntiReplayWindowSize; + UINT64 AntiReplayBitmap[4]; // bitmap for received packet + EFI_IPSEC_ALGO_INFO AlgoInfo; + EFI_IPSEC_SA_LIFETIME SaLifetime; + UINT32 PathMTU; + IPSEC_SPD_ENTRY *SpdEntry; + EFI_IPSEC_SPD_SELECTOR *SpdSelector; + BOOLEAN ESNEnabled; // Extended (64-bit) SN enabled + BOOLEAN ManualSet; + EFI_IP_ADDRESS TunnelDestAddress; + EFI_IP_ADDRESS TunnelSourceAddress; +} IPSEC_SAD_DATA; + +typedef struct _IPSEC_SAD_ENTRY { + EFI_IPSEC_SA_ID *Id; + IPSEC_SAD_DATA *Data; + LIST_ENTRY List; + LIST_ENTRY BySpd; // Linked on IPSEC_SPD_DATA.Sas +} IPSEC_SAD_ENTRY; + +struct _IPSEC_PAD_ENTRY { + EFI_IPSEC_PAD_ID *Id; + EFI_IPSEC_PAD_DATA *Data; + LIST_ENTRY List; +}; + +typedef struct _IPSEC_RECYCLE_CONTEXT { + EFI_IPSEC_FRAGMENT_DATA *FragmentTable; + UINT8 *PayloadBuffer; +} IPSEC_RECYCLE_CONTEXT; + +// +// Struct used to store the Hash and its data. +// +typedef struct { + UINTN DataSize; + UINT8 *Data; +} HASH_DATA_FRAGMENT; + +struct _IPSEC_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Handle; // Virtual handle to install private prtocol + EFI_HANDLE ImageHandle; + EFI_IPSEC2_PROTOCOL IpSec; + EFI_IPSEC_CONFIG_PROTOCOL IpSecConfig; + BOOLEAN SetBySelf; + LIST_ENTRY Udp4List; + UINTN Udp4Num; + LIST_ENTRY Udp6List; + UINTN Udp6Num; + LIST_ENTRY Ikev1SessionList; + LIST_ENTRY Ikev1EstablishedList; + LIST_ENTRY Ikev2SessionList; + LIST_ENTRY Ikev2EstablishedList; + BOOLEAN IsIPsecDisabling; +}; + +/** + This function processes the inbound traffic with IPsec. + + It checks the received packet security property, trims the ESP/AH header, and then + returns without an IPsec protected IP Header and FragmentTable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Points to IP header containing the ESP/AH header + to be trimed on input, and without ESP/AH header + on return. + @param[in, out] LastHead The Last Header in IP header on return. + @param[in, out] OptionsBuffer Pointer to the options buffer. + @param[in, out] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments in form of IPsec + protected on input, and without IPsec protected + on return. + @param[in, out] FragmentCount The number of fragments. + @param[out] SpdEntry Pointer to contain the address of SPD entry on return. + @param[out] RecycleEvent The event for recycling of resources. + + @retval EFI_SUCCESS The operation was successful. + @retval EFI_UNSUPPORTED The IPSEC protocol is not supported. + +**/ +EFI_STATUS +IpSecProtectInboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN OUT UINT8 *LastHead, + IN OUT VOID **OptionsBuffer, + IN OUT UINT32 *OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN OUT UINT32 *FragmentCount, + OUT EFI_IPSEC_SPD_SELECTOR **SpdEntry, + OUT EFI_EVENT *RecycleEvent + ); + + +/** + This fucntion processes the output traffic with IPsec. + + It protected the sending packet by encrypting it payload and inserting ESP/AH header + in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable. + + @param[in] IpVersion The version of IP. + @param[in, out] IpHead Point to IP header containing the orginal IP header + to be processed on input, and inserted ESP/AH header + on return. + @param[in, out] LastHead The Last Header in IP header. + @param[in, out] OptionsBuffer Pointer to the options buffer. + @param[in, out] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments to be protected by + IPsec on input, and with IPsec protected + on return. + @param[in, out] FragmentCount Number of fragments. + @param[in] SadEntry Related SAD entry. + @param[out] RecycleEvent Event for recycling of resources. + + @retval EFI_SUCCESS The operation is successful. + @retval EFI_UNSUPPORTED If the IPSEC protocol is not supported. + +**/ +EFI_STATUS +IpSecProtectOutboundPacket ( + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN OUT UINT8 *LastHead, + IN OUT VOID **OptionsBuffer, + IN OUT UINT32 *OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN OUT UINT32 *FragmentCount, + IN IPSEC_SAD_ENTRY *SadEntry, + OUT EFI_EVENT *RecycleEvent + ); + +/** + Check if the IP Address in the address range of AddressInfos specified. + + @param[in] IpVersion The IP version. + @param[in] IpAddr Points to EFI_IP_ADDRESS to be check. + @param[in] AddressInfo A list of EFI_IP_ADDRESS_INFO that is used to check + the IP Address is matched. + @param[in] AddressCount The total numbers of the AddressInfo. + + @retval TRUE If the Specified IP Address is in the range of the AddressInfos specified. + @retval FALSE If the Specified IP Address is not in the range of the AddressInfos specified. + +**/ +BOOLEAN +IpSecMatchIpAddress ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *IpAddr, + IN EFI_IP_ADDRESS_INFO *AddressInfo, + IN UINT32 AddressCount + ); + +/** + Find a PAD entry according to remote IP address. + + @param[in] IpVersion The version of IP. + @param[in] IpAddr Point to remote IP address. + + @return The pointer of related PAD entry. + +**/ +IPSEC_PAD_ENTRY * +IpSecLookupPadEntry ( + IN UINT8 IpVersion, + IN EFI_IP_ADDRESS *IpAddr + ); + +/** + Check if the specified IP packet can be serviced by this SPD entry. + + @param[in] SpdEntry Point to SPD entry. + @param[in] IpVersion Version of IP. + @param[in] IpHead Point to IP header. + @param[in] IpPayload Point to IP payload. + @param[in] Protocol The Last protocol of IP packet. + @param[in] IsOutbound Traffic direction. + @param[out] Action The support action of SPD entry. + + @retval EFI_SUCCESS Find the related SPD. + @retval EFI_NOT_FOUND Not find the related SPD entry; + +**/ +EFI_STATUS +IpSecLookupSpdEntry ( + IN IPSEC_SPD_ENTRY *SpdEntry, + IN UINT8 IpVersion, + IN VOID *IpHead, + IN UINT8 *IpPayload, + IN UINT8 Protocol, + IN BOOLEAN IsOutbound, + OUT EFI_IPSEC_ACTION *Action + ); + +/** + Look up if there is existing SAD entry for specified IP packet sending. + + This function is called by the IPsecProcess when there is some IP packet needed to + send out. This function checks if there is an existing SAD entry that can be serviced + to this IP packet sending. If no existing SAD entry could be used, this + function will invoke an IPsec Key Exchange Negotiation. + + @param[in] Private Points to private data. + @param[in] NicHandle Points to a NIC handle. + @param[in] IpVersion The version of IP. + @param[in] IpHead The IP Header of packet to be sent out. + @param[in] IpPayload The IP Payload to be sent out. + @param[in] OldLastHead The Last protocol of the IP packet. + @param[in] SpdEntry Points to a related SPD entry. + @param[out] SadEntry Contains the Point of a related SAD entry. + + @retval EFI_DEVICE_ERROR One of following conditions is TRUE: + - If don't find related UDP service. + - Sequence Number is used up. + - Extension Sequence Number is used up. + @retval EFI_NOT_READY No existing SAD entry could be used. + @retval EFI_SUCCESS Find the related SAD entry. + +**/ +EFI_STATUS +IpSecLookupSadEntry ( + IN IPSEC_PRIVATE_DATA *Private, + IN EFI_HANDLE NicHandle, + IN UINT8 IpVersion, + IN VOID *IpHead, + IN UINT8 *IpPayload, + IN UINT8 OldLastHead, + IN IPSEC_SPD_ENTRY *SpdEntry, + OUT IPSEC_SAD_ENTRY **SadEntry + ); + +/** + Find the SAD through whole SAD list. + + @param[in] Spi The SPI used to search the SAD entry. + @param[in] DestAddress The destination used to search the SAD entry. + @param[in] IpVersion The IP version. Ip4 or Ip6. + + @return The pointer to a certain SAD entry. + +**/ +IPSEC_SAD_ENTRY * +IpSecLookupSadBySpi ( + IN UINT32 Spi, + IN EFI_IP_ADDRESS *DestAddress, + IN UINT8 IpVersion + ) +; + +/** + Handles IPsec packet processing for inbound and outbound IP packets. + + The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet. + The behavior is that it can perform one of the following actions: + bypass the packet, discard the packet, or protect the packet. + + @param[in] This Pointer to the EFI_IPSEC2_PROTOCOL instance. + @param[in] NicHandle Instance of the network interface. + @param[in] IpVersion IPV4 or IPV6. + @param[in, out] IpHead Pointer to the IP Header. + @param[in, out] LastHead The protocol of the next layer to be processed by IPsec. + @param[in, out] OptionsBuffer Pointer to the options buffer. + @param[in, out] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments. + @param[in, out] FragmentCount Number of fragments. + @param[in] TrafficDirection Traffic direction. + @param[out] RecycleSignal Event for recycling of resources. + + @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + +**/ +EFI_STATUS +EFIAPI +IpSecProcess ( + IN EFI_IPSEC2_PROTOCOL *This, + IN EFI_HANDLE NicHandle, + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN OUT UINT8 *LastHead, + IN OUT VOID **OptionsBuffer, + IN OUT UINT32 *OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN OUT UINT32 *FragmentCount, + IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection, + OUT EFI_EVENT *RecycleSignal + ); + +extern EFI_DPC_PROTOCOL *mDpc; +extern EFI_IPSEC2_PROTOCOL mIpSecInstance; + +extern EFI_COMPONENT_NAME2_PROTOCOL gIpSecComponentName2; +extern EFI_COMPONENT_NAME_PROTOCOL gIpSecComponentName; + + +#endif diff --git a/Core/NetworkPkg/IpSecDxe/IpSecMain.c b/Core/NetworkPkg/IpSecDxe/IpSecMain.c new file mode 100644 index 0000000000..a2fefa70d7 --- /dev/null +++ b/Core/NetworkPkg/IpSecDxe/IpSecMain.c @@ -0,0 +1,242 @@ +/** @file + The mian interface of IPsec Protocol. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "IpSecConfigImpl.h" +#include "IpSecImpl.h" + +EFI_IPSEC2_PROTOCOL mIpSecInstance = { IpSecProcess, NULL, TRUE }; + +/** + Handles IPsec packet processing for inbound and outbound IP packets. + + The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet. + The behavior is that it can perform one of the following actions: + bypass the packet, discard the packet, or protect the packet. + + @param[in] This Pointer to the EFI_IPSEC2_PROTOCOL instance. + @param[in] NicHandle Instance of the network interface. + @param[in] IpVersion IPV4 or IPV6. + @param[in, out] IpHead Pointer to the IP Header. + @param[in, out] LastHead The protocol of the next layer to be processed by IPsec. + @param[in, out] OptionsBuffer Pointer to the options buffer. + @param[in, out] OptionsLength Length of the options buffer. + @param[in, out] FragmentTable Pointer to a list of fragments. + @param[in, out] FragmentCount Number of fragments. + @param[in] TrafficDirection Traffic direction. + @param[out] RecycleSignal Event for recycling of resources. + + @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + +**/ +EFI_STATUS +EFIAPI +IpSecProcess ( + IN EFI_IPSEC2_PROTOCOL *This, + IN EFI_HANDLE NicHandle, + IN UINT8 IpVersion, + IN OUT VOID *IpHead, + IN OUT UINT8 *LastHead, + IN OUT VOID **OptionsBuffer, + IN OUT UINT32 *OptionsLength, + IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable, + IN OUT UINT32 *FragmentCount, + IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection, + OUT EFI_EVENT *RecycleSignal + ) +{ + IPSEC_PRIVATE_DATA *Private; + IPSEC_SPD_ENTRY *SpdEntry; + EFI_IPSEC_SPD_SELECTOR *SpdSelector; + IPSEC_SAD_ENTRY *SadEntry; + LIST_ENTRY *SpdList; + LIST_ENTRY *Entry; + EFI_IPSEC_ACTION Action; + EFI_STATUS Status; + UINT8 *IpPayload; + UINT8 OldLastHead; + BOOLEAN IsOutbound; + + if (OptionsBuffer == NULL || + OptionsLength == NULL || + FragmentTable == NULL || + FragmentCount == NULL + ) { + return EFI_INVALID_PARAMETER; + } + Private = IPSEC_PRIVATE_DATA_FROM_IPSEC (This); + IpPayload = (*FragmentTable)[0].FragmentBuffer; + IsOutbound = (BOOLEAN) ((TrafficDirection == EfiIPsecOutBound) ? TRUE : FALSE); + OldLastHead = *LastHead; + *RecycleSignal = NULL; + SpdList = &mConfigData[IPsecConfigDataTypeSpd]; + + if (!IsOutbound) { + // + // For inbound traffic, process the ipsec header of the packet. + // + Status = IpSecProtectInboundPacket ( + IpVersion, + IpHead, + LastHead, + OptionsBuffer, + OptionsLength, + FragmentTable, + FragmentCount, + &SpdSelector, + RecycleSignal + ); + + if (Status == EFI_ACCESS_DENIED || Status == EFI_OUT_OF_RESOURCES) { + // + // The packet is denied to access. + // + goto ON_EXIT; + } + + if (Status == EFI_SUCCESS) { + + // + // Check the spd entry if the packet is accessible. + // + if (SpdSelector == NULL) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + Status = EFI_ACCESS_DENIED; + NET_LIST_FOR_EACH (Entry, SpdList) { + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + if (IsSubSpdSelector ( + (EFI_IPSEC_CONFIG_SELECTOR *) SpdSelector, + (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector + )) { + Status = EFI_SUCCESS; + } + } + goto ON_EXIT; + } + } + + Status = EFI_ACCESS_DENIED; + + NET_LIST_FOR_EACH (Entry, SpdList) { + // + // For outbound and non-ipsec Inbound traffic: check the spd entry. + // + SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); + + if (EFI_ERROR (IpSecLookupSpdEntry ( + SpdEntry, + IpVersion, + IpHead, + IpPayload, + OldLastHead, + IsOutbound, + &Action + ))) { + // + // If the related SPD not find + // + continue; + } + + switch (Action) { + + case EfiIPsecActionProtect: + + if (IsOutbound) { + // + // For outbound traffic, lookup the sad entry. + // + Status = IpSecLookupSadEntry ( + Private, + NicHandle, + IpVersion, + IpHead, + IpPayload, + OldLastHead, + SpdEntry, + &SadEntry + ); + + if (SadEntry != NULL) { + // + // Process the packet by the found sad entry. + // + Status = IpSecProtectOutboundPacket ( + IpVersion, + IpHead, + LastHead, + OptionsBuffer, + OptionsLength, + FragmentTable, + FragmentCount, + SadEntry, + RecycleSignal + ); + + } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) { + // + // TODO: if no need return not ready to upper layer, change here. + // + Status = EFI_SUCCESS; + } + } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) { + // + // For inbound icmpv6 traffic except ping request, accept the packet + // although no sad entry associated with protect spd entry. + // + Status = IpSecLookupSadEntry ( + Private, + NicHandle, + IpVersion, + IpHead, + IpPayload, + OldLastHead, + SpdEntry, + &SadEntry + ); + if (SadEntry == NULL) { + Status = EFI_SUCCESS; + } + } + + goto ON_EXIT; + + case EfiIPsecActionBypass: + Status = EFI_SUCCESS; + goto ON_EXIT; + + case EfiIPsecActionDiscard: + goto ON_EXIT; + } + } + + // + // If don't find the related SPD entry, return the EFI_ACCESS_DENIED and discard it. + // But it the packet is NS/NA, it should be by passed even not find the related SPD entry. + // + if (OldLastHead == IP6_ICMP && + (*IpPayload == ICMP_V6_NEIGHBOR_SOLICIT || *IpPayload == ICMP_V6_NEIGHBOR_ADVERTISE) + ){ + Status = EFI_SUCCESS; + } + +ON_EXIT: + return Status; +} + diff --git a/Core/NetworkPkg/License.txt b/Core/NetworkPkg/License.txt new file mode 100644 index 0000000000..be68999be6 --- /dev/null +++ b/Core/NetworkPkg/License.txt @@ -0,0 +1,25 @@ +Copyright (c) 2012, Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Core/NetworkPkg/Mtftp6Dxe/ComponentName.c b/Core/NetworkPkg/Mtftp6Dxe/ComponentName.c new file mode 100644 index 0000000000..f4327abcf9 --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/ComponentName.c @@ -0,0 +1,430 @@ +/** @file + UEFI Component Name(2) protocol implementation for Mtftp6 driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Mtftp6Impl.h" + + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for bus drivers + attempting to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that attempts to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gMtftp6ComponentName = { + Mtftp6ComponentNameGetDriverName, + Mtftp6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp6ComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mMtftp6DriverNameTable[] = { + { + "eng;en", + L"MTFTP6 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gMtftp6ControllerNameTable = NULL; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mMtftp6DriverNameTable, + DriverName, + (BOOLEAN)(This == &gMtftp6ComponentName) + ); +} + +/** + Update the component name for the Mtftp6 child handle. + + @param Mtftp6[in] A pointer to the EFI_MTFTP6_PROTOCOL. + + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + +**/ +EFI_STATUS +UpdateName ( + IN EFI_MTFTP6_PROTOCOL *Mtftp6 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[128]; + EFI_MTFTP6_MODE_DATA Mtftp6ModeData; + CHAR16 Address[sizeof"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"]; + + if (Mtftp6 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer. + // + Status = Mtftp6->GetModeData (Mtftp6, &Mtftp6ModeData); + if (!EFI_ERROR (Status)) { + Status = NetLibIp6ToStr (&Mtftp6ModeData.ConfigData.ServerIp, Address, sizeof(Address)); + if (EFI_ERROR (Status)) { + return Status; + } + UnicodeSPrint (HandleName, sizeof (HandleName), + L"MTFTPv6(ServerIp=%s, InitialServerPort=%d)", + Address, + Mtftp6ModeData.ConfigData.InitialServerPort + ); + } else { + UnicodeSPrint (HandleName, 0x100, L"MTFTPv6(%r)", Status); + } + + if (gMtftp6ControllerNameTable != NULL) { + FreeUnicodeStringTable (gMtftp6ControllerNameTable); + gMtftp6ControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gMtftp6ComponentName.SupportedLanguages, + &gMtftp6ControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gMtftp6ComponentName2.SupportedLanguages, + &gMtftp6ControllerNameTable, + HandleName, + FALSE + ); +} + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + attempting to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that attempts to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_MTFTP6_PROTOCOL *Mtftp6; + + // + // Only provide names for child handles. + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiUdp6ProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + (VOID **)&Mtftp6, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateName (Mtftp6); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gMtftp6ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gMtftp6ComponentName) + ); +} + diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c new file mode 100644 index 0000000000..5f47938c2d --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c @@ -0,0 +1,755 @@ +/** @file + Driver Binding functions and Service Binding functions + implementation for Mtftp6 Driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Mtftp6Impl.h" + + +EFI_DRIVER_BINDING_PROTOCOL gMtftp6DriverBinding = { + Mtftp6DriverBindingSupported, + Mtftp6DriverBindingStart, + Mtftp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL gMtftp6ServiceBindingTemplate = { + Mtftp6ServiceBindingCreateChild, + Mtftp6ServiceBindingDestroyChild +}; + + +/** + Destroy the MTFTP6 service. The MTFTP6 service may be partly initialized, + or partly destroyed. If a resource is destroyed, it is marked as such in + case the destroy failed and is called again later. + + @param[in] Service The MTFTP6 service to be destroyed. + +**/ +VOID +Mtftp6DestroyService ( + IN MTFTP6_SERVICE *Service + ) +{ + // + // Make sure all children instances have been already destroyed. + // + ASSERT (Service->ChildrenNum == 0); + + if (Service->DummyUdpIo != NULL) { + UdpIoFreeIo (Service->DummyUdpIo); + } + + if (Service->Timer != NULL) { + gBS->CloseEvent (Service->Timer); + } + + FreePool (Service); +} + + +/** + Create then initialize a MTFTP6 service binding instance. + + @param[in] Controller The controller to install the MTFTP6 service + binding on. + @param[in] Image The driver binding image of the MTFTP6 driver. + @param[out] Service The variable to receive the created service + binding instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to create the instance + @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep connection with UDP. + @retval EFI_SUCCESS The service instance is created for the controller. + +**/ +EFI_STATUS +Mtftp6CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image, + OUT MTFTP6_SERVICE **Service + ) +{ + MTFTP6_SERVICE *Mtftp6Srv; + EFI_STATUS Status; + + ASSERT (Service != NULL); + + *Service = NULL; + Mtftp6Srv = AllocateZeroPool (sizeof (MTFTP6_SERVICE)); + + if (Mtftp6Srv == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Mtftp6Srv->Signature = MTFTP6_SERVICE_SIGNATURE; + Mtftp6Srv->Controller = Controller; + Mtftp6Srv->Image = Image; + Mtftp6Srv->ChildrenNum = 0; + + CopyMem ( + &Mtftp6Srv->ServiceBinding, + &gMtftp6ServiceBindingTemplate, + sizeof (EFI_SERVICE_BINDING_PROTOCOL) + ); + + InitializeListHead (&Mtftp6Srv->Children); + + // + // Create a internal timer for all instances. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Mtftp6OnTimerTick, + Mtftp6Srv, + &Mtftp6Srv->Timer + ); + + if (EFI_ERROR (Status)) { + FreePool (Mtftp6Srv); + return Status; + } + + // + // Create a dummy Udp6Io to build parent-child relationship between Udp6 driver + // and Mtftp6 driver. + // + Mtftp6Srv->DummyUdpIo = UdpIoCreateIo ( + Controller, + Image, + Mtftp6ConfigDummyUdpIo, + UDP_IO_UDP6_VERSION, + NULL + ); + + if (Mtftp6Srv->DummyUdpIo == NULL) { + gBS->CloseEvent (Mtftp6Srv->Timer); + FreePool (Mtftp6Srv); + return EFI_DEVICE_ERROR; + } + + *Service = Mtftp6Srv; + return EFI_SUCCESS; +} + + +/** + Destroy the MTFTP6 instance and recycle the resources. + + @param[in] Instance The pointer to the MTFTP6 instance. + +**/ +VOID +Mtftp6DestroyInstance ( + IN MTFTP6_INSTANCE *Instance + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + MTFTP6_BLOCK_RANGE *Block; + + if (Instance->Config != NULL) { + FreePool (Instance->Config); + } + + if (Instance->Token != NULL && Instance->Token->Event != NULL) { + gBS->SignalEvent (Instance->Token->Event); + } + + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + if (Instance->UdpIo!= NULL) { + UdpIoFreeIo (Instance->UdpIo); + } + + if (Instance->McastUdpIo != NULL) { + UdpIoFreeIo (Instance->McastUdpIo); + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) { + Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link); + RemoveEntryList (Entry); + FreePool (Block); + } + + FreePool (Instance); +} + + +/** + Create the MTFTP6 instance and initialize it. + + @param[in] Service The pointer to the MTFTP6 service. + @param[out] Instance The pointer to the MTFTP6 instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The MTFTP6 instance is created. + +**/ +EFI_STATUS +Mtftp6CreateInstance ( + IN MTFTP6_SERVICE *Service, + OUT MTFTP6_INSTANCE **Instance + ) +{ + MTFTP6_INSTANCE *Mtftp6Ins; + + *Instance = NULL; + Mtftp6Ins = AllocateZeroPool (sizeof (MTFTP6_INSTANCE)); + + if (Mtftp6Ins == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Mtftp6Ins->Signature = MTFTP6_INSTANCE_SIGNATURE; + Mtftp6Ins->InDestroy = FALSE; + Mtftp6Ins->Service = Service; + + CopyMem ( + &Mtftp6Ins->Mtftp6, + &gMtftp6ProtocolTemplate, + sizeof (EFI_MTFTP6_PROTOCOL) + ); + + InitializeListHead (&Mtftp6Ins->Link); + InitializeListHead (&Mtftp6Ins->BlkList); + + *Instance = Mtftp6Ins; + + return EFI_SUCCESS; +} + + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = NET_LIST_USER_STRUCT_S (Entry, MTFTP6_INSTANCE, Link, MTFTP6_INSTANCE_SIGNATURE); + ServiceBinding = ((MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (Instance->Handle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); +} + + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + Entry point of the MTFTP6 driver to install various protocols. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable The pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gMtftp6DriverBinding, + ImageHandle, + &gMtftp6ComponentName, + &gMtftp6ComponentName2 + ); +} + + +/** + Test to see if this driver supports Controller. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to test + @param[in] RemainingDevicePath Optional parameter use to pick a specific child. + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + return gBS->OpenProtocol ( + Controller, + &gEfiUdp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); +} + + +/** + Start this driver on Controller. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval EFI_ALREADY_STARTED This driver is already running on Controller. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + MTFTP6_SERVICE *Service; + EFI_STATUS Status; + + // + // Directly return if driver is already running on this Nic handle. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiMtftp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Create Mtftp6 service for this Nic handle + // + Status = Mtftp6CreateService ( + Controller, + This->DriverBindingHandle, + &Service + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Service != NULL); + + // + // Start the internal timer to track the packet retransmission. + // + Status = gBS->SetTimer ( + Service->Timer, + TimerPeriodic, + TICKS_PER_SECOND + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the Mtftp6 service on the Nic handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiMtftp6ServiceBindingProtocolGuid, + &Service->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + Mtftp6DestroyService (Service); + return Status; +} + + +/** + Stop this driver on Controller. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + MTFTP6_SERVICE *Service; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + LIST_ENTRY *List; + MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + + // + // Locate the Nic handle to retrieve the Mtftp6 private data. + // + NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp6ProtocolGuid); + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Service = MTFTP6_SERVICE_FROM_THIS (ServiceBinding); + + if (!IsListEmpty (&Service->Children)) { + // + // Destroy the Mtftp6 child instance in ChildHandleBuffer. + // + List = &Service->Children; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + Mtftp6DestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } + + if (NumberOfChildren == 0 && IsListEmpty (&Service->Children)) { + // + // Destroy the Mtftp6 service if there is no Mtftp6 child instance left. + // + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + ServiceBinding + ); + + Mtftp6DestroyService (Service); + Status = EFI_SUCCESS; + } + + return Status; +} + + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing + UEFI handle, then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval Others The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_STATUS Status; + EFI_TPL OldTpl; + VOID *Udp6; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + Service = MTFTP6_SERVICE_FROM_THIS (This); + + Status = Mtftp6CreateInstance (Service, &Instance); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Instance != NULL); + + // + // Install the Mtftp6 protocol on the new child handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + &Instance->Mtftp6, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->Handle = *ChildHandle; + + // + // Open the Udp6 protocol by child. + // + Status = gBS->OpenProtocol ( + Service->DummyUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + gMtftp6DriverBinding.DriverBindingHandle, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Instance->Handle, + &gEfiMtftp6ProtocolGuid, + &Instance->Mtftp6, + NULL + ); + + goto ON_ERROR; + } + + // + // Add the new Mtftp6 instance into the children list of Mtftp6 service. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&Service->Children, &Instance->Link); + Service->ChildrenNum++; + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +ON_ERROR: + + Mtftp6DestroyInstance (Instance); + return Status; +} + + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval Others The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Locate the Nic handle to retrieve the Mtftp6 private data. + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + (VOID **) &Mtftp6, + gMtftp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = MTFTP6_INSTANCE_FROM_THIS (Mtftp6); + Service = MTFTP6_SERVICE_FROM_THIS (This); + + if (Instance->Service != Service) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether the instance already in Destroy state. + // + if (Instance->InDestroy) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->InDestroy = TRUE; + + gBS->CloseProtocol ( + Service->DummyUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gMtftp6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + if (Instance->UdpIo != NULL) { + gBS->CloseProtocol ( + Instance->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gMtftp6DriverBinding.DriverBindingHandle, + Instance->Handle + ); + } + + if (Instance->McastUdpIo != NULL) { + gBS->CloseProtocol ( + Instance->McastUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gMtftp6DriverBinding.DriverBindingHandle, + Instance->Handle + ); + } + + // + // Uninstall the MTFTP6 protocol first to enable a top down destruction. + // + gBS->RestoreTPL (OldTpl); + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + Mtftp6 + ); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (EFI_ERROR (Status)) { + Instance->InDestroy = FALSE; + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // Remove the Mtftp6 instance from the children list of Mtftp6 service. + // + RemoveEntryList (&Instance->Link); + Service->ChildrenNum --; + + gBS->RestoreTPL (OldTpl); + + Mtftp6DestroyInstance (Instance); + + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h new file mode 100644 index 0000000000..55ac1ddffb --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h @@ -0,0 +1,152 @@ +/** @file + Driver Binding functions and Service Binding functions + declaration for Mtftp6 Driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_MTFTP6_DRIVER_H__ +#define __EFI_MTFTP6_DRIVER_H__ + +#include + +extern EFI_COMPONENT_NAME_PROTOCOL gMtftp6ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2; +extern EFI_UNICODE_STRING_TABLE *gMtftp6ControllerNameTable; + +/** + Test to see if this driver supports Controller. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start this driver on Controller. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval EFI_ALREADY_STARTED This driver is already running on Controller. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on Controller. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing + UEFI handle, then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval Others The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval Others The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf new file mode 100644 index 0000000000..dcc04ade33 --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf @@ -0,0 +1,76 @@ +## @file +# Client-side Mtftp6 service. +# +# This module produces EFI MTFTPv6 Protocol which provides basic services for +# client-side unicast and/or multicast TFTP. +# +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Mtftp6Dxe + FILE_GUID = 99F03B99-98D8-49dd-A8D3-3219D0FFE41E + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Mtftp6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = Mtftp6Dxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gMtftp6DriverBinding +# COMPONENT_NAME = gMtftp6ComponentName +# COMPONENT_NAME2 = gMtftp6ComponentName2 +# + +[Sources] + Mtftp6Driver.c + Mtftp6Driver.h + Mtftp6Impl.c + Mtftp6Impl.h + Mtftp6Option.c + Mtftp6Option.h + Mtftp6Support.h + Mtftp6Support.c + Mtftp6Rrq.c + Mtftp6Wrq.c + ComponentName.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiDriverEntryPoint + DebugLib + NetLib + UdpIoLib + + +[Protocols] + gEfiUdp6ServiceBindingProtocolGuid ## TO_START + gEfiUdp6ProtocolGuid ## TO_START + gEfiMtftp6ServiceBindingProtocolGuid ## BY_START + gEfiMtftp6ProtocolGuid ## BY_START + +[UserExtensions.TianoCore."ExtraFiles"] + Mtftp6DxeExtra.uni diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni new file mode 100644 index 0000000000..af978d5b2e Binary files /dev/null and b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni differ diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni new file mode 100644 index 0000000000..81c5b9b814 Binary files /dev/null and b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni differ diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c new file mode 100644 index 0000000000..9b08455ef3 --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c @@ -0,0 +1,647 @@ +/** @file + This EFI_MTFTP6_PROTOCOL interface implementation. + + It supports the following RFCs: + RFC1350 - THE TFTP PROTOCOL (REVISION 2) + RFC2090 - TFTP Multicast Option + RFC2347 - TFTP Option Extension + RFC2348 - TFTP Blocksize Option + RFC2349 - TFTP Timeout Interval and Transfer Size Options + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Mtftp6Impl.h" + +EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate = { + EfiMtftp6GetModeData, + EfiMtftp6Configure, + EfiMtftp6GetInfo, + EfiMtftp6ParseOptions, + EfiMtftp6ReadFile, + EfiMtftp6WriteFile, + EfiMtftp6ReadDirectory, + EfiMtftp6Poll + }; + +/** + Returns the current operating mode data for the MTFTP6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the MTFTP6 instance. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode + data is returned. + + @retval EFI_SUCCESS The configuration data was returned successfully. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + @retval EFI_INVALID_PARAMETER This is NULL or ModeData is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6GetModeData ( + IN EFI_MTFTP6_PROTOCOL *This, + OUT EFI_MTFTP6_MODE_DATA *ModeData + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_TPL OldTpl; + + if (This == NULL || ModeData == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + + // + // Copy back the configure data if the instance already configured. + // + if (Instance->Config != NULL) { + CopyMem ( + &ModeData->ConfigData, + Instance->Config, + sizeof (EFI_MTFTP6_CONFIG_DATA) + ); + } else { + ZeroMem ( + &ModeData->ConfigData, + sizeof (EFI_MTFTP6_CONFIG_DATA) + ); + } + + // + // Set the current support options in mode data. + // + ModeData->SupportedOptionCount = MTFTP6_SUPPORTED_OPTIONS_NUM; + ModeData->SupportedOptions = (UINT8 **) mMtftp6SupportedOptions; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Initializes, changes, or resets the default operational setting for + this EFI MTFTPv6 Protocol driver instance. + + The Configure() function is used to set and change the configuration + data for this EFI MTFTPv6 Protocol driver instance. The configuration + data can be reset to startup defaults by calling Configure() with + MtftpConfigData set to NULL. Whenever the instance is reset, any + pending operation is aborted. By changing the EFI MTFTPv6 Protocol + driver instance configuration data, the client can connect to + different MTFTPv6 servers. The configuration parameters in + MtftpConfigData are used as the default parameters in later MTFTPv6 + operations and can be overridden in later operations. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] MtftpConfigData Pointer to the configuration data structure. + + @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + - This is NULL. + - MtftpConfigData.StationIp is neither zero nor one + of the configured IP addresses in the underlying IPv6 driver. + - MtftpCofigData.ServerIp is not a valid IPv6 unicast address. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there + is some MTFTP background operation in progress. + - MtftpCofigData.LocalPort is already in use. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be + allocated. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI + MTFTPv6 Protocol driver instance is not configured. + Note: It is not defined in the UEFI 2.3 Specification. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Configure ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA Udp6Cfg; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (MtftpConfigData != NULL && !NetIp6IsValidUnicast (&MtftpConfigData->ServerIp)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + Status = EFI_SUCCESS; + + if (MtftpConfigData == NULL) { + // + // Configure the instance as NULL to abort the current session. + // + Mtftp6OperationClean (Instance, EFI_ABORTED); + FreePool (Instance->Config); + Instance->Config = NULL; + } else { + // + // It's not allowed to configure one instance twice without configure null. + // + if (Instance->Config != NULL) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + // + // Allocate the configure buffer of the instance and store the user's data. + // + Instance->Config = AllocateZeroPool (sizeof (EFI_MTFTP6_CONFIG_DATA)); + + if (Instance->Config == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (Instance->Config, MtftpConfigData, sizeof (EFI_MTFTP6_CONFIG_DATA)); + + // + // Don't configure the udpio here because each operation might override + // the configuration, so delay udpio configuration in each operation. + // + if (Instance->UdpIo == NULL) { + Instance->UdpIo = UdpIoCreateIo ( + Service->Controller, + Service->Image, + Mtftp6ConfigDummyUdpIo, + UDP_IO_UDP6_VERSION, + NULL + ); + if (Instance->UdpIo != NULL) { + Status = gBS->OpenProtocol ( + Instance->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + Service->Image, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + } + + if (Instance->UdpIo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Continue to configure the downside Udp6 instance by user's data. + // + ZeroMem (&Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + Udp6Cfg.AcceptPromiscuous = FALSE; + Udp6Cfg.AcceptAnyPort = FALSE; + Udp6Cfg.AllowDuplicatePort = FALSE; + Udp6Cfg.TrafficClass = 0; + Udp6Cfg.HopLimit = 128; + Udp6Cfg.ReceiveTimeout = 0; + Udp6Cfg.TransmitTimeout = 0; + Udp6Cfg.StationPort = Instance->Config->LocalPort; + Udp6Cfg.RemotePort = Instance->Config->InitialServerPort; + + CopyMem ( + &Udp6Cfg.StationAddress, + &Instance->Config->StationIp, + sizeof(EFI_IPv6_ADDRESS) + ); + + CopyMem ( + &Udp6Cfg.RemoteAddress, + &Instance->Config->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + Udp6 = Instance->UdpIo->Protocol.Udp6; + Status = Udp6->Configure (Udp6, &Udp6Cfg); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + +ON_EXIT: + if (EFI_ERROR (Status)) { + if (Instance->Config != NULL) { + FreePool (Instance->Config); + Instance->Config = NULL; + } + if (Instance->UdpIo != NULL) { + UdpIoFreeIo (Instance->UdpIo); + Instance->UdpIo = NULL; + } + } + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Get the information of the download from the server. + + The GetInfo() function assembles an MTFTPv6 request packet + with options, sends it to the MTFTPv6 server, and may return + an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries + occur only if no response packets are received from the MTFTPv6 + server before the timeout expires. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the + default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure() + function are used. + @param[in] Filename Pointer to null-terminated ASCII file name string. + @param[in] ModeStr Pointer to null-terminated ASCII mode string. If NULL, octet will be used. + @param[in] OptionCount Number of option/value string pairs in OptionList. + @param[in] OptionList Pointer to array of option/value string pairs. Ignored if + OptionCount is zero. + @param[out] PacketLength The number of bytes in the returned packet. + @param[out] Packet The pointer to the received packet. This buffer must be freed by + the caller. + + @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Filename is NULL. + - OptionCount is not zero and OptionList is NULL. + - One or more options in OptionList have wrong format. + - PacketLength is NULL. + - OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by + this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received and is in the Packet. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received and the Packet is set to NULL. + Note: It is not defined in UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received and the Packet is set to NULL. + @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received and the Packet is set to NULL. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + @retval EFI_NO_MEDIA There was a media error. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6GetInfo ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL, + IN UINT8 *Filename, + IN UINT8 *ModeStr OPTIONAL, + IN UINT8 OptionCount, + IN EFI_MTFTP6_OPTION *OptionList OPTIONAL, + OUT UINT32 *PacketLength, + OUT EFI_MTFTP6_PACKET **Packet OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_MTFTP6_TOKEN Token; + MTFTP6_GETINFO_CONTEXT Context; + + if (This == NULL || + Filename == NULL || + PacketLength == NULL || + (OptionCount != 0 && OptionList == NULL) || + (OverrideData != NULL && !NetIp6IsValidUnicast (&OverrideData->ServerIp)) + ) { + return EFI_INVALID_PARAMETER; + } + + if (Packet != NULL) { + *Packet = NULL; + } + + *PacketLength = 0; + + Context.Packet = Packet; + Context.PacketLen = PacketLength; + Context.Status = EFI_SUCCESS; + + // + // Fill fields of the Token for GetInfo operation. + // + Token.Status = EFI_SUCCESS; + Token.Event = NULL; + Token.OverrideData = OverrideData; + Token.Filename = Filename; + Token.ModeStr = ModeStr; + Token.OptionCount = OptionCount; + Token.OptionList = OptionList; + Token.BufferSize = 0; + Token.Buffer = NULL; + Token.Context = &Context; + Token.CheckPacket = Mtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + // + // Start the GetInfo operation by issue the Token. + // + Status = Mtftp6OperationStart (This, &Token, EFI_MTFTP6_OPCODE_RRQ); + + if (Status == EFI_ABORTED) { + // + // Return the status if failed to issue. + // + return Context.Status; + } + + return Status; +} + + +/** + Parse the options in an MTFTPv6 OACK packet. + + The ParseOptions() function parses the option fields in an MTFTPv6 OACK + packet and returns the number of options that were found, and optionally, + a list of pointers to the options in the packet. If one or more of the + option fields are not valid, then EFI_PROTOCOL_ERROR is returned and + *OptionCount and *OptionList stop at the last valid option. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] PacketLen Length of the OACK packet to be parsed. + @param[in] Packet Pointer to the OACK packet to be parsed. + @param[out] OptionCount Pointer to the number of options in the following OptionList. + @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the + OptionList points to the corresponding MTFTP option buffer + in the Packet. Call the EFI Boot Service FreePool() to + release the OptionList if the options in this OptionList + are not needed anymore. + + @retval EFI_SUCCESS The OACK packet was valid and the OptionCount and + OptionList parameters have been updated. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - PacketLen is 0. + - Packet is NULL or Packet is not a valid MTFTPv6 packet. + - OptionCount is NULL. + @retval EFI_NOT_FOUND No options were found in the OACK packet. + @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array can not be allocated. + @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ParseOptions ( + IN EFI_MTFTP6_PROTOCOL *This, + IN UINT32 PacketLen, + IN EFI_MTFTP6_PACKET *Packet, + OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ) +{ + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + return Mtftp6ParseStart (Packet, PacketLen, OptionCount, OptionList); +} + + +/** + Download a file from an MTFTPv6 server. + + The ReadFile() function is used to initialize and start an MTFTPv6 download + process, and optionally, wait for completion. When the download operation + completes, whether successfully or not, the Token.Status field is updated + by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it + is not NULL. + Data can be downloaded from the MTFTPv6 server into either of the following + locations: + - A fixed buffer that is pointed to by Token.Buffer + - A download service function that is pointed to by Token.CheckPacket. + If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket + will be called first. If the call is successful, the packet will be stored + in Token.Buffer. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The data file has been transferred successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_BUFFER_TOO_SMALL BufferSize is not zero but not large enough to hold the + downloaded data in downloading process. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ABORTED Current operation is aborted by user. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_ICMP_ERROR An ICMP ERROR packet was received. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + @retval EFI_NO_MEDIA There was a media error. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ReadFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ) +{ + return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_RRQ); +} + + +/** + Send a file to an MTFTPv6 server. + + The WriteFile() function is used to initialize an uploading operation + with the given option list and optionally wait for completion. If one + or more of the options is not supported by the server, the unsupported + options are ignored and a standard TFTP process starts instead. When + the upload process completes, whether successfully or not, Token.Event + is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status. + The caller can supply the data to be uploaded in the following two modes: + - Through the user-provided buffer + - Through a callback function + With the user-provided buffer, the Token.BufferSize field indicates + the length of the buffer, and the driver will upload the data in the + buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver + will call this callback function to get more data from the user to upload. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The upload session has started. + @retval EFI_UNSUPPORTED The operation is not supported by this implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.PacketNeeded are both NULL. + - Token.OverrideData.ServerIp is not a valid unicast IPv6 address. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6WriteFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ) +{ + return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_WRQ); +} + + +/** + Download a data file directory from an MTFTPv6 server. + + The ReadDirectory() function is used to return a list of files on the + MTFTPv6 server that are logically (or operationally) related to + Token.Filename. The directory request packet that is sent to the server + is built with the option list that was provided by the caller, if present. + The file information that the server returns is put into either of + the following locations: + - A fixed buffer that is pointed to by Token.Buffer. + - A download service function that is pointed to by Token.CheckPacket. + If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket + will be called first. If the call is successful, the packet will be stored + in Token.Buffer. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded. + @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.CheckPacket are both NULL. + - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ReadDirectory ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ) +{ + return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_DIR); +} + + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function can be used by network drivers and applications + to increase the rate that data packets are moved between the + communications device and the transmit and receive queues. In some + systems, the periodic timer event in the managed network driver may + not poll the underlying communications device fast enough to transmit + and/or receive all data packets without missing incoming packets or + dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This The MTFTP6 protocol instance. + + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Poll ( + IN EFI_MTFTP6_PROTOCOL *This + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_UDP6_PROTOCOL *Udp6; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + + // + // Check the instance whether configured or in destroy. + // + if (Instance->Config == NULL) { + return EFI_NOT_STARTED; + } + + Udp6 = Instance->UdpIo->Protocol.Udp6; + + return Udp6->Poll (Udp6); +} diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h new file mode 100644 index 0000000000..6b1ce7f853 --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h @@ -0,0 +1,475 @@ +/** @file + Mtftp6 internal data structure and definition declaration. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_MTFTP6_IMPL_H__ +#define __EFI_MTFTP6_IMPL_H__ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef struct _MTFTP6_SERVICE MTFTP6_SERVICE; +typedef struct _MTFTP6_INSTANCE MTFTP6_INSTANCE; + +#include "Mtftp6Driver.h" +#include "Mtftp6Option.h" +#include "Mtftp6Support.h" + +#define MTFTP6_SERVICE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'S') +#define MTFTP6_INSTANCE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'I') + +#define MTFTP6_DEFAULT_SERVER_CMD_PORT 69 +#define MTFTP6_DEFAULT_TIMEOUT 3 +#define MTFTP6_GET_MAPPING_TIMEOUT 3 +#define MTFTP6_DEFAULT_MAX_RETRY 5 +#define MTFTP6_DEFAULT_BLK_SIZE 512 +#define MTFTP6_TICK_PER_SECOND 10000000U + +#define MTFTP6_SERVICE_FROM_THIS(a) CR (a, MTFTP6_SERVICE, ServiceBinding, MTFTP6_SERVICE_SIGNATURE) +#define MTFTP6_INSTANCE_FROM_THIS(a) CR (a, MTFTP6_INSTANCE, Mtftp6, MTFTP6_INSTANCE_SIGNATURE) + +extern EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate; + +typedef struct _MTFTP6_GETINFO_CONTEXT{ + EFI_MTFTP6_PACKET **Packet; + UINT32 *PacketLen; + EFI_STATUS Status; +} MTFTP6_GETINFO_CONTEXT; + +// +// Control block for MTFTP6 instance, it's per configuration data. +// +struct _MTFTP6_INSTANCE { + UINT32 Signature; + EFI_HANDLE Handle; + LIST_ENTRY Link; + EFI_MTFTP6_PROTOCOL Mtftp6; + MTFTP6_SERVICE *Service; + EFI_MTFTP6_CONFIG_DATA *Config; + + EFI_MTFTP6_TOKEN *Token; + MTFTP6_EXT_OPTION_INFO ExtInfo; + + UINT16 BlkSize; + UINT16 LastBlk; + LIST_ENTRY BlkList; + + EFI_IPv6_ADDRESS ServerIp; + UINT16 ServerCmdPort; + UINT16 ServerDataPort; + UDP_IO *UdpIo; + + EFI_IPv6_ADDRESS McastIp; + UINT16 McastPort; + UDP_IO *McastUdpIo; + + NET_BUF *LastPacket; + UINT32 CurRetry; + UINT32 MaxRetry; + UINT32 PacketToLive; + UINT32 Timeout; + + EFI_TPL OldTpl; + BOOLEAN IsTransmitted; + BOOLEAN IsMaster; + BOOLEAN InDestroy; +}; + +// +// Control block for MTFTP6 service, it's per Nic handle. +// +struct _MTFTP6_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_HANDLE Controller; + EFI_HANDLE Image; + + UINT16 ChildrenNum; + LIST_ENTRY Children; + // + // It is used to be as internal calculagraph for all instances. + // + EFI_EVENT Timer; + // + // It is used to maintain the parent-child relationship between + // mtftp driver and udp driver. + // + UDP_IO *DummyUdpIo; +}; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +} MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +/** + Returns the current operating mode data for the MTFTP6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the MTFTP6 instance. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode + data is returned. + + @retval EFI_SUCCESS The configuration data was returned successfully. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + @retval EFI_INVALID_PARAMETER This is NULL, or ModeData is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6GetModeData ( + IN EFI_MTFTP6_PROTOCOL *This, + OUT EFI_MTFTP6_MODE_DATA *ModeData + ); + +/** + Initializes, changes, or resets the default operational setting for + this EFI MTFTPv6 Protocol driver instance. + + The Configure() function is used to set and change the configuration + data for this EFI MTFTPv6 Protocol driver instance. The configuration + data can be reset to startup defaults by calling Configure() with + MtftpConfigData set to NULL. Whenever the instance is reset, any + pending operation is aborted. By changing the EFI MTFTPv6 Protocol + driver instance configuration data, the client can connect to + different MTFTPv6 servers. The configuration parameters in + MtftpConfigData are used as the default parameters in later MTFTPv6 + operations and can be overridden in later operations. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] MtftpConfigData Pointer to the configuration data structure. + + @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + - This is NULL. + - MtftpConfigData.StationIp is neither zero nor one + of the configured IP addresses in the underlying IPv6 driver. + - MtftpCofigData.ServerIp is not a valid IPv6 unicast address. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there + is some MTFTP background operation in progress. + - MtftpCofigData.LocalPort is already in use. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be + allocated. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI + MTFTPv6 Protocol driver instance is not configured. + Note: It is not defined in the UEFI 2.3 Specification. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Configure ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL + ); + +/** + Get the information of the download from the server. + + The GetInfo() function assembles an MTFTPv6 request packet + with options, sends it to the MTFTPv6 server, and may return + an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries + occur only if no response packets are received from the MTFTPv6 + server before the timeout expires. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the + default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure() + function are used. + @param[in] Filename Pointer to null-terminated ASCII file name string. + @param[in] ModeStr Pointer to null-terminated ASCII mode string. If NULL, octet will be used + @param[in] OptionCount Number of option/value string pairs in OptionList. + @param[in] OptionList Pointer to array of option/value string pairs. Ignored if + OptionCount is zero. + @param[out] PacketLength The number of bytes in the returned packet. + @param[out] Packet The pointer to the received packet. This buffer must be freed by + the caller. + + @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Filename is NULL. + - OptionCount is not zero and OptionList is NULL. + - One or more options in OptionList have wrong format. + - PacketLength is NULL. + - OverrideData.ServerIp is not a valid unicast IPv6 address. + @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by + this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received and is in the Packet. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received, and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received, and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received, and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received, and the Packet is set to NULL. + @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received, and the Packet is set to NULL. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6GetInfo ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL, + IN UINT8 *Filename, + IN UINT8 *ModeStr OPTIONAL, + IN UINT8 OptionCount, + IN EFI_MTFTP6_OPTION *OptionList OPTIONAL, + OUT UINT32 *PacketLength, + OUT EFI_MTFTP6_PACKET **Packet OPTIONAL + ); + +/** + Parse the options in an MTFTPv6 OACK packet. + + The ParseOptions() function parses the option fields in an MTFTPv6 OACK + packet and returns the number of options that were found, and optionally, + a list of pointers to the options in the packet. If one or more of the + option fields are not valid, then EFI_PROTOCOL_ERROR is returned and + *OptionCount and *OptionList stop at the last valid option. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] PacketLen Length of the OACK packet to be parsed. + @param[in] Packet Pointer to the OACK packet to be parsed. + @param[out] OptionCount Pointer to the number of options in the following OptionList. + @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the + OptionList points to the corresponding MTFTP option buffer + in the Packet. Call the EFI Boot Service FreePool() to + release the OptionList if the options in this OptionList + are not needed any more. + + @retval EFI_SUCCESS The OACK packet was valid and the OptionCount, and + OptionList parameters have been updated. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - PacketLen is 0. + - Packet is NULL or Packet is not a valid MTFTPv6 packet. + - OptionCount is NULL. + @retval EFI_NOT_FOUND No options were found in the OACK packet. + @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array can not be allocated. + @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ParseOptions ( + IN EFI_MTFTP6_PROTOCOL *This, + IN UINT32 PacketLen, + IN EFI_MTFTP6_PACKET *Packet, + OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ); + +/** + Download a file from an MTFTPv6 server. + + The ReadFile() function is used to initialize and start an MTFTPv6 download + process and optionally wait for completion. When the download operation + completes, whether successfully or not, the Token.Status field is updated + by the EFI MTFTPv6 Protocol driver, and then Token.Event is signaled if it + is not NULL. + Data can be downloaded from the MTFTPv6 server into either of the following + locations: + - A fixed buffer that is pointed to by Token.Buffer. + - A download service function that is pointed to by Token.CheckPacket. + If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket + will be called first. If the call is successful, the packet will be stored + in Token.Buffer. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The data file has been transferred successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_BUFFER_TOO_SMALL BufferSize is not zero but not large enough to hold the + downloaded data in downloading process. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ABORTED Current operation is aborted by user. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_ICMP_ERROR An ICMP ERROR packet was received. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ReadFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ); + +/** + Send a file to an MTFTPv6 server. + + The WriteFile() function is used to initialize an uploading operation + with the given option list, and optionally, wait for completion. If one + or more of the options is not supported by the server, the unsupported + options are ignored and a standard TFTP process starts instead. When + the upload process completes, whether successfully or not, Token.Event + is signaled, and the EFI MTFTPv6 Protocol driver updates Token.Status. + The caller can supply the data to be uploaded in the following two modes: + - Through the user-provided buffer. + - Through a callback function. + With the user-provided buffer, the Token.BufferSize field indicates + the length of the buffer, and the driver will upload the data in the + buffer. With an EFI_MTFTP6_PACKET_NEEDED callback function, the driver + will call this callback function to get more data from the user to upload. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The upload session has started. + @retval EFI_UNSUPPORTED The operation is not supported by this implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.PacketNeeded are both NULL. + - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6WriteFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ); + +/** + Download a data file directory from an MTFTPv6 server. + + The ReadDirectory() function is used to return a list of files on the + MTFTPv6 server that are logically (or operationally) related to + Token.Filename. The directory request packet that is sent to the server + is built with the option list that was provided by caller, if present. + The file information that the server returns is put into either of + the following locations: + - A fixed buffer that is pointed to by Token.Buffer. + - A download service function that is pointed to by Token.CheckPacket. + If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket + will be called first. If the call is successful, the packet will be stored + in Token.Buffer. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded. + @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.CheckPacket are both NULL. + - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 session. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The previous operation has not completed yet. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ReadDirectory ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ); + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function can be used by network drivers and applications + to increase the rate that data packets are moved between the + communications device and the transmit and receive queues.In some + systems, the periodic timer event in the managed network driver may + not poll the underlying communications device fast enough to transmit + and/or receive all data packets without missing incoming packets or + dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This The MTFTP6 protocol instance. + + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Poll ( + IN EFI_MTFTP6_PROTOCOL *This + ); + +#endif diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c new file mode 100644 index 0000000000..0dcf546fa8 --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c @@ -0,0 +1,416 @@ +/** @file + Mtftp6 option parse functions implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Mtftp6Impl.h" + +CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM] = { + "blksize", + "timeout", + "tsize", + "multicast" +}; + + +/** + Parse the NULL terminated ASCII string of multicast option. + + @param[in] Str The pointer to the Ascii string of multicast option. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER The string is malformatted. + @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of + resources. + +**/ +EFI_STATUS +Mtftp6ParseMcastOption ( + IN UINT8 *Str, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ) +{ + EFI_STATUS Status; + UINT32 Num; + CHAR8 *Ip6Str; + CHAR8 *TempStr; + + // + // The multicast option is formated like "addr,port,mc" + // The server can also omit the ip and port, use ",,1" + // + if (*Str == ',') { + + ZeroMem (&ExtInfo->McastIp, sizeof (EFI_IPv6_ADDRESS)); + } else { + + Ip6Str = (CHAR8 *) AllocateCopyPool (AsciiStrSize ((CHAR8 *) Str), Str); + if (Ip6Str == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // The IPv6 address locates before comma in the input Str. + // + TempStr = Ip6Str; + while ((*TempStr != '\0') && (*TempStr != ',')) { + TempStr++; + } + + *TempStr = '\0'; + + Status = NetLibAsciiStrToIp6 (Ip6Str, &ExtInfo->McastIp); + FreePool (Ip6Str); + + if (EFI_ERROR (Status)) { + return Status; + } + + while ((*Str != '\0') && (*Str != ',')) { + Str++; + } + } + + if (*Str != ',') { + return EFI_INVALID_PARAMETER; + } + + Str++; + + // + // Convert the port setting. the server can send us a port number or + // empty string. such as the port in ",,1" + // + if (*Str == ',') { + + ExtInfo->McastPort = 0; + } else { + + Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str); + + if (Num > 65535) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->McastPort = (UINT16) Num; + + while (NET_IS_DIGIT (*Str)) { + Str++; + } + } + + if (*Str != ',') { + return EFI_INVALID_PARAMETER; + } + + Str++; + + // + // Check the master/slave setting, 1 for master, 0 for slave. + // + Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str); + + if (Num != 0 && Num != 1) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->IsMaster = (BOOLEAN) (Num == 1); + + while (NET_IS_DIGIT (*Str)) { + Str++; + } + + if (*Str != '\0') { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +/** + Parse the MTFTP6 extesion options. + + @param[in] Options The pointer to the extension options list. + @param[in] Count The num of the extension options. + @param[in] IsRequest If FALSE, the extension options is included + by a request packet. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER There is one option is malformatted at least. + @retval EFI_UNSUPPORTED There is one option is not supported at least. + +**/ +EFI_STATUS +Mtftp6ParseExtensionOption ( + IN EFI_MTFTP6_OPTION *Options, + IN UINT32 Count, + IN BOOLEAN IsRequest, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ) +{ + EFI_STATUS Status; + EFI_MTFTP6_OPTION *Opt; + UINT32 Index; + UINT32 Value; + + ExtInfo->BitMap = 0; + + for (Index = 0; Index < Count; Index++) { + + Opt = Options + Index; + + if (Opt->OptionStr == NULL || Opt->ValueStr == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "blksize") == 0) { + // + // block size option, valid value is between [8, 65464] + // + Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + + if ((Value < 8) || (Value > 65464)) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->BlkSize = (UINT16) Value; + ExtInfo->BitMap |= MTFTP6_OPT_BLKSIZE_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "timeout") == 0) { + // + // timeout option, valid value is between [1, 255] + // + Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + + if (Value < 1 || Value > 255) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->Timeout = (UINT8) Value; + ExtInfo->BitMap |= MTFTP6_OPT_TIMEOUT_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "tsize") == 0) { + // + // tsize option, the biggest transfer supported is 4GB with block size option + // + ExtInfo->Tsize = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + ExtInfo->BitMap |= MTFTP6_OPT_TSIZE_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "multicast") == 0) { + // + // Multicast option, if it is a request, the value must be a zero string, + // otherwise, it must be like "addr,port,mc" string, mc indicates master. + // + if (!IsRequest) { + + Status = Mtftp6ParseMcastOption (Opt->ValueStr, ExtInfo); + + if (EFI_ERROR (Status)) { + return Status; + } + } else if (*(Opt->ValueStr) != '\0') { + + return EFI_INVALID_PARAMETER; + } + + ExtInfo->BitMap |= MTFTP6_OPT_MCAST_BIT; + + } else if (IsRequest) { + // + // If it's a request, unsupported; else if it's a reply, ignore. + // + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + + +/** + Go through the packet to fill the options array with the start + addresses of each MTFTP option name/value pair. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] Count The num of the Options on input. + The actual one on output. + @param[in] Options The option array to be filled. + It is optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_BUFFER_TOO_SMALL The Options array is too small. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received. + +**/ +EFI_STATUS +Mtftp6ParsePacketOption ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *Count, + IN EFI_MTFTP6_OPTION *Options OPTIONAL + ) +{ + UINT8 *Cur; + UINT8 *Last; + UINT8 Num; + UINT8 *Name; + UINT8 *Value; + + Num = 0; + Cur = (UINT8 *) Packet + MTFTP6_OPCODE_LEN; + Last = (UINT8 *) Packet + PacketLen - 1; + + // + // process option name and value pairs. + // The last byte is always zero. + // + while (Cur < Last) { + Name = Cur; + + while (*Cur != 0) { + Cur++; + } + + if (Cur == Last) { + return EFI_PROTOCOL_ERROR; + } + + Value = ++Cur; + + while (*Cur != 0) { + Cur++; + } + + Num++; + + if (Options != NULL && Num <= *Count) { + Options[Num - 1].OptionStr = Name; + Options[Num - 1].ValueStr = Value; + } + + Cur++; + } + + // + // Return buffer too small if the buffer passed-in isn't enough. + // + if (*Count < Num || Options == NULL) { + *Count = Num; + return EFI_BUFFER_TOO_SMALL; + } + + *Count = Num; + return EFI_SUCCESS; +} + + +/** + Go through the packet, generate option list array and fill it + by the result of parse options. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] OptionCount The num of the Options on input. + The actual one on output. + @param[out] OptionList The option list array to be generated + and filled. It is optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_PROTOCOL_ERROR There is one option is malformatted at least. + @retval EFI_NOT_FOUND The packet has no options. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array. + @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small. + +**/ +EFI_STATUS +Mtftp6ParseStart ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ) +{ + EFI_STATUS Status; + + if (PacketLen == 0 || Packet == NULL || OptionCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + *OptionCount = 0; + + if (OptionList != NULL) { + *OptionList = NULL; + } + + if (NTOHS (Packet->OpCode) != EFI_MTFTP6_OPCODE_OACK) { + return EFI_INVALID_PARAMETER; + } + + // + // The last byte must be zero to terminate the options. + // + if (*((UINT8 *) Packet + PacketLen - 1) != 0) { + return EFI_PROTOCOL_ERROR; + } + + // + // Parse packet with NULL buffer for the first time to get the number + // of options in the packet. + // + Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, NULL); + + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + // + // Return not found if there is no option parsed. + // + if (*OptionCount == 0) { + return EFI_NOT_FOUND; + } + + // + // Only need parse out the number of options. + // + if (OptionList == NULL) { + return EFI_SUCCESS; + } + + // + // Allocate the buffer according to the option number parsed before. + // + *OptionList = AllocateZeroPool (*OptionCount * sizeof (EFI_MTFTP6_OPTION)); + + if (*OptionList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Parse packet with allocated buffer for the second time to fill the pointer array + // of the options in the packet. + // + Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, *OptionList); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h new file mode 100644 index 0000000000..8e2671fa21 --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h @@ -0,0 +1,148 @@ +/** @file + Mtftp6 option parse functions declaration. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_MTFTP6_OPTION_H__ +#define __EFI_MTFTP6_OPTION_H__ + +#include + +#include + +#include +#include +#include +#include +#include + +#define MTFTP6_SUPPORTED_OPTIONS_NUM 4 +#define MTFTP6_OPCODE_LEN 2 +#define MTFTP6_ERRCODE_LEN 2 +#define MTFTP6_BLKNO_LEN 2 +#define MTFTP6_DATA_HEAD_LEN 4 + +// +// The bit map definition for Mtftp6 extension options. +// +#define MTFTP6_OPT_BLKSIZE_BIT 0x01 +#define MTFTP6_OPT_TIMEOUT_BIT 0x02 +#define MTFTP6_OPT_TSIZE_BIT 0x04 +#define MTFTP6_OPT_MCAST_BIT 0x08 + +extern CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM]; + +typedef struct { + UINT16 BlkSize; + UINT8 Timeout; + UINT32 Tsize; + EFI_IPv6_ADDRESS McastIp; + UINT16 McastPort; + BOOLEAN IsMaster; + UINT32 BitMap; +} MTFTP6_EXT_OPTION_INFO; + +/** + Parse the Ascii string of multi-cast option. + + @param[in] Str The pointer to the Ascii string of multi-cast option. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER The string is malformatted. + +**/ +EFI_STATUS +Mtftp6ParseMcastOption ( + IN UINT8 *Str, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ); + + +/** + Parse the MTFTP6 extesion options. + + @param[in] Options The pointer to the extension options list. + @param[in] Count The num of the extension options. + @param[in] IsRequest If FALSE, the extension options is included + by a request packet. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multi-cast option successfully. + @retval EFI_INVALID_PARAMETER An option is malformatted. + @retval EFI_UNSUPPORTED An option is not supported. + +**/ +EFI_STATUS +Mtftp6ParseExtensionOption ( + IN EFI_MTFTP6_OPTION *Options, + IN UINT32 Count, + IN BOOLEAN IsRequest, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ); + + +/** + Go through the packet to fill the options array with the start + addresses of each MTFTP option name/value pair. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] Count The num of the Options on input. + The actual one on output. + @param[in] Options The option array to be filled + it's optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted + @retval EFI_BUFFER_TOO_SMALL The Options array is too small + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received. + +**/ +EFI_STATUS +Mtftp6ParsePacketOption ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *Count, + IN EFI_MTFTP6_OPTION *Options OPTIONAL + ); + + +/** + Go through the packet, generate option list array and fill it + by the result of parse options. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] OptionCount The num of the Options on input. + The actual one on output. + @param[out] OptionList The option list array to be generated + and filled. It is optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_PROTOCOL_ERROR An option is malformatted. + @retval EFI_NOT_FOUND The packet has no options. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array. + @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small. + +**/ +EFI_STATUS +Mtftp6ParseStart ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ); + +#endif diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c new file mode 100644 index 0000000000..2aadef076c --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c @@ -0,0 +1,921 @@ +/** @file + Mtftp6 Rrq process functions implementation. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Mtftp6Impl.h" + + +/** + Build and send a ACK packet for download. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] BlockNum The block number to be acked. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet. + @retval EFI_SUCCESS The ACK has been sent. + @retval Others Failed to send the ACK. + +**/ +EFI_STATUS +Mtftp6RrqSendAck ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 BlockNum + ) +{ + EFI_MTFTP6_PACKET *Ack; + NET_BUF *Packet; + + // + // Allocate net buffer to create ack packet. + // + Packet = NetbufAlloc (sizeof (EFI_MTFTP6_ACK_HEADER)); + + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ack = (EFI_MTFTP6_PACKET *) NetbufAllocSpace ( + Packet, + sizeof (EFI_MTFTP6_ACK_HEADER), + FALSE + ); + ASSERT (Ack != NULL); + + Ack->Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK); + Ack->Ack.Block[0] = HTONS (BlockNum); + + // + // Reset current retry count of the instance. + // + Instance->CurRetry = 0; + Instance->LastPacket = Packet; + + return Mtftp6TransmitPacket (Instance, Packet); +} + + +/** + Deliver the received data block to the user, which can be saved + in the user provide buffer or through the CheckPacket callback. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The packet length. + @param[out] UdpPacket The net buf of the received packet. + + @retval EFI_SUCCESS The data was saved successfully. + @retval EFI_ABORTED The user tells to abort by return an error through + CheckPacket. + @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small, and buffer length is + updated to the actual buffer size needed. + +**/ +EFI_STATUS +Mtftp6RrqSaveBlock ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket + ) +{ + EFI_MTFTP6_TOKEN *Token; + EFI_STATUS Status; + UINT16 Block; + UINT64 Start; + UINT32 DataLen; + UINT64 TotalBlock; + BOOLEAN Completed; + + Completed = FALSE; + Token = Instance->Token; + Block = NTOHS (Packet->Data.Block); + DataLen = Len - MTFTP6_DATA_HEAD_LEN; + + // + // This is the last block, save the block num + // + if (DataLen < Instance->BlkSize) { + Completed = TRUE; + Instance->LastBlk = Block; + Mtftp6SetLastBlockNum (&Instance->BlkList, Block); + } + + // + // Remove this block number from the file hole. If Mtftp6RemoveBlockNum + // returns EFI_NOT_FOUND, the block has been saved, don't save it again. + // Note that : For bigger files, allowing the block counter to roll over + // to accept transfers of unlimited size. So TotalBlock is memorised as + // continuous block counter. + // + Status = Mtftp6RemoveBlockNum (&Instance->BlkList, Block, Completed, &TotalBlock); + + if (Status == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } else if (EFI_ERROR (Status)) { + return Status; + } + + if (Token->CheckPacket != NULL) { + // + // Callback to the check packet routine with the received packet. + // + Status = Token->CheckPacket (&Instance->Mtftp6, Token, (UINT16) Len, Packet); + + if (EFI_ERROR (Status)) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the Udp6Io might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "User aborted download" + ); + + return EFI_ABORTED; + } + } + + if (Token->Buffer != NULL) { + + Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize); + if (Start + DataLen <= Token->BufferSize) { + CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen); + // + // Update the file size when received the last block + // + if ((Instance->LastBlk == Block) && Completed) { + Token->BufferSize = Start + DataLen; + } + } else if (Instance->LastBlk != 0) { + // + // Don't save the data if the buffer is too small, return + // EFI_BUFFER_TOO_SMALL if received the last packet. This + // will give a accurate file length. + // + Token->BufferSize = Start + DataLen; + + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if no enough buffer. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_DISK_FULL, + (UINT8 *) "User provided memory block is too small" + ); + + return EFI_BUFFER_TOO_SMALL; + } + } + + return EFI_SUCCESS; +} + + +/** + Process the received data packets. It will save the block + then send back an ACK if it is active. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the download has been completed. + Otherwise, the download has not been completed. + + @retval EFI_SUCCESS The data packet was successfully processed. + @retval EFI_ABORTED The download was aborted by the user. + @retval EFI_BUFFER_TOO_SMALL The user-provided buffer is too small. + +**/ +EFI_STATUS +Mtftp6RrqHandleData ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_STATUS Status; + UINT16 BlockNum; + INTN Expected; + + *IsCompleted = FALSE; + BlockNum = NTOHS (Packet->Data.Block); + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + ASSERT (Expected >= 0); + + // + // If we are active and received an unexpected packet, retransmit + // the last ACK then restart receiving. If we are passive, save + // the block. + // + if (Instance->IsMaster && (Expected != BlockNum)) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + Mtftp6TransmitPacket (Instance, Instance->LastPacket); + return EFI_SUCCESS; + } + + Status = Mtftp6RrqSaveBlock (Instance, Packet, Len, UdpPacket); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Reset the passive client's timer whenever it received a valid data packet. + // + if (!Instance->IsMaster) { + Instance->PacketToLive = Instance->Timeout * 2; + } + + // + // Check whether we have received all the blocks. Send the ACK if we + // are active (unicast client or master client for multicast download). + // If we have received all the blocks, send an ACK even if we are passive + // to tell the server that we are done. + // + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Instance->IsMaster || Expected < 0) { + if (Expected < 0) { + // + // If we are passive client, then the just received Block maybe + // isn't the last block. We need to send an ACK to the last block + // to inform the server that we are done. If we are active client, + // the Block == Instance->LastBlock. + // + BlockNum = Instance->LastBlk; + *IsCompleted = TRUE; + + } else { + BlockNum = (UINT16) (Expected - 1); + } + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + Mtftp6RrqSendAck (Instance, BlockNum); + } + + return EFI_SUCCESS; +} + + +/** + Validate whether the options received in the server's OACK packet is valid. + The options are valid only if: + 1. The server doesn't include options not requested by us. + 2. The server can only use smaller blksize than that is requested. + 3. The server can only use the same timeout as requested. + 4. The server doesn't change its multicast channel. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] ReplyInfo The pointer to options information in reply packet. + @param[in] RequestInfo The pointer to requested options info. + + @retval TRUE If the option in the OACK is valid. + @retval FALSE If the option is invalid. + +**/ +BOOLEAN +Mtftp6RrqOackValid ( + IN MTFTP6_INSTANCE *Instance, + IN MTFTP6_EXT_OPTION_INFO *ReplyInfo, + IN MTFTP6_EXT_OPTION_INFO *RequestInfo + ) +{ + // + // It is invalid for server to return options we don't request + // + if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size to be used and + // return the timeout matches that requested. + // + if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) || + (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout)) + ) { + return FALSE; + } + + // + // The server can send ",,master" to client to change its master + // setting. But if it use the specific multicast channel, it can't + // change the setting. + // + if (((ReplyInfo->BitMap & MTFTP6_OPT_MCAST_BIT) != 0) && !NetIp6IsUnspecifiedAddr (&Instance->McastIp)) { + + if (!NetIp6IsUnspecifiedAddr (&ReplyInfo->McastIp) && CompareMem ( + &ReplyInfo->McastIp, + &Instance->McastIp, + sizeof (EFI_IPv6_ADDRESS) + ) != 0) { + return FALSE; + } + + if ((ReplyInfo->McastPort != 0) && (ReplyInfo->McastPort != Instance->McastPort)) { + return FALSE; + } + } + + return TRUE; +} + + +/** + Configure Udp6Io to receive a packet from a multicast address. + + @param[in] McastIo The pointer to the mcast Udp6Io. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS The mcast Udp6Io was successfully configured. + @retval Others Failed to configure the Udp6Io. + +**/ +EFI_STATUS +EFIAPI +Mtftp6RrqConfigMcastUdpIo ( + IN UDP_IO *McastIo, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA *Udp6Cfg; + EFI_IPv6_ADDRESS Group; + MTFTP6_INSTANCE *Instance; + + Udp6 = McastIo->Protocol.Udp6; + Udp6Cfg = &(McastIo->Config.Udp6); + Instance = (MTFTP6_INSTANCE *) Context; + + // + // Set the configure data for the mcast Udp6Io. + // + ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + Udp6Cfg->AcceptPromiscuous = FALSE; + Udp6Cfg->AcceptAnyPort = FALSE; + Udp6Cfg->AllowDuplicatePort = FALSE; + Udp6Cfg->TrafficClass = 0; + Udp6Cfg->HopLimit = 128; + Udp6Cfg->ReceiveTimeout = 0; + Udp6Cfg->TransmitTimeout = 0; + Udp6Cfg->StationPort = Instance->McastPort; + Udp6Cfg->RemotePort = 0; + + CopyMem ( + &Udp6Cfg->RemoteAddress, + &Instance->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + // + // Configure the mcast Udp6Io. + // + Status = Udp6->Configure (Udp6, Udp6Cfg); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Join the multicast group + // + CopyMem (&Group, &Instance->McastIp, sizeof (EFI_IPv6_ADDRESS)); + + return Udp6->Groups (Udp6, TRUE, &Group); +} + + +/** + Process the OACK packet for Rrq. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the download has been completed. + Otherwise, the download has not been completed. + + @retval EFI_DEVICE_ERROR Failed to create/start a multicast Udp6 child. + @retval EFI_TFTP_ERROR An error happened during the process. + @retval EFI_SUCCESS The OACK packet successfully processed. + +**/ +EFI_STATUS +Mtftp6RrqHandleOack ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_MTFTP6_OPTION *Options; + UINT32 Count; + MTFTP6_EXT_OPTION_INFO ExtInfo; + EFI_STATUS Status; + INTN Expected; + EFI_UDP6_PROTOCOL *Udp6; + + *IsCompleted = FALSE; + Options = NULL; + + // + // If already started the master download, don't change the + // setting. Master download always succeeds. + // + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + ASSERT (Expected != -1); + + if (Instance->IsMaster && Expected != 1) { + return EFI_SUCCESS; + } + + ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + + // + // Parse the options in the packet. + // + Status = Mtftp6ParseStart (Packet, Len, &Count, &Options); + + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Options != NULL); + + // + // Parse the extensive options in the packet. + // + Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo); + + if (EFI_ERROR (Status) || !Mtftp6RrqOackValid (Instance, &ExtInfo, &Instance->ExtInfo)) { + // + // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES. + // + if (Status != EFI_OUT_OF_RESOURCES) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid packet. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Mal-formated OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if ((ExtInfo.BitMap & MTFTP6_OPT_MCAST_BIT) != 0) { + + // + // Save the multicast info. Always update the Master, only update the + // multicast IP address, block size, timeoute at the first time. If IP + // address is updated, create a UDP child to receive the multicast. + // + Instance->IsMaster = ExtInfo.IsMaster; + + if (NetIp6IsUnspecifiedAddr (&Instance->McastIp)) { + if (NetIp6IsUnspecifiedAddr (&ExtInfo.McastIp) || ExtInfo.McastPort == 0) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid multi-cast setting. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Illegal multicast setting" + ); + + return EFI_TFTP_ERROR; + } + + // + // Create a UDP child then start receive the multicast from it. + // + CopyMem ( + &Instance->McastIp, + &ExtInfo.McastIp, + sizeof (EFI_IP_ADDRESS) + ); + + Instance->McastPort = ExtInfo.McastPort; + if (Instance->McastUdpIo == NULL) { + Instance->McastUdpIo = UdpIoCreateIo ( + Instance->Service->Controller, + Instance->Service->Image, + Mtftp6RrqConfigMcastUdpIo, + UDP_IO_UDP6_VERSION, + Instance + ); + if (Instance->McastUdpIo != NULL) { + Status = gBS->OpenProtocol ( + Instance->McastUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + Instance->Service->Image, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + UdpIoFreeIo (Instance->McastUdpIo); + Instance->McastUdpIo = NULL; + return EFI_DEVICE_ERROR; + } + } + } + + if (Instance->McastUdpIo == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = UdpIoRecvDatagram ( + Instance->McastUdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); + + if (EFI_ERROR (Status)) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if failed to create Udp6Io to receive. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ACCESS_VIOLATION, + (UINT8 *) "Failed to create socket to receive multicast packet" + ); + + return Status; + } + + // + // Update the parameters used. + // + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + } + + } else { + + Instance->IsMaster = TRUE; + + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + } + + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send an ACK to (Expected - 1) which is 0 for unicast download, + // or tell the server we want to receive the Expected block. + // + return Mtftp6RrqSendAck (Instance, (UINT16) (Expected - 1)); +} + + +/** + The packet process callback for Mtftp6 download. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6RrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PACKET *Packet; + BOOLEAN IsCompleted; + BOOLEAN IsMcast; + EFI_STATUS Status; + UINT16 Opcode; + UINT32 TotalNum; + UINT32 Len; + + Instance = (MTFTP6_INSTANCE *) Context; + + NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE); + + Status = EFI_SUCCESS; + Packet = NULL; + IsCompleted = FALSE; + IsMcast = FALSE; + TotalNum = 0; + + // + // Return error status if Udp6 instance failed to receive. + // + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + + // + // Find the port this packet is from to restart receive correctly. + // + if (CompareMem ( + Ip6Swap128 (&UdpEpt->LocalAddr.v6), + &Instance->McastIp, + sizeof (EFI_IPv6_ADDRESS) + ) == 0) { + IsMcast = TRUE; + } else { + IsMcast = FALSE; + } + + // + // Client send initial request to server's listening port. Server + // will select a UDP port to communicate with the client. The server + // is required to use the same port as RemotePort to multicast the + // data. + // + if (UdpEpt->RemotePort != Instance->ServerDataPort) { + if (Instance->ServerDataPort != 0) { + goto ON_EXIT; + } else { + // + // For the subsequent exchange of requests, reconfigure the udpio as + // (serverip, serverport, localip, localport). + // Ususally, the client set serverport as 0 to receive and reset it + // once the first packet arrives to send ack. + // + Instance->ServerDataPort = UdpEpt->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + TotalNum = UdpPacket->BlockOpNum; + + if (TotalNum > 1) { + Packet = AllocateZeroPool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + ASSERT (Packet != NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Callback to the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if ((Instance->Token->CheckPacket != NULL) && + (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR) + ) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp6, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP6_OPCODE_ERROR) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (UdpPacket); + UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Switch the process routines by the operation code. + // + switch (Opcode) { + case EFI_MTFTP6_OPCODE_DATA: + if ((Len > (UINT32) (MTFTP6_DATA_HEAD_LEN + Instance->BlkSize)) || (Len < (UINT32) MTFTP6_DATA_HEAD_LEN)) { + goto ON_EXIT; + } + // + // Handle the data packet of Rrq. + // + Status = Mtftp6RrqHandleData ( + Instance, + Packet, + Len, + &UdpPacket, + &IsCompleted + ); + break; + + case EFI_MTFTP6_OPCODE_OACK: + if (IsMcast || Len <= MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + // + // Handle the Oack packet of Rrq. + // + Status = Mtftp6RrqHandleOack ( + Instance, + Packet, + Len, + &UdpPacket, + &IsCompleted + ); + break; + + default: + // + // Drop and return eror if received error message. + // + Status = EFI_TFTP_ERROR; + break; + } + +ON_EXIT: + // + // Free the resources, then if !EFI_ERROR (Status), restart the + // receive, otherwise end the session. + // + if (Packet != NULL && TotalNum > 1) { + FreePool (Packet); + } + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + if (!EFI_ERROR (Status) && !IsCompleted) { + if (IsMcast) { + Status = UdpIoRecvDatagram ( + Instance->McastUdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); + } else { + Status = UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); + } + } + // + // Clean up the current session if failed to continue. + // + if (EFI_ERROR (Status) || IsCompleted) { + Mtftp6OperationClean (Instance, Status); + } +} + + +/** + Start the Mtftp6 instance to download. It first initializes some + of the internal states, then builds and sends an RRQ reqeuest packet. + Finally, it starts receive for the downloading. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of current packet. + + @retval EFI_SUCCESS The Mtftp6 is started to download. + @retval Others Failed to start to download. + +**/ +EFI_STATUS +Mtftp6RrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ) +{ + EFI_STATUS Status; + + // + // The valid block number range are [1, 0xffff]. For example: + // the client sends an RRQ request to the server, the server + // transfers the DATA1 block. If option negoitation is ongoing, + // the server will send back an OACK, then client will send ACK0. + // + Status = Mtftp6InitBlockRange (&Instance->BlkList, 1, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6SendRequest (Instance, Operation); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); +} + diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c new file mode 100644 index 0000000000..c31fc9dc27 --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c @@ -0,0 +1,1213 @@ +/** @file + Mtftp6 support functions implementation. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Mtftp6Impl.h" + + +/** + Allocate a MTFTP block range, then init it to the range of [Start, End]. + + @param[in] Start The start block number. + @param[in] End The last block number in the range. + + @return Range The range of the allocated block buffer. + +**/ +MTFTP6_BLOCK_RANGE * +Mtftp6AllocateRange ( + IN UINT16 Start, + IN UINT16 End + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE)); + + if (Range == NULL) { + return NULL; + } + + InitializeListHead (&Range->Link); + Range->Start = Start; + Range->End = End; + Range->Bound = End; + + return Range; +} + + +/** + Initialize the block range for either RRQ or WRQ. RRQ and WRQ have + different requirements for Start and End. For example, during startup, + WRQ initializes its whole valid block range to [0, 0xffff]. This + is bacause the server will send an ACK0 to inform the user to start the + upload. When the client receives an ACK0, it will remove 0 from the range, + get the next block number, which is 1, then upload the BLOCK1. For RRQ + without option negotiation, the server will directly send the BLOCK1 + in response to the client's RRQ. When received BLOCK1, the client will + remove it from the block range and send an ACK. It also works if there + is option negotiation. + + @param[in] Head The block range head to initialize. + @param[in] Start The Start block number. + @param[in] End The last block number. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range. + @retval EFI_SUCCESS The initial block range is created. + +**/ +EFI_STATUS +Mtftp6InitBlockRange ( + IN LIST_ENTRY *Head, + IN UINT16 Start, + IN UINT16 End + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + Range = Mtftp6AllocateRange (Start, End); + + if (Range == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InsertTailList (Head, &Range->Link); + return EFI_SUCCESS; +} + + +/** + Get the first valid block number on the range list. + + @param[in] Head The block range head. + + @retval ==-1 If the block range is empty. + @retval >-1 The first valid block number. + +**/ +INTN +Mtftp6GetNextBlockNum ( + IN LIST_ENTRY *Head + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + if (IsListEmpty (Head)) { + return -1; + } + + Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link); + return Range->Start; +} + + +/** + Set the last block number of the block range list. It + removes all the blocks after the Last. MTFTP initialize the + block range to the maximum possible range, such as [0, 0xffff] + for WRQ. When it gets the last block number, it calls + this function to set the last block number. + + @param[in] Head The block range list. + @param[in] Last The last block number. + +**/ +VOID +Mtftp6SetLastBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Last + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + // + // Iterate from the tail to head to remove the block number + // after the last. + // + while (!IsListEmpty (Head)) { + Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link); + + if (Range->Start > Last) { + RemoveEntryList (&Range->Link); + FreePool (Range); + continue; + } + + if (Range->End > Last) { + Range->End = Last; + } + return ; + } +} + + +/** + Remove the block number from the block range list. + + @param[in] Head The block range list to remove from. + @param[in] Num The block number to remove. + @param[in] Completed Whether Num is the last block number + @param[out] TotalBlock The continuous block number in all + + @retval EFI_NOT_FOUND The block number isn't in the block range list. + @retval EFI_SUCCESS The block number has been removed from the list. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +Mtftp6RemoveBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Num, + IN BOOLEAN Completed, + OUT UINT64 *TotalBlock + ) +{ + MTFTP6_BLOCK_RANGE *Range; + MTFTP6_BLOCK_RANGE *NewRange; + LIST_ENTRY *Entry; + + NET_LIST_FOR_EACH (Entry, Head) { + + // + // Each block represents a hole [Start, End] in the file, + // skip to the first range with End >= Num + // + Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link); + + if (Range->End < Num) { + continue; + } + + // + // There are three different cases for Start + // 1. (Start > Num) && (End >= Num): + // because all the holes before this one has the condition of + // End < Num, so this block number has been removed. + // + // 2. (Start == Num) && (End >= Num): + // Need to increase the Start by one, and if End == Num, this + // hole has been removed completely, remove it. + // + // 3. (Start < Num) && (End >= Num): + // if End == Num, only need to decrease the End by one because + // we have (Start < Num) && (Num == End), so (Start <= End - 1). + // if (End > Num), the hold is splited into two holes, with + // [Start, Num - 1] and [Num + 1, End]. + // + if (Range->Start > Num) { + return EFI_NOT_FOUND; + + } else if (Range->Start == Num) { + Range->Start++; + + // + // Note that: RFC 1350 does not mention block counter roll-over, + // but several TFTP hosts implement the roll-over be able to accept + // transfers of unlimited size. There is no consensus, however, whether + // the counter should wrap around to zero or to one. Many implementations + // wrap to zero, because this is the simplest to implement. Here we choose + // this solution. + // + *TotalBlock = Num; + + if (Range->Round > 0) { + *TotalBlock += Range->Bound + MultU64x32 ((UINT64) (Range->Round -1), (UINT32)(Range->Bound + 1)) + 1; + } + + if (Range->Start > Range->Bound) { + Range->Start = 0; + Range->Round ++; + } + + if ((Range->Start > Range->End) || Completed) { + RemoveEntryList (&Range->Link); + FreePool (Range); + } + + return EFI_SUCCESS; + + } else { + if (Range->End == Num) { + Range->End--; + } else { + NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End); + + if (NewRange == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Range->End = Num - 1; + NetListInsertAfter (&Range->Link, &NewRange->Link); + } + + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Configure the opened Udp6 instance until the corresponding Ip6 instance + has been configured. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] UdpCfgData The pointer to the Udp6 configure data. + + @retval EFI_SUCCESS Configure the Udp6 instance successfully. + @retval EFI_NO_MAPPING The corresponding Ip6 instance has not + been configured yet. + +**/ +EFI_STATUS +Mtftp6GetMapping ( + IN UDP_IO *UdpIo, + IN EFI_UDP6_CONFIG_DATA *UdpCfgData + ) +{ + EFI_IP6_MODE_DATA Ip6Mode; + EFI_UDP6_PROTOCOL *Udp6; + EFI_STATUS Status; + EFI_EVENT Event; + + Event = NULL; + Udp6 = UdpIo->Protocol.Udp6; + + // + // Create a timer to check whether the Ip6 instance configured or not. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + Event, + TimerRelative, + MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Check the Ip6 mode data till timeout. + // + while (EFI_ERROR (gBS->CheckEvent (Event))) { + + Udp6->Poll (Udp6); + + Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL); + + if (!EFI_ERROR (Status)) { + + if (Ip6Mode.IsConfigured) { + // + // Continue to configure the Udp6 instance. + // + Status = Udp6->Configure (Udp6, UdpCfgData); + } else { + Status = EFI_NO_MAPPING; + } + } + } + +ON_EXIT: + + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + return Status; +} + + +/** + The dummy configure routine for create a new Udp6 Io. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS This value is always returned. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ConfigDummyUdpIo ( + IN UDP_IO *UdpIo, + IN VOID *Context + ) +{ + return EFI_SUCCESS; +} + + +/** + The configure routine for Mtftp6 instance to transmit/receive. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] ServerIp The pointer to the server address. + @param[in] ServerPort The pointer to the server port. + @param[in] LocalIp The pointer to the local address. + @param[in] LocalPort The pointer to the local port. + + @retval EFI_SUCCESS Configured the Udp6 Io for Mtftp6 successfully. + @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been + configured yet. + +**/ +EFI_STATUS +Mtftp6ConfigUdpIo ( + IN UDP_IO *UdpIo, + IN EFI_IPv6_ADDRESS *ServerIp, + IN UINT16 ServerPort, + IN EFI_IPv6_ADDRESS *LocalIp, + IN UINT16 LocalPort + ) +{ + EFI_STATUS Status; + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA *Udp6Cfg; + + Udp6 = UdpIo->Protocol.Udp6; + Udp6Cfg = &(UdpIo->Config.Udp6); + + ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + // + // Set the Udp6 Io configure data. + // + Udp6Cfg->AcceptPromiscuous = FALSE; + Udp6Cfg->AcceptAnyPort = FALSE; + Udp6Cfg->AllowDuplicatePort = FALSE; + Udp6Cfg->TrafficClass = 0; + Udp6Cfg->HopLimit = 128; + Udp6Cfg->ReceiveTimeout = 0; + Udp6Cfg->TransmitTimeout = 0; + Udp6Cfg->StationPort = LocalPort; + Udp6Cfg->RemotePort = ServerPort; + + CopyMem ( + &Udp6Cfg->StationAddress, + LocalIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + CopyMem ( + &Udp6Cfg->RemoteAddress, + ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + // + // Configure the Udp6 instance with current configure data. + // + Status = Udp6->Configure (Udp6, Udp6Cfg); + + if (Status == EFI_NO_MAPPING) { + + return Mtftp6GetMapping (UdpIo, Udp6Cfg); + } + + return Status; +} + + +/** + Build and transmit the request packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of this packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request. + @retval EFI_SUCCESS The request is built and sent. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendRequest ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ) +{ + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_OPTION *Options; + EFI_MTFTP6_TOKEN *Token; + RETURN_STATUS Status; + NET_BUF *Nbuf; + UINT8 *Mode; + UINT8 *Cur; + UINTN Index; + UINT32 BufferLength; + UINTN FileNameLength; + UINTN ModeLength; + UINTN OptionStrLength; + UINTN ValueStrLength; + + Token = Instance->Token; + Options = Token->OptionList; + Mode = Token->ModeStr; + + if (Mode == NULL) { + Mode = (UINT8 *) "octet"; + } + + // + // The header format of RRQ/WRQ packet is: + // + // 2 bytes string 1 byte string 1 byte + // ------------------------------------------------ + // | Opcode | Filename | 0 | Mode | 0 | + // ------------------------------------------------ + // + // The common option format is: + // + // string 1 byte string 1 byte + // --------------------------------------- + // | OptionStr | 0 | ValueStr | 0 | + // --------------------------------------- + // + + // + // Compute the size of new Mtftp6 packet. + // + FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename); + ModeLength = AsciiStrLen ((CHAR8 *) Mode); + BufferLength = (UINT32) FileNameLength + (UINT32) ModeLength + 4; + + for (Index = 0; Index < Token->OptionCount; Index++) { + OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr); + ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr); + BufferLength += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2; + } + + // + // Allocate a packet then copy the data. + // + if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the opcode, filename and mode into packet. + // + Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE); + ASSERT (Packet != NULL); + + Packet->OpCode = HTONS (Operation); + BufferLength -= sizeof (Packet->OpCode); + + Cur = Packet->Rrq.Filename; + Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename); + ASSERT_EFI_ERROR (Status); + BufferLength -= (UINT32) (FileNameLength + 1); + Cur += FileNameLength + 1; + Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode); + ASSERT_EFI_ERROR (Status); + BufferLength -= (UINT32) (ModeLength + 1); + Cur += ModeLength + 1; + + // + // Copy all the extension options into the packet. + // + for (Index = 0; Index < Token->OptionCount; ++Index) { + OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr); + ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr); + + Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr); + ASSERT_EFI_ERROR (Status); + BufferLength -= (UINT32) (OptionStrLength + 1); + Cur += OptionStrLength + 1; + + Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr); + ASSERT_EFI_ERROR (Status); + BufferLength -= (UINT32) (ValueStrLength + 1); + Cur += ValueStrLength + 1; + + } + + // + // Save the packet buf for retransmit + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + Instance->LastPacket = Nbuf; + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, Nbuf); +} + + +/** + Build and send an error packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] ErrCode The error code in the packet. + @param[in] ErrInfo The error message in the packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet. + @retval EFI_SUCCESS The error packet is transmitted. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendError ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 ErrCode, + IN UINT8* ErrInfo + ) +{ + NET_BUF *Nbuf; + EFI_MTFTP6_PACKET *TftpError; + UINT32 Len; + + // + // Allocate a packet then copy the data. + // + Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER)); + Nbuf = NetbufAlloc (Len); + + if (Nbuf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE); + + if (TftpError == NULL) { + NetbufFree (Nbuf); + return EFI_OUT_OF_RESOURCES; + } + + TftpError->OpCode = HTONS (EFI_MTFTP6_OPCODE_ERROR); + TftpError->Error.ErrorCode = HTONS (ErrCode); + + AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, AsciiStrLen ((CHAR8 *) ErrInfo) + 1 , (CHAR8 *) ErrInfo); + + // + // Save the packet buf for retransmit + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + Instance->LastPacket = Nbuf; + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, Nbuf); +} + + +/** + The callback function called when the packet is transmitted. + + @param[in] Packet The pointer to the packet. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The result of the transmission. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6OnPacketSent ( + IN NET_BUF *Packet, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + NetbufFree (Packet); + *(BOOLEAN *) Context = TRUE; +} + + +/** + Send the packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the packet to be sent. + + @retval EFI_SUCCESS The packet was sent out + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6TransmitPacket ( + IN MTFTP6_INSTANCE *Instance, + IN NET_BUF *Packet + ) +{ + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA Udp6CfgData; + EFI_STATUS Status; + UINT16 *Temp; + UINT16 Value; + UINT16 OpCode; + + ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA)); + Udp6 = Instance->UdpIo->Protocol.Udp6; + + // + // Set the live time of the packet. + // + Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2); + + Temp = (UINT16 *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Temp != NULL); + + Value = *Temp; + OpCode = NTOHS (Value); + + if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) { + // + // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as + // (serverip, 69, localip, localport) to send. + // Usually local address and local port are both default as zero. + // + Status = Udp6->Configure (Udp6, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ConfigUdpIo ( + Instance->UdpIo, + &Instance->ServerIp, + Instance->ServerCmdPort, + &Instance->Config->StationIp, + Instance->Config->LocalPort + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the current local address and port by get Udp6 mode data. + // + Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + NET_GET_REF (Packet); + + Instance->IsTransmitted = FALSE; + + Status = UdpIoSendDatagram ( + Instance->UdpIo, + Packet, + NULL, + NULL, + Mtftp6OnPacketSent, + &Instance->IsTransmitted + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (Packet); + return Status; + } + + // + // Poll till the packet sent out from the ip6 queue. + // + gBS->RestoreTPL (Instance->OldTpl); + + while (!Instance->IsTransmitted) { + Udp6->Poll (Udp6); + } + + Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // For the subsequent exchange of such requests, reconfigure the Udp6Io as + // (serverip, 0, localip, localport) to receive. + // Currently local address and local port are specified by Udp6 mode data. + // + Status = Udp6->Configure (Udp6, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ConfigUdpIo ( + Instance->UdpIo, + &Instance->ServerIp, + Instance->ServerDataPort, + &Udp6CfgData.StationAddress, + Udp6CfgData.StationPort + ); + } else { + // + // For the data exchange, configure the Udp6Io as (serverip, dataport, + // localip, localport) to send/receive. + // Currently local address and local port are specified by Udp6 mode data. + // + Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Udp6CfgData.RemotePort != Instance->ServerDataPort) { + + Status = Udp6->Configure (Udp6, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ConfigUdpIo ( + Instance->UdpIo, + &Instance->ServerIp, + Instance->ServerDataPort, + &Udp6CfgData.StationAddress, + Udp6CfgData.StationPort + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + NET_GET_REF (Packet); + + Instance->IsTransmitted = FALSE; + + Status = UdpIoSendDatagram ( + Instance->UdpIo, + Packet, + NULL, + NULL, + Mtftp6OnPacketSent, + &Instance->IsTransmitted + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (Packet); + } + + // + // Poll till the packet sent out from the ip6 queue. + // + gBS->RestoreTPL (Instance->OldTpl); + + while (!Instance->IsTransmitted) { + Udp6->Poll (Udp6); + } + + Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + } + + return Status; +} + + +/** + Check packet for GetInfo callback routine. + + GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect + the first packet from server, then abort the session. + + @param[in] This The pointer to the Mtftp6 protocol. + @param[in] Token The pointer to the Mtftp6 token. + @param[in] PacketLen The length of the packet. + @param[in] Packet The pointer to the received packet. + + @retval EFI_ABORTED Abort the Mtftp6 operation. + +**/ +EFI_STATUS +EFIAPI +Mtftp6CheckPacket ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP6_PACKET *Packet + ) +{ + MTFTP6_GETINFO_CONTEXT *Context; + UINT16 OpCode; + + Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context; + OpCode = NTOHS (Packet->OpCode); + + // + // Set the GetInfo's return status according to the OpCode. + // + switch (OpCode) { + case EFI_MTFTP6_OPCODE_ERROR: + Context->Status = EFI_TFTP_ERROR; + break; + + case EFI_MTFTP6_OPCODE_OACK: + Context->Status = EFI_SUCCESS; + break; + + default: + Context->Status = EFI_PROTOCOL_ERROR; + } + + // + // Allocate buffer then copy the packet over. Use gBS->AllocatePool + // in case NetAllocatePool will implements something tricky. + // + *(Context->Packet) = AllocateZeroPool (PacketLen); + + if (*(Context->Packet) == NULL) { + Context->Status = EFI_OUT_OF_RESOURCES; + return EFI_ABORTED; + } + + *(Context->PacketLen) = PacketLen; + CopyMem (*(Context->Packet), Packet, PacketLen); + + return EFI_ABORTED; +} + + +/** + Clean up the current Mtftp6 operation. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Result The result to be returned to the user. + +**/ +VOID +Mtftp6OperationClean ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_STATUS Result + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + MTFTP6_BLOCK_RANGE *Block; + + // + // Clean up the current token and event. + // + if (Instance->Token != NULL) { + Instance->Token->Status = Result; + if (Instance->Token->Event != NULL) { + gBS->SignalEvent (Instance->Token->Event); + } + Instance->Token = NULL; + } + + // + // Clean up the corresponding Udp6Io. + // + if (Instance->UdpIo != NULL) { + UdpIoCleanIo (Instance->UdpIo); + } + + if (Instance->McastUdpIo != NULL) { + gBS->CloseProtocol ( + Instance->McastUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + Instance->McastUdpIo->Image, + Instance->Handle + ); + UdpIoFreeIo (Instance->McastUdpIo); + Instance->McastUdpIo = NULL; + } + + // + // Clean up the stored last packet. + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + Instance->LastPacket = NULL; + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) { + Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link); + RemoveEntryList (Entry); + FreePool (Block); + } + + // + // Reinitialize the corresponding fields of the Mtftp6 operation. + // + ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS)); + + Instance->ServerCmdPort = 0; + Instance->ServerDataPort = 0; + Instance->McastPort = 0; + Instance->BlkSize = 0; + Instance->LastBlk = 0; + Instance->PacketToLive = 0; + Instance->MaxRetry = 0; + Instance->CurRetry = 0; + Instance->Timeout = 0; + Instance->IsMaster = TRUE; +} + + +/** + Start the Mtftp6 instance to perform the operation, such as read file, + write file, and read directory. + + @param[in] This The MTFTP session. + @param[in] Token The token than encapsues the user's request. + @param[in] OpCode The operation to perform. + + @retval EFI_INVALID_PARAMETER Some of the parameters are invalid. + @retval EFI_NOT_STARTED The MTFTP session hasn't been configured. + @retval EFI_ALREADY_STARTED There is pending operation for the session. + @retval EFI_SUCCESS The operation is successfully started. + +**/ +EFI_STATUS +Mtftp6OperationStart ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 OpCode + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_STATUS Status; + + if (This == NULL || + Token == NULL || + Token->Filename == NULL || + (Token->OptionCount != 0 && Token->OptionList == NULL) || + (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp)) + ) { + return EFI_INVALID_PARAMETER; + } + + // + // At least define one method to collect the data for download. + // + if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) && + Token->Buffer == NULL && + Token->CheckPacket == NULL + ) { + return EFI_INVALID_PARAMETER; + } + + // + // At least define one method to provide the data for upload. + // + if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + + if (Instance->Config == NULL) { + return EFI_NOT_STARTED; + } + + if (Instance->Token != NULL) { + return EFI_ACCESS_DENIED; + } + + Status = EFI_SUCCESS; + Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Parse the extension options in the request packet. + // + if (Token->OptionCount != 0) { + + Status = Mtftp6ParseExtensionOption ( + Token->OptionList, + Token->OptionCount, + TRUE, + &Instance->ExtInfo + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Initialize runtime data from config data or override data. + // + Instance->Token = Token; + Instance->ServerCmdPort = Instance->Config->InitialServerPort; + Instance->ServerDataPort = 0; + Instance->MaxRetry = Instance->Config->TryCount; + Instance->Timeout = Instance->Config->TimeoutValue; + Instance->IsMaster = TRUE; + + CopyMem ( + &Instance->ServerIp, + &Instance->Config->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + if (Token->OverrideData != NULL) { + Instance->ServerCmdPort = Token->OverrideData->ServerPort; + Instance->MaxRetry = Token->OverrideData->TryCount; + Instance->Timeout = Token->OverrideData->TimeoutValue; + + CopyMem ( + &Instance->ServerIp, + &Token->OverrideData->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + } + + // + // Set default value for undefined parameters. + // + if (Instance->ServerCmdPort == 0) { + Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT; + } + if (Instance->BlkSize == 0) { + Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE; + } + if (Instance->MaxRetry == 0) { + Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY; + } + if (Instance->Timeout == 0) { + Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT; + } + + Token->Status = EFI_NOT_READY; + + // + // Switch the routines by the operation code. + // + switch (OpCode) { + case EFI_MTFTP6_OPCODE_RRQ: + Status = Mtftp6RrqStart (Instance, OpCode); + break; + + case EFI_MTFTP6_OPCODE_DIR: + Status = Mtftp6RrqStart (Instance, OpCode); + break; + + case EFI_MTFTP6_OPCODE_WRQ: + Status = Mtftp6WrqStart (Instance, OpCode); + break; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Return immediately for asynchronous or poll the instance for synchronous. + // + gBS->RestoreTPL (Instance->OldTpl); + + if (Token->Event == NULL) { + while (Token->Status == EFI_NOT_READY) { + This->Poll (This); + } + return Token->Status; + } + + return EFI_SUCCESS; + +ON_ERROR: + + Mtftp6OperationClean (Instance, Status); + gBS->RestoreTPL (Instance->OldTpl); + + return Status; +} + + +/** + The timer ticking routine for the Mtftp6 instance. + + @param[in] Event The pointer to the ticking event. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + EFI_MTFTP6_TOKEN *Token; + EFI_STATUS Status; + + Service = (MTFTP6_SERVICE *) Context; + + // + // Iterate through all the children of the Mtftp service instance. Time + // out the packet. If maximum retries reached, clean the session up. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) { + + Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link); + + if (Instance->Token == NULL) { + continue; + } + + if (Instance->PacketToLive > 0) { + Instance->PacketToLive--; + continue; + } + + Instance->CurRetry++; + Token = Instance->Token; + + if (Token->TimeoutCallback != NULL) { + // + // Call the timeout callback routine if has. + // + Status = Token->TimeoutCallback (&Instance->Mtftp6, Token); + + if (EFI_ERROR (Status)) { + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer in time out" + ); + Mtftp6OperationClean (Instance, EFI_ABORTED); + continue; + } + } + + // + // Retransmit the packet if haven't reach the maxmium retry count, + // otherwise exit the transfer. + // + if (Instance->CurRetry < Instance->MaxRetry) { + Mtftp6TransmitPacket (Instance, Instance->LastPacket); + } else { + Mtftp6OperationClean (Instance, EFI_TIMEOUT); + continue; + } + } +} diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h new file mode 100644 index 0000000000..37f03fe298 --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h @@ -0,0 +1,359 @@ +/** @file + Mtftp6 support functions declaration. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_MTFTP6_SUPPORT_H__ +#define __EFI_MTFTP6_SUPPORT_H__ + +// +// The structure representing a range of block numbers, [Start, End]. +// It is used to remember the holes in the MTFTP block space. If all +// the holes are filled in, then the download or upload has completed. +// +typedef struct { + LIST_ENTRY Link; + INTN Start; + INTN End; + INTN Round; + INTN Bound; +} MTFTP6_BLOCK_RANGE; + + +/** + Initialize the block range for either RRQ or WRQ. RRQ and WRQ have + different requirements for Start and End. For example, during startup, + WRQ initializes its whole valid block range to [0, 0xffff]. This + is because the server will send an ACK0 to inform the user to start the + upload. When the client receives an ACK0, it will remove 0 from the range, + get the next block number, which is 1, then upload the BLOCK1. For RRQ + without option negotiation, the server will directly send us the BLOCK1 + in response to the client's RRQ. When BLOCK1 is received, the client will + remove it from the block range and send an ACK. It also works if there + is option negotiation. + + @param[in] Head The block range head to initialize. + @param[in] Start The Start block number. + @param[in] End The last block number. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range. + @retval EFI_SUCCESS The initial block range is created. + +**/ +EFI_STATUS +Mtftp6InitBlockRange ( + IN LIST_ENTRY *Head, + IN UINT16 Start, + IN UINT16 End + ); + + +/** + Get the first valid block number on the range list. + + @param[in] Head The block range head. + + @retval ==-1 If the block range is empty. + @retval >-1 The first valid block number. + +**/ +INTN +Mtftp6GetNextBlockNum ( + IN LIST_ENTRY *Head + ); + + +/** + Set the last block number of the block range list. It + removes all the blocks after the Last. MTFTP initialize the + block range to the maximum possible range, such as [0, 0xffff] + for WRQ. When it gets the last block number, it calls + this function to set the last block number. + + @param[in] Head The block range list. + @param[in] Last The last block number. + +**/ +VOID +Mtftp6SetLastBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Last + ); + + +/** + Remove the block number from the block range list. + + @param[in] Head The block range list to remove from. + @param[in] Num The block number to remove. + @param[in] Completed Whether Num is the last block number + @param[out] TotalBlock The continuous block number in all + + @retval EFI_NOT_FOUND The block number isn't in the block range list. + @retval EFI_SUCCESS The block number has been removed from the list. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +Mtftp6RemoveBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Num, + IN BOOLEAN Completed, + OUT UINT64 *TotalBlock + ); + + +/** + Build and transmit the request packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of this packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request. + @retval EFI_SUCCESS The request was built and sent. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendRequest ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ); + + +/** + Build and send an error packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] ErrCode The error code in the packet. + @param[in] ErrInfo The error message in the packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet. + @retval EFI_SUCCESS The error packet was transmitted. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendError ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 ErrCode, + IN UINT8* ErrInfo + ); + + +/** + Send the packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the packet to be sent. + + @retval EFI_SUCCESS The packet was sent out + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6TransmitPacket ( + IN MTFTP6_INSTANCE *Instance, + IN NET_BUF *Packet + ); + + +/** + Check packet for GetInfo callback routine. + + @param[in] This The pointer to the Mtftp6 protocol. + @param[in] Token The pointer to the Mtftp6 token. + @param[in] PacketLen The length of the packet + @param[in] Packet The pointer to the received packet. + + @retval EFI_SUCCESS The check process passed successfully. + @retval EFI_ABORTED Abort the Mtftp6 operation. + +**/ +EFI_STATUS +EFIAPI +Mtftp6CheckPacket ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP6_PACKET *Packet + ); + + +/** + The dummy configure routine for create a new Udp6 Io. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS The value is always returned. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ConfigDummyUdpIo ( + IN UDP_IO *UdpIo, + IN VOID *Context + ); + + +/** + The configure routine for the Mtftp6 instance to transmit/receive. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] ServerIp The pointer to the server address. + @param[in] ServerPort The pointer to the server port. + @param[in] LocalIp The pointer to the local address. + @param[in] LocalPort The pointer to the local port. + + @retval EFI_SUCCESS Configure the Udp6 Io for Mtftp6 successfully. + @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been + configured yet. + +**/ +EFI_STATUS +Mtftp6ConfigUdpIo ( + IN UDP_IO *UdpIo, + IN EFI_IPv6_ADDRESS *ServerIp, + IN UINT16 ServerPort, + IN EFI_IPv6_ADDRESS *LocalIp, + IN UINT16 LocalPort + ); + + +/** + Clean up the current Mtftp6 operation. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Result The result to be returned to the user. + +**/ +VOID +Mtftp6OperationClean ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_STATUS Result + ); + + +/** + Start the Mtftp6 instance to perform the operation, such as read file, + write file, and read directory. + + @param[in] This The MTFTP session + @param[in] Token The token that encapsulates the user's request. + @param[in] OpCode The operation to perform. + + @retval EFI_INVALID_PARAMETER Some of the parameters are invalid. + @retval EFI_NOT_STARTED The MTFTP session hasn't been configured. + @retval EFI_ALREADY_STARTED There is pending operation for the session. + @retval EFI_SUCCESS The operation was successfully started. + +**/ +EFI_STATUS +Mtftp6OperationStart ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 OpCode + ); + + +/** + The timer ticking routine for the Mtftp6 instance. + + @param[in] Event The pointer to the ticking event. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + The packet process callback for Mtftp6 upload. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from the Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + + +/** + Start the Mtftp6 instance to upload. It will first init some states, + then send the WRQ request packet, and start to receive the packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to upload. + @retval Others Failed to start to upload. + +**/ +EFI_STATUS +Mtftp6WrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ); + + +/** + The packet process callback for Mtftp6 download. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6RrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + + +/** + Start the Mtftp6 instance to download. It first initializes some + of the internal states then builds and sends an RRQ reqeuest packet. + Finally, it starts receive for the downloading. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to download. + @retval Others Failed to start to download. + +**/ +EFI_STATUS +Mtftp6RrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ); + +#endif diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c new file mode 100644 index 0000000000..254b757f7e --- /dev/null +++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c @@ -0,0 +1,604 @@ +/** @file + Mtftp6 Wrq process functions implementation. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Mtftp6Impl.h" + + + +/** + Build and send a Mtftp6 data packet for upload. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] BlockNum The block num to be sent. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet. + @retval EFI_SUCCESS The data packet was sent. + @retval EFI_ABORTED The user aborted this process. + +**/ +EFI_STATUS +Mtftp6WrqSendBlock ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 BlockNum + ) +{ + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_TOKEN *Token; + NET_BUF *UdpPacket; + EFI_STATUS Status; + UINT16 DataLen; + UINT8 *DataBuf; + UINT64 Start; + + // + // Allocate net buffer to create data packet. + // + UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN); + + if (UdpPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace ( + UdpPacket, + MTFTP6_DATA_HEAD_LEN, + FALSE + ); + ASSERT (Packet != NULL); + + Packet->Data.OpCode = HTONS (EFI_MTFTP6_OPCODE_DATA); + Packet->Data.Block = HTONS (BlockNum); + + // + // Read the block from either the buffer or PacketNeeded callback + // + Token = Instance->Token; + DataLen = Instance->BlkSize; + + if (Token->Buffer != NULL) { + Start = MultU64x32 (BlockNum - 1, Instance->BlkSize); + + if (Token->BufferSize < Start + Instance->BlkSize) { + DataLen = (UINT16) (Token->BufferSize - Start); + Instance->LastBlk = BlockNum; + Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen); + } + + } else { + // + // Get data from PacketNeeded + // + DataBuf = NULL; + Status = Token->PacketNeeded (&Instance->Mtftp6, Token, &DataLen, (VOID*) &DataBuf); + + if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) { + if (DataBuf != NULL) { + gBS->FreePool (DataBuf); + } + // + // The received packet has already been freed. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + + return EFI_ABORTED; + } + + if (DataLen < Instance->BlkSize) { + Instance->LastBlk = BlockNum; + Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + CopyMem (Packet->Data.Data, DataBuf, DataLen); + gBS->FreePool (DataBuf); + } + } + + // + // Reset current retry count of the instance. + // + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, UdpPacket); +} + + +/** + Function to handle received ACK packet. If the ACK number matches the + expected block number, with more data pending, send the next + block. Otherwise, tell the caller that we are done. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the upload has been completed. + Otherwise, the upload has not been completed. + + @retval EFI_SUCCESS The ACK packet successfully processed. + @retval EFI_TFTP_ERROR The block number loops back. + @retval Others Failed to transmit the next data packet. + +**/ +EFI_STATUS +Mtftp6WrqHandleAck ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + UINT16 AckNum; + INTN Expected; + UINT64 TotalBlock; + + *IsCompleted = FALSE; + AckNum = NTOHS (Packet->Ack.Block[0]); + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + ASSERT (Expected >= 0); + + // + // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput + // restart receive. + // + if (Expected != AckNum) { + return EFI_SUCCESS; + } + + // + // Remove the acked block number, if this is the last block number, + // tell the Mtftp6WrqInput to finish the transfer. This is the last + // block number if the block range are empty.. + // + Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &TotalBlock); + + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Expected < 0) { + // + // The block range is empty. It may either because the the last + // block has been ACKed, or the sequence number just looped back, + // that is, there is more than 0xffff blocks. + // + if (Instance->LastBlk == AckNum) { + ASSERT (Instance->LastBlk >= 1); + *IsCompleted = TRUE; + return EFI_SUCCESS; + + } else { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if block number rolls back. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "Block number rolls back, not supported, try blksize option" + ); + + return EFI_TFTP_ERROR; + } + } + + // + // Free the receive buffer before send new packet since it might need + // reconfigure udpio. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + return Mtftp6WrqSendBlock (Instance, (UINT16) Expected); +} + + +/** + Check whether the received OACK is valid. The OACK is valid + only if: + 1. It only include options requested by us. + 2. It can only include a smaller block size. + 3. It can't change the proposed time out value. + 4. Other requirements of the individal MTFTP6 options as required. + + @param[in] ReplyInfo The pointer to options information in reply packet. + @param[in] RequestInfo The pointer to requested options information. + + @retval TRUE If the option in OACK is valid. + @retval FALSE If the option is invalid. + +**/ +BOOLEAN +Mtftp6WrqOackValid ( + IN MTFTP6_EXT_OPTION_INFO *ReplyInfo, + IN MTFTP6_EXT_OPTION_INFO *RequestInfo + ) +{ + // + // It is invalid for server to return options we don't request + // + if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size to be used and + // return the timeout matches that requested. + // + if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) || + (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout)) + ) { + + return FALSE; + } + + return TRUE; +} + + +/** + Process the OACK packet for Wrq. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the upload has been completed. + Otherwise, the upload has not been completed. + + @retval EFI_SUCCESS The OACK packet successfully processed. + @retval EFI_TFTP_ERROR An TFTP communication error happened. + @retval Others Failed to process the OACK packet. + +**/ +EFI_STATUS +Mtftp6WrqHandleOack ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_MTFTP6_OPTION *Options; + UINT32 Count; + MTFTP6_EXT_OPTION_INFO ExtInfo; + EFI_MTFTP6_PACKET Dummy; + EFI_STATUS Status; + INTN Expected; + + *IsCompleted = FALSE; + Options = NULL; + + // + // Ignore the OACK if already started the upload + // + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Expected != 0) { + return EFI_SUCCESS; + } + + // + // Parse and validate the options from server + // + ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + + Status = Mtftp6ParseStart (Packet, Len, &Count, &Options); + + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Options != NULL); + + Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo); + + if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) { + // + // Don't send a MTFTP error packet when out of resource, it can + // only make it worse. + // + if (Status != EFI_OUT_OF_RESOURCES) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid Oack packet received. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Mal-formated OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + + // + // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck, + // which will start the transmission of the first data block. + // + Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK); + Dummy.Ack.Block[0] = 0; + + return Mtftp6WrqHandleAck ( + Instance, + &Dummy, + sizeof (EFI_MTFTP6_ACK_HEADER), + UdpPacket, + IsCompleted + ); +} + + +/** + The packet process callback for Mtftp6 upload. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PACKET *Packet; + BOOLEAN IsCompleted; + EFI_STATUS Status; + UINT32 TotalNum; + UINT32 Len; + UINT16 Opcode; + + Instance = (MTFTP6_INSTANCE *) Context; + + NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE); + + IsCompleted = FALSE; + Packet = NULL; + Status = EFI_SUCCESS; + TotalNum = 0; + + // + // Return error status if Udp6 instance failed to receive. + // + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + + // + // Client send initial request to server's listening port. Server + // will select a UDP port to communicate with the client. + // + if (UdpEpt->RemotePort != Instance->ServerDataPort) { + if (Instance->ServerDataPort != 0) { + goto ON_EXIT; + } else { + Instance->ServerDataPort = UdpEpt->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + TotalNum = UdpPacket->BlockOpNum; + + if (TotalNum > 1) { + Packet = AllocateZeroPool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + ASSERT (Packet != NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Callback to the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if (Instance->Token->CheckPacket != NULL && + (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR) + ) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp6, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP6_OPCODE_ERROR) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (UdpPacket); + UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Switch the process routines by the operation code. + // + switch (Opcode) { + case EFI_MTFTP6_OPCODE_ACK: + if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) { + goto ON_EXIT; + } + // + // Handle the Ack packet of Wrq. + // + Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted); + break; + + case EFI_MTFTP6_OPCODE_OACK: + if (Len <= MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + // + // Handle the Oack packet of Wrq. + // + Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted); + break; + + default: + // + // Drop and return eror if received error message. + // + Status = EFI_TFTP_ERROR; + break; + } + +ON_EXIT: + // + // Free the resources, then if !EFI_ERROR (Status) and not completed, + // restart the receive, otherwise end the session. + // + if (Packet != NULL && TotalNum > 1) { + FreePool (Packet); + } + + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + + if (!EFI_ERROR (Status) && !IsCompleted) { + Status = UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6WrqInput, + Instance, + 0 + ); + } + // + // Clean up the current session if failed to continue. + // + if (EFI_ERROR (Status) || IsCompleted) { + Mtftp6OperationClean (Instance, Status); + } +} + + +/** + Start the Mtftp6 instance to upload. It will first init some states, + then send the WRQ request packet, and start to receive the packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of the current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to upload. + @retval Others Failed to start to upload. + +**/ +EFI_STATUS +Mtftp6WrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ) +{ + EFI_STATUS Status; + + // + // The valid block number range are [0, 0xffff]. For example: + // the client sends an WRQ request to the server, the server + // ACK with an ACK0 to let client start transfer the first + // packet. + // + Status = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6SendRequest (Instance, Operation); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6WrqInput, + Instance, + 0 + ); +} + diff --git a/Core/NetworkPkg/NetworkPkg.dec b/Core/NetworkPkg/NetworkPkg.dec new file mode 100644 index 0000000000..de5dabea36 --- /dev/null +++ b/Core/NetworkPkg/NetworkPkg.dec @@ -0,0 +1,82 @@ +## @file +# Network Package. +# +# This package provides network modules that conform to UEFI 2.4 specification. +# +# (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+# Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials are licensed and made available under +# the terms and conditions of the BSD License which accompanies this distribution. +# The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + DEC_SPECIFICATION = 0x00010005 + PACKAGE_NAME = NetworkPkg + PACKAGE_GUID = 947988BE-8D5C-471a-893D-AD181C46BEBB + PACKAGE_VERSION = 0.96 + PACKAGE_UNI_FILE = NetworkPkg.uni + +[Includes] + Include + +[Guids] + ## Network package token space guid. + # Include/Guid/NetworkPkgTokenSpace.h + gEfiNetworkPkgTokenSpaceGuid = { 0x40e064b2, 0x0ae0, 0x48b1, { 0xa0, 0x7d, 0xf8, 0xcf, 0x1e, 0x1a, 0x23, 0x10}} + + # Include/Guid/Ip6ConfigHii.h + gIp6ConfigNvDataGuid = { 0x2eea107, 0x98db, 0x400e, { 0x98, 0x30, 0x46, 0xa, 0x15, 0x42, 0xd7, 0x99}} + + # Include/Guid/IscsiConfigHii.h + gIScsiConfigGuid = { 0x4b47d616, 0xa8d6, 0x4552, { 0x9d, 0x44, 0xcc, 0xad, 0x2e, 0xf, 0x4c, 0xf9}} + +[PcdsFeatureFlag] + ## Indicates if the IPsec IKEv2 Certificate Authentication feature is enabled or not.

+ # TRUE - Certificate Authentication feature is enabled.
+ # FALSE - Does not support Certificate Authentication.
+ # @Prompt Enable IPsec IKEv2 Certificate Authentication. + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecCertificateEnabled|TRUE|BOOLEAN|0x00000007 + +[PcdsFixedAtBuild, PcdsPatchableInModule] + ## CA certificate used by IPsec. + # @Prompt CA file. + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCaFile|{0x30, 0x82, 0x02, 0x76, 0x30, 0x82, 0x01, 0xDF, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x80, 0x1D, 0xB9, 0x63, 0x93, 0x7C, 0x9D, 0xE0, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x05, 0x00, 0x30, 0x74, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0A, 0x4D, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02, 0x48, 0x5A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4E, 0x31, 0x1C, 0x30, 0x1A, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x0D, 0x74, 0x65, 0x73, 0x74, 0x40, 0x63, 0x65, 0x72, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x1C, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31, 0x35, 0x33, 0x33, 0x37, 0x5A, 0x17, 0x0D, 0x31, 0x31, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31, 0x35, 0x33, 0x33, 0x37, 0x5A, 0x30, 0x74, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0A, 0x4D, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02, 0x48, 0x5A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4E, 0x31, 0x1C, 0x30, 0x1A, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x0D, 0x74, 0x65, 0x73, 0x74, 0x40, 0x63, 0x65, 0x72, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x1C, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x30, 0x81, 0x9F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8D, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xFC, 0x80, 0x5D, 0x32, 0x55, 0xC7, 0x4C, 0xC6, 0xA8, 0x2F, 0xF7, 0xEC, 0x1F, 0x75, 0x48, 0x02, 0x79, 0xEB, 0xDF, 0x17, 0x1B, 0x08, 0xBA, 0x21, 0xDD, 0xE5, 0x43, 0x06, 0xE8, 0x81, 0xC5, 0x50, 0x3C, 0x18, 0xDD, 0x53, 0xF4, 0xC9, 0xC9, 0xE1, 0x7A, 0xD3, 0xB3, 0x99, 0xA7, 0xC6, 0x43, 0x2A, 0x51, 0x65, 0x10, 0x93, 0xBA, 0x5F, 0x48, 0xAC, 0x54, 0x12, 0x70, 0x9E, 0xF2, 0x9E, 0x7D, 0xF7, 0x22, 0xAA, 0xB7, 0x19, 0xDE, 0xA9, 0x4D, 0x55, 0xAA, 0x41, 0x8F, 0x08, 0xBD, 0x74, 0xFA, 0xE5, 0x57, 0x13, 0xB4, 0x30, 0x9A, 0xBA, 0x56, 0x01, 0x55, 0x8A, 0x9B, 0x5B, 0x50, 0x29, 0x82, 0xF9, 0x00, 0x69, 0x7E, 0x7B, 0x91, 0xA7, 0x2D, 0x48, 0x1A, 0x93, 0x7C, 0xA2, 0xF9, 0x06, 0x64, 0x4B, 0x80, 0xF8, 0x47, 0x58, 0x45, 0x90, 0x09, 0xEA, 0xD6, 0x7B, 0x85, 0x49, 0x2A, 0x4E, 0xB6, 0x71, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x10, 0x30, 0x0E, 0x30, 0x0C, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0xEF, 0x38, 0x6A, 0x43, 0x1C, 0x1D, 0x37, 0xBD, 0xF7, 0xCF, 0x15, 0x6A, 0x99, 0x44, 0xE1, 0xFC, 0x68, 0x6E, 0x91, 0x31, 0x9C, 0x1E, 0x8C, 0x1F, 0x72, 0x4B, 0x93, 0x16, 0x1F, 0x06, 0xFE, 0x94, 0xA9, 0x41, 0x64, 0x81, 0xFD, 0xFF, 0xE7, 0x27, 0x4D, 0xE7, 0x59, 0x55, 0xE1, 0x20, 0x14, 0x07, 0x3C, 0x26, 0x78, 0xB0, 0x72, 0x48, 0x76, 0x0C, 0x8B, 0x3F, 0x08, 0xD0, 0x75, 0x7D, 0x76, 0xA4, 0xB5, 0x56, 0xA6, 0xC9, 0x88, 0x17, 0x27, 0x95, 0x85, 0xEE, 0x42, 0x1E, 0x15, 0x0B, 0x05, 0xDC, 0x2F, 0x97, 0x7B, 0x26, 0x82, 0x62, 0x23, 0xDF, 0xBF, 0x55, 0x09, 0xBF, 0x5E, 0x28, 0x1A, 0xCA, 0x1B, 0xEC, 0xA4, 0x81, 0xB7, 0x9D, 0x91, 0xC9, 0x60, 0x5B, 0x29, 0x2B, 0x4C, 0x6F, 0x8B, 0xCC, 0x17, 0xA8, 0xD6, 0x5D, 0x6B, 0xBC, 0x0D, 0x03, 0x31, 0xB0, 0x57, 0xC9, 0xF8, 0x59, 0x88, 0x3D}|VOID*|0x00000001 + + ## CA certificate file's size. + # @Prompt CA file's size. + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCaFileSize|0x0000027A|UINT32|0x00000002 + + ## X509 certificate as Public Key which is used by IPsec (DER format) + # @Prompt Pubic Key for remote peer. + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCertificate|{0x30, 0x82, 0x02, 0x4D, 0x30, 0x82, 0x01, 0xB6, 0x02, 0x01, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x05, 0x00, 0x30, 0x74, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0A, 0x4D, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02, 0x48, 0x5A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4E, 0x31, 0x1C, 0x30, 0x1A, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x0D, 0x74, 0x65, 0x73, 0x74, 0x40, 0x63, 0x65, 0x72, 0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x1C, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x32, 0x30, 0x34, 0x35, 0x39, 0x5A, 0x17, 0x0D, 0x31, 0x31, 0x31, 0x31, 0x30, 0x31, 0x30, 0x32, 0x30, 0x34, 0x35, 0x39, 0x5A, 0x30, 0x6A, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x04, 0x55, 0x45, 0x46, 0x49, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02, 0x53, 0x48, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4E, 0x31, 0x23, 0x30, 0x21, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x14, 0x75, 0x65, 0x66, 0x69, 0x2E, 0x74, 0x69, 0x61, 0x6E, 0x6F, 0x40, 0x69, 0x6E, 0x74, 0x65, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x0C, 0x30, 0x0A, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x03, 0x53, 0x53, 0x47, 0x31, 0x0C, 0x30, 0x0A, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x03, 0x53, 0x53, 0x47, 0x30, 0x81, 0x9F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8D, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xE9, 0x90, 0x47, 0x0D, 0x79, 0x93, 0xED, 0xF5, 0xBD, 0xC9, 0x56, 0x03, 0xDF, 0xE2, 0x71, 0xA9, 0x42, 0x3B, 0x20, 0x1E, 0xAF, 0x88, 0x9D, 0x3F, 0xE1, 0xDE, 0x61, 0xEE, 0x83, 0xC4, 0x2E, 0x48, 0x7A, 0x1F, 0x86, 0x54, 0xD2, 0xD5, 0x61, 0x94, 0xE1, 0x15, 0x79, 0x65, 0xCB, 0x39, 0xEE, 0x78, 0x68, 0x3D, 0x2C, 0xEB, 0xE4, 0x7A, 0x8D, 0x98, 0x14, 0x28, 0x7E, 0x6B, 0xFD, 0xC5, 0xF5, 0x1B, 0x62, 0xB9, 0x86, 0x7C, 0xA1, 0x7C, 0xE9, 0x8F, 0xC8, 0xF4, 0xF3, 0x95, 0x5A, 0xAF, 0x0C, 0x21, 0x39, 0xEA, 0x47, 0x5A, 0x1E, 0xBD, 0xBE, 0x7F, 0x1B, 0x0F, 0x31, 0xFB, 0xBD, 0x57, 0xAE, 0xD7, 0xCB, 0x46, 0x83, 0x8B, 0x16, 0x19, 0x74, 0xD9, 0x9E, 0x2D, 0x18, 0xE6, 0xA4, 0x5F, 0x90, 0x90, 0x54, 0xE1, 0x4B, 0x7B, 0x57, 0x76, 0xBD, 0xF4, 0xC0, 0x4D, 0x79, 0x5F, 0x64, 0x6C, 0x0D, 0x2D, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x5A, 0x80, 0x5F, 0xD3, 0x3C, 0x93, 0x81, 0xB9, 0x1B, 0xAA, 0x08, 0x1F, 0x47, 0x9C, 0x88, 0xF3, 0x1E, 0xE6, 0x6B, 0xBB, 0x99, 0xE6, 0x23, 0x1A, 0xCB, 0x25, 0x81, 0x54, 0x51, 0x88, 0xDF, 0x9B, 0xC6, 0xBF, 0x60, 0xDB, 0x6C, 0x5D, 0x69, 0xB1, 0x3A, 0xDE, 0x94, 0xEE, 0xD7, 0x6C, 0xF2, 0x2D, 0x63, 0xD3, 0xB3, 0xAB, 0xE6, 0xB5, 0x0A, 0xBF, 0xCE, 0x61, 0xC0, 0xD3, 0x73, 0x9E, 0x80, 0xB5, 0x0C, 0xC0, 0x03, 0x57, 0xA9, 0x56, 0x59, 0x1B, 0xA2, 0x99, 0x03, 0xA6, 0xA3, 0xC4, 0x59, 0xB3, 0xD9, 0x14, 0xA1, 0x34, 0x18, 0xF3, 0x73, 0xB8, 0x54, 0xAA, 0xED, 0x7D, 0x31, 0x3E, 0x23, 0xAD, 0xF1, 0x86, 0xF7, 0xE6, 0xD9, 0x01, 0x0D, 0x68, 0xC6, 0xC5, 0x95, 0x18, 0xD2, 0x89, 0xB7, 0x06, 0x96, 0xC9, 0x11, 0xB9, 0xF0, 0xDA, 0xD9, 0x02, 0x25, 0xC4, 0xB9, 0x72, 0xF8, 0x6D, 0xC5, 0x5B}|VOID*|0x00000003 + + ## X509 certificate as Public Key's size. + # @Prompt Pubic Key's size. + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCertificateSize|0x251|UINT32|0x00000004 + + ## Private Key used by IPsec (PEM format). + # @Prompt Private Key. + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCertificateKey|{0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x52, 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x50, 0x72, 0x6F, 0x63, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A, 0x20, 0x34, 0x2C, 0x45, 0x4E, 0x43, 0x52, 0x59, 0x50, 0x54, 0x45, 0x44, 0x0A, 0x44, 0x45, 0x4B, 0x2D, 0x49, 0x6E, 0x66, 0x6F, 0x3A, 0x20, 0x44, 0x45, 0x53, 0x2D, 0x45, 0x44, 0x45, 0x33, 0x2D, 0x43, 0x42, 0x43, 0x2C, 0x32, 0x42, 0x31, 0x46, 0x42, 0x41, 0x43, 0x41, 0x38, 0x36, 0x32, 0x36, 0x33, 0x34, 0x41, 0x37, 0x0A, 0x0A, 0x61, 0x52, 0x78, 0x49, 0x58, 0x33, 0x59, 0x4D, 0x68, 0x49, 0x50, 0x41, 0x73, 0x59, 0x79, 0x6F, 0x6A, 0x49, 0x76, 0x46, 0x7A, 0x42, 0x75, 0x6B, 0x74, 0x6B, 0x4A, 0x47, 0x5A, 0x38, 0x4D, 0x64, 0x33, 0x5A, 0x53, 0x73, 0x39, 0x41, 0x2B, 0x52, 0x2B, 0x57, 0x45, 0x59, 0x41, 0x70, 0x34, 0x63, 0x4F, 0x55, 0x43, 0x4A, 0x78, 0x51, 0x2F, 0x66, 0x4A, 0x38, 0x58, 0x4F, 0x45, 0x64, 0x58, 0x38, 0x0A, 0x31, 0x63, 0x4E, 0x66, 0x4B, 0x2B, 0x49, 0x62, 0x76, 0x4B, 0x4D, 0x68, 0x55, 0x67, 0x30, 0x4B, 0x4E, 0x35, 0x38, 0x37, 0x71, 0x66, 0x2F, 0x4C, 0x31, 0x76, 0x57, 0x58, 0x6F, 0x31, 0x74, 0x5A, 0x6B, 0x59, 0x2B, 0x5A, 0x53, 0x4E, 0x63, 0x46, 0x45, 0x41, 0x76, 0x37, 0x43, 0x43, 0x50, 0x51, 0x6B, 0x64, 0x4A, 0x42, 0x48, 0x35, 0x65, 0x6B, 0x35, 0x44, 0x51, 0x2F, 0x37, 0x6D, 0x71, 0x55, 0x0A, 0x6B, 0x76, 0x78, 0x48, 0x53, 0x50, 0x70, 0x34, 0x66, 0x41, 0x71, 0x47, 0x61, 0x68, 0x54, 0x31, 0x75, 0x37, 0x37, 0x56, 0x66, 0x4E, 0x66, 0x31, 0x53, 0x74, 0x61, 0x73, 0x31, 0x6E, 0x4F, 0x67, 0x6A, 0x50, 0x31, 0x41, 0x6C, 0x7A, 0x6E, 0x6B, 0x6A, 0x57, 0x61, 0x72, 0x6A, 0x51, 0x4F, 0x73, 0x48, 0x46, 0x33, 0x41, 0x46, 0x31, 0x62, 0x61, 0x51, 0x4A, 0x50, 0x5A, 0x31, 0x6A, 0x71, 0x4C, 0x0A, 0x61, 0x30, 0x49, 0x45, 0x6E, 0x30, 0x6C, 0x59, 0x6C, 0x78, 0x35, 0x79, 0x4D, 0x6D, 0x78, 0x54, 0x47, 0x57, 0x79, 0x52, 0x35, 0x70, 0x57, 0x51, 0x35, 0x71, 0x66, 0x78, 0x2B, 0x62, 0x37, 0x64, 0x37, 0x75, 0x71, 0x67, 0x47, 0x69, 0x66, 0x36, 0x6A, 0x44, 0x47, 0x4D, 0x37, 0x68, 0x38, 0x43, 0x78, 0x2F, 0x74, 0x67, 0x2B, 0x61, 0x62, 0x45, 0x31, 0x34, 0x30, 0x2F, 0x50, 0x66, 0x6C, 0x33, 0x0A, 0x33, 0x6A, 0x50, 0x6C, 0x52, 0x75, 0x73, 0x57, 0x6F, 0x6F, 0x63, 0x49, 0x41, 0x76, 0x49, 0x74, 0x79, 0x51, 0x6D, 0x39, 0x39, 0x71, 0x74, 0x34, 0x64, 0x6E, 0x74, 0x6E, 0x74, 0x6F, 0x4A, 0x43, 0x6D, 0x4F, 0x53, 0x79, 0x71, 0x67, 0x4D, 0x6E, 0x76, 0x2F, 0x76, 0x2B, 0x51, 0x48, 0x74, 0x79, 0x4D, 0x73, 0x42, 0x64, 0x38, 0x34, 0x78, 0x45, 0x57, 0x46, 0x36, 0x72, 0x58, 0x4D, 0x52, 0x63, 0x0A, 0x53, 0x2B, 0x66, 0x68, 0x54, 0x71, 0x58, 0x74, 0x54, 0x38, 0x44, 0x50, 0x65, 0x70, 0x2F, 0x56, 0x44, 0x66, 0x65, 0x78, 0x6B, 0x41, 0x63, 0x6D, 0x63, 0x75, 0x41, 0x69, 0x6F, 0x2B, 0x79, 0x64, 0x51, 0x75, 0x49, 0x31, 0x32, 0x7A, 0x50, 0x70, 0x45, 0x68, 0x50, 0x45, 0x68, 0x31, 0x44, 0x50, 0x58, 0x73, 0x64, 0x58, 0x67, 0x64, 0x77, 0x39, 0x75, 0x46, 0x47, 0x6D, 0x63, 0x35, 0x68, 0x52, 0x0A, 0x35, 0x31, 0x57, 0x41, 0x31, 0x65, 0x63, 0x44, 0x48, 0x6A, 0x31, 0x58, 0x32, 0x45, 0x72, 0x36, 0x39, 0x59, 0x70, 0x31, 0x50, 0x69, 0x43, 0x37, 0x49, 0x47, 0x79, 0x6F, 0x71, 0x57, 0x43, 0x37, 0x69, 0x2F, 0x71, 0x6D, 0x6D, 0x72, 0x49, 0x66, 0x6F, 0x41, 0x54, 0x74, 0x39, 0x58, 0x34, 0x30, 0x54, 0x56, 0x63, 0x37, 0x42, 0x63, 0x6A, 0x34, 0x63, 0x54, 0x31, 0x78, 0x37, 0x6B, 0x70, 0x4F, 0x0A, 0x4C, 0x71, 0x67, 0x33, 0x6C, 0x50, 0x78, 0x33, 0x2B, 0x4A, 0x63, 0x33, 0x43, 0x67, 0x34, 0x79, 0x5A, 0x54, 0x66, 0x6E, 0x4A, 0x5A, 0x37, 0x48, 0x76, 0x36, 0x64, 0x68, 0x67, 0x45, 0x6D, 0x70, 0x4D, 0x73, 0x74, 0x46, 0x65, 0x35, 0x34, 0x49, 0x53, 0x76, 0x74, 0x38, 0x37, 0x59, 0x4E, 0x77, 0x74, 0x4C, 0x65, 0x6C, 0x34, 0x67, 0x50, 0x4A, 0x79, 0x53, 0x42, 0x30, 0x4B, 0x76, 0x37, 0x69, 0x0A, 0x33, 0x32, 0x74, 0x37, 0x67, 0x4F, 0x30, 0x79, 0x6D, 0x73, 0x62, 0x71, 0x4A, 0x55, 0x75, 0x79, 0x41, 0x68, 0x47, 0x64, 0x33, 0x63, 0x2B, 0x78, 0x4C, 0x46, 0x2F, 0x63, 0x63, 0x4F, 0x57, 0x44, 0x52, 0x34, 0x79, 0x72, 0x30, 0x6A, 0x79, 0x64, 0x74, 0x70, 0x79, 0x69, 0x64, 0x52, 0x45, 0x66, 0x56, 0x46, 0x66, 0x53, 0x6C, 0x39, 0x54, 0x30, 0x6D, 0x53, 0x72, 0x4E, 0x76, 0x43, 0x71, 0x45, 0x0A, 0x52, 0x52, 0x5A, 0x6E, 0x42, 0x56, 0x76, 0x37, 0x50, 0x66, 0x6C, 0x75, 0x72, 0x31, 0x59, 0x35, 0x70, 0x2F, 0x65, 0x78, 0x54, 0x63, 0x56, 0x34, 0x72, 0x4B, 0x52, 0x69, 0x6C, 0x35, 0x58, 0x6A, 0x2F, 0x39, 0x59, 0x56, 0x31, 0x4E, 0x6E, 0x6D, 0x4E, 0x2B, 0x2F, 0x31, 0x31, 0x74, 0x36, 0x58, 0x74, 0x6A, 0x72, 0x75, 0x52, 0x62, 0x33, 0x79, 0x70, 0x38, 0x76, 0x64, 0x6C, 0x61, 0x65, 0x5A, 0x0A, 0x6C, 0x67, 0x45, 0x69, 0x73, 0x30, 0x42, 0x7A, 0x4B, 0x59, 0x39, 0x59, 0x64, 0x58, 0x48, 0x64, 0x46, 0x58, 0x57, 0x59, 0x4F, 0x41, 0x71, 0x50, 0x48, 0x45, 0x65, 0x4B, 0x57, 0x79, 0x61, 0x59, 0x5A, 0x56, 0x79, 0x43, 0x70, 0x51, 0x65, 0x43, 0x53, 0x71, 0x4F, 0x71, 0x48, 0x38, 0x67, 0x42, 0x6B, 0x4F, 0x62, 0x43, 0x69, 0x72, 0x41, 0x6A, 0x65, 0x56, 0x70, 0x35, 0x7A, 0x37, 0x6B, 0x31, 0x0A, 0x64, 0x4F, 0x2F, 0x6D, 0x56, 0x74, 0x49, 0x2B, 0x57, 0x47, 0x30, 0x48, 0x72, 0x37, 0x5A, 0x4C, 0x53, 0x52, 0x78, 0x6F, 0x61, 0x44, 0x47, 0x42, 0x33, 0x4E, 0x35, 0x38, 0x4B, 0x56, 0x45, 0x4F, 0x34, 0x65, 0x46, 0x56, 0x75, 0x6E, 0x59, 0x77, 0x51, 0x42, 0x54, 0x7A, 0x4F, 0x65, 0x57, 0x39, 0x6C, 0x4B, 0x79, 0x49, 0x38, 0x67, 0x4D, 0x45, 0x57, 0x6C, 0x62, 0x4B, 0x72, 0x41, 0x45, 0x49, 0x0A, 0x46, 0x4B, 0x38, 0x7A, 0x58, 0x6F, 0x44, 0x74, 0x39, 0x6A, 0x7A, 0x54, 0x37, 0x67, 0x68, 0x6A, 0x79, 0x45, 0x54, 0x67, 0x44, 0x6C, 0x69, 0x50, 0x53, 0x49, 0x46, 0x6A, 0x79, 0x31, 0x64, 0x6B, 0x6A, 0x6D, 0x68, 0x53, 0x78, 0x79, 0x6A, 0x67, 0x62, 0x71, 0x45, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x52, 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A}|VOID*|0x00000005 + + ## Private Key's size. + # @Prompt Private Key's size. + gEfiNetworkPkgTokenSpaceGuid.PcdIpsecUefiCertificateKeySize|0x3d5|UINT32|0x00000006 + +[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx] + ## IPv6 DHCP Unique Identifier (DUID) Type configuration (From RFCs 3315 and 6355). + # 01 = DUID Based on Link-layer Address Plus Time [DUID-LLT] + # 04 = UUID-Based DHCPv6 Unique Identifier (DUID-UUID) + # 02 = DUID Assigned by Vendor Based on Enterprise Number [DUID-EN] (not supported) + # 03 = DUID Based on Link-layer Address [DUID-LL] (not supported) + # @Prompt Type Value of Dhcp6 Unique Identifier (DUID). + gEfiNetworkPkgTokenSpaceGuid.PcdDhcp6UidType|4|UINT8|0x10000001 + +[UserExtensions.TianoCore."ExtraFiles"] + NetworkPkgExtra.uni diff --git a/Core/NetworkPkg/NetworkPkg.dsc b/Core/NetworkPkg/NetworkPkg.dsc new file mode 100644 index 0000000000..39224f3425 --- /dev/null +++ b/Core/NetworkPkg/NetworkPkg.dsc @@ -0,0 +1,117 @@ +## @file +# UEFI 2.4 Network Module Package for All Architectures +# +# (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+# Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + PLATFORM_NAME = NetworkPkg + PLATFORM_GUID = 3FD34E9B-E90C-44e1-B510-1F632A509F10 + PLATFORM_VERSION = 0.96 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/NetworkPkg + SUPPORTED_ARCHITECTURES = IA32|IPF|X64|EBC|ARM|AARCH64 + BUILD_TARGETS = DEBUG|RELEASE + SKUID_IDENTIFIER = DEFAULT + +[LibraryClasses] + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf + + DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf + NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf + IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf + UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf + TcpIoLib|MdeModulePkg/Library/DxeTcpIoLib/DxeTcpIoLib.inf + HttpLib|MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.inf + BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf + OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLib.inf + IntrinsicLib|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf + SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf + +[LibraryClasses.common.UEFI_DRIVER] + DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf + +[LibraryClasses.common.UEFI_APPLICATION] + DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf + ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf + +[LibraryClasses.ARM, LibraryClasses.AARCH64] + # + # It is not possible to prevent ARM compiler calls to generic intrinsic functions. + # This library provides the instrinsic functions generated by a given compiler. + # [LibraryClasses.ARM] and NULL mean link this library into all ARM images. + # + NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf + +[PcdsFeatureFlag] + gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable|TRUE + gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable|TRUE + +[PcdsFixedAtBuild] + gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2f + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000000 + +################################################################################################### +# +# Components Section - list of the modules and components that will be processed by compilation +# tools and the EDK II tools to generate PE32/PE32+/Coff image files. +# +# Note: The EDK II DSC file is not used to specify how compiled binary images get placed +# into firmware volume images. This section is just a list of modules to compile from +# source into UEFI-compliant binaries. +# It is the FDF file that contains information on combining binary files into firmware +# volume images, whose concept is beyond UEFI and is described in PI specification. +# Binary modules do not need to be listed in this section, as they should be +# specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi), +# Logo (Logo.bmp), and etc. +# There may also be modules listed in this section that are not required in the FDF file, +# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be +# generated for it, but the binary will not be put into any firmware volume. +# +################################################################################################### + +[Components] + NetworkPkg/Ip6Dxe/Ip6Dxe.inf + NetworkPkg/TcpDxe/TcpDxe.inf + NetworkPkg/Udp6Dxe/Udp6Dxe.inf + NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf + NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf + NetworkPkg/DnsDxe/DnsDxe.inf + NetworkPkg/HttpDxe/HttpDxe.inf + NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf + NetworkPkg/HttpBootDxe/HttpBootDxe.inf + + NetworkPkg/Application/IfConfig6/IfConfig6.inf + NetworkPkg/Application/IpsecConfig/IpSecConfig.inf + NetworkPkg/Application/VConfig/VConfig.inf + +[Components.IA32, Components.X64, Components.IPF] + NetworkPkg/IpSecDxe/IpSecDxe.inf + NetworkPkg/IScsiDxe/IScsiDxe.inf + NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf + NetworkPkg/Application/Ping6/Ping6.inf diff --git a/Core/NetworkPkg/NetworkPkg.uni b/Core/NetworkPkg/NetworkPkg.uni new file mode 100644 index 0000000000..e7b1cc5eed Binary files /dev/null and b/Core/NetworkPkg/NetworkPkg.uni differ diff --git a/Core/NetworkPkg/NetworkPkgExtra.uni b/Core/NetworkPkg/NetworkPkgExtra.uni new file mode 100644 index 0000000000..51793d3513 Binary files /dev/null and b/Core/NetworkPkg/NetworkPkgExtra.uni differ diff --git a/Core/NetworkPkg/TcpDxe/ComponentName.c b/Core/NetworkPkg/TcpDxe/ComponentName.c new file mode 100644 index 0000000000..41eb5a5e13 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/ComponentName.c @@ -0,0 +1,528 @@ +/** @file + Implementation of protocols EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +// +// EFI Component Name Functions +// + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This, and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language or DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language, from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/// +/// EFI Component Name Protocol +/// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName = { + TcpComponentNameGetDriverName, + TcpComponentNameGetControllerName, + "eng" +}; + +/// +/// EFI Component Name 2 Protocol +/// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) TcpComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) TcpComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mTcpDriverNameTable[] = { + { + "eng;en", + L"TCP Network Service Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gTcpControllerNameTable = NULL; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This, and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language or DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mTcpDriverNameTable, + DriverName, + (BOOLEAN) (This == &gTcpComponentName) + ); +} + +/** + Update the component name for the Tcp4 child handle. + + @param Tcp4[in] A pointer to the EFI_TCP4_PROTOCOL. + + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + +**/ +EFI_STATUS +UpdateTcp4Name ( + IN EFI_TCP4_PROTOCOL *Tcp4 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[80]; + EFI_TCP4_CONFIG_DATA Tcp4ConfigData; + + if (Tcp4 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer as: + // TCPv4 (SrcPort=59, DestPort=60, ActiveFlag=TRUE) + // + ZeroMem (&Tcp4ConfigData, sizeof (Tcp4ConfigData)); + Status = Tcp4->GetModeData (Tcp4, NULL, &Tcp4ConfigData, NULL, NULL, NULL); + if (!EFI_ERROR (Status)) { + UnicodeSPrint (HandleName, sizeof (HandleName), + L"TCPv4 (SrcPort=%d, DestPort=%d, ActiveFlag=%s)", + Tcp4ConfigData.AccessPoint.StationPort, + Tcp4ConfigData.AccessPoint.RemotePort, + (Tcp4ConfigData.AccessPoint.ActiveFlag ? L"TRUE" : L"FALSE") + ); + } else if (Status == EFI_NOT_STARTED) { + UnicodeSPrint ( + HandleName, + sizeof (HandleName), + L"TCPv4 (Not started)" + ); + } else { + return Status; + } + + if (gTcpControllerNameTable != NULL) { + FreeUnicodeStringTable (gTcpControllerNameTable); + gTcpControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gTcpComponentName.SupportedLanguages, + &gTcpControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gTcpComponentName2.SupportedLanguages, + &gTcpControllerNameTable, + HandleName, + FALSE + ); +} + +/** + Update the component name for the Tcp6 child handle. + + @param Tcp6[in] A pointer to the EFI_TCP6_PROTOCOL. + + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + +**/ +EFI_STATUS +UpdateTcp6Name ( + IN EFI_TCP6_PROTOCOL *Tcp6 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[80]; + EFI_TCP6_CONFIG_DATA Tcp6ConfigData; + + if (Tcp6 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer. + // + ZeroMem (&Tcp6ConfigData, sizeof (Tcp6ConfigData)); + Status = Tcp6->GetModeData (Tcp6, NULL, &Tcp6ConfigData, NULL, NULL, NULL); + if (!EFI_ERROR (Status)) { + UnicodeSPrint (HandleName, sizeof (HandleName), + L"TCPv6(SrcPort=%d, DestPort=%d, ActiveFlag=%d)", + Tcp6ConfigData.AccessPoint.StationPort, + Tcp6ConfigData.AccessPoint.RemotePort, + Tcp6ConfigData.AccessPoint.ActiveFlag + ); + } else if (Status == EFI_NOT_STARTED) { + UnicodeSPrint (HandleName, sizeof (HandleName), L"TCPv6(Not started)"); + } else { + return Status; + } + + + if (gTcpControllerNameTable != NULL) { + FreeUnicodeStringTable (gTcpControllerNameTable); + gTcpControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gTcpComponentName.SupportedLanguages, + &gTcpControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gTcpComponentName2.SupportedLanguages, + &gTcpControllerNameTable, + HandleName, + FALSE + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language, from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP6_PROTOCOL *Tcp6; + + // + // Only provide names for child handles. + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiIp6ProtocolGuid + ); + if (!EFI_ERROR (Status)) { + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **)&Tcp6, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateTcp6Name (Tcp6); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiIp4ProtocolGuid + ); + if (!EFI_ERROR (Status)) { + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **)&Tcp4, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateTcp4Name (Tcp4); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gTcpControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gTcpComponentName) + ); +} + diff --git a/Core/NetworkPkg/TcpDxe/SockImpl.c b/Core/NetworkPkg/TcpDxe/SockImpl.c new file mode 100644 index 0000000000..f941556402 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/SockImpl.c @@ -0,0 +1,1230 @@ +/** @file + Implementation of the Socket. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "SockImpl.h" + +/** + Get the first buffer block in the specific socket buffer. + + @param[in] Sockbuf Pointer to the socket buffer. + + @return Pointer to the first buffer in the queue. NULL if the queue is empty. + +**/ +NET_BUF * +SockBufFirst ( + IN SOCK_BUFFER *Sockbuf + ) +{ + LIST_ENTRY *NetbufList; + + NetbufList = &(Sockbuf->DataQueue->BufList); + + if (IsListEmpty (NetbufList)) { + return NULL; + } + + return NET_LIST_HEAD (NetbufList, NET_BUF, List); +} + +/** + Get the next buffer block in the specific socket buffer. + + @param[in] Sockbuf Pointer to the socket buffer. + @param[in] SockEntry Pointer to the buffer block prior to the required one. + + @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is + the tail or head entry. + +**/ +NET_BUF * +SockBufNext ( + IN SOCK_BUFFER *Sockbuf, + IN NET_BUF *SockEntry + ) +{ + LIST_ENTRY *NetbufList; + + NetbufList = &(Sockbuf->DataQueue->BufList); + + if ((SockEntry->List.ForwardLink == NetbufList) || + (SockEntry->List.BackLink == &SockEntry->List) || + (SockEntry->List.ForwardLink == &SockEntry->List) + ) { + + return NULL; + } + + return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List); +} + +/** + User provided callback function for NetbufFromExt. + + @param[in] Event The Event this notify function registered to, ignored. + +**/ +VOID +EFIAPI +SockFreeFoo ( + IN EFI_EVENT Event + ) +{ + return; +} + +/** + Get the length of the data that can be retrieved from the socket + receive buffer. + + @param[in] SockBuffer Pointer to the socket receive buffer. + @param[out] IsUrg Pointer to a BOOLEAN variable. + If TRUE the data is OOB. + @param[in] BufLen The maximum length of the data buffer to + store the received data in the socket layer. + + @return The length of the data can be retreived. + +**/ +UINT32 +SockTcpDataToRcv ( + IN SOCK_BUFFER *SockBuffer, + OUT BOOLEAN *IsUrg, + IN UINT32 BufLen + ) +{ + NET_BUF *RcvBufEntry; + UINT32 DataLen; + TCP_RSV_DATA *TcpRsvData; + BOOLEAN Urg; + + ASSERT ((SockBuffer != NULL) && (IsUrg != NULL) && (BufLen > 0)); + + // + // Get the first socket receive buffer + // + RcvBufEntry = SockBufFirst (SockBuffer); + ASSERT (RcvBufEntry != NULL); + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + // + // Check whether the receive data is out of bound. If yes, calculate the maximum + // allowed length of the urgent data and output it. + // + *IsUrg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE); + + if (*IsUrg && (TcpRsvData->UrgLen < RcvBufEntry->TotalSize)) { + + DataLen = MIN (TcpRsvData->UrgLen, BufLen); + + if (DataLen < TcpRsvData->UrgLen) { + TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen; + } else { + TcpRsvData->UrgLen = 0; + } + + return DataLen; + + } + + // + // Process the next socket receive buffer to get the maximum allowed length + // of the received data. + // + DataLen = RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + + while ((BufLen > DataLen) && (RcvBufEntry != NULL)) { + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + Urg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE); + + if (*IsUrg != Urg) { + break; + } + + if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) { + + if (TcpRsvData->UrgLen + DataLen < BufLen) { + TcpRsvData->UrgLen = 0; + } else { + TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen); + } + + return MIN (TcpRsvData->UrgLen + DataLen, BufLen); + + } + + DataLen += RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + } + + DataLen = MIN (BufLen, DataLen); + return DataLen; +} + +/** + Copy data from socket buffer to an application provided receive buffer. + + @param[in] Sock Pointer to the socket. + @param[in] TcpRxData Pointer to the application provided receive buffer. + @param[in] RcvdBytes The maximum length of the data can be copied. + @param[in] IsUrg If TRUE the data is Out of Bound, FALSE the data is normal. + +**/ +VOID +SockSetTcpRxData ( + IN SOCKET *Sock, + IN VOID *TcpRxData, + IN UINT32 RcvdBytes, + IN BOOLEAN IsUrg + ) +{ + UINT32 Index; + UINT32 CopyBytes; + UINT32 OffSet; + EFI_TCP4_RECEIVE_DATA *RxData; + EFI_TCP4_FRAGMENT_DATA *Fragment; + + RxData = (EFI_TCP4_RECEIVE_DATA *) TcpRxData; + + OffSet = 0; + + ASSERT (RxData->DataLength >= RcvdBytes); + + RxData->DataLength = RcvdBytes; + RxData->UrgentFlag = IsUrg; + + // + // Copy the CopyBytes data from socket receive buffer to RxData. + // + for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) { + + Fragment = &RxData->FragmentTable[Index]; + CopyBytes = MIN ((UINT32) (Fragment->FragmentLength), RcvdBytes); + + NetbufQueCopy ( + Sock->RcvBuffer.DataQueue, + OffSet, + CopyBytes, + Fragment->FragmentBuffer + ); + + Fragment->FragmentLength = CopyBytes; + RcvdBytes -= CopyBytes; + OffSet += CopyBytes; + } +} + +/** + Process the send token. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockProcessSndToken ( + IN OUT SOCKET *Sock + ) +{ + UINT32 FreeSpace; + SOCK_TOKEN *SockToken; + UINT32 DataLen; + SOCK_IO_TOKEN *SndToken; + EFI_TCP4_TRANSMIT_DATA *TxData; + EFI_STATUS Status; + + ASSERT ((Sock != NULL) && (SockStream == Sock->Type)); + + FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF); + + // + // to determine if process a send token using + // socket layer flow control policy + // + while ((FreeSpace >= Sock->SndBuffer.LowWater) && !IsListEmpty (&Sock->SndTokenList)) { + + SockToken = NET_LIST_HEAD ( + &(Sock->SndTokenList), + SOCK_TOKEN, + TokenList + ); + + // + // process this token + // + RemoveEntryList (&(SockToken->TokenList)); + InsertTailList ( + &(Sock->ProcessingSndTokenList), + &(SockToken->TokenList) + ); + + // + // Proceess it in the light of SockType + // + SndToken = (SOCK_IO_TOKEN *) SockToken->Token; + TxData = SndToken->Packet.TxData; + + DataLen = TxData->DataLength; + Status = SockProcessTcpSndData (Sock, TxData); + + if (EFI_ERROR (Status)) { + goto OnError; + } + + if (DataLen >= FreeSpace) { + FreeSpace = 0; + + } else { + FreeSpace -= DataLen; + + } + } + + return; + +OnError: + + RemoveEntryList (&SockToken->TokenList); + SIGNAL_TOKEN (SockToken->Token, Status); + FreePool (SockToken); +} + +/** + Get received data from the socket layer to the receive token. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] RcvToken Pointer to the application provided receive token. + + @return The length of data received in this token. + +**/ +UINT32 +SockProcessRcvToken ( + IN OUT SOCKET *Sock, + IN OUT SOCK_IO_TOKEN *RcvToken + ) +{ + UINT32 TokenRcvdBytes; + EFI_TCP4_RECEIVE_DATA *RxData; + BOOLEAN IsUrg; + + ASSERT (Sock != NULL); + + ASSERT (SockStream == Sock->Type); + + RxData = RcvToken->Packet.RxData; + + TokenRcvdBytes = SockTcpDataToRcv ( + &Sock->RcvBuffer, + &IsUrg, + RxData->DataLength + ); + + // + // Copy data from RcvBuffer of socket to user + // provided RxData and set the fields in TCP RxData + // + SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg); + + NetbufQueTrim (Sock->RcvBuffer.DataQueue, TokenRcvdBytes); + SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS); + + return TokenRcvdBytes; +} + +/** + Process the TCP send data, buffer the tcp txdata, and append + the buffer to socket send buffer, then try to send it. + + @param[in] Sock Pointer to the socket. + @param[in] TcpTxData Pointer to the application provided send buffer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +SockProcessTcpSndData ( + IN SOCKET *Sock, + IN VOID *TcpTxData + ) +{ + NET_BUF *SndData; + EFI_STATUS Status; + EFI_TCP4_TRANSMIT_DATA *TxData; + + TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData; + + // + // transform this TxData into a NET_BUFFER + // and insert it into Sock->SndBuffer + // + SndData = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + 0, + 0, + SockFreeFoo, + NULL + ); + + if (NULL == SndData) { + DEBUG ( + (EFI_D_ERROR, + "SockKProcessSndData: Failed to call NetBufferFromExt\n") + ); + + return EFI_OUT_OF_RESOURCES; + } + + NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData); + + // + // notify the low layer protocol to handle this send token + // + if (TxData->Urgent) { + Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (TxData->Push) { + Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // low layer protocol should really handle the sending + // process when catching SOCK_SND request + // + Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Flush the tokens in the specific token list. + + @param[in] Sock Pointer to the socket. + @param[in, out] PendingTokenList Pointer to the token list to be flushed. + +**/ +VOID +SockFlushPendingToken ( + IN SOCKET *Sock, + IN OUT LIST_ENTRY *PendingTokenList + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *Token; + + ASSERT ((Sock != NULL) && (PendingTokenList != NULL)); + + while (!IsListEmpty (PendingTokenList)) { + SockToken = NET_LIST_HEAD ( + PendingTokenList, + SOCK_TOKEN, + TokenList + ); + + Token = SockToken->Token; + SIGNAL_TOKEN (Token, Sock->SockError); + + RemoveEntryList (&(SockToken->TokenList)); + FreePool (SockToken); + } +} + +/** + Wake up the connection token while the connection is successfully established, + then try to process any pending send token. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockWakeConnToken ( + IN OUT SOCKET *Sock + ) +{ + ASSERT (Sock->ConnectionToken != NULL); + + SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS); + Sock->ConnectionToken = NULL; + + // + // check to see if some pending send token existed? + // + SockProcessSndToken (Sock); +} + +/** + Wake up the listen token while the connection is established successfully. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockWakeListenToken ( + IN OUT SOCKET *Sock + ) +{ + SOCKET *Parent; + SOCK_TOKEN *SockToken; + EFI_TCP4_LISTEN_TOKEN *ListenToken; + + Parent = Sock->Parent; + + ASSERT ((Parent != NULL) && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock)); + + if (!IsListEmpty (&Parent->ListenTokenList)) { + SockToken = NET_LIST_HEAD ( + &Parent->ListenTokenList, + SOCK_TOKEN, + TokenList + ); + + ListenToken = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token; + ListenToken->NewChildHandle = Sock->SockHandle; + + SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS); + + RemoveEntryList (&SockToken->TokenList); + FreePool (SockToken); + + RemoveEntryList (&Sock->ConnectionList); + + Parent->ConnCnt--; + DEBUG ( + (EFI_D_INFO, + "SockWakeListenToken: accept a socket, now conncnt is %d", + Parent->ConnCnt) + ); + + Sock->Parent = NULL; + } +} + +/** + Wake up the receive token while some data is received. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockWakeRcvToken ( + IN OUT SOCKET *Sock + ) +{ + UINT32 RcvdBytes; + UINT32 TokenRcvdBytes; + SOCK_TOKEN *SockToken; + SOCK_IO_TOKEN *RcvToken; + + ASSERT (Sock->RcvBuffer.DataQueue != NULL); + + RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize; + + ASSERT (RcvdBytes > 0); + + while (RcvdBytes > 0 && !IsListEmpty (&Sock->RcvTokenList)) { + + SockToken = NET_LIST_HEAD ( + &Sock->RcvTokenList, + SOCK_TOKEN, + TokenList + ); + + RcvToken = (SOCK_IO_TOKEN *) SockToken->Token; + TokenRcvdBytes = SockProcessRcvToken (Sock, RcvToken); + + if (0 == TokenRcvdBytes) { + return ; + } + + RemoveEntryList (&(SockToken->TokenList)); + FreePool (SockToken); + RcvdBytes -= TokenRcvdBytes; + } +} + +/** + Create a socket with initial data SockInitData. + + @param[in] SockInitData Pointer to the initial data of the socket. + + @return Pointer to the newly created socket, return NULL when an exception occurs. + +**/ +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ) +{ + SOCKET *Sock; + SOCKET *Parent; + EFI_STATUS Status; + EFI_GUID *TcpProtocolGuid; + UINTN ProtocolLength; + + ASSERT ((SockInitData != NULL) && (SockInitData->ProtoHandler != NULL)); + ASSERT (SockInitData->Type == SockStream); + ASSERT ((SockInitData->ProtoData != NULL) && (SockInitData->DataSize <= PROTO_RESERVED_LEN)); + + if (SockInitData->IpVersion == IP_VERSION_4) { + TcpProtocolGuid = &gEfiTcp4ProtocolGuid; + ProtocolLength = sizeof (EFI_TCP4_PROTOCOL); + } else { + TcpProtocolGuid = &gEfiTcp6ProtocolGuid; + ProtocolLength = sizeof (EFI_TCP6_PROTOCOL); + } + + + Parent = SockInitData->Parent; + + if ((Parent != NULL) && (Parent->ConnCnt == Parent->BackLog)) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: Socket parent has reached its connection limit with %d ConnCnt and %d BackLog\n", + Parent->ConnCnt, + Parent->BackLog) + ); + + return NULL; + } + + Sock = AllocateZeroPool (sizeof (SOCKET)); + if (NULL == Sock) { + + DEBUG ((EFI_D_ERROR, "SockCreate: No resource to create a new socket\n")); + return NULL; + } + + InitializeListHead (&Sock->Link); + InitializeListHead (&Sock->ConnectionList); + InitializeListHead (&Sock->ListenTokenList); + InitializeListHead (&Sock->RcvTokenList); + InitializeListHead (&Sock->SndTokenList); + InitializeListHead (&Sock->ProcessingSndTokenList); + + EfiInitializeLock (&(Sock->Lock), TPL_CALLBACK); + + Sock->SndBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->SndBuffer.DataQueue) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: No resource to allocate SndBuffer for new socket\n") + ); + + goto OnError; + } + + Sock->RcvBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->RcvBuffer.DataQueue) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: No resource to allocate RcvBuffer for new socket\n") + ); + + goto OnError; + } + + Sock->Signature = SOCK_SIGNATURE; + + Sock->Parent = Parent; + Sock->BackLog = SockInitData->BackLog; + Sock->ProtoHandler = SockInitData->ProtoHandler; + Sock->SndBuffer.HighWater = SockInitData->SndBufferSize; + Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize; + Sock->Type = SockInitData->Type; + Sock->DriverBinding = SockInitData->DriverBinding; + Sock->State = SockInitData->State; + Sock->CreateCallback = SockInitData->CreateCallback; + Sock->DestroyCallback = SockInitData->DestroyCallback; + Sock->Context = SockInitData->Context; + + Sock->SockError = EFI_ABORTED; + Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER; + Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER; + + Sock->IpVersion = SockInitData->IpVersion; + + // + // Install protocol on Sock->SockHandle + // + CopyMem (&Sock->NetProtocol, SockInitData->Protocol, ProtocolLength); + + // + // copy the protodata into socket + // + CopyMem (Sock->ProtoReserved, SockInitData->ProtoData, SockInitData->DataSize); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Sock->SockHandle, + TcpProtocolGuid, + &Sock->NetProtocol, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: Install TCP protocol in socket failed with %r\n", + Status) + ); + + goto OnError; + } + + if (Parent != NULL) { + ASSERT (Parent->BackLog > 0); + ASSERT (SOCK_IS_LISTENING (Parent)); + + // + // need to add it into Parent->ConnectionList + // if the Parent->ConnCnt < Parent->BackLog + // + Parent->ConnCnt++; + + DEBUG ( + (EFI_D_INFO, + "SockCreate: Create a new socket and add to parent, now conncnt is %d\n", + Parent->ConnCnt) + ); + + InsertTailList (&Parent->ConnectionList, &Sock->ConnectionList); + } + + if (Sock->CreateCallback != NULL) { + Status = Sock->CreateCallback (Sock, Sock->Context); + if (EFI_ERROR (Status)) { + goto OnError; + } + } + + return Sock; + +OnError: + + if (Sock->SockHandle != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + Sock->SockHandle, + TcpProtocolGuid, + &Sock->NetProtocol, + NULL + ); + } + + if (NULL != Sock->SndBuffer.DataQueue) { + NetbufQueFree (Sock->SndBuffer.DataQueue); + } + + if (NULL != Sock->RcvBuffer.DataQueue) { + NetbufQueFree (Sock->RcvBuffer.DataQueue); + } + + FreePool (Sock); + + return NULL; +} + +/** + Destroy a socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockDestroy ( + IN OUT SOCKET *Sock + ) +{ + VOID *SockProtocol; + EFI_GUID *TcpProtocolGuid; + EFI_STATUS Status; + + ASSERT (SockStream == Sock->Type); + + if (Sock->DestroyCallback != NULL) { + Sock->DestroyCallback (Sock, Sock->Context); + } + + // + // Flush the completion token buffered + // by sock and rcv, snd buffer + // + if (!SOCK_IS_UNCONFIGURED (Sock)) { + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + Sock->ConfigureState = SO_UNCONFIGURED; + + } + // + // Destroy the RcvBuffer Queue and SendBuffer Queue + // + NetbufQueFree (Sock->RcvBuffer.DataQueue); + NetbufQueFree (Sock->SndBuffer.DataQueue); + + // + // Remove it from parent connection list if needed + // + if (Sock->Parent != NULL) { + + RemoveEntryList (&(Sock->ConnectionList)); + (Sock->Parent->ConnCnt)--; + + DEBUG ( + (EFI_D_WARN, + "SockDestroy: Delete a unaccepted socket from parent now conncnt is %d\n", + Sock->Parent->ConnCnt) + ); + + Sock->Parent = NULL; + } + + // + // Set the protocol guid and driver binding handle + // in the light of Sock->SockType + // + if (Sock->IpVersion == IP_VERSION_4) { + TcpProtocolGuid = &gEfiTcp4ProtocolGuid; + } else { + TcpProtocolGuid = &gEfiTcp6ProtocolGuid; + } + + // + // Retrieve the protocol installed on this sock + // + Status = gBS->OpenProtocol ( + Sock->SockHandle, + TcpProtocolGuid, + &SockProtocol, + Sock->DriverBinding, + Sock->SockHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockDestroy: Open protocol installed on socket failed with %r\n", + Status) + ); + + goto FreeSock; + } + + // + // Uninstall the protocol installed on this sock + // in the light of Sock->SockType + // + gBS->UninstallMultipleProtocolInterfaces ( + Sock->SockHandle, + TcpProtocolGuid, + SockProtocol, + NULL + ); + +FreeSock: + + FreePool (Sock); +} + +/** + Flush the sndBuffer and rcvBuffer of socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockConnFlush ( + IN OUT SOCKET *Sock + ) +{ + SOCKET *Child; + + ASSERT (Sock != NULL); + + // + // Clear the flag in this socket + // + Sock->Flag = 0; + + // + // Flush the SndBuffer and RcvBuffer of Sock + // + NetbufQueFlush (Sock->SndBuffer.DataQueue); + NetbufQueFlush (Sock->RcvBuffer.DataQueue); + + // + // Signal the pending token + // + if (Sock->ConnectionToken != NULL) { + SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError); + Sock->ConnectionToken = NULL; + } + + if (Sock->CloseToken != NULL) { + SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError); + Sock->CloseToken = NULL; + } + + SockFlushPendingToken (Sock, &(Sock->ListenTokenList)); + SockFlushPendingToken (Sock, &(Sock->RcvTokenList)); + SockFlushPendingToken (Sock, &(Sock->SndTokenList)); + SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList)); + + // + // Destroy the pending connection, if it is a listening socket + // + if (SOCK_IS_LISTENING (Sock)) { + while (!IsListEmpty (&Sock->ConnectionList)) { + Child = NET_LIST_HEAD ( + &Sock->ConnectionList, + SOCKET, + ConnectionList + ); + + SockDestroyChild (Child); + } + + Sock->ConnCnt = 0; + } + +} + +/** + Set the state of the socket. + + @param[in, out] Sock Pointer to the socket. + @param[in] State The new socket state to be set. + +**/ +VOID +SockSetState ( + IN OUT SOCKET *Sock, + IN UINT8 State + ) +{ + Sock->State = State; +} + +/** + Clone a new socket, including its associated protocol control block. + + @param[in] Sock Pointer to the socket to be cloned. + + @return Pointer to the newly cloned socket. If NULL, an error condition occurred. + +**/ +SOCKET * +SockClone ( + IN SOCKET *Sock + ) +{ + SOCKET *ClonedSock; + SOCK_INIT_DATA InitData; + + InitData.BackLog = Sock->BackLog; + InitData.Parent = Sock; + InitData.State = Sock->State; + InitData.ProtoHandler = Sock->ProtoHandler; + InitData.Type = Sock->Type; + InitData.RcvBufferSize = Sock->RcvBuffer.HighWater; + InitData.SndBufferSize = Sock->SndBuffer.HighWater; + InitData.DriverBinding = Sock->DriverBinding; + InitData.IpVersion = Sock->IpVersion; + InitData.Protocol = &(Sock->NetProtocol); + InitData.CreateCallback = Sock->CreateCallback; + InitData.DestroyCallback = Sock->DestroyCallback; + InitData.Context = Sock->Context; + InitData.ProtoData = Sock->ProtoReserved; + InitData.DataSize = sizeof (Sock->ProtoReserved); + + ClonedSock = SockCreate (&InitData); + + if (NULL == ClonedSock) { + DEBUG ((EFI_D_ERROR, "SockClone: no resource to create a cloned sock\n")); + return NULL; + } + + SockSetState (ClonedSock, SO_CONNECTING); + ClonedSock->ConfigureState = Sock->ConfigureState; + + return ClonedSock; +} + +/** + Called by the low layer protocol to indicate the socket a connection is + established. + + This function just changes the socket's state to SO_CONNECTED + and signals the token used for connection establishment. + + @param[in, out] Sock Pointer to the socket associated with the + established connection. + +**/ +VOID +SockConnEstablished ( + IN OUT SOCKET *Sock + ) +{ + + ASSERT (SO_CONNECTING == Sock->State); + + SockSetState (Sock, SO_CONNECTED); + + if (NULL == Sock->Parent) { + SockWakeConnToken (Sock); + } else { + SockWakeListenToken (Sock); + } + +} + +/** + Called by the low layer protocol to indicate the connection is closed. + + This function flushes the socket, sets the state to SO_CLOSED, and signals + the close token. + + @param[in, out] Sock Pointer to the socket associated with the closed + connection. + +**/ +VOID +SockConnClosed ( + IN OUT SOCKET *Sock + ) +{ + if (Sock->CloseToken != NULL) { + SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS); + Sock->CloseToken = NULL; + } + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + if (Sock->Parent != NULL) { + SockDestroyChild (Sock); + } + +} + +/** + Called by low layer protocol to indicate that some data was sent or processed. + + This function trims the sent data in the socket send buffer, and signals the data + token if proper. + + @param[in, out] Sock Pointer to the socket. + @param[in] Count The length of the data processed or sent, in bytes. + +**/ +VOID +SockDataSent ( + IN OUT SOCKET *Sock, + IN UINT32 Count + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *SndToken; + + ASSERT (!IsListEmpty (&Sock->ProcessingSndTokenList)); + ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize); + + NetbufQueTrim (Sock->SndBuffer.DataQueue, Count); + + // + // To check if we can signal some snd token in this socket + // + while (Count > 0) { + SockToken = NET_LIST_HEAD ( + &(Sock->ProcessingSndTokenList), + SOCK_TOKEN, + TokenList + ); + + SndToken = SockToken->Token; + + if (SockToken->RemainDataLen <= Count) { + + RemoveEntryList (&(SockToken->TokenList)); + SIGNAL_TOKEN (SndToken, EFI_SUCCESS); + Count -= SockToken->RemainDataLen; + FreePool (SockToken); + } else { + + SockToken->RemainDataLen -= Count; + Count = 0; + } + } + + // + // to judge if we can process some send token in + // Sock->SndTokenList, if so process those send token + // + SockProcessSndToken (Sock); +} + +/** + Called by the low layer protocol to copy some data in the socket send + buffer starting from the specific offset to a buffer provided by + the caller. + + @param[in] Sock Pointer to the socket. + @param[in] Offset The start point of the data to be copied. + @param[in] Len The length of the data to be copied. + @param[out] Dest Pointer to the destination to copy the data. + + @return The data size copied. + +**/ +UINT32 +SockGetDataToSend ( + IN SOCKET *Sock, + IN UINT32 Offset, + IN UINT32 Len, + OUT UINT8 *Dest + ) +{ + ASSERT ((Sock != NULL) && SockStream == Sock->Type); + + return NetbufQueCopy ( + Sock->SndBuffer.DataQueue, + Offset, + Len, + Dest + ); +} + +/** + Called by the low layer protocol to deliver received data to socket layer. + + This function will append the data to the socket receive buffer, set the + urgent data length, and then check if any receive token can be signaled. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] NetBuffer Pointer to the buffer that contains the received data. + @param[in] UrgLen The length of the urgent data in the received data. + +**/ +VOID +SockDataRcvd ( + IN OUT SOCKET *Sock, + IN OUT NET_BUF *NetBuffer, + IN UINT32 UrgLen + ) +{ + ASSERT ((Sock != NULL) && (Sock->RcvBuffer.DataQueue != NULL) && + UrgLen <= NetBuffer->TotalSize); + + NET_GET_REF (NetBuffer); + + ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen; + + NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer); + + SockWakeRcvToken (Sock); +} + +/** + Get the length of the free space of the specific socket buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Which Flag to indicate which socket buffer to check: + either send buffer or receive buffer. + + @return The length of the free space, in bytes. + +**/ +UINT32 +SockGetFreeSpace ( + IN SOCKET *Sock, + IN UINT32 Which + ) +{ + UINT32 BufferCC; + SOCK_BUFFER *SockBuffer; + + ASSERT (Sock != NULL && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which))); + + if (SOCK_SND_BUF == Which) { + SockBuffer = &(Sock->SndBuffer); + } else { + SockBuffer = &(Sock->RcvBuffer); + } + + BufferCC = (SockBuffer->DataQueue)->BufSize; + + if (BufferCC >= SockBuffer->HighWater) { + + return 0; + } + + return SockBuffer->HighWater - BufferCC; +} + +/** + Called by the low layer protocol to indicate that there will be no more data + from the communication peer. + + This function sets the socket's state to SO_NO_MORE_DATA and signals all queued + IO tokens with the error status EFI_CONNECTION_FIN. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockNoMoreData ( + IN OUT SOCKET *Sock + ) +{ + EFI_STATUS Err; + + SOCK_NO_MORE_DATA (Sock); + + if (!IsListEmpty (&Sock->RcvTokenList)) { + + ASSERT (0 == GET_RCV_DATASIZE (Sock)); + + Err = Sock->SockError; + + SOCK_ERROR (Sock, EFI_CONNECTION_FIN); + + SockFlushPendingToken (Sock, &Sock->RcvTokenList); + + SOCK_ERROR (Sock, Err); + + } +} + diff --git a/Core/NetworkPkg/TcpDxe/SockImpl.h b/Core/NetworkPkg/TcpDxe/SockImpl.h new file mode 100644 index 0000000000..bb4f6c2085 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/SockImpl.h @@ -0,0 +1,103 @@ +/** @file + The function declaration that provided for Socket Interface. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _SOCK_IMPL_H_ +#define _SOCK_IMPL_H_ + +#include "Socket.h" + +/** + Signal a event with the given status. + + @param[in] Token The token's event is to be signaled. + @param[in] TokenStatus The status to be sent with the event. + +**/ +#define SIGNAL_TOKEN(Token, TokenStatus) \ + do { \ + (Token)->Status = (TokenStatus); \ + gBS->SignalEvent ((Token)->Event); \ + } while (0) + +#define SOCK_HEADER_SPACE (60 + 60 + 72) + +/** + Process the TCP send data, buffer the tcp txdata and append + the buffer to socket send buffer, then try to send it. + + @param[in] Sock Pointer to the socket. + @param[in] TcpTxData Pointer to the application provided send buffer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +SockProcessTcpSndData ( + IN SOCKET *Sock, + IN VOID *TcpTxData + ); + +/** + Get received data from the socket layer to the receive token. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] RcvToken Pointer to the application provided receive token. + + @return The length of data received in this token. + +**/ +UINT32 +SockProcessRcvToken ( + IN OUT SOCKET *Sock, + IN OUT SOCK_IO_TOKEN *RcvToken + ); + +/** + Flush the sndBuffer and rcvBuffer of socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockConnFlush ( + IN OUT SOCKET *Sock + ); + +/** + Create a socket with initial data SockInitData. + + @param[in] SockInitData Pointer to the initial data of the socket. + + @return Pointer to the newly created socket, return NULL when exception occured. + +**/ +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ); + +/** + Destroy a socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockDestroy ( + IN OUT SOCKET *Sock + ); + +#endif diff --git a/Core/NetworkPkg/TcpDxe/SockInterface.c b/Core/NetworkPkg/TcpDxe/SockInterface.c new file mode 100644 index 0000000000..4abda74220 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/SockInterface.c @@ -0,0 +1,999 @@ +/** @file + Interface function of the Socket. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "SockImpl.h" + +/** + Check whether the Event is in the List. + + @param[in] List Pointer to the token list to be searched. + @param[in] Event The event to be checked. + + @retval TRUE The specific Event exists in the List. + @retval FALSE The specific Event is not in the List. + +**/ +BOOLEAN +SockTokenExistedInList ( + IN LIST_ENTRY *List, + IN EFI_EVENT Event + ) +{ + LIST_ENTRY *ListEntry; + SOCK_TOKEN *SockToken; + + NET_LIST_FOR_EACH (ListEntry, List) { + SockToken = NET_LIST_USER_STRUCT ( + ListEntry, + SOCK_TOKEN, + TokenList + ); + + if (Event == SockToken->Token->Event) { + return TRUE; + } + } + + return FALSE; +} + +/** + Call SockTokenExistedInList() to check whether the Event is + in the related socket's lists. + + @param[in] Sock Pointer to the instance's socket. + @param[in] Event The event to be checked. + + @retval TRUE The Event exists in related socket's lists. + @retval FALSE The Event is not in related socket's lists. + +**/ +BOOLEAN +SockTokenExisted ( + IN SOCKET *Sock, + IN EFI_EVENT Event + ) +{ + + if (SockTokenExistedInList (&Sock->SndTokenList, Event) || + SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) || + SockTokenExistedInList (&Sock->RcvTokenList, Event) || + SockTokenExistedInList (&Sock->ListenTokenList, Event) + ) { + + return TRUE; + } + + if ((Sock->ConnectionToken != NULL) && (Sock->ConnectionToken->Event == Event)) { + + return TRUE; + } + + if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) { + return TRUE; + } + + return FALSE; +} + +/** + Buffer a token into the specific list of the socket Sock. + + @param[in] Sock Pointer to the instance's socket. + @param[in] List Pointer to the list to store the token. + @param[in] Token Pointer to the token to be buffered. + @param[in] DataLen The data length of the buffer contained in Token. + + @return Pointer to the token that wraps Token. If NULL, an error condition occurred. + +**/ +SOCK_TOKEN * +SockBufferToken ( + IN SOCKET *Sock, + IN LIST_ENTRY *List, + IN VOID *Token, + IN UINT32 DataLen + ) +{ + SOCK_TOKEN *SockToken; + + SockToken = AllocateZeroPool (sizeof (SOCK_TOKEN)); + if (NULL == SockToken) { + + DEBUG ( + (EFI_D_ERROR, + "SockBufferIOToken: No Memory to allocate SockToken\n") + ); + + return NULL; + } + + SockToken->Sock = Sock; + SockToken->Token = (SOCK_COMPLETION_TOKEN *) Token; + SockToken->RemainDataLen = DataLen; + InsertTailList (List, &SockToken->TokenList); + + return SockToken; +} + +/** + Destroy the socket Sock and its associated protocol control block. + + @param[in, out] Sock The socket to be destroyed. + + @retval EFI_SUCCESS The socket Sock was destroyed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockDestroyChild ( + IN OUT SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL)); + + if (Sock->InDestroy) { + return EFI_SUCCESS; + } + + Sock->InDestroy = TRUE; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockDestroyChild: Get the lock to access socket failed with %r\n", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + // + // force protocol layer to detach the PCB + // + Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL); + + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockDestroyChild: Protocol detach socket failed with %r\n", + Status) + ); + + Sock->InDestroy = FALSE; + } else if (SOCK_IS_CONFIGURED (Sock)) { + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + Sock->ConfigureState = SO_UNCONFIGURED; + } + + EfiReleaseLock (&(Sock->Lock)); + + if (EFI_ERROR (Status)) { + return Status; + } + + SockDestroy (Sock); + return EFI_SUCCESS; +} + +/** + Create a socket and its associated protocol control block + with the intial data SockInitData and protocol specific + data ProtoData. + + @param[in] SockInitData Inital data to setting the socket. + + @return Pointer to the newly created socket. If NULL, an error condition occured. + +**/ +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + // + // create a new socket + // + Sock = SockCreate (SockInitData); + if (NULL == Sock) { + + DEBUG ( + (EFI_D_ERROR, + "SockCreateChild: No resource to create a new socket\n") + ); + + return NULL; + } + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockCreateChild: Get the lock to access socket failed with %r\n", + Status) + ); + + SockDestroy (Sock); + return NULL; + } + // + // inform the protocol layer to attach the socket + // with a new protocol control block + // + Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL); + EfiReleaseLock (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockCreateChild: Protocol failed to attach a socket with %r\n", + Status) + ); + + SockDestroy (Sock); + Sock = NULL; + } + + return Sock; +} + +/** + Configure the specific socket Sock using configuration data ConfigData. + + @param[in] Sock Pointer to the socket to be configured. + @param[in] ConfigData Pointer to the configuration data. + + @retval EFI_SUCCESS The socket configured successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is already configured. + +**/ +EFI_STATUS +SockConfigure ( + IN SOCKET *Sock, + IN VOID *ConfigData + ) +{ + EFI_STATUS Status; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockConfigure: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_CONFIGURED (Sock)) { + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + ASSERT (Sock->State == SO_CLOSED); + + Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData); + +OnExit: + EfiReleaseLock (&(Sock->Lock)); + + return Status; +} + +/** + Initiate a connection establishment process. + + @param[in] Sock Pointer to the socket to initiate the initate the + connection. + @param[in] Token Pointer to the token used for the connection + operation. + + @retval EFI_SUCCESS The connection initialized successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be an active one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockConnect ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockConnect: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto OnExit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto OnExit; + } + + if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token; + SockSetState (Sock, SO_CONNECTING); + Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL); + +OnExit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Issue a listen token to get an existed connected network instance + or wait for a connection if there is none. + + @param[in] Sock Pointer to the socket to accept connections. + @param[in] Token The token to accept a connection. + + @retval EFI_SUCCESS Either a connection is accpeted or the Token is + buffered for further acception. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be a passive one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limits. + +**/ +EFI_STATUS +SockAccept ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + EFI_TCP4_LISTEN_TOKEN *ListenToken; + LIST_ENTRY *ListEntry; + EFI_STATUS Status; + SOCKET *Socket; + EFI_EVENT Event; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockAccept: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!SOCK_IS_LISTENING (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token; + + // + // Check if a connection has already in this Sock->ConnectionList + // + NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) { + + Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList); + + if (SOCK_IS_CONNECTED (Socket)) { + ListenToken->NewChildHandle = Socket->SockHandle; + SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS); + + RemoveEntryList (ListEntry); + + ASSERT (Socket->Parent != NULL); + + Socket->Parent->ConnCnt--; + + DEBUG ( + (EFI_D_INFO, + "SockAccept: Accept a socket, now conncount is %d", + Socket->Parent->ConnCnt) + ); + Socket->Parent = NULL; + + goto Exit; + } + } + + // + // Buffer this token for latter incoming connection request + // + if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) { + + Status = EFI_OUT_OF_RESOURCES; + } + +Exit: + EfiReleaseLock (&(Sock->Lock)); + + return Status; +} + +/** + Issue a token with data to the socket to send out. + + @param[in] Sock Pointer to the socket to process the token with + data. + @param[in] Token The token with data that needs to send out. + + @retval EFI_SUCCESS The token processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limits. + +**/ +EFI_STATUS +SockSend ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + SOCK_IO_TOKEN *SndToken; + EFI_EVENT Event; + UINT32 FreeSpace; + EFI_TCP4_TRANSMIT_DATA *TxData; + EFI_STATUS Status; + SOCK_TOKEN *SockToken; + UINT32 DataLen; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockSend: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + SndToken = (SOCK_IO_TOKEN *) Token; + TxData = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData; + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // check if a token is already in the token buffer + // + Event = SndToken->Token.Event; + + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + DataLen = TxData->DataLength; + + // + // process this sending token now or buffer it only? + // + FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF); + + if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) { + + SockToken = SockBufferToken ( + Sock, + &Sock->SndTokenList, + SndToken, + DataLen + ); + + if (NULL == SockToken) { + Status = EFI_OUT_OF_RESOURCES; + } + } else { + + SockToken = SockBufferToken ( + Sock, + &Sock->ProcessingSndTokenList, + SndToken, + DataLen + ); + + if (NULL == SockToken) { + DEBUG ( + (EFI_D_ERROR, + "SockSend: Failed to buffer IO token into socket processing SndToken List\n", + Status) + ); + + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + Status = SockProcessTcpSndData (Sock, TxData); + + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "SockSend: Failed to process Snd Data\n", + Status) + ); + + RemoveEntryList (&(SockToken->TokenList)); + FreePool (SockToken); + } + } + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Issue a token to get data from the socket. + + @param[in] Sock Pointer to the socket to get data from. + @param[in] Token The token to store the received data from the + socket. + + @retval EFI_SUCCESS The token processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_CONNECTION_FIN The connection is closed and there is no more data. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit. + +**/ +EFI_STATUS +SockRcv ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + SOCK_IO_TOKEN *RcvToken; + UINT32 RcvdBytes; + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockRcv: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + RcvToken = (SOCK_IO_TOKEN *) Token; + + // + // check if a token is already in the token buffer of this socket + // + Event = RcvToken->Token.Event; + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + RcvToken = (SOCK_IO_TOKEN *) Token; + RcvdBytes = GET_RCV_DATASIZE (Sock); + + // + // check whether an error has happened before + // + if (EFI_ABORTED != Sock->SockError) { + + SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError); + Sock->SockError = EFI_ABORTED; + goto Exit; + } + + // + // check whether can not receive and there is no any + // data buffered in Sock->RcvBuffer + // + if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) { + + Status = EFI_CONNECTION_FIN; + goto Exit; + } + + if (RcvdBytes != 0) { + Status = SockProcessRcvToken (Sock, RcvToken); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL); + } else { + + if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) { + Status = EFI_OUT_OF_RESOURCES; + } + } + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Reset the socket and its associated protocol control block. + + @param[in, out] Sock Pointer to the socket to be flushed. + + @retval EFI_SUCCESS The socket is flushed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockFlush ( + IN OUT SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockFlush: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (!SOCK_IS_CONFIGURED (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockFlush: Protocol failed handling SOCK_FLUSH with %r", + Status) + ); + + goto Exit; + } + + SOCK_ERROR (Sock, EFI_ABORTED); + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + Sock->ConfigureState = SO_UNCONFIGURED; + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Close or abort the socket associated connection. + + @param[in, out] Sock Pointer to the socket of the connection to close + or abort. + @param[in] Token The token for a close operation. + @param[in] OnAbort TRUE for aborting the connection; FALSE to close it. + + @retval EFI_SUCCESS The close or abort operation initialized + successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockClose ( + IN OUT SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "SockClose: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (SOCK_IS_DISCONNECTING (Sock)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Sock->CloseToken = Token; + SockSetState (Sock, SO_DISCONNECTING); + + if (OnAbort) { + Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL); + } else { + Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL); + } + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Get the mode data of the low layer protocol. + + @param[in] Sock Pointer to the socket to get mode data from. + @param[in, out] Mode Pointer to the data to store the low layer mode + information. + + @retval EFI_SUCCESS The mode data was obtained successfully. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN OUT VOID *Mode + ) +{ + return Sock->ProtoHandler (Sock, SOCK_MODE, Mode); +} + +/** + Configure the low level protocol to join a multicast group for + this socket's connection. + + @param[in] Sock Pointer to the socket of the connection to join the + specific multicast group. + @param[in] GroupInfo Pointer to the multicast group info. + + @retval EFI_SUCCESS The configuration completed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGroup ( + IN SOCKET *Sock, + IN VOID *GroupInfo + ) +{ + EFI_STATUS Status; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockGroup: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo); + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Add or remove route information in IP route table associated + with this socket. + + @param[in] Sock Pointer to the socket associated with the IP route + table to operate on. + @param[in] RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The route table updated successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockRoute ( + IN SOCKET *Sock, + IN VOID *RouteInfo + ) +{ + EFI_STATUS Status; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "SockRoute: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo); + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + diff --git a/Core/NetworkPkg/TcpDxe/Socket.h b/Core/NetworkPkg/TcpDxe/Socket.h new file mode 100644 index 0000000000..5a63047f90 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/Socket.h @@ -0,0 +1,924 @@ +/** @file + Common head file for TCP socket. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _SOCKET_H_ +#define _SOCKET_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCK_SND_BUF 0 +#define SOCK_RCV_BUF 1 + +#define SOCK_BUFF_LOW_WATER (2 * 1024) +#define SOCK_RCV_BUFF_SIZE (8 * 1024) +#define SOCK_SND_BUFF_SIZE (8 * 1024) +#define SOCK_BACKLOG 5 + +#define PROTO_RESERVED_LEN 20 + +#define SO_NO_MORE_DATA 0x0001 + +// +// +// +// When a socket is created it enters into SO_UNCONFIGURED, +// no actions can be taken on this socket, only after calling +// SockConfigure. The state transition diagram of socket is +// as following: +// +// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING +// ^ | | +// | ---> SO_LISTENING | +// | | +// |------------------SO_DISCONNECTING<-- SO_CONNECTED +// +// A passive socket can only go into SO_LISTENING and +// SO_UNCONFIGURED state. SO_XXXING state is a middle state +// when a socket is undergoing a protocol procedure such +// as requesting a TCP connection. +// +// +// + +/// +/// Socket state +/// +#define SO_CLOSED 0 +#define SO_LISTENING 1 +#define SO_CONNECTING 2 +#define SO_CONNECTED 3 +#define SO_DISCONNECTING 4 + +/// +/// Socket configure state +/// +#define SO_UNCONFIGURED 0 +#define SO_CONFIGURED_ACTIVE 1 +#define SO_CONFIGURED_PASSIVE 2 +#define SO_NO_MAPPING 3 + +/// +/// The request issued from socket layer to protocol layer. +/// +#define SOCK_ATTACH 0 ///< Attach current socket to a new PCB +#define SOCK_DETACH 1 ///< Detach current socket from the PCB +#define SOCK_CONFIGURE 2 ///< Configure attached PCB +#define SOCK_FLUSH 3 ///< Flush attached PCB +#define SOCK_SND 4 ///< Need protocol to send something +#define SOCK_SNDPUSH 5 ///< Need protocol to send pushed data +#define SOCK_SNDURG 6 ///< Need protocol to send urgent data +#define SOCK_CONSUMED 7 ///< Application has retrieved data from socket +#define SOCK_CONNECT 8 ///< Need to connect to a peer +#define SOCK_CLOSE 9 ///< Need to close the protocol process +#define SOCK_ABORT 10 ///< Need to reset the protocol process +#define SOCK_POLL 11 ///< Need to poll to the protocol layer +#define SOCK_ROUTE 12 ///< Need to add a route information +#define SOCK_MODE 13 ///< Need to get the mode data of the protocol +#define SOCK_GROUP 14 ///< Need to join a mcast group + +/** + Set socket SO_NO_MORE_DATA flag. + + @param[in] Sock Pointer to the socket + +**/ +#define SOCK_NO_MORE_DATA(Sock) ((Sock)->Flag |= SO_NO_MORE_DATA) + +/** + Check whether the socket is unconfigured. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is unconfigued. + @retval FALSE The socket is not unconfigued. + +**/ +#define SOCK_IS_UNCONFIGURED(Sock) ((Sock)->ConfigureState == SO_UNCONFIGURED) + +/** + Check whether the socket is configured. + + @param[in] Sock Pointer to the socket + + @retval TRUE The socket is configued + @retval FALSE The socket is not configued + +**/ +#define SOCK_IS_CONFIGURED(Sock) \ + (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \ + ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)) + +/** + Check whether the socket is configured to active mode. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is configued to active mode. + @retval FALSE The socket is not configued to active mode. + +**/ +#define SOCK_IS_CONFIGURED_ACTIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) + +/** + Check whether the socket is configured to passive mode. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is configued to passive mode. + @retval FALSE The socket is not configued to passive mode. + +**/ +#define SOCK_IS_CONNECTED_PASSIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE) + +/** + Check whether the socket is mapped. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is not mapping. + @retval FALSE The socket is mapped. + +**/ +#define SOCK_IS_NO_MAPPING(Sock) ((Sock)->ConfigureState == SO_NO_MAPPING) + +/** + Check whether the socket is closed. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is closed. + @retval FALSE The socket is not closed. + +**/ +#define SOCK_IS_CLOSED(Sock) ((Sock)->State == SO_CLOSED) + +/** + Check whether the socket is listening. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is listening. + @retval FALSE The socket is not listening. + +**/ +#define SOCK_IS_LISTENING(Sock) ((Sock)->State == SO_LISTENING) + +/** + Check whether the socket is connecting. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is connecting. + @retval FALSE The socket is not connecting. + +**/ +#define SOCK_IS_CONNECTING(Sock) ((Sock)->State == SO_CONNECTING) + +/** + Check whether the socket has connected. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket has connected. + @retval FALSE The socket has not connected. + +**/ +#define SOCK_IS_CONNECTED(Sock) ((Sock)->State == SO_CONNECTED) + +/** + Check whether the socket is disconnecting. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is disconnecting. + @retval FALSE The socket is not disconnecting. + +**/ +#define SOCK_IS_DISCONNECTING(Sock) ((Sock)->State == SO_DISCONNECTING) + +/** + Check whether the socket is no more data. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is no more data. + @retval FALSE The socket still has data. + +**/ +#define SOCK_IS_NO_MORE_DATA(Sock) (0 != ((Sock)->Flag & SO_NO_MORE_DATA)) + +/** + Set the size of the receive buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Size The size to set. + +**/ +#define SET_RCV_BUFFSIZE(Sock, Size) ((Sock)->RcvBuffer.HighWater = (Size)) + +/** + Get the size of the receive buffer. + + @param[in] Sock Pointer to the socket. + + @return The receive buffer size. + +**/ +#define GET_RCV_BUFFSIZE(Sock) ((Sock)->RcvBuffer.HighWater) + +/** + Get the size of the receive data. + + @param[in] Sock Pointer to the socket. + + @return The received data size. + +**/ +#define GET_RCV_DATASIZE(Sock) (((Sock)->RcvBuffer.DataQueue)->BufSize) + +/** + Set the size of the send buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Size The size to set. + +**/ +#define SET_SND_BUFFSIZE(Sock, Size) ((Sock)->SndBuffer.HighWater = (Size)) + +/** + Get the size of the send buffer. + + @param[in] Sock Pointer to the socket. + + @return The send buffer size. + +**/ +#define GET_SND_BUFFSIZE(Sock) ((Sock)->SndBuffer.HighWater) + +/** + Get the size of the send data. + + @param[in] Sock Pointer to the socket. + + @return The send data size. + +**/ +#define GET_SND_DATASIZE(Sock) (((Sock)->SndBuffer.DataQueue)->BufSize) + +/** + Set the backlog value of the socket. + + @param[in] Sock Pointer to the socket. + @param[in] Value The value to set. + +**/ +#define SET_BACKLOG(Sock, Value) ((Sock)->BackLog = (Value)) + +/** + Get the backlog value of the socket. + + @param[in] Sock Pointer to the socket. + + @return The backlog value. + +**/ +#define GET_BACKLOG(Sock) ((Sock)->BackLog) + +/** + Set the socket with error state. + + @param[in] Sock Pointer to the socket. + @param[in] Error The error state. + +**/ +#define SOCK_ERROR(Sock, Error) ((Sock)->SockError = (Error)) + +#define SOCK_SIGNATURE SIGNATURE_32 ('S', 'O', 'C', 'K') + +#define SOCK_FROM_THIS(a) CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE) + +#define SOCK_FROM_TOKEN(Token) (((SOCK_TOKEN *) (Token))->Sock) + +#define PROTO_TOKEN_FORM_SOCK(SockToken, Type) ((Type *) (((SOCK_TOKEN *) (SockToken))->Token)) + +typedef struct _TCP_SOCKET SOCKET; + +/// +/// Socket completion token +/// +typedef struct _SOCK_COMPLETION_TOKEN { + EFI_EVENT Event; ///< The event to be issued + EFI_STATUS Status; ///< The status to be issued +} SOCK_COMPLETION_TOKEN; + +typedef union { + VOID *RxData; + VOID *TxData; +} SOCK_IO_DATA; + +/// +/// The application token with data packet +/// +typedef struct _SOCK_IO_TOKEN { + SOCK_COMPLETION_TOKEN Token; + SOCK_IO_DATA Packet; +} SOCK_IO_TOKEN; + +/// +/// The socket type. +/// +typedef enum { + SockDgram, ///< This socket providing datagram service + SockStream ///< This socket providing stream service +} SOCK_TYPE; + +/// +/// The buffer structure of rcvd data and send data used by socket. +/// +typedef struct _SOCK_BUFFER { + UINT32 HighWater; ///< The buffersize upper limit of sock_buffer + UINT32 LowWater; ///< The low warter mark of sock_buffer + NET_BUF_QUEUE *DataQueue; ///< The queue to buffer data +} SOCK_BUFFER; + +/** + The handler of protocol for request from socket. + + @param[in] Socket The socket issuing the request to protocol. + @param[in] Request The request issued by socket. + @param[in] RequestData The request related data. + + @retval EFI_SUCCESS The socket request is completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +typedef +EFI_STATUS +(*SOCK_PROTO_HANDLER) ( + IN SOCKET *Socket, + IN UINT8 Request, + IN VOID *RequestData + ); + +/** + The Callback funtion called after the TCP socket is created. + + @param[in] This Pointer to the socket just created. + @param[in] Context Context of the socket. + + @retval EFI_SUCCESS This protocol installed successfully. + @retval other Some error occured. + +**/ +typedef +EFI_STATUS +(*SOCK_CREATE_CALLBACK) ( + IN SOCKET *This, + IN VOID *Context + ); + +/** + The callback function called before the TCP socket is to be destroyed. + + @param[in] This The TCP socket to be destroyed. + @param[in] Context The context. + +**/ +typedef +VOID +(*SOCK_DESTROY_CALLBACK) ( + IN SOCKET *This, + IN VOID *Context + ); + +/// +/// The initialize data for create a new socket. +/// +typedef struct _SOCK_INIT_DATA { + SOCK_TYPE Type; + UINT8 State; + + SOCKET *Parent; ///< The parent of this socket + UINT32 BackLog; ///< The connection limit for listening socket + UINT32 SndBufferSize; ///< The high warter mark of send buffer + UINT32 RcvBufferSize; ///< The high warter mark of receive buffer + UINT8 IpVersion; + VOID *Protocol; ///< The pointer to protocol function template + ///< wanted to install on socket + + // + // Callbacks after socket is created and before socket is to be destroyed. + // + SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created + SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied + VOID *Context; ///< The context of the callback + + // + // Opaque protocol data. + // + VOID *ProtoData; + UINT32 DataSize; + + SOCK_PROTO_HANDLER ProtoHandler; ///< The handler of protocol for socket request + + EFI_HANDLE DriverBinding; ///< The driver binding handle +} SOCK_INIT_DATA; + +/// +/// The union type of TCP and UDP protocol. +/// +typedef union _NET_PROTOCOL { + EFI_TCP4_PROTOCOL Tcp4Protocol; ///< Tcp4 protocol + EFI_TCP6_PROTOCOL Tcp6Protocol; ///< Tcp6 protocol +} NET_PROTOCOL; +/// +/// The socket structure representing a network service access point. +/// +struct _TCP_SOCKET { + // + // Socket description information + // + UINT32 Signature; ///< Signature of the socket + EFI_HANDLE SockHandle; ///< The virtual handle of the socket + EFI_HANDLE DriverBinding; ///< Socket's driver binding protocol + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + LIST_ENTRY Link; + UINT8 ConfigureState; + SOCK_TYPE Type; + UINT8 State; + UINT16 Flag; + EFI_LOCK Lock; ///< The lock of socket + SOCK_BUFFER SndBuffer; ///< Send buffer of application's data + SOCK_BUFFER RcvBuffer; ///< Receive buffer of received data + EFI_STATUS SockError; ///< The error returned by low layer protocol + BOOLEAN InDestroy; + + // + // Fields used to manage the connection request + // + UINT32 BackLog; ///< the limit of connection to this socket + UINT32 ConnCnt; ///< the current count of connections to it + SOCKET *Parent; ///< listening parent that accept the connection + LIST_ENTRY ConnectionList; ///< the connections maintained by this socket + // + // The queue to buffer application's asynchronous token + // + LIST_ENTRY ListenTokenList; + LIST_ENTRY RcvTokenList; + LIST_ENTRY SndTokenList; + LIST_ENTRY ProcessingSndTokenList; + + SOCK_COMPLETION_TOKEN *ConnectionToken; ///< app's token to signal if connected + SOCK_COMPLETION_TOKEN *CloseToken; ///< app's token to signal if closed + // + // Interface for low level protocol + // + SOCK_PROTO_HANDLER ProtoHandler; ///< The request handler of protocol + UINT8 ProtoReserved[PROTO_RESERVED_LEN]; ///< Data fields reserved for protocol + UINT8 IpVersion; + NET_PROTOCOL NetProtocol; ///< TCP or UDP protocol socket used + // + // Callbacks after socket is created and before socket is to be destroyed. + // + SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created + SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied + VOID *Context; ///< The context of the callback +}; + +/// +/// The token structure buffered in socket layer. +/// +typedef struct _SOCK_TOKEN { + LIST_ENTRY TokenList; ///< The entry to add in the token list + SOCK_COMPLETION_TOKEN *Token; ///< The application's token + UINT32 RemainDataLen; ///< Unprocessed data length + SOCKET *Sock; ///< The poninter to the socket this token + ///< belongs to +} SOCK_TOKEN; + +/// +/// Reserved data to access the NET_BUF delivered by TCP driver. +/// +typedef struct _TCP_RSV_DATA { + UINT32 UrgLen; +} TCP_RSV_DATA; + +// +// Socket provided oprerations for low layer protocol implemented in SockImpl.c +// + +/** + Set the state of the socket. + + @param[in, out] Sock Pointer to the socket. + @param[in] State The new socket state to be set. + +**/ +VOID +SockSetState ( + IN OUT SOCKET *Sock, + IN UINT8 State + ); + +/** + Clone a new socket including its associated protocol control block. + + @param[in] Sock Pointer to the socket to be cloned. + + @return Pointer to the newly cloned socket. If NULL, an error condition occurred. + +**/ +SOCKET * +SockClone ( + IN SOCKET *Sock + ); + +/** + Called by the low layer protocol to indicate the socket a connection is + established. + + This function just changes the socket's state to SO_CONNECTED + and signals the token used for connection establishment. + + @param[in, out] Sock Pointer to the socket associated with the + established connection. + +**/ +VOID +SockConnEstablished ( + IN OUT SOCKET *Sock + ); + +/** + Called by the low layer protocol to indicate that the connection is closed. + + This function flushes the socket, sets the state to SO_CLOSED, and signals + the close token. + + @param[in, out] Sock Pointer to the socket associated with the closed + connection. + +**/ +VOID +SockConnClosed ( + IN OUT SOCKET *Sock + ); + +/** + Called by low layer protocol to indicate that some data is sent or processed. + + This function trims the sent data in the socket send buffer and signals the data + token, if proper. + + @param[in, out] Sock Pointer to the socket. + @param[in] Count The length of the data processed or sent, in bytes. + +**/ +VOID +SockDataSent ( + IN OUT SOCKET *Sock, + IN UINT32 Count + ); + +/** + Called by the low layer protocol to copy some data in socket send + buffer starting from the specific offset to a buffer provided by + the caller. + + @param[in] Sock Pointer to the socket. + @param[in] Offset The start point of the data to be copied. + @param[in] Len The length of the data to be copied. + @param[out] Dest Pointer to the destination to copy the data. + + @return The data size copied. + +**/ +UINT32 +SockGetDataToSend ( + IN SOCKET *Sock, + IN UINT32 Offset, + IN UINT32 Len, + OUT UINT8 *Dest + ); + +/** + Called by the low layer protocol to deliver received data to socket layer. + + This function appends the data to the socket receive buffer, set the + urgent data length, then checks if any receive token can be signaled. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] NetBuffer Pointer to the buffer that contains the received data. + @param[in] UrgLen The length of the urgent data in the received data. + +**/ +VOID +SockDataRcvd ( + IN OUT SOCKET *Sock, + IN OUT NET_BUF *NetBuffer, + IN UINT32 UrgLen + ); + +/** + Get the length of the free space of the specific socket buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Which Flag to indicate which socket buffer to check: + either send buffer or receive buffer. + + @return The length of the free space, in bytes. + +**/ +UINT32 +SockGetFreeSpace ( + IN SOCKET *Sock, + IN UINT32 Which + ); + +/** + Called by the low layer protocol to indicate that there will be no more data + from the communication peer. + + This function sets the socket's state to SO_NO_MORE_DATA and signals all queued + IO tokens with the error status EFI_CONNECTION_FIN. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockNoMoreData ( + IN OUT SOCKET *Sock + ); + +// +// Socket provided operations for user interface implemented in SockInterface.c +// + +/** + Create a socket and its associated protocol control block + with the intial data SockInitData and protocol specific + data ProtoData. + + @param[in] SockInitData Inital data to setting the socket. + + @return Pointer to the newly created socket. If NULL, an error condition occured. + +**/ +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData + ); + +/** + Destroy the socket Sock and its associated protocol control block. + + @param[in, out] Sock The socket to be destroyed. + + @retval EFI_SUCCESS The socket Sock was destroyed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockDestroyChild ( + IN OUT SOCKET *Sock + ); + +/** + Configure the specific socket Sock using configuration data ConfigData. + + @param[in] Sock Pointer to the socket to be configured. + @param[in] ConfigData Pointer to the configuration data. + + @retval EFI_SUCCESS The socket configured successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is already configured. + +**/ +EFI_STATUS +SockConfigure ( + IN SOCKET *Sock, + IN VOID *ConfigData + ); + +/** + Initiate a connection establishment process. + + @param[in] Sock Pointer to the socket to initiate the initate the + connection. + @param[in] Token Pointer to the token used for the connection + operation. + + @retval EFI_SUCCESS The connection initialized successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be an active one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockConnect ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Issue a listen token to get an existed connected network instance, + or wait for a connection if there is none. + + @param[in] Sock Pointer to the socket to accept connections. + @param[in] Token The token to accept a connection. + + @retval EFI_SUCCESS Either a connection is accepted or the Token is + buffered for further acceptance. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be a passive one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limit. + +**/ +EFI_STATUS +SockAccept ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Issue a token with data to the socket to send out. + + @param[in] Sock Pointer to the socket to process the token with + data. + @param[in] Token The token with data that needs to send out. + + @retval EFI_SUCCESS The token processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to a memory limit. + +**/ +EFI_STATUS +SockSend ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Issue a token to get data from the socket. + + @param[in] Sock Pointer to the socket to get data from. + @param[in] Token The token to store the received data from the + socket. + + @retval EFI_SUCCESS The token processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_CONNECTION_FIN The connection is closed and there is no more data. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to a memory limit. + +**/ +EFI_STATUS +SockRcv ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Reset the socket and its associated protocol control block. + + @param[in, out] Sock Pointer to the socket to be flushed. + + @retval EFI_SUCCESS The socket flushed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockFlush ( + IN OUT SOCKET *Sock + ); + +/** + Close or abort the socket associated connection. + + @param[in, out] Sock Pointer to the socket of the connection to close + or abort. + @param[in] Token The token for close operation. + @param[in] OnAbort TRUE for aborting the connection, FALSE to close it. + + @retval EFI_SUCCESS The close or abort operation initialized + successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockClose ( + IN OUT SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ); + +/** + Get the mode data of the low layer protocol. + + @param[in] Sock Pointer to the socket to get mode data from. + @param[in, out] Mode Pointer to the data to store the low layer mode + information. + + @retval EFI_SUCCESS The mode data was obtained successfully. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN OUT VOID *Mode + ); + +/** + Configure the low level protocol to join a multicast group for + this socket's connection. + + @param[in] Sock Pointer to the socket of the connection to join the + specific multicast group. + @param[in] GroupInfo Pointer to the multicast group information. + + @retval EFI_SUCCESS The configuration completed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGroup ( + IN SOCKET *Sock, + IN VOID *GroupInfo + ); + +/** + Add or remove route information in IP route table associated + with this socket. + + @param[in] Sock Pointer to the socket associated with the IP route + table to operate on. + @param[in] RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The route table updated successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockRoute ( + IN SOCKET *Sock, + IN VOID *RouteInfo + ); + +#endif diff --git a/Core/NetworkPkg/TcpDxe/TcpDispatcher.c b/Core/NetworkPkg/TcpDxe/TcpDispatcher.c new file mode 100644 index 0000000000..d4bc8ace55 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpDispatcher.c @@ -0,0 +1,913 @@ +/** @file + The implementation of a dispatch routine for processing TCP requests. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +/** + Add or remove a route entry in the IP route table associated with this TCP instance. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration(DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table + (when RouteInfo->DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table + (when RouteInfo->DeleteRoute is FALSE). +**/ +EFI_STATUS +Tcp4Route ( + IN TCP_CB *Tcb, + IN TCP4_ROUTE_INFO *RouteInfo + ) +{ + IP_IO_IP_PROTOCOL Ip; + + Ip = Tcb->IpInfo->Ip; + + ASSERT (Ip.Ip4!= NULL); + + return Ip.Ip4->Routes ( + Ip.Ip4, + RouteInfo->DeleteRoute, + RouteInfo->SubnetAddress, + RouteInfo->SubnetMask, + RouteInfo->GatewayAddress + ); + +} + +/** + Get the operational settings of this TCPv4 instance. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] Mode Pointer to the buffer to store the operational + settings. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + +**/ +EFI_STATUS +Tcp4GetMode ( + IN TCP_CB *Tcb, + IN OUT TCP4_MODE_DATA *Mode + ) +{ + SOCKET *Sock; + EFI_TCP4_CONFIG_DATA *ConfigData; + EFI_TCP4_ACCESS_POINT *AccessPoint; + EFI_TCP4_OPTION *Option; + EFI_IP4_PROTOCOL *Ip; + + Sock = Tcb->Sk; + + if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + if (Mode->Tcp4State != NULL) { + *(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State; + } + + if (Mode->Tcp4ConfigData != NULL) { + + ConfigData = Mode->Tcp4ConfigData; + AccessPoint = &(ConfigData->AccessPoint); + Option = ConfigData->ControlOption; + + ConfigData->TypeOfService = Tcb->Tos; + ConfigData->TimeToLive = Tcb->Ttl; + + AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr; + + IP4_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip); + + IP4_COPY_ADDRESS (&AccessPoint->SubnetMask, &Tcb->SubnetMask); + AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port); + + IP4_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip); + + AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port); + AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN); + + if (Option != NULL) { + Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk); + Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk); + Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk); + + Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ; + Option->DataRetries = Tcb->MaxRexmit; + Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ; + Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ; + Option->KeepAliveProbes = Tcb->MaxKeepAlive; + Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ; + Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ; + + Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE)); + Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)); + Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)); + + Option->EnableSelectiveAck = FALSE; + Option->EnablePathMtuDiscovery = FALSE; + } + } + + Ip = Tcb->IpInfo->Ip.Ip4; + ASSERT (Ip != NULL); + + return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData); +} + +/** + Get the operational settings of this TCPv6 instance. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] Mode Pointer to the buffer to store the operational + settings. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + +**/ +EFI_STATUS +Tcp6GetMode ( + IN TCP_CB *Tcb, + IN OUT TCP6_MODE_DATA *Mode + ) +{ + SOCKET *Sock; + EFI_TCP6_CONFIG_DATA *ConfigData; + EFI_TCP6_ACCESS_POINT *AccessPoint; + EFI_TCP6_OPTION *Option; + EFI_IP6_PROTOCOL *Ip; + + Sock = Tcb->Sk; + + if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp6ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + if (Mode->Tcp6State != NULL) { + *(Mode->Tcp6State) = (EFI_TCP6_CONNECTION_STATE) (Tcb->State); + } + + if (Mode->Tcp6ConfigData != NULL) { + + ConfigData = Mode->Tcp6ConfigData; + AccessPoint = &(ConfigData->AccessPoint); + Option = ConfigData->ControlOption; + + ConfigData->TrafficClass = Tcb->Tos; + ConfigData->HopLimit = Tcb->Ttl; + + AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port); + AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port); + AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN); + + IP6_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip); + IP6_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip); + + if (Option != NULL) { + Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk); + Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk); + Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk); + + Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ; + Option->DataRetries = Tcb->MaxRexmit; + Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ; + Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ; + Option->KeepAliveProbes = Tcb->MaxKeepAlive; + Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ; + Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ; + + Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE)); + Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)); + Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)); + + Option->EnableSelectiveAck = FALSE; + Option->EnablePathMtuDiscovery = FALSE; + } + } + + Ip = Tcb->IpInfo->Ip.Ip6; + ASSERT (Ip != NULL); + + return Ip->GetModeData (Ip, Mode->Ip6ModeData, Mode->MnpConfigData, Mode->SnpModeData); +} + +/** + If TcpAp->StationPort isn't zero, check whether the access point + is registered, else generate a random station port for this + access point. + + @param[in] TcpAp Pointer to the access point. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6 + + @retval EFI_SUCCESS The check passed or the port is assigned. + @retval EFI_INVALID_PARAMETER The non-zero station port is already used. + @retval EFI_OUT_OF_RESOURCES No port can be allocated. + +**/ +EFI_STATUS +TcpBind ( + IN TCP_ACCESS_POINT *TcpAp, + IN UINT8 IpVersion + ) +{ + BOOLEAN Cycle; + EFI_IP_ADDRESS Local; + UINT16 *Port; + UINT16 *RandomPort; + + if (IpVersion == IP_VERSION_4) { + IP4_COPY_ADDRESS (&Local, &TcpAp->Tcp4Ap.StationAddress); + Port = &TcpAp->Tcp4Ap.StationPort; + RandomPort = &mTcp4RandomPort; + } else { + IP6_COPY_ADDRESS (&Local, &TcpAp->Tcp6Ap.StationAddress); + Port = &TcpAp->Tcp6Ap.StationPort; + RandomPort = &mTcp6RandomPort; + } + + if (0 != *Port) { + // + // Check if a same endpoing is bound. + // + if (TcpFindTcbByPeer (&Local, *Port, IpVersion)) { + + return EFI_INVALID_PARAMETER; + } + } else { + // + // generate a random port + // + Cycle = FALSE; + + if (TCP_PORT_USER_RESERVED == *RandomPort) { + *RandomPort = TCP_PORT_KNOWN; + } + + (*RandomPort)++; + + while (TcpFindTcbByPeer (&Local, *RandomPort, IpVersion)) { + (*RandomPort)++; + + if (*RandomPort <= TCP_PORT_KNOWN) { + if (Cycle) { + DEBUG ( + (EFI_D_ERROR, + "TcpBind: no port can be allocated for this pcb\n") + ); + return EFI_OUT_OF_RESOURCES; + } + + *RandomPort = TCP_PORT_KNOWN + 1; + + Cycle = TRUE; + } + } + + *Port = *RandomPort; + } + + return EFI_SUCCESS; +} + +/** + Flush the Tcb add its associated protocols. + + @param[in, out] Tcb Pointer to the TCP_CB to be flushed. + +**/ +VOID +TcpFlushPcb ( + IN OUT TCP_CB *Tcb + ) +{ + SOCKET *Sock; + + IpIoConfigIp (Tcb->IpInfo, NULL); + + Sock = Tcb->Sk; + + if (SOCK_IS_CONFIGURED (Sock)) { + RemoveEntryList (&Tcb->List); + + if (Sock->DevicePath != NULL) { + // + // Uninstall the device path protocl. + // + gBS->UninstallProtocolInterface ( + Sock->SockHandle, + &gEfiDevicePathProtocolGuid, + Sock->DevicePath + ); + + FreePool (Sock->DevicePath); + Sock->DevicePath = NULL; + } + } + + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); + Tcb->State = TCP_CLOSED; + Tcb->RemoteIpZero = FALSE; +} + +/** + Attach a Pcb to the socket. + + @param[in] Sk Pointer to the socket of this TCP instance. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +TcpAttachPcb ( + IN SOCKET *Sk + ) +{ + TCP_CB *Tcb; + TCP_PROTO_DATA *ProtoData; + IP_IO *IpIo; + EFI_STATUS Status; + VOID *Ip; + EFI_GUID *IpProtocolGuid; + + if (Sk->IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } + + Tcb = AllocateZeroPool (sizeof (TCP_CB)); + + if (Tcb == NULL) { + + DEBUG ((EFI_D_ERROR, "TcpConfigurePcb: failed to allocate a TCB\n")); + + return EFI_OUT_OF_RESOURCES; + } + + ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved; + IpIo = ProtoData->TcpService->IpIo; + + // + // Create an IpInfo for this Tcb. + // + Tcb->IpInfo = IpIoAddIp (IpIo); + if (Tcb->IpInfo == NULL) { + + FreePool (Tcb); + return EFI_OUT_OF_RESOURCES; + } + + // + // Open the new created IP instance BY_CHILD. + // + Status = gBS->OpenProtocol ( + Tcb->IpInfo->ChildHandle, + IpProtocolGuid, + &Ip, + IpIo->Image, + Sk->SockHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + IpIoRemoveIp (IpIo, Tcb->IpInfo); + return Status; + } + + InitializeListHead (&Tcb->List); + InitializeListHead (&Tcb->SndQue); + InitializeListHead (&Tcb->RcvQue); + + Tcb->State = TCP_CLOSED; + Tcb->Sk = Sk; + ProtoData->TcpPcb = Tcb; + + return EFI_SUCCESS; +} + +/** + Detach the Pcb of the socket. + + @param[in, out] Sk Pointer to the socket of this TCP instance. + +**/ +VOID +TcpDetachPcb ( + IN OUT SOCKET *Sk + ) +{ + TCP_PROTO_DATA *ProtoData; + TCP_CB *Tcb; + EFI_GUID *IpProtocolGuid; + + if (Sk->IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } + + ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + ASSERT (Tcb != NULL); + + TcpFlushPcb (Tcb); + + // + // Close the IP protocol. + // + gBS->CloseProtocol ( + Tcb->IpInfo->ChildHandle, + IpProtocolGuid, + ProtoData->TcpService->IpIo->Image, + Sk->SockHandle + ); + + IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo); + + FreePool (Tcb); + + ProtoData->TcpPcb = NULL; +} + +/** + Configure the Pcb using CfgData. + + @param[in] Sk Pointer to the socket of this TCP instance. + @param[in] CfgData Pointer to the TCP configuration data. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A same access point has been configured in + another TCP instance. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +TcpConfigurePcb ( + IN SOCKET *Sk, + IN TCP_CONFIG_DATA *CfgData + ) +{ + IP_IO_IP_CONFIG_DATA IpCfgData; + EFI_STATUS Status; + EFI_TCP4_OPTION *Option; + TCP_PROTO_DATA *TcpProto; + TCP_CB *Tcb; + TCP_ACCESS_POINT *TcpAp; + + ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL)); + + TcpProto = (TCP_PROTO_DATA *) Sk->ProtoReserved; + Tcb = TcpProto->TcpPcb; + + ASSERT (Tcb != NULL); + + if (Sk->IpVersion == IP_VERSION_4) { + // + // Add Ip for send pkt to the peer + // + CopyMem (&IpCfgData.Ip4CfgData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA)); + IpCfgData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + IpCfgData.Ip4CfgData.TypeOfService = CfgData->Tcp4CfgData.TypeOfService; + IpCfgData.Ip4CfgData.TimeToLive = CfgData->Tcp4CfgData.TimeToLive; + IpCfgData.Ip4CfgData.UseDefaultAddress = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress; + IP4_COPY_ADDRESS ( + &IpCfgData.Ip4CfgData.SubnetMask, + &CfgData->Tcp4CfgData.AccessPoint.SubnetMask + ); + IpCfgData.Ip4CfgData.ReceiveTimeout = (UINT32) (-1); + IP4_COPY_ADDRESS ( + &IpCfgData.Ip4CfgData.StationAddress, + &CfgData->Tcp4CfgData.AccessPoint.StationAddress + ); + + } else { + ASSERT (Sk->IpVersion == IP_VERSION_6); + + CopyMem (&IpCfgData.Ip6CfgData, &mIp6IoDefaultIpConfigData, sizeof (EFI_IP6_CONFIG_DATA)); + IpCfgData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + IpCfgData.Ip6CfgData.TrafficClass = CfgData->Tcp6CfgData.TrafficClass; + IpCfgData.Ip6CfgData.HopLimit = CfgData->Tcp6CfgData.HopLimit; + IpCfgData.Ip6CfgData.ReceiveTimeout = (UINT32) (-1); + IP6_COPY_ADDRESS ( + &IpCfgData.Ip6CfgData.StationAddress, + &CfgData->Tcp6CfgData.AccessPoint.StationAddress + ); + IP6_COPY_ADDRESS ( + &IpCfgData.Ip6CfgData.DestinationAddress, + &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress + ); + } + + // + // Configure the IP instance this Tcb consumes. + // + Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData); + if (EFI_ERROR (Status)) { + goto OnExit; + } + + if (Sk->IpVersion == IP_VERSION_4) { + // + // Get the default address information if the instance is configured to use default address. + // + IP4_COPY_ADDRESS ( + &CfgData->Tcp4CfgData.AccessPoint.StationAddress, + &IpCfgData.Ip4CfgData.StationAddress + ); + IP4_COPY_ADDRESS ( + &CfgData->Tcp4CfgData.AccessPoint.SubnetMask, + &IpCfgData.Ip4CfgData.SubnetMask + ); + + TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp4CfgData.AccessPoint; + } else { + IP6_COPY_ADDRESS ( + &CfgData->Tcp6CfgData.AccessPoint.StationAddress, + &IpCfgData.Ip6CfgData.StationAddress + ); + + TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp6CfgData.AccessPoint; + } + + // + // check if we can bind this endpoint in CfgData + // + Status = TcpBind (TcpAp, Sk->IpVersion); + + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "TcpConfigurePcb: Bind endpoint failed with %r\n", + Status) + ); + + goto OnExit; + } + + // + // Initalize the operating information in this Tcb + // + ASSERT (Tcb->State == TCP_CLOSED && + IsListEmpty (&Tcb->SndQue) && + IsListEmpty (&Tcb->RcvQue)); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); + Tcb->State = TCP_CLOSED; + + Tcb->SndMss = 536; + Tcb->RcvMss = TcpGetRcvMss (Sk); + + Tcb->SRtt = 0; + Tcb->Rto = 3 * TCP_TICK_HZ; + + Tcb->CWnd = Tcb->SndMss; + Tcb->Ssthresh = 0xffffffff; + + Tcb->CongestState = TCP_CONGEST_OPEN; + + Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN; + Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD; + Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE; + Tcb->MaxRexmit = TCP_MAX_LOSS; + Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME; + Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME; + Tcb->ConnectTimeout = TCP_CONNECT_TIME; + + if (Sk->IpVersion == IP_VERSION_4) { + // + // initialize Tcb in the light of CfgData + // + Tcb->Ttl = CfgData->Tcp4CfgData.TimeToLive; + Tcb->Tos = CfgData->Tcp4CfgData.TypeOfService; + + Tcb->UseDefaultAddr = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress; + + CopyMem (&Tcb->LocalEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.StationAddress, sizeof (IP4_ADDR)); + Tcb->LocalEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.StationPort); + IP4_COPY_ADDRESS (&Tcb->SubnetMask, &CfgData->Tcp4CfgData.AccessPoint.SubnetMask); + + CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.RemoteAddress, sizeof (IP4_ADDR)); + Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.RemotePort); + + Option = CfgData->Tcp4CfgData.ControlOption; + } else { + Tcb->Ttl = CfgData->Tcp6CfgData.HopLimit; + Tcb->Tos = CfgData->Tcp6CfgData.TrafficClass; + + IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.StationAddress); + Tcb->LocalEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.StationPort); + + IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress); + Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.RemotePort); + + // + // Type EFI_TCP4_OPTION and EFI_TCP6_OPTION are the same. + // + Option = (EFI_TCP4_OPTION *) CfgData->Tcp6CfgData.ControlOption; + } + + if (Option != NULL) { + SET_RCV_BUFFSIZE ( + Sk, + (UINT32) (TCP_COMP_VAL ( + TCP_RCV_BUF_SIZE_MIN, + TCP_RCV_BUF_SIZE, + TCP_RCV_BUF_SIZE, + Option->ReceiveBufferSize + ) + ) + ); + SET_SND_BUFFSIZE ( + Sk, + (UINT32) (TCP_COMP_VAL ( + TCP_SND_BUF_SIZE_MIN, + TCP_SND_BUF_SIZE, + TCP_SND_BUF_SIZE, + Option->SendBufferSize + ) + ) + ); + + SET_BACKLOG ( + Sk, + (UINT32) (TCP_COMP_VAL ( + TCP_BACKLOG_MIN, + TCP_BACKLOG, + TCP_BACKLOG, + Option->MaxSynBackLog + ) + ) + ); + + Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL ( + TCP_MAX_LOSS_MIN, + TCP_MAX_LOSS, + TCP_MAX_LOSS, + Option->DataRetries + ); + Tcb->FinWait2Timeout = TCP_COMP_VAL ( + TCP_FIN_WAIT2_TIME, + TCP_FIN_WAIT2_TIME_MAX, + TCP_FIN_WAIT2_TIME, + (UINT32) (Option->FinTimeout * TCP_TICK_HZ) + ); + + if (Option->TimeWaitTimeout != 0) { + Tcb->TimeWaitTimeout = TCP_COMP_VAL ( + TCP_TIME_WAIT_TIME, + TCP_TIME_WAIT_TIME_MAX, + TCP_TIME_WAIT_TIME, + (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ) + ); + } else { + Tcb->TimeWaitTimeout = 0; + } + + if (Option->KeepAliveProbes != 0) { + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); + + Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL ( + TCP_MAX_KEEPALIVE_MIN, + TCP_MAX_KEEPALIVE, + TCP_MAX_KEEPALIVE, + Option->KeepAliveProbes + ); + Tcb->KeepAliveIdle = TCP_COMP_VAL ( + TCP_KEEPALIVE_IDLE_MIN, + TCP_KEEPALIVE_IDLE_MAX, + TCP_KEEPALIVE_IDLE_MIN, + (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ) + ); + Tcb->KeepAlivePeriod = TCP_COMP_VAL ( + TCP_KEEPALIVE_PERIOD_MIN, + TCP_KEEPALIVE_PERIOD, + TCP_KEEPALIVE_PERIOD, + (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ) + ); + } + + Tcb->ConnectTimeout = TCP_COMP_VAL ( + TCP_CONNECT_TIME_MIN, + TCP_CONNECT_TIME, + TCP_CONNECT_TIME, + (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ) + ); + + if (!Option->EnableNagle) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE); + } + + if (!Option->EnableTimeStamp) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS); + } + + if (!Option->EnableWindowScaling) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS); + } + } + + // + // The socket is bound, the is + // determined, construct the IP device path and install it. + // + Status = TcpInstallDevicePath (Sk); + if (EFI_ERROR (Status)) { + goto OnExit; + } + + // + // update state of Tcb and socket + // + if (((Sk->IpVersion == IP_VERSION_4) && !CfgData->Tcp4CfgData.AccessPoint.ActiveFlag) || + ((Sk->IpVersion == IP_VERSION_6) && !CfgData->Tcp6CfgData.AccessPoint.ActiveFlag) + ) { + + TcpSetState (Tcb, TCP_LISTEN); + SockSetState (Sk, SO_LISTENING); + + Sk->ConfigureState = SO_CONFIGURED_PASSIVE; + } else { + + Sk->ConfigureState = SO_CONFIGURED_ACTIVE; + } + + if (Sk->IpVersion == IP_VERSION_6) { + Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK; + + if (NetIp6IsUnspecifiedAddr (&Tcb->RemoteEnd.Ip.v6)) { + Tcb->RemoteIpZero = TRUE; + } + } + + TcpInsertTcb (Tcb); + +OnExit: + + return Status; +} + +/** + The procotol handler provided to the socket layer, which is used to + dispatch the socket level requests by calling the corresponding + TCP layer functions. + + @param[in] Sock Pointer to the socket of this TCP instance. + @param[in] Request The code of this operation request. + @param[in] Data Pointer to the operation specific data passed in + together with the operation request. This is an + optional parameter that may be NULL. + + @retval EFI_SUCCESS The socket request completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +EFI_STATUS +TcpDispatcher ( + IN SOCKET *Sock, + IN UINT8 Request, + IN VOID *Data OPTIONAL + ) +{ + TCP_CB *Tcb; + TCP_PROTO_DATA *ProtoData; + + ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + switch (Request) { + case SOCK_POLL: + if (Tcb->Sk->IpVersion == IP_VERSION_4) { + ProtoData->TcpService->IpIo->Ip.Ip4->Poll (ProtoData->TcpService->IpIo->Ip.Ip4); + } else { + ProtoData->TcpService->IpIo->Ip.Ip6->Poll (ProtoData->TcpService->IpIo->Ip.Ip6); + } + + break; + + case SOCK_CONSUMED: + // + // After user received data from socket buffer, socket will + // notify TCP using this message to give it a chance to send out + // window update information + // + ASSERT (Tcb != NULL); + TcpOnAppConsume (Tcb); + break; + + case SOCK_SND: + + ASSERT (Tcb != NULL); + TcpOnAppSend (Tcb); + break; + + case SOCK_CLOSE: + + TcpOnAppClose (Tcb); + + break; + + case SOCK_ABORT: + + TcpOnAppAbort (Tcb); + + break; + + case SOCK_SNDPUSH: + Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH); + + break; + + case SOCK_SNDURG: + Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1; + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG); + + break; + + case SOCK_CONNECT: + + TcpOnAppConnect (Tcb); + + break; + + case SOCK_ATTACH: + + return TcpAttachPcb (Sock); + + break; + + case SOCK_FLUSH: + + TcpFlushPcb (Tcb); + + break; + + case SOCK_DETACH: + + TcpDetachPcb (Sock); + + break; + + case SOCK_CONFIGURE: + + return TcpConfigurePcb ( + Sock, + (TCP_CONFIG_DATA *) Data + ); + + break; + + case SOCK_MODE: + + ASSERT ((Data != NULL) && (Tcb != NULL)); + + if (Tcb->Sk->IpVersion == IP_VERSION_4) { + + return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data); + } else { + + return Tcp6GetMode (Tcb, (TCP6_MODE_DATA *) Data); + } + + break; + + case SOCK_ROUTE: + + ASSERT ((Data != NULL) && (Tcb != NULL) && (Tcb->Sk->IpVersion == IP_VERSION_4)); + + return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data); + + default: + + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/TcpDxe/TcpDriver.c b/Core/NetworkPkg/TcpDxe/TcpDriver.c new file mode 100644 index 0000000000..ce3dd0be63 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpDriver.c @@ -0,0 +1,1006 @@ +/** @file + The driver binding and service binding protocol for the TCP driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +UINT16 mTcp4RandomPort; +UINT16 mTcp6RandomPort; + +TCP_HEARTBEAT_TIMER mTcpTimer = { + NULL, + 0 +}; + +EFI_TCP4_PROTOCOL gTcp4ProtocolTemplate = { + Tcp4GetModeData, + Tcp4Configure, + Tcp4Routes, + Tcp4Connect, + Tcp4Accept, + Tcp4Transmit, + Tcp4Receive, + Tcp4Close, + Tcp4Cancel, + Tcp4Poll +}; + +EFI_TCP6_PROTOCOL gTcp6ProtocolTemplate = { + Tcp6GetModeData, + Tcp6Configure, + Tcp6Connect, + Tcp6Accept, + Tcp6Transmit, + Tcp6Receive, + Tcp6Close, + Tcp6Cancel, + Tcp6Poll +}; + +SOCK_INIT_DATA mTcpDefaultSockData = { + SockStream, + SO_CLOSED, + NULL, + TCP_BACKLOG, + TCP_SND_BUF_SIZE, + TCP_RCV_BUF_SIZE, + IP_VERSION_4, + NULL, + TcpCreateSocketCallback, + TcpDestroySocketCallback, + NULL, + NULL, + 0, + TcpDispatcher, + NULL, +}; + +EFI_DRIVER_BINDING_PROTOCOL gTcp4DriverBinding = { + Tcp4DriverBindingSupported, + Tcp4DriverBindingStart, + Tcp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gTcp6DriverBinding = { + Tcp6DriverBindingSupported, + Tcp6DriverBindingStart, + Tcp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL gTcpServiceBinding = { + TcpServiceBindingCreateChild, + TcpServiceBindingDestroyChild +}; + + +/** + Create and start the heartbeat timer for the TCP driver. + + @retval EFI_SUCCESS The timer was successfully created and started. + @retval other The timer was not created. + +**/ +EFI_STATUS +TcpCreateTimer ( + VOID + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (mTcpTimer.RefCnt == 0) { + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + TcpTicking, + NULL, + &mTcpTimer.TimerEvent + ); + if (!EFI_ERROR (Status)) { + + Status = gBS->SetTimer ( + mTcpTimer.TimerEvent, + TimerPeriodic, + (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ) + ); + } + } + + if (!EFI_ERROR (Status)) { + + mTcpTimer.RefCnt++; + } + + return Status; +} + +/** + Stop and destroy the heartbeat timer for TCP driver. + +**/ +VOID +TcpDestroyTimer ( + VOID + ) +{ + ASSERT (mTcpTimer.RefCnt > 0); + + mTcpTimer.RefCnt--; + + if (mTcpTimer.RefCnt > 0) { + return; + } + + gBS->SetTimer (mTcpTimer.TimerEvent, TimerCancel, 0); + gBS->CloseEvent (mTcpTimer.TimerEvent); + mTcpTimer.TimerEvent = NULL; +} + +/** + The entry point for Tcp driver, which is used to install Tcp driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS The driver loaded. + @retval other The driver did not load. + +**/ +EFI_STATUS +EFIAPI +TcpDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINT32 Seed; + + // + // Install the TCP Driver Binding Protocol + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gTcp4DriverBinding, + ImageHandle, + &gTcpComponentName, + &gTcpComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Install the TCP Driver Binding Protocol + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gTcp6DriverBinding, + NULL, + &gTcpComponentName, + &gTcpComponentName2 + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gTcp4DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gTcpComponentName2, + &gEfiComponentNameProtocolGuid, + &gTcpComponentName, + NULL + ); + return Status; + } + + // + // Initialize ISS and random port. + // + Seed = NetRandomInitSeed (); + mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss; + mTcp4RandomPort = (UINT16) (TCP_PORT_KNOWN + (NET_RANDOM (Seed) % TCP_PORT_KNOWN)); + mTcp6RandomPort = mTcp4RandomPort; + + return EFI_SUCCESS; +} + +/** + Create a new TCP4 or TCP6 driver service binding protocol + + @param[in] Controller Controller handle of device to bind driver to. + @param[in] Image The TCP driver's image handle. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_SUCCESS A new IP6 service binding private was created. + +**/ +EFI_STATUS +TcpCreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *IpServiceBindingGuid; + EFI_GUID *TcpServiceBindingGuid; + TCP_SERVICE_DATA *TcpServiceData; + IP_IO_OPEN_DATA OpenData; + + if (IpVersion == IP_VERSION_4) { + IpServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid; + TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + IpServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid; + TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + Status = gBS->OpenProtocol ( + Controller, + TcpServiceBindingGuid, + NULL, + Image, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + Status = gBS->OpenProtocol ( + Controller, + IpServiceBindingGuid, + NULL, + Image, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Create the TCP service data. + // + TcpServiceData = AllocateZeroPool (sizeof (TCP_SERVICE_DATA)); + if (TcpServiceData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TcpServiceData->Signature = TCP_DRIVER_SIGNATURE; + TcpServiceData->ControllerHandle = Controller; + TcpServiceData->DriverBindingHandle = Image; + TcpServiceData->IpVersion = IpVersion; + CopyMem ( + &TcpServiceData->ServiceBinding, + &gTcpServiceBinding, + sizeof (EFI_SERVICE_BINDING_PROTOCOL) + ); + + TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion); + if (TcpServiceData->IpIo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + + InitializeListHead (&TcpServiceData->SocketList); + ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA)); + + if (IpVersion == IP_VERSION_4) { + CopyMem ( + &OpenData.IpConfigData.Ip4CfgData, + &mIp4IoDefaultIpConfigData, + sizeof (EFI_IP4_CONFIG_DATA) + ); + OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + } else { + CopyMem ( + &OpenData.IpConfigData.Ip6CfgData, + &mIp6IoDefaultIpConfigData, + sizeof (EFI_IP6_CONFIG_DATA) + ); + OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + } + + OpenData.PktRcvdNotify = TcpRxCallback; + Status = IpIoOpen (TcpServiceData->IpIo, &OpenData); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = TcpCreateTimer (); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + TcpServiceBindingGuid, + &TcpServiceData->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + TcpDestroyTimer (); + + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (TcpServiceData->IpIo != NULL) { + IpIoDestroy (TcpServiceData->IpIo); + TcpServiceData->IpIo = NULL; + } + + FreePool (TcpServiceData); + + return Status; +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +TcpDestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + SOCKET *Sock; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + Sock = NET_LIST_USER_STRUCT_S (Entry, SOCKET, Link, SOCK_SIGNATURE); + ServiceBinding = ((TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (Sock->SockHandle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle); +} + +/** + Destroy a TCP6 or TCP4 service binding instance. It will release all + the resources allocated by the instance. + + @param[in] Controller Controller handle of device to bind driver to. + @param[in] ImageHandle The TCP driver's image handle. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6 + + @retval EFI_SUCCESS The resources used by the instance were cleaned up. + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +TcpDestroyService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer, OPTIONAL + IN UINT8 IpVersion + ) +{ + EFI_HANDLE NicHandle; + EFI_GUID *IpProtocolGuid; + EFI_GUID *ServiceBindingGuid; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + TCP_SERVICE_DATA *TcpServiceData; + EFI_STATUS Status; + LIST_ENTRY *List; + TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + + ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6)); + + if (IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + NicHandle = NetLibGetNicHandle (Controller, IpProtocolGuid); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + ServiceBindingGuid, + (VOID **) &ServiceBinding, + ImageHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + TcpServiceData = TCP_SERVICE_FROM_THIS (ServiceBinding); + + if (NumberOfChildren != 0) { + List = &TcpServiceData->SocketList; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + TcpDestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } else if (IsListEmpty (&TcpServiceData->SocketList)) { + // + // Uninstall TCP servicebinding protocol + // + gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + ServiceBindingGuid, + ServiceBinding, + NULL + ); + + // + // Destroy the IpIO consumed by TCP driver + // + IpIoDestroy (TcpServiceData->IpIo); + TcpServiceData->IpIo = NULL; + + // + // Destroy the heartbeat timer. + // + TcpDestroyTimer (); + + // + // Release the TCP service data + // + FreePool (TcpServiceData); + + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the Tcp4ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Test for the Ip4ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + return Status; +} + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the + driver. + @retval other The driver cannot be added to ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4); + if ((Status == EFI_ALREADY_STARTED) || (Status == EFI_UNSUPPORTED)) { + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Stop this driver on ControllerHandle. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + return TcpDestroyService ( + ControllerHandle, + This->DriverBindingHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_4 + ); +} + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Tcp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the Tcp6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Test for the Ip6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + return Status; +} + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the + driver. + @retval other The driver cannot be added to ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +Tcp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6); + if ((Status == EFI_ALREADY_STARTED) || (Status == EFI_UNSUPPORTED)) { + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Stop this driver on ControllerHandle. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Tcp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + return TcpDestroyService ( + ControllerHandle, + This->DriverBindingHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_6 + ); +} + +/** + The Callback funtion called after the TCP socket was created. + + @param[in] This Pointer to the socket just created + @param[in] Context Context of the socket + + @retval EFI_SUCCESS This protocol installed successfully. + @retval other An error occured. + +**/ +EFI_STATUS +TcpCreateSocketCallback ( + IN SOCKET *This, + IN VOID *Context + ) +{ + EFI_STATUS Status; + TCP_SERVICE_DATA *TcpServiceData; + EFI_GUID *IpProtocolGuid; + VOID *Ip; + + if (This->IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } + + TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService; + + // + // Open the default IP protocol of IP_IO BY_DRIVER. + // + Status = gBS->OpenProtocol ( + TcpServiceData->IpIo->ChildHandle, + IpProtocolGuid, + &Ip, + TcpServiceData->DriverBindingHandle, + This->SockHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the device path on the handle where service binding resides on. + // + Status = gBS->OpenProtocol ( + TcpServiceData->ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &This->ParentDevicePath, + TcpServiceData->DriverBindingHandle, + This->SockHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + TcpServiceData->IpIo->ChildHandle, + IpProtocolGuid, + TcpServiceData->DriverBindingHandle, + This->SockHandle + ); + } else { + // + // Insert this socket into the SocketList. + // + InsertTailList (&TcpServiceData->SocketList, &This->Link); + } + + return Status; +} + +/** + The callback function called before the TCP socket was to be destroyed. + + @param[in] This The TCP socket to be destroyed. + @param[in] Context The context of the socket. + +**/ +VOID +TcpDestroySocketCallback ( + IN SOCKET *This, + IN VOID *Context + ) +{ + TCP_SERVICE_DATA *TcpServiceData; + EFI_GUID *IpProtocolGuid; + + if (This->IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } + + TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService; + + // + // Remove this node from the list. + // + RemoveEntryList (&This->Link); + + // + // Close the IP protocol. + // + gBS->CloseProtocol ( + TcpServiceData->IpIo->ChildHandle, + IpProtocolGuid, + TcpServiceData->DriverBindingHandle, + This->SockHandle + ); +} + +/** + Creates a child handle with a set of TCP services. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. + If it is NULL, then a new handle is created. + If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + SOCKET *Sock; + TCP_SERVICE_DATA *TcpServiceData; + TCP_PROTO_DATA TcpProto; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = EFI_SUCCESS; + TcpServiceData = TCP_SERVICE_FROM_THIS (This); + TcpProto.TcpService = TcpServiceData; + TcpProto.TcpPcb = NULL; + + // + // Create a tcp instance with defualt Tcp default + // sock init data and TcpProto + // + mTcpDefaultSockData.ProtoData = &TcpProto; + mTcpDefaultSockData.DataSize = sizeof (TCP_PROTO_DATA); + mTcpDefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle; + mTcpDefaultSockData.IpVersion = TcpServiceData->IpVersion; + + if (TcpServiceData->IpVersion == IP_VERSION_4) { + mTcpDefaultSockData.Protocol = &gTcp4ProtocolTemplate; + } else { + mTcpDefaultSockData.Protocol = &gTcp6ProtocolTemplate; + } + + Sock = SockCreateChild (&mTcpDefaultSockData); + if (NULL == Sock) { + DEBUG ( + (EFI_D_ERROR, + "TcpDriverBindingCreateChild: No resource to create a Tcp Child\n") + ); + + Status = EFI_OUT_OF_RESOURCES; + } else { + *ChildHandle = Sock->SockHandle; + } + + mTcpDefaultSockData.ProtoData = NULL; + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Destroys a child handle with a set of TCP services. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to be destroyed. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + VOID *Tcp; + SOCKET *Sock; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + // + // retrieve the Tcp4 protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp4ProtocolGuid, + &Tcp, + gTcp4DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // No Tcp4, try the Tcp6 protocol + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp6ProtocolGuid, + &Tcp, + gTcp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + } + } + + if (!EFI_ERROR (Status)) { + // + // destroy this sock and related Tcp protocol control + // block + // + Sock = SOCK_FROM_THIS (Tcp); + + SockDestroyChild (Sock); + } + + return Status; +} diff --git a/Core/NetworkPkg/TcpDxe/TcpDriver.h b/Core/NetworkPkg/TcpDxe/TcpDriver.h new file mode 100644 index 0000000000..19dd168e89 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpDriver.h @@ -0,0 +1,296 @@ +/** @file + The prototype of driver binding and service binding protocol for TCP driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _TCP_DRIVER_H_ +#define _TCP_DRIVER_H_ + +#define TCP_DRIVER_SIGNATURE SIGNATURE_32 ('T', 'C', 'P', 'D') + +#define TCP_PORT_KNOWN 1024 +#define TCP_PORT_USER_RESERVED 65535 + +typedef struct _TCP_HEARTBEAT_TIMER { + EFI_EVENT TimerEvent; + INTN RefCnt; +} TCP_HEARTBEAT_TIMER; + +typedef struct _TCP_SERVICE_DATA { + UINT32 Signature; + EFI_HANDLE ControllerHandle; + EFI_HANDLE DriverBindingHandle; + UINT8 IpVersion; + IP_IO *IpIo; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + LIST_ENTRY SocketList; +} TCP_SERVICE_DATA; + +typedef struct _TCP_PROTO_DATA { + TCP_SERVICE_DATA *TcpService; + TCP_CB *TcpPcb; +} TCP_PROTO_DATA; + +#define TCP_SERVICE_FROM_THIS(a) \ + CR ( \ + (a), \ + TCP_SERVICE_DATA, \ + ServiceBinding, \ + TCP_DRIVER_SIGNATURE \ + ) + +// +// Function prototype for the driver's entry point +// + +/** + The entry point for Tcp driver, used to install Tcp driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS The driver loaded. + @retval other The driver did not load. + +**/ +EFI_STATUS +EFIAPI +TcpDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Driver Binding Protocol +// + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of the device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver was added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the + driver. + @retval other The driver cannot be added to ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of the device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific + child device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Tcp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver was added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the + driver. + @retval other The driver cannot be added to ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +Tcp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Tcp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + The Callback funtion called after the TCP socket is created. + + @param[in] This Pointer to the socket just created. + @param[in] Context The context of the socket. + + @retval EFI_SUCCESS This protocol is installed successfully. + @retval other An error occured. + +**/ +EFI_STATUS +TcpCreateSocketCallback ( + IN SOCKET *This, + IN VOID *Context + ); + +/** + The callback function called before the TCP socket is to be destroyed. + + @param[in] This The TCP socket to be destroyed. + @param[in] Context The context of the socket. + +**/ +VOID +TcpDestroySocketCallback ( + IN SOCKET *This, + IN VOID *Context + ); + +// +// Function ptototypes for the ServiceBinding Prococol +// + +/** + Creates a child handle with a set of TCP services. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. + If it is NULL, then a new handle is created. + If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a set of TCP services. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER The child handle is NULL. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/Core/NetworkPkg/TcpDxe/TcpDxe.inf b/Core/NetworkPkg/TcpDxe/TcpDxe.inf new file mode 100644 index 0000000000..fde0dcecbe --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpDxe.inf @@ -0,0 +1,93 @@ +## @file +# TCPv4 I/O and TCPv6 I/O services. +# +# This module provides EFI TCPv4 Protocol and EFI TCPv6 Protocol to send and receive data stream. +# It might provide TCPv4 Protocol or TCPv6 Protocol or both of them that depends on +# which network stack has been loaded in system. +# +# +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TcpDxe + FILE_GUID = 1A7E4468-2F55-4a56-903C-01265EB7622B + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = TcpDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = TcpDxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + TcpDriver.c + SockImpl.c + SockInterface.c + TcpDispatcher.c + TcpOutput.c + TcpMain.c + SockImpl.h + TcpMisc.c + TcpProto.h + TcpOption.c + TcpInput.c + TcpFunc.h + TcpOption.h + TcpTimer.c + TcpMain.h + Socket.h + ComponentName.c + TcpIo.c + TcpDriver.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + BaseMemoryLib + DevicePathLib + DebugLib + MemoryAllocationLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + DpcLib + NetLib + IpIoLib + + +[Protocols] + ## SOMETIMES_CONSUMES + ## SOMETIMES_PRODUCES + gEfiDevicePathProtocolGuid + gEfiIp4ProtocolGuid ## TO_START + gEfiIp4ServiceBindingProtocolGuid ## TO_START + gEfiTcp4ProtocolGuid ## BY_START + gEfiTcp4ServiceBindingProtocolGuid ## BY_START + gEfiIp6ProtocolGuid ## TO_START + gEfiIp6ServiceBindingProtocolGuid ## TO_START + gEfiTcp6ProtocolGuid ## BY_START + gEfiTcp6ServiceBindingProtocolGuid ## BY_START + +[UserExtensions.TianoCore."ExtraFiles"] + TcpDxeExtra.uni diff --git a/Core/NetworkPkg/TcpDxe/TcpDxe.uni b/Core/NetworkPkg/TcpDxe/TcpDxe.uni new file mode 100644 index 0000000000..759ccbde80 Binary files /dev/null and b/Core/NetworkPkg/TcpDxe/TcpDxe.uni differ diff --git a/Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni b/Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni new file mode 100644 index 0000000000..bddb35688d Binary files /dev/null and b/Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni differ diff --git a/Core/NetworkPkg/TcpDxe/TcpFunc.h b/Core/NetworkPkg/TcpDxe/TcpFunc.h new file mode 100644 index 0000000000..2076109254 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpFunc.h @@ -0,0 +1,699 @@ +/** @file + Declaration of external functions shared in TCP driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _TCP_FUNC_H_ +#define _TCP_FUNC_H_ + +#include "TcpOption.h" + +#define TCP_COMP_VAL(Min, Max, Default, Val) \ + ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default)) + +/** + Timeout handler prototype. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +typedef +VOID +(*TCP_TIMER_HANDLER) ( + IN OUT TCP_CB *Tcb + ); + +// +// Functions in TcpMisc.c +// + +/** + Initialize the Tcb locally related members. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpInitTcbLocal ( + IN OUT TCP_CB *Tcb + ); + +/** + Initialize the peer related members. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Pointer to the segment that contains the peer's intial information. + @param[in] Opt Pointer to the options announced by the peer. + +**/ +VOID +TcpInitTcbPeer ( + IN OUT TCP_CB *Tcb, + IN TCP_SEG *Seg, + IN TCP_OPTION *Opt + ); + +/** + Try to find one Tcb whose equals to . + + @param[in] Addr Pointer to the IP address needs to match. + @param[in] Port The port number needs to match. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack. + IP_VERSION_6 indicates TCP is running on IP6 stack. + + + @retval TRUE The Tcb which matches the pairs exists. + @retval FALSE Otherwise + +**/ +BOOLEAN +TcpFindTcbByPeer ( + IN EFI_IP_ADDRESS *Addr, + IN TCP_PORTNO Port, + IN UINT8 Version + ); + +/** + Locate the TCP_CB related to the socket pair. + + @param[in] LocalPort The local port number. + @param[in] LocalIp The local IP address. + @param[in] RemotePort The remote port number. + @param[in] RemoteIp The remote IP address. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + @param[in] Syn If TRUE, the listen sockets are searched. + + @return Pointer to the related TCP_CB. If NULL, no match is found. + +**/ +TCP_CB * +TcpLocateTcb ( + IN TCP_PORTNO LocalPort, + IN EFI_IP_ADDRESS *LocalIp, + IN TCP_PORTNO RemotePort, + IN EFI_IP_ADDRESS *RemoteIp, + IN UINT8 Version, + IN BOOLEAN Syn + ); + +/** + Insert a Tcb into the proper queue. + + @param[in] Tcb Pointer to the TCP_CB to be inserted. + + @retval 0 The Tcb was inserted successfully. + @retval -1 An error condition occurred. + +**/ +INTN +TcpInsertTcb ( + IN TCP_CB *Tcb + ); + +/** + Clone a TCP_CB from Tcb. + + @param[in] Tcb Pointer to the TCP_CB to be cloned. + + @return Pointer to the new cloned TCP_CB. If NULL, an error condition occurred. + +**/ +TCP_CB * +TcpCloneTcb ( + IN TCP_CB *Tcb + ); + +/** + Compute an ISS to be used by a new connection. + + @return The result ISS. + +**/ +TCP_SEQNO +TcpGetIss ( + VOID + ); + +/** + Get the local mss. + + @param[in] Sock Pointer to the socket to get mss. + + @return The mss size. + +**/ +UINT16 +TcpGetRcvMss ( + IN SOCKET *Sock + ); + +/** + Set the Tcb's state. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] State The state to be set. + +**/ +VOID +TcpSetState ( + IN TCP_CB *Tcb, + IN UINT8 State + ); + +/** + Compute the TCP segment's checksum. + + @param[in] Nbuf Pointer to the buffer that contains the TCP segment. + @param[in] HeadSum The checksum value of the fixed part of pseudo header. + + @return The checksum value. + +**/ +UINT16 +TcpChecksum ( + IN NET_BUF *Nbuf, + IN UINT16 HeadSum + ); + +/** + Translate the information from the head of the received TCP + segment Nbuf contains, and fill it into a TCP_SEG structure. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] Nbuf Pointer to the buffer contains the TCP segment. + + @return Pointer to the TCP_SEG that contains the translated TCP head information. + +**/ +TCP_SEG * +TcpFormatNetbuf ( + IN TCP_CB *Tcb, + IN OUT NET_BUF *Nbuf + ); + +/** + Initialize an active connection, + + @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a + connection. + +**/ +VOID +TcpOnAppConnect ( + IN OUT TCP_CB *Tcb + ); + +/** + Application has consumed some data, check whether + to send a window update ack or a delayed ack. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpOnAppConsume ( + IN TCP_CB *Tcb + ); + +/** + Initiate the connection close procedure, called when + applications want to close the connection. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpOnAppClose ( + IN OUT TCP_CB *Tcb + ); + +/** + Check whether the application's newly delivered data can be sent out. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The data has been sent out successfully. + @retval -1 The Tcb is not in a state that data is permitted to + be sent out. + +**/ +INTN +TcpOnAppSend ( + IN OUT TCP_CB *Tcb + ); + +/** + Abort the connection by sending a reset segment: called + when the application wants to abort the connection. + + @param[in] Tcb Pointer to the TCP_CB of the TCP instance. + +**/ +VOID +TcpOnAppAbort ( + IN TCP_CB *Tcb + ); + +/** + Reset the connection related with Tcb. + + @param[in] Tcb Pointer to the TCP_CB of the connection to be reset. + +**/ +VOID +TcpResetConnection ( + IN TCP_CB *Tcb + ); + +/** + Install the device path protocol on the TCP instance. + + @param[in] Sock Pointer to the socket representing the TCP instance. + + @retval EFI_SUCCESS The device path protocol installed. + @retval other Failed to install the device path protocol. + +**/ +EFI_STATUS +TcpInstallDevicePath ( + IN SOCKET *Sock + ); + + +// +// Functions in TcpOutput.c +// + +/** + Compute the sequence space left in the old receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence space left in the old receive window. + +**/ +UINT32 +TcpRcvWinOld ( + IN TCP_CB *Tcb + ); + +/** + Compute the current receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The size of the current receive window, in bytes. + +**/ +UINT32 +TcpRcvWinNow ( + IN TCP_CB *Tcb + ); + +/** + Get the maximum SndNxt. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence number of the maximum SndNxt. + +**/ +TCP_SEQNO +TcpGetMaxSndNxt ( + IN TCP_CB *Tcb + ); + +/** + Compute how much data to send. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm + and send out data by force. + + @return The length of the data that can be sent. If 0, no data can be sent. + +**/ +UINT32 +TcpDataToSend ( + IN TCP_CB *Tcb, + IN INTN Force + ); + +/** + Retransmit the segment from sequence Seq. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment to be retransmitted. + + @retval 0 The retransmission succeeded. + @retval -1 An error condition occurred. + +**/ +INTN +TcpRetransmit ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq + ); + +/** + Check whether to send data/SYN/FIN and piggyback an ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm + and send out data by force. + + @return The number of bytes sent. + +**/ +INTN +TcpToSendData ( + IN OUT TCP_CB *Tcb, + IN INTN Force + ); + +/** + Check whether to send an ACK or delayed ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpToSendAck ( + IN OUT TCP_CB *Tcb + ); + +/** + Send an ACK immediately. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSendAck ( + IN OUT TCP_CB *Tcb + ); + +/** + Send a zero probe segment. It can be used by keepalive and zero window probe. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The zero probe segment was sent out successfully. + @retval other An error condition occurred. + +**/ +INTN +TcpSendZeroProbe ( + IN OUT TCP_CB *Tcb + ); + +/** + Send a RESET segment in response to the segment received. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance, may be NULL. + @param[in] Head TCP header of the segment that triggers the reset. + @param[in] Len Length of the segment that triggers the reset. + @param[in] Local Local IP address. + @param[in] Remote Remote peer's IP address. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + + @retval 0 A reset is sent or no need to send it. + @retval -1 No reset is sent. + +**/ +INTN +TcpSendReset ( + IN TCP_CB *Tcb, + IN TCP_HEAD *Head, + IN INT32 Len, + IN EFI_IP_ADDRESS *Local, + IN EFI_IP_ADDRESS *Remote, + IN UINT8 Version + ); + +/** + Verify that the segment is in good shape. + + @param[in] Nbuf Buffer that contains the segment to be checked. + + @retval 0 The segment is broken. + @retval 1 The segment is in good shape. + +**/ +INTN +TcpVerifySegment ( + IN NET_BUF *Nbuf + ); + +// +// Functions from TcpInput.c +// + +/** + Process the received ICMP error messages for TCP. + + @param[in] Nbuf Buffer that contains part of the TCP segment without IP header + truncated from the ICMP error packet. + @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet. + @param[in] Src Source address of the ICMP error message. + @param[in] Dst Destination address of the ICMP error message. + @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates + IP6 stack. + +**/ +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN UINT8 IcmpErr, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ); + +/** + Process the received TCP segments. + + @param[in] Nbuf Buffer that contains received TCP segment without an IP header. + @param[in] Src Source address of the segment, or the peer's IP address. + @param[in] Dst Destination address of the segment, or the local end's IP + address. + @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates + IP6 stack. + + @retval 0 The segment processed successfully. It is either accepted or + discarded. But no connection is reset by the segment. + @retval -1 A connection is reset by the segment. + +**/ +INTN +TcpInput ( + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ); + +// +// Functions in TcpTimer.c +// + +/** + Close the TCP connection. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClose ( + IN OUT TCP_CB *Tcb + ); + +/** + Heart beat timer handler, queues the DPC at TPL_CALLBACK. + + @param[in] Event Timer event signaled, ignored. + @param[in] Context Context of the timer event, ignored. + +**/ +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Enable a TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be enabled. + @param[in] TimeOut The timeout value of this timer. + +**/ +VOID +TcpSetTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer, + IN UINT32 TimeOut + ); + +/** + Clear one TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be cleared. + +**/ +VOID +TcpClearTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer + ); + +/** + Clear all TCP timers. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClearAllTimer ( + IN OUT TCP_CB *Tcb + ); + +/** + Enable the window prober timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetProbeTimer ( + IN OUT TCP_CB *Tcb + ); + +/** + Enable the keepalive timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetKeepaliveTimer ( + IN OUT TCP_CB *Tcb + ); + +// +// Functions in TcpIo.c +// + +/** + Packet receive callback function provided to IP_IO. Used to call + the proper function to handle the packet received by IP. + + @param[in] Status Result of the receive request. + @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR. + @param[in] NetSession The IP session for the received packet. + @param[in] Pkt Packet received. + @param[in] Context The data provided by the user for the received packet when + the callback is registered in IP_IO_OPEN_DATA::RcvdContext. + This is an optional parameter that may be NULL. + +**/ +VOID +EFIAPI +TcpRxCallback ( + IN EFI_STATUS Status, + IN UINT8 IcmpErr, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Pkt, + IN VOID *Context OPTIONAL + ); + +/** + Send the segment to IP via IpIo function. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the TCP segment to be sent. + @param[in] Src Source address of the TCP segment. + @param[in] Dest Destination address of the TCP segment. + @param[in] Version IP_VERSION_4 or IP_VERSION_6 + + @retval 0 The segment was sent out successfully. + @retval -1 The segment failed to be sent. + +**/ +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dest, + IN UINT8 Version + ); + +/** + Refresh the remote peer's Neighbor Cache State if already exists. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Neighbor Source address of the TCP segment. + @param[in] Timeout Time in 100-ns units that this entry will remain + in the neighbor cache. A value of zero means that + the entry is permanent. A value of non-zero means + that the entry is dynamic and will be deleted + after Timeout. + + @retval EFI_SUCCESS Successfully updated the neighbor relationship. + @retval EFI_NOT_STARTED The IpIo is not configured. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_NOT_FOUND This entry is not in the neighbor table. + +**/ +EFI_STATUS +Tcp6RefreshNeighbor ( + IN TCP_CB *Tcb, + IN EFI_IP_ADDRESS *Neighbor, + IN UINT32 Timeout + ); + +// +// Functions in TcpDispatcher.c +// + +/** + The procotol handler provided to the socket layer, used to + dispatch the socket level requests by calling the corresponding + TCP layer functions. + + @param[in] Sock Pointer to the socket of this TCP instance. + @param[in] Request The code of this operation request. + @param[in] Data Pointer to the operation specific data passed in + together with the operation request. This is an + optional parameter that may be NULL. + + @retval EFI_SUCCESS The socket request completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +EFI_STATUS +TcpDispatcher ( + IN SOCKET *Sock, + IN UINT8 Request, + IN VOID *Data OPTIONAL + ); + +#endif diff --git a/Core/NetworkPkg/TcpDxe/TcpInput.c b/Core/NetworkPkg/TcpDxe/TcpInput.c new file mode 100644 index 0000000000..d0118f1d88 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpInput.c @@ -0,0 +1,1608 @@ +/** @file + TCP input process routines. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +/** + Check whether the sequence number of the incoming segment is acceptable. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Pointer to the incoming segment. + + @retval 1 The sequence number is acceptable. + @retval 0 The sequence number is not acceptable. + +**/ +INTN +TcpSeqAcceptable ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) && + TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd)); +} + +/** + NewReno fast recovery defined in RFC3782. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Segment that triggers the fast recovery. + +**/ +VOID +TcpFastRecover ( + IN OUT TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + UINT32 FlightSize; + UINT32 Acked; + + // + // Step 1: Three duplicate ACKs and not in fast recovery + // + if (Tcb->CongestState != TCP_CONGEST_RECOVER) { + + // + // Step 1A: Invoking fast retransmission. + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + + Tcb->Ssthresh = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss)); + Tcb->Recover = Tcb->SndNxt; + + Tcb->CongestState = TCP_CONGEST_RECOVER; + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + + // + // Step 2: Entering fast retransmission + // + TcpRetransmit (Tcb, Tcb->SndUna); + Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss; + + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: enter fast retransmission for TCB %p, recover point is %d\n", + Tcb, + Tcb->Recover) + ); + return; + } + + // + // During fast recovery, execute Step 3, 4, 5 of RFC3782 + // + if (Seg->Ack == Tcb->SndUna) { + + // + // Step 3: Fast Recovery, + // If this is a duplicated ACK, increse Cwnd by SMSS. + // + + // Step 4 is skipped here only to be executed later + // by TcpToSendData + // + Tcb->CWnd += Tcb->SndMss; + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: received another duplicated ACK (%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + + } else { + + // + // New data is ACKed, check whether it is a + // full ACK or partial ACK + // + if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) { + + // + // Step 5 - Full ACK: + // deflate the congestion window, and exit fast recovery + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + + Tcb->CWnd = MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss); + + Tcb->CongestState = TCP_CONGEST_OPEN; + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: received a full ACK(%d) for TCB %p, exit fast recovery\n", + Seg->Ack, + Tcb) + ); + + } else { + + // + // Step 5 - Partial ACK: + // fast retransmit the first unacknowledge field + // , then deflate the CWnd + // + TcpRetransmit (Tcb, Seg->Ack); + Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna); + + // + // Deflate the CWnd by the amount of new data + // ACKed by SEG.ACK. If more than one SMSS data + // is ACKed, add back SMSS byte to CWnd after + // + if (Acked >= Tcb->SndMss) { + Acked -= Tcb->SndMss; + + } + + Tcb->CWnd -= Acked; + + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: received a partial ACK(%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + + } + } +} + +/** + NewReno fast loss recovery defined in RFC3792. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Segment that triggers the fast loss recovery. + +**/ +VOID +TcpFastLossRecover ( + IN OUT TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + // + // New data is ACKed, check whether it is a + // full ACK or partial ACK + // + if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) { + + // + // Full ACK: exit the loss recovery. + // + Tcb->LossTimes = 0; + Tcb->CongestState = TCP_CONGEST_OPEN; + + DEBUG ( + (EFI_D_INFO, + "TcpFastLossRecover: received a full ACK(%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + + } else { + + // + // Partial ACK: + // fast retransmit the first unacknowledge field. + // + TcpRetransmit (Tcb, Seg->Ack); + DEBUG ( + (EFI_D_INFO, + "TcpFastLossRecover: received a partial ACK(%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + } + } +} + +/** + Compute the RTT as specified in RFC2988. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Measure Currently measured RTT in heartbeats. + +**/ +VOID +TcpComputeRtt ( + IN OUT TCP_CB *Tcb, + IN UINT32 Measure + ) +{ + INT32 Var; + + // + // Step 2.3: Compute the RTO for subsequent RTT measurement. + // + if (Tcb->SRtt != 0) { + + Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT); + + if (Var < 0) { + Var = -Var; + } + + Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2; + Tcb->SRtt = 7 * (Tcb->SRtt >> 3) + Measure; + + } else { + // + // Step 2.2: compute the first RTT measure + // + Tcb->SRtt = Measure << TCP_RTT_SHIFT; + Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1); + } + + Tcb->Rto = (Tcb->SRtt + MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT; + + // + // Step 2.4: Limit the RTO to at least 1 second + // Step 2.5: Limit the RTO to a maxium value that + // is at least 60 second + // + if (Tcb->Rto < TCP_RTO_MIN) { + Tcb->Rto = TCP_RTO_MIN; + + } else if (Tcb->Rto > TCP_RTO_MAX) { + Tcb->Rto = TCP_RTO_MAX; + + } + + DEBUG ( + (EFI_D_INFO, + "TcpComputeRtt: new RTT for TCB %p computed SRTT: %d RTTVAR: %d RTO: %d\n", + Tcb, + Tcb->SRtt, + Tcb->RttVar, + Tcb->Rto) + ); + +} + +/** + Trim the data; SYN and FIN to fit into the window defined by Left and Right. + + @param[in] Nbuf The buffer that contains a received TCP segment without an IP header. + @param[in] Left The sequence number of the window's left edge. + @param[in] Right The sequence number of the window's right edge. + +**/ +VOID +TcpTrimSegment ( + IN NET_BUF *Nbuf, + IN TCP_SEQNO Left, + IN TCP_SEQNO Right + ) +{ + TCP_SEG *Seg; + TCP_SEQNO Urg; + UINT32 Drop; + + Seg = TCPSEG_NETBUF (Nbuf); + + // + // If the segment is completely out of window, + // truncate every thing, include SYN and FIN. + // + if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) { + + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN); + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN); + + Seg->Seq = Seg->End; + NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD); + return; + } + + // + // Adjust the buffer header + // + if (TCP_SEQ_LT (Seg->Seq, Left)) { + + Drop = TCP_SUB_SEQ (Left, Seg->Seq); + Urg = Seg->Seq + Seg->Urg; + Seg->Seq = Left; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN); + Drop--; + } + + // + // Adjust the urgent point + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) { + + if (TCP_SEQ_LT (Urg, Seg->Seq)) { + + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG); + } else { + Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq); + } + } + + if (Drop != 0) { + NetbufTrim (Nbuf, Drop, NET_BUF_HEAD); + } + } + + // + // Adjust the buffer tail + // + if (TCP_SEQ_GT (Seg->End, Right)) { + + Drop = TCP_SUB_SEQ (Seg->End, Right); + Seg->End = Right; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN); + Drop--; + } + + if (Drop != 0) { + NetbufTrim (Nbuf, Drop, NET_BUF_TAIL); + } + } + + ASSERT (TcpVerifySegment (Nbuf) != 0); +} + +/** + Trim off the data outside the tcb's receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the NET_BUF containing the received tcp segment. + +**/ +VOID +TcpTrimInWnd ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd); +} + +/** + Process the data and FIN flag, and check whether to deliver + data to the socket layer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 No error occurred to deliver data. + @retval -1 An error condition occurred. The proper response is to reset the + connection. + +**/ +INTN +TcpDeliverData ( + IN OUT TCP_CB *Tcb + ) +{ + LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + TCP_SEG *Seg; + UINT32 Urgent; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + // + // make sure there is some data queued, + // and TCP is in a proper state + // + if (IsListEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) { + + return 0; + } + + // + // Deliver data to the socket layer + // + Entry = Tcb->RcvQue.ForwardLink; + Seq = Tcb->RcvNxt; + + while (Entry != &Tcb->RcvQue) { + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Seg = TCPSEG_NETBUF (Nbuf); + + ASSERT (TcpVerifySegment (Nbuf) != 0); + ASSERT (Nbuf->Tcp == NULL); + + if (TCP_SEQ_GT (Seg->Seq, Seq)) { + break; + } + + Entry = Entry->ForwardLink; + Seq = Seg->End; + Tcb->RcvNxt = Seq; + + RemoveEntryList (&Nbuf->List); + + // + // RFC793 Eighth step: process FIN in sequence + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + + // + // The peer sends to us junky data after FIN, + // reset the connection. + // + if (!IsListEmpty (&Tcb->RcvQue)) { + DEBUG ( + (EFI_D_ERROR, + "TcpDeliverData: data received after FIN from peer of TCB %p, reset connection\n", + Tcb) + ); + + NetbufFree (Nbuf); + return -1; + } + + DEBUG ( + (EFI_D_INFO, + "TcpDeliverData: processing FIN from peer of TCB %p\n", + Tcb) + ); + + switch (Tcb->State) { + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + + TcpSetState (Tcb, TCP_CLOSE_WAIT); + break; + + case TCP_FIN_WAIT_1: + + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_CLOSING); + break; + } + + // + // fall through + // + case TCP_FIN_WAIT_2: + + TcpSetState (Tcb, TCP_TIME_WAIT); + TcpClearAllTimer (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + DEBUG ( + (EFI_D_WARN, + "Connection closed immediately because app disables TIME_WAIT timer for %p\n", + Tcb) + ); + + TcpSendAck (Tcb); + TcpClose (Tcb); + } + break; + + case TCP_CLOSE_WAIT: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + // + // The peer sends to us junk FIN byte. Discard + // the buffer then reset the connection + // + NetbufFree (Nbuf); + return -1; + break; + default: + break; + } + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + Seg->End--; + } + + // + // Don't delay the ack if PUSH flag is on. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + + if (Nbuf->TotalSize != 0) { + Urgent = 0; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && + TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)) + { + + if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) { + Urgent = Nbuf->TotalSize; + } else { + Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1; + } + } + + SockDataRcvd (Tcb->Sk, Nbuf, Urgent); + } + + if (TCP_FIN_RCVD (Tcb->State)) { + + SockNoMoreData (Tcb->Sk); + } + + NetbufFree (Nbuf); + } + + return 0; +} + +/** + Store the data into the reassemble queue. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer containing the data to be queued. + +**/ +VOID +TcpQueueData ( + IN OUT TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TCP_SEG *Seg; + LIST_ENTRY *Head; + LIST_ENTRY *Prev; + LIST_ENTRY *Cur; + NET_BUF *Node; + + ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); + + NET_GET_REF (Nbuf); + + Seg = TCPSEG_NETBUF (Nbuf); + Head = &Tcb->RcvQue; + + // + // Fast path to process normal case. That is, + // no out-of-order segments are received. + // + if (IsListEmpty (Head)) { + + InsertTailList (Head, &Nbuf->List); + return; + } + + // + // Find the point to insert the buffer + // + for (Prev = Head, Cur = Head->ForwardLink; + Cur != Head; + Prev = Cur, Cur = Cur->ForwardLink) { + + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) { + break; + } + } + + // + // Check whether the current segment overlaps with the + // previous segment. + // + if (Prev != Head) { + Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); + + if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) { + + if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) { + + NetbufFree (Nbuf); + return; + } + + TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End); + } + } + + InsertHeadList (Prev, &Nbuf->List); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + // + // Check the segments after the insert point. + // + while (Cur != Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) { + + Cur = Cur->ForwardLink; + + RemoveEntryList (&Node->List); + NetbufFree (Node); + continue; + } + + if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) { + + if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) { + + RemoveEntryList (&Nbuf->List); + NetbufFree (Nbuf); + return; + } + + TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq); + break; + } + + Cur = Cur->ForwardLink; + } +} + + +/** + Adjust the send queue or the retransmit queue. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Ack The acknowledge seuqence number of the received segment. + +**/ +VOID +TcpAdjustSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Ack + ) +{ + LIST_ENTRY *Head; + LIST_ENTRY *Cur; + NET_BUF *Node; + TCP_SEG *Seg; + + Head = &Tcb->SndQue; + Cur = Head->ForwardLink; + + while (Cur != Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Seg = TCPSEG_NETBUF (Node); + + if (TCP_SEQ_GEQ (Seg->Seq, Ack)) { + break; + } + + // + // Remove completely ACKed segments + // + if (TCP_SEQ_LEQ (Seg->End, Ack)) { + Cur = Cur->ForwardLink; + + RemoveEntryList (&Node->List); + NetbufFree (Node); + continue; + } + + TcpTrimSegment (Node, Ack, Seg->End); + break; + } +} + +/** + Process the received TCP segments. + + @param[in] Nbuf Buffer that contains received a TCP segment without an IP header. + @param[in] Src Source address of the segment, or the peer's IP address. + @param[in] Dst Destination address of the segment, or the local end's IP + address. + @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates + IP6 stack. + + @retval 0 Segment processed successfully. It is either accepted or + discarded. However, no connection is reset by the segment. + @retval -1 A connection is reset by the segment. + +**/ +INTN +TcpInput ( + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ) +{ + TCP_CB *Tcb; + TCP_CB *Parent; + TCP_OPTION Option; + TCP_HEAD *Head; + INT32 Len; + TCP_SEG *Seg; + TCP_SEQNO Right; + TCP_SEQNO Urg; + UINT16 Checksum; + + ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6)); + + NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE); + + Parent = NULL; + Tcb = NULL; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + ASSERT (Head != NULL); + Len = Nbuf->TotalSize - (Head->HeadLen << 2); + + if ((Head->HeadLen < 5) || (Len < 0)) { + + DEBUG ((EFI_D_INFO, "TcpInput: received a malformed packet\n")); + goto DISCARD; + } + + if (Version == IP_VERSION_4) { + Checksum = NetPseudoHeadChecksum (Src->Addr[0], Dst->Addr[0], 6, 0); + } else { + Checksum = NetIp6PseudoHeadChecksum (&Src->v6, &Dst->v6, 6, 0); + } + + Checksum = TcpChecksum (Nbuf, Checksum); + + if (Checksum != 0) { + DEBUG ((EFI_D_ERROR, "TcpInput: received a checksum error packet\n")); + goto DISCARD; + } + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) { + Len++; + } + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) { + Len++; + } + + Tcb = TcpLocateTcb ( + Head->DstPort, + Dst, + Head->SrcPort, + Src, + Version, + (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN) + ); + + if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) { + DEBUG ((EFI_D_INFO, "TcpInput: send reset because no TCB found\n")); + + Tcb = NULL; + goto SEND_RESET; + } + + Seg = TcpFormatNetbuf (Tcb, Nbuf); + + // + // RFC1122 recommended reaction to illegal option + // (in fact, an illegal option length) is reset. + // + if (TcpParseOption (Nbuf->Tcp, &Option) == -1) { + DEBUG ( + (EFI_D_ERROR, + "TcpInput: reset the peer because of malformed option for TCB %p\n", + Tcb) + ); + + goto SEND_RESET; + } + + // + // From now on, the segment is headless + // + NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + // + // Process the segment in LISTEN state. + // + if (Tcb->State == TCP_LISTEN) { + // + // First step: Check RST + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: discard a reset segment for TCB %p in listening\n", + Tcb) + ); + + goto DISCARD; + } + + // + // Second step: Check ACK. + // Any ACK sent to TCP in LISTEN is reseted. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: send reset because of segment with ACK for TCB %p in listening\n", + Tcb) + ); + + goto SEND_RESET; + } + + // + // Third step: Check SYN + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + // + // create a child TCB to handle the data + // + Parent = Tcb; + + Tcb = TcpCloneTcb (Parent); + if (Tcb == NULL) { + DEBUG ( + (EFI_D_ERROR, + "TcpInput: discard a segment because failed to clone a child for TCB %p\n", + Tcb) + ); + + goto DISCARD; + } + + DEBUG ( + (EFI_D_INFO, + "TcpInput: create a child for TCB %p in listening\n", + Tcb) + ); + + // + // init the TCB structure + // + IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, Dst); + IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, Src); + Tcb->LocalEnd.Port = Head->DstPort; + Tcb->RemoteEnd.Port = Head->SrcPort; + + TcpInitTcbLocal (Tcb); + TcpInitTcbPeer (Tcb, Seg, &Option); + + TcpSetState (Tcb, TCP_SYN_RCVD); + TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout); + TcpTrimInWnd (Tcb, Nbuf); + + goto StepSix; + } + + goto DISCARD; + + } else if (Tcb->State == TCP_SYN_SENT) { + // + // First step: Check ACK bit + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: send reset because of wrong ACK received for TCB %p in SYN_SENT\n", + Tcb) + ); + + goto SEND_RESET; + } + + // + // Second step: Check RST bit + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset by peer for TCB %p in SYN_SENT\n", + Tcb) + ); + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + goto DROP_CONNECTION; + } else { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: discard a reset segment because of no ACK for TCB %p in SYN_SENT\n", + Tcb) + ); + + goto DISCARD; + } + } + + // + // Third step: Check security and precedence. Skipped + // + + // + // Fourth step: Check SYN. Pay attention to simultaneous open + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + TcpInitTcbPeer (Tcb, Seg, &Option); + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + + Tcb->SndUna = Seg->Ack; + } + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + + if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) { + + TcpSetState (Tcb, TCP_ESTABLISHED); + + TcpClearTimer (Tcb, TCP_TIMER_CONNECT); + TcpDeliverData (Tcb); + + if ((Tcb->CongestState == TCP_CONGEST_OPEN) && + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) + { + + TcpComputeRtt (Tcb, Tcb->RttMeasure); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + } + + TcpTrimInWnd (Tcb, Nbuf); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + DEBUG ( + (EFI_D_INFO, + "TcpInput: connection established for TCB %p in SYN_SENT\n", + Tcb) + ); + + goto StepSix; + } else { + // + // Received a SYN segment without ACK, simultanous open. + // + TcpSetState (Tcb, TCP_SYN_RCVD); + + ASSERT (Tcb->SndNxt == Tcb->Iss + 1); + TcpAdjustSndQue (Tcb, Tcb->SndNxt); + + TcpTrimInWnd (Tcb, Nbuf); + + DEBUG ( + (EFI_D_WARN, + "TcpInput: simultaneous open for TCB %p in SYN_SENT\n", + Tcb) + ); + + goto StepSix; + } + } + + goto DISCARD; + } + + // + // Process segment in SYN_RCVD or TCP_CONNECTED states + // + + // + // Clear probe timer since the RecvWindow is opened. + // + if (Tcb->ProbeTimerOn && (Seg->Wnd != 0)) { + TcpClearTimer (Tcb, TCP_TIMER_PROBE); + Tcb->ProbeTimerOn = FALSE; + } + + // + // First step: Check whether SEG.SEQ is acceptable + // + if (TcpSeqAcceptable (Tcb, Seg) == 0) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: sequence acceptance test failed for segment of TCB %p\n", + Tcb) + ); + + if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + TcpSendAck (Tcb); + } + + goto DISCARD; + } + + if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) && + (Tcb->RcvWl2 == Seg->End) && + !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)) + { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + + // + // Second step: Check the RST + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + + DEBUG ((EFI_D_WARN, "TcpInput: connection reset for TCB %p\n", Tcb)); + + if (Tcb->State == TCP_SYN_RCVD) { + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED); + + // + // This TCB comes from either a LISTEN TCB, + // or active open TCB with simultanous open. + // Do NOT signal user CONNECTION refused + // if it comes from a LISTEN TCB. + // + } else if ((Tcb->State == TCP_ESTABLISHED) || + (Tcb->State == TCP_FIN_WAIT_1) || + (Tcb->State == TCP_FIN_WAIT_2) || + (Tcb->State == TCP_CLOSE_WAIT)) + { + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + + } else { + } + + goto DROP_CONNECTION; + } + + // + // Trim the data and flags. + // + TcpTrimInWnd (Tcb, Nbuf); + + // + // Third step: Check security and precedence, Ignored + // + + // + // Fourth step: Check the SYN bit. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset because received extra SYN for TCB %p\n", + Tcb) + ); + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + goto RESET_THEN_DROP; + } + // + // Fifth step: Check the ACK + // + if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: segment discard because of no ACK for connected TCB %p\n", + Tcb) + ); + + goto DISCARD; + } else { + if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick == 0) { + Tcp6RefreshNeighbor (Tcb, Src, TCP6_KEEP_NEIGHBOR_TIME * TICKS_PER_SECOND); + Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK; + } + } + + if (Tcb->State == TCP_SYN_RCVD) { + + if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) && + TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) + { + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax); + Tcb->SndWl1 = Seg->Seq; + Tcb->SndWl2 = Seg->Ack; + TcpSetState (Tcb, TCP_ESTABLISHED); + + TcpClearTimer (Tcb, TCP_TIMER_CONNECT); + TcpDeliverData (Tcb); + + DEBUG ( + (EFI_D_INFO, + "TcpInput: connection established for TCB %p in SYN_RCVD\n", + Tcb) + ); + + // + // Continue the process as ESTABLISHED state + // + } else { + DEBUG ( + (EFI_D_WARN, + "TcpInput: send reset because of wrong ACK for TCB %p in SYN_RCVD\n", + Tcb) + ); + + goto SEND_RESET; + } + } + + if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: ignore the out-of-data ACK for connected TCB %p\n", + Tcb) + ); + + goto StepSix; + + } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: discard segment for future ACK for connected TCB %p\n", + Tcb) + ); + + TcpSendAck (Tcb); + goto DISCARD; + } + + // + // From now on: SND.UNA <= SEG.ACK <= SND.NXT. + // + if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) { + // + // update TsRecent as specified in page 16 RFC1323. + // RcvWl2 equals to the variable "LastAckSent" + // defined there. + // + if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) && + TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) + { + + Tcb->TsRecent = Option.TSVal; + Tcb->TsRecentAge = mTcpTick; + } + + TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr)); + + } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN); + + TcpComputeRtt (Tcb, Tcb->RttMeasure); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + } + + if (Seg->Ack == Tcb->SndNxt) { + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + } else { + + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + } + + // + // Count duplicate acks. + // + if ((Seg->Ack == Tcb->SndUna) && + (Tcb->SndUna != Tcb->SndNxt) && + (Seg->Wnd == Tcb->SndWnd) && + (0 == Len)) + { + + Tcb->DupAck++; + } else { + + Tcb->DupAck = 0; + } + + // + // Congestion avoidance, fast recovery and fast retransmission. + // + if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) || + (Tcb->CongestState == TCP_CONGEST_LOSS)) + { + + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + if (Tcb->CWnd < Tcb->Ssthresh) { + + Tcb->CWnd += Tcb->SndMss; + } else { + + Tcb->CWnd += MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1); + } + + Tcb->CWnd = MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale); + } + + if (Tcb->CongestState == TCP_CONGEST_LOSS) { + TcpFastLossRecover (Tcb, Seg); + } + } else { + + TcpFastRecover (Tcb, Seg); + } + + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + TcpAdjustSndQue (Tcb, Seg->Ack); + Tcb->SndUna = Seg->Ack; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && + TCP_SEQ_LT (Tcb->SndUp, Seg->Ack)) + { + + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG); + } + } + + // + // Update window info + // + if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) || + ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))) + { + + Right = Seg->Ack + Seg->Wnd; + + if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) { + + if ((Tcb->SndWl1 == Seg->Seq) && + (Tcb->SndWl2 == Seg->Ack) && + (Len == 0)) + { + + goto NO_UPDATE; + } + + DEBUG ( + (EFI_D_WARN, + "TcpInput: peer shrinks the window for connected TCB %p\n", + Tcb) + ); + + if ((Tcb->CongestState == TCP_CONGEST_RECOVER) && + (TCP_SEQ_LT (Right, Tcb->Recover))) + { + + Tcb->Recover = Right; + } + + if ((Tcb->CongestState == TCP_CONGEST_LOSS) && + (TCP_SEQ_LT (Right, Tcb->LossRecover))) + { + + Tcb->LossRecover = Right; + } + + if (TCP_SEQ_LT (Right, Tcb->SndNxt)) { + + Tcb->SndNxt = Right; + + if (Right == Tcb->SndUna) { + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + TcpSetProbeTimer (Tcb); + } + } + } + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = MAX (Tcb->SndWnd, Tcb->SndWndMax); + Tcb->SndWl1 = Seg->Seq; + Tcb->SndWl2 = Seg->Ack; + } + +NO_UPDATE: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) && + (Tcb->SndUna == Tcb->SndNxt)) + { + + DEBUG ( + (EFI_D_INFO, + "TcpInput: local FIN is ACKed by peer for connected TCB %p\n", + Tcb) + ); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED); + } + + // + // Transit the state if proper. + // + switch (Tcb->State) { + case TCP_FIN_WAIT_1: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_FIN_WAIT_2); + + TcpClearAllTimer (Tcb); + TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout); + } + + case TCP_FIN_WAIT_2: + + break; + + case TCP_CLOSE_WAIT: + break; + + case TCP_CLOSING: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_TIME_WAIT); + + TcpClearAllTimer (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + DEBUG ( + (EFI_D_WARN, + "Connection closed immediately because app disables TIME_WAIT timer for %p\n", + Tcb) + ); + + TcpClose (Tcb); + } + } + break; + + case TCP_LAST_ACK: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_CLOSED); + } + + break; + + case TCP_TIME_WAIT: + + TcpSendAck (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + DEBUG ( + (EFI_D_WARN, + "Connection closed immediately because app disables TIME_WAIT timer for %p\n", + Tcb) + ); + + TcpClose (Tcb); + } + break; + + default: + break; + } + // + // Sixth step: Check the URG bit.update the Urg point + // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact. + // +StepSix: + + Tcb->Idle = 0; + TcpSetKeepaliveTimer (Tcb); + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) && !TCP_FIN_RCVD (Tcb->State)) { + + DEBUG ( + (EFI_D_INFO, + "TcpInput: received urgent data from peer for connected TCB %p\n", + Tcb) + ); + + Urg = Seg->Seq + Seg->Urg; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && + TCP_SEQ_GT (Urg, Tcb->RcvUp)) + { + + Tcb->RcvUp = Urg; + } else { + + Tcb->RcvUp = Urg; + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG); + } + } + // + // Seventh step: Process the segment data + // + if (Seg->End != Seg->Seq) { + + if (TCP_FIN_RCVD (Tcb->State)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset because data is lost for connected TCB %p\n", + Tcb) + ); + + goto RESET_THEN_DROP; + } + + if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset because data is lost for connected TCB %p\n", + Tcb) + ); + + goto RESET_THEN_DROP; + } + + TcpQueueData (Tcb, Nbuf); + if (TcpDeliverData (Tcb) == -1) { + goto RESET_THEN_DROP; + } + + if (!IsListEmpty (&Tcb->RcvQue)) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + } + + // + // Eighth step: check the FIN. + // This step is moved to TcpDeliverData. FIN will be + // processed in sequence there. Check the comments in + // the beginning of the file header for information. + // + + // + // Tcb is a new child of the listening Parent, + // commit it. + // + if (Parent != NULL) { + Tcb->Parent = Parent; + TcpInsertTcb (Tcb); + } + + if ((Tcb->State != TCP_CLOSED) && + (TcpToSendData (Tcb, 0) == 0) && + (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Nbuf->TotalSize != 0))) + { + + TcpToSendAck (Tcb); + } + + NetbufFree (Nbuf); + return 0; + +RESET_THEN_DROP: + TcpSendReset (Tcb, Head, Len, Dst, Src, Version); + +DROP_CONNECTION: + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + NetbufFree (Nbuf); + TcpClose (Tcb); + + return -1; + +SEND_RESET: + + TcpSendReset (Tcb, Head, Len, Dst, Src, Version); + +DISCARD: + + // + // Tcb is a child of Parent, and it doesn't survive + // + DEBUG ((EFI_D_WARN, "TcpInput: Discard a packet\n")); + NetbufFree (Nbuf); + + if ((Parent != NULL) && (Tcb != NULL)) { + + ASSERT (Tcb->Sk != NULL); + TcpClose (Tcb); + } + + return 0; +} + +/** + Process the received ICMP error messages for TCP. + + @param[in] Nbuf The buffer that contains part of the TCP segment without an IP header + truncated from the ICMP error packet. + @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet. + @param[in] Src Source address of the ICMP error message. + @param[in] Dst Destination address of the ICMP error message. + @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates + IP6 stack. + +**/ +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN UINT8 IcmpErr, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ) +{ + TCP_HEAD *Head; + TCP_CB *Tcb; + TCP_SEQNO Seq; + EFI_STATUS IcmpErrStatus; + BOOLEAN IcmpErrIsHard; + BOOLEAN IcmpErrNotify; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + ASSERT (Head != NULL); + + Tcb = TcpLocateTcb ( + Head->DstPort, + Dst, + Head->SrcPort, + Src, + Version, + FALSE + ); + if (Tcb == NULL || Tcb->State == TCP_CLOSED) { + + goto CLEAN_EXIT; + } + + // + // Validate the sequence number. + // + Seq = NTOHL (Head->Seq); + if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) { + + goto CLEAN_EXIT; + } + + IcmpErrStatus = IpIoGetIcmpErrStatus ( + IcmpErr, + Tcb->Sk->IpVersion, + &IcmpErrIsHard, + &IcmpErrNotify + ); + + if (IcmpErrNotify) { + + SOCK_ERROR (Tcb->Sk, IcmpErrStatus); + } + + if (IcmpErrIsHard) { + + TcpClose (Tcb); + } + +CLEAN_EXIT: + + NetbufFree (Nbuf); +} diff --git a/Core/NetworkPkg/TcpDxe/TcpIo.c b/Core/NetworkPkg/TcpDxe/TcpIo.c new file mode 100644 index 0000000000..2b99927ec5 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpIo.c @@ -0,0 +1,192 @@ +/** @file + Implementation of I/O interfaces between TCP and IpIoLib. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +/** + Packet receive callback function provided to IP_IO, used to call + the proper function to handle the packet received by IP. + + @param[in] Status Result of the receive request. + @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR. + @param[in] NetSession The IP session for the received packet. + @param[in] Pkt Packet received. + @param[in] Context The data provided by the user for the received packet when + the callback is registered in IP_IO_OPEN_DATA::RcvdContext. + This is an optional parameter that may be NULL. + +**/ +VOID +EFIAPI +TcpRxCallback ( + IN EFI_STATUS Status, + IN UINT8 IcmpErr, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Pkt, + IN VOID *Context OPTIONAL + ) +{ + if (EFI_SUCCESS == Status) { + TcpInput (Pkt, &NetSession->Source, &NetSession->Dest, NetSession->IpVersion); + } else { + TcpIcmpInput ( + Pkt, + IcmpErr, + &NetSession->Source, + &NetSession->Dest, + NetSession->IpVersion + ); + } +} + +/** + Send the segment to IP via IpIo function. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the TCP segment to be sent. + @param[in] Src Source address of the TCP segment. + @param[in] Dest Destination address of the TCP segment. + @param[in] Version IP_VERSION_4 or IP_VERSION_6 + + @retval 0 The segment was sent out successfully. + @retval -1 The segment failed to send. + +**/ +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dest, + IN UINT8 Version + ) +{ + EFI_STATUS Status; + IP_IO *IpIo; + IP_IO_OVERRIDE Override; + SOCKET *Sock; + VOID *IpSender; + TCP_PROTO_DATA *TcpProto; + + if (NULL == Tcb) { + + IpIo = NULL; + IpSender = IpIoFindSender (&IpIo, Version, Src); + + if (IpSender == NULL) { + DEBUG ((EFI_D_WARN, "TcpSendIpPacket: No appropriate IpSender.\n")); + return -1; + } + + if (Version == IP_VERSION_6) { + // + // It's tricky here. EFI IPv6 Spec don't allow an instance overriding the + // destination address if the dest is already specified through the + // configuration data. Here we get the IpIo we need and use the default IP + // instance in this IpIo to send the packet. The dest address is configured + // to be the unspecified address for the default IP instance. + // + IpSender = NULL; + } + } else { + + Sock = Tcb->Sk; + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + IpIo = TcpProto->TcpService->IpIo; + IpSender = Tcb->IpInfo; + + if (Version == IP_VERSION_6) { + // + // It's IPv6 and this TCP segment belongs to a solid TCB, in such case + // the destination address can't be overridden, so reset the Dest to NULL. + // + if (!Tcb->RemoteIpZero) { + Dest = NULL; + } + } + } + + ASSERT (Version == IpIo->IpVersion); + + if (Version == IP_VERSION_4) { + Override.Ip4OverrideData.TypeOfService = 0; + Override.Ip4OverrideData.TimeToLive = 255; + Override.Ip4OverrideData.DoNotFragment = FALSE; + Override.Ip4OverrideData.Protocol = EFI_IP_PROTO_TCP; + ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Override.Ip4OverrideData.SourceAddress, Src, sizeof (EFI_IPv4_ADDRESS)); + } else { + Override.Ip6OverrideData.Protocol = EFI_IP_PROTO_TCP; + Override.Ip6OverrideData.HopLimit = 255; + Override.Ip6OverrideData.FlowLabel = 0; + } + + Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TcpSendIpPacket: return %r error\n", Status)); + return -1; + } + + return 0; +} + +/** + Refresh the remote peer's Neighbor Cache State if already exists. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Neighbor Source address of the TCP segment. + @param[in] Timeout Time in 100-ns units that this entry will remain + in the neighbor cache. A value of zero means that + the entry is permanent. A value of non-zero means + that the entry is dynamic and will be deleted + after Timeout. + + @retval EFI_SUCCESS Successfully updated the neighbor relationship. + @retval EFI_NOT_STARTED The IpIo is not configured. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_NOT_FOUND This entry is not in the neighbor table. + +**/ +EFI_STATUS +Tcp6RefreshNeighbor ( + IN TCP_CB *Tcb, + IN EFI_IP_ADDRESS *Neighbor, + IN UINT32 Timeout + ) +{ + IP_IO *IpIo; + SOCKET *Sock; + TCP_PROTO_DATA *TcpProto; + + if (NULL == Tcb) { + IpIo = NULL; + IpIoFindSender (&IpIo, IP_VERSION_6, Neighbor); + + if (IpIo == NULL) { + DEBUG ((EFI_D_WARN, "Tcp6AddNeighbor: No appropriate IpIo.\n")); + return EFI_NOT_STARTED; + } + + } else { + Sock = Tcb->Sk; + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + IpIo = TcpProto->TcpService->IpIo; + } + + return IpIoRefreshNeighbor (IpIo, Neighbor, Timeout); +} + diff --git a/Core/NetworkPkg/TcpDxe/TcpMain.c b/Core/NetworkPkg/TcpDxe/TcpMain.c new file mode 100644 index 0000000000..f79db48af3 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpMain.c @@ -0,0 +1,1075 @@ +/** @file + Implementation of EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +/** + Check the integrity of the data buffer. + + @param[in] DataLen The total length of the data buffer. + @param[in] FragmentCount The fragment count of the fragment table. + @param[in] FragmentTable Pointer to the fragment table of the data + buffer. + + @retval EFI_SUCCESS The integrity check passed. + @retval EFI_INVALID_PARAMETER The integrity check failed. + +**/ +EFI_STATUS +TcpChkDataBuf ( + IN UINT32 DataLen, + IN UINT32 FragmentCount, + IN EFI_TCP4_FRAGMENT_DATA *FragmentTable + ) +{ + UINT32 Index; + + UINT32 Len; + + for (Index = 0, Len = 0; Index < FragmentCount; Index++) { + Len = Len + FragmentTable[Index].FragmentLength; + } + + if (DataLen != Len) { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Get the current operational status. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[out] Tcp4State Pointer to the buffer to receive the current TCP + state. Optional parameter that may be NULL. + @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP + configuration. Optional parameter that may be NULL. + @param[out] Ip4ModeData Pointer to the buffer to receive the current + IPv4 configuration. Optional parameter that may be NULL. + @param[out] MnpConfigData Pointer to the buffer to receive the current MNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + @param[out] SnpModeData Pointer to the buffer to receive the current SNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp4GetModeData ( + IN EFI_TCP4_PROTOCOL *This, + OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL, + OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + TCP4_MODE_DATA TcpMode; + SOCKET *Sock; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + TcpMode.Tcp4State = Tcp4State; + TcpMode.Tcp4ConfigData = Tcp4ConfigData; + TcpMode.Ip4ModeData = Ip4ModeData; + TcpMode.MnpConfigData = MnpConfigData; + TcpMode.SnpModeData = SnpModeData; + + return SockGetMode (Sock, &TcpMode); +} + +/** + Initialize or brutally reset the operational parameters for + this EFI TCPv4 instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] TcpConfigData Pointer to the configure data to configure the + instance. Optional parameter that may be NULL. + + @retval EFI_SUCCESS The operational settings were set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED Configuring TCP instance when it is already + configured. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + +**/ +EFI_STATUS +EFIAPI +Tcp4Configure ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL + ) +{ + EFI_TCP4_OPTION *Option; + SOCKET *Sock; + EFI_STATUS Status; + IP4_ADDR Ip; + IP4_ADDR SubnetMask; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + // + // Tcp protocol related parameter check will be conducted here + // + if (NULL != TcpConfigData) { + + CopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR)); + if ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) { + return EFI_INVALID_PARAMETER; + } + + if (TcpConfigData->AccessPoint.ActiveFlag && (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) { + return EFI_INVALID_PARAMETER; + } + + if (!TcpConfigData->AccessPoint.UseDefaultAddress) { + + CopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR)); + CopyMem (&SubnetMask, &TcpConfigData->AccessPoint.SubnetMask, sizeof (IP4_ADDR)); + if (!NetIp4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (SubnetMask))) { + return EFI_INVALID_PARAMETER; + } + } + + Option = TcpConfigData->ControlOption; + if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) { + return EFI_UNSUPPORTED; + } + } + + Sock = SOCK_FROM_THIS (This); + + if (NULL == TcpConfigData) { + return SockFlush (Sock); + } + + Status = SockConfigure (Sock, TcpConfigData); + + if (EFI_NO_MAPPING == Status) { + Sock->ConfigureState = SO_NO_MAPPING; + } + + return Status; +} + +/** + Add or delete routing entries. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] DeleteRoute If TRUE, delete the specified route from routing + table; if FALSE, add the specified route to + routing table. + @param[in] SubnetAddress The destination network. + @param[in] SubnetMask The subnet mask for the destination network. + @param[in] GatewayAddress The gateway address for this route. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the + entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table. + @retval EFI_ACCESS_DENIED This route is already in the routing table. + @retval EFI_UNSUPPORTED The TCP driver does not support this operation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Routes ( + IN EFI_TCP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ) +{ + SOCKET *Sock; + TCP4_ROUTE_INFO RouteInfo; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + RouteInfo.DeleteRoute = DeleteRoute; + RouteInfo.SubnetAddress = SubnetAddress; + RouteInfo.SubnetMask = SubnetMask; + RouteInfo.GatewayAddress = GatewayAddress; + + return SockRoute (Sock, &RouteInfo); +} + +/** + Initiate a non-blocking TCP connection request for an active TCP instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when + the TCP three way handshake finishes. + + @retval EFI_SUCCESS The connection request successfully + initiated. + @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instance is not configured as an active one, + or it is not in Tcp4StateClosed state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to + initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Connect ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockConnect (Sock, ConnectionToken); +} + +/** + Listen on the passive instance to accept an incoming connection request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when + operation finishes. + + @retval EFI_SUCCESS The listen token was queued successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not + in Tcp4StateListen state or a same listen token + has already existed in the listen token queue of + this TCP instance. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish + the operation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Accept ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_LISTEN_TOKEN *ListenToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockAccept (Sock, ListenToken); +} + +/** + Queues outgoing data into the transmit queue + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to the completion token to queue to the + transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A transmit completion token with the same + Token-> CompletionToken.Event was already in the + transmission queue. * The current instance is in + Tcp4StateClosed state. * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a + resource shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or + address. + +**/ +EFI_STATUS +EFIAPI +Tcp4Transmit ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.TxData || + 0 == Token->Packet.TxData->FragmentCount || + 0 == Token->Packet.TxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = TcpChkDataBuf ( + Token->Packet.TxData->DataLength, + Token->Packet.TxData->FragmentCount, + Token->Packet.TxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockSend (Sock, Token); +} + +/** + Place an asynchronous receive request into the receiving queue. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A receive completion token with the same + Token->CompletionToken.Event was already in the + receive queue. * The current instance is in + Tcp4StateClosed state. * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection, + and there is no any buffered data in the receive + buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp4Receive ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.RxData || + 0 == Token->Packet.RxData->FragmentCount || + 0 == Token->Packet.RxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = TcpChkDataBuf ( + Token->Packet.RxData->DataLength, + Token->Packet.RxData->FragmentCount, + Token->Packet.RxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockRcv (Sock, Token); + +} + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when + operation finishes. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: * + Configure() has been called with TcpConfigData + set to NULL, and this function has not returned. + * Previous Close() call on this instance has not + finished. + @retval EFI_INVALID_PARAMETER One ore more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the + operation. + @retval EFI_DEVICE_ERROR Any unexpected category error not belonging to those + listed above. + +**/ +EFI_STATUS +EFIAPI +Tcp4Close ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockClose (Sock, CloseToken, CloseToken->AbortOnClose); +} + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + Connect(), Accept(), Transmit() or Receive(). If + NULL, all pending tokens issued by the four + functions listed above will be aborted. + + @retval EFI_UNSUPPORTED The operation is not supported in the current + implementation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Cancel ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Poll to receive incoming data and transmit outgoing segments. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or + receive queue. Consider increasing the polling + rate. + +**/ +EFI_STATUS +EFIAPI +Tcp4Poll ( + IN EFI_TCP4_PROTOCOL *This + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL); + + return Status; +} + +/** + Get the current operational status. + + The GetModeData() function copies the current operational settings of this EFI TCPv6 + Protocol instance into user-supplied buffers. This function can also be used to retrieve + the operational setting of underlying drivers such as IPv6, MNP, or SNP. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[out] Tcp6State The buffer in which the current TCP state is + returned. Optional parameter that may be NULL. + @param[out] Tcp6ConfigData The buffer in which the current TCP configuration + is returned. Optional parameter that may be NULL. + @param[out] Ip6ModeData The buffer in which the current IPv6 configuration + data used by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] MnpConfigData The buffer in which the current MNP configuration + data indirectly used by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] SnpModeData The buffer in which the current SNP mode data + indirectly used by the TCP instance is returned. + Optional parameter that may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this instance hasn't + been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp6GetModeData ( + IN EFI_TCP6_PROTOCOL *This, + OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL, + OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + TCP6_MODE_DATA TcpMode; + SOCKET *Sock; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + TcpMode.Tcp6State = Tcp6State; + TcpMode.Tcp6ConfigData = Tcp6ConfigData; + TcpMode.Ip6ModeData = Ip6ModeData; + TcpMode.MnpConfigData = MnpConfigData; + TcpMode.SnpModeData = SnpModeData; + + return SockGetMode (Sock, &TcpMode); +} + +/** + Initialize or brutally reset the operational parameters for this EFI TCPv6 instance. + + The Configure() function does the following: + - Initialize this TCP instance, i.e., initialize the communication end settings and + specify active open or passive open for an instance. + - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush + transmission and receiving buffer directly without informing the communication peer. + + No other TCPv6 Protocol operation except Poll() can be executed by this instance until + it is configured properly. For an active TCP instance, after a proper configuration it + may call Connect() to initiate a three-way handshake. For a passive TCP instance, + its state transits to Tcp6StateListen after configuration, and Accept() may be + called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL, + the instance is reset. The resetting process will be done brutally, the state machine will + be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed, + and no traffic is allowed through this instance. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance. + If Tcp6ConfigData is set to NULL, the instance is reset. + + @retval EFI_SUCCESS The operational settings were set, changed, or reset + successfully. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for + use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE: + - This is NULL. + - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor + one of the configured IP addresses in the underlying IPv6 driver. + - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast + IPv6 address. + - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or + Tcp6ConfigData->AccessPoint.RemotePort is zero when + Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE. + - A same access point has been configured in other TCP + instance properly. + @retval EFI_ACCESS_DENIED Configuring a TCP instance when it is configured without + calling Configure() with NULL to reset it. + @retval EFI_UNSUPPORTED One or more of the control options are not supported in + the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Configure ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL + ) +{ + EFI_TCP6_OPTION *Option; + SOCKET *Sock; + EFI_STATUS Status; + EFI_IPv6_ADDRESS *Ip; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + // + // Tcp protocol related parameter check will be conducted here + // + if (NULL != Tcp6ConfigData) { + + Ip = &Tcp6ConfigData->AccessPoint.RemoteAddress; + if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) { + return EFI_INVALID_PARAMETER; + } + + if (Tcp6ConfigData->AccessPoint.ActiveFlag && + (0 == Tcp6ConfigData->AccessPoint.RemotePort || NetIp6IsUnspecifiedAddr (Ip)) + ) { + return EFI_INVALID_PARAMETER; + } + + Ip = &Tcp6ConfigData->AccessPoint.StationAddress; + if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) { + return EFI_INVALID_PARAMETER; + } + + Option = Tcp6ConfigData->ControlOption; + if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) { + return EFI_UNSUPPORTED; + } + } + + Sock = SOCK_FROM_THIS (This); + + if (NULL == Tcp6ConfigData) { + return SockFlush (Sock); + } + + Status = SockConfigure (Sock, Tcp6ConfigData); + + if (EFI_NO_MAPPING == Status) { + Sock->ConfigureState = SO_NO_MAPPING; + } + + return Status; +} + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + The Connect() function will initiate an active open to the remote peer configured + in a current TCP instance if it is configured active. If the connection succeeds or + fails due to any error, the ConnectionToken->CompletionToken.Event will be signaled + and ConnectionToken->CompletionToken.Status will be updated accordingly. This + function can only be called for the TCP instance in the Tcp6StateClosed state. The + instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS. + If a TCP three-way handshake succeeds, its state will become Tcp6StateEstablished. + Otherwise, the state will return to Tcp6StateClosed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when the TCP three + way handshake finishes. + + @retval EFI_SUCCESS The connection request successfully initiated and the state of + this TCP instance has been changed to Tcp6StateSynSent. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - This instance is not configured as an active one. + - This instance is not in Tcp6StateClosed state. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ConnectionToken is NULL. + - ConnectionToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Connect ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONNECTION_TOKEN *ConnectionToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == ConnectionToken || NULL == ConnectionToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockConnect (Sock, ConnectionToken); +} + +/** + Listen on the passive instance to accept an incoming connection request. This is a + nonblocking operation. + + The Accept() function initiates an asynchronous accept request to wait for an incoming + connection on the passive TCP instance. If a remote peer successfully establishes a + connection with this instance, a new TCP instance will be created and its handle will + be returned in ListenToken->NewChildHandle. The newly created instance is configured + by inheriting the passive instance's configuration and is ready for use upon return. + The new instance is in the Tcp6StateEstablished state. + + The ListenToken->CompletionToken.Event will be signaled when a new connection is + accepted, when a user aborts the listen or when a connection is reset. + + This function only can be called when a current TCP instance is in Tcp6StateListen state. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when operation finishes. + + + @retval EFI_SUCCESS The listen token queued successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - This instance is not a passive instance. + - This instance is not in Tcp6StateListen state. + - The same listen token has already existed in the listen + token queue of this TCP instance. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ListenToken is NULL. + - ListentToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to a category listed above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Accept ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_LISTEN_TOKEN *ListenToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == ListenToken || NULL == ListenToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockAccept (Sock, ListenToken); +} + +/** + Queues outgoing data into the transmit queue. + + The Transmit() function queues a sending request to this TCP instance along with the + user data. The status of the token is updated and the event in the token will be + signaled once the data is sent out or an error occurs. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to the completion token to queue to the transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a + source address for this instance, but no source address was + available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.TxData is NULL. + - Token->Packet.FragmentCount is zero. + - Token->Packet.DataLength is not equal to the sum of fragment lengths. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - A transmit completion token with the same Token-> + CompletionToken.Event was already in the + transmission queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - User has called Close() to disconnect this connection. + @retval EFI_NOT_READY The completion token could not be queued because the + transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of resource + shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address. + +**/ +EFI_STATUS +EFIAPI +Tcp6Transmit ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.TxData || + 0 == Token->Packet.TxData->FragmentCount || + 0 == Token->Packet.TxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = TcpChkDataBuf ( + Token->Packet.TxData->DataLength, + Token->Packet.TxData->FragmentCount, + (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.TxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockSend (Sock, Token); +} + +/** + Places an asynchronous receive request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. This + function is always asynchronous. The caller must allocate the Token->CompletionToken.Event + and the FragmentBuffer used to receive data. The caller also must fill the DataLength that + represents the whole length of all FragmentBuffer. When the receive operation completes, the + EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData + fields, and the Token->CompletionToken.Event is signaled. If data obtained, the data and its length + will be copied into the FragmentTable; at the same time the full length of received data will + be recorded in the DataLength fields. Providing a proper notification function and context + for the event enables the user to receive the notification and receiving status. That + notification function is guaranteed to not be re-entered. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the receive data + descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.RxData is NULL. + - Token->Packet.RxData->DataLength is 0. + - The Token->Packet.RxData->DataLength is not the + sum of all FragmentBuffer length in FragmentTable. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of + system resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI TCPv6 Protocol instance has been reset to startup defaults. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + - A receive completion token with the same Token->CompletionToken.Event + was already in the receive queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - User has called Close() to disconnect this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection and there is no + buffered data in the receive buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp6Receive ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.RxData || + 0 == Token->Packet.RxData->FragmentCount || + 0 == Token->Packet.RxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = TcpChkDataBuf ( + Token->Packet.RxData->DataLength, + Token->Packet.RxData->FragmentCount, + (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.RxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockRcv (Sock, Token); +} + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a + nonblocking operation. + + Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered + transmission data will be sent by the TCP driver, and the current instance will have a graceful close + working flow described as RFC 793 if AbortOnClose is set to FALSE. Otherwise, a rest packet + will be sent by TCP driver to fast disconnect this connection. When the close operation completes + successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous + operations are signaled, and any buffers used for TCP network traffic are flushed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when operation finishes. + + @retval EFI_SUCCESS The Close() was called successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - CloseToken or CloseToken->CompletionToken.Event is already in use. + - Previous Close() call on this instance has not finished. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - CloseToken is NULL. + - CloseToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to error categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Close ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CLOSE_TOKEN *CloseToken + ) +{ + SOCKET *Sock; + + if (NULL == This || NULL == CloseToken || NULL == CloseToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockClose (Sock, CloseToken, CloseToken->AbortOnClose); +} + +/** + Abort an asynchronous connection, listen, transmission, or receive request. + + The Cancel() function aborts a pending connection, listen, transmit, or + receive request. + + If Token is not NULL and the token is in the connection, listen, transmission, + or receive queue when it is being cancelled, its Token->Status will be set + to EFI_ABORTED, and then Token->Event will be signaled. + + If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. + + If Token is NULL all asynchronous token issued by Connect(), Accept(), + Transmit(), and Receive() will be aborted. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_TCP6_PROTOCOL.Connect(), + EFI_TCP6_PROTOCOL.Accept(), + EFI_TCP6_PROTOCOL.Transmit() or + EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending + tokens issued by above four functions will be aborted. Type + EFI_TCP6_COMPLETION_TOKEN is defined in + EFI_TCP_PROTOCOL.Connect(). + + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +EFI_STATUS +EFIAPI +Tcp6Cancel ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Poll to receive incoming data and transmit outgoing segments. + + The Poll() function increases the rate that data is moved between the network + and application, and can be called when the TCP instance is created successfully. + Its use is optional. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Tcp6Poll ( + IN EFI_TCP6_PROTOCOL *This + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL); + + return Status; +} + diff --git a/Core/NetworkPkg/TcpDxe/TcpMain.h b/Core/NetworkPkg/TcpDxe/TcpMain.h new file mode 100644 index 0000000000..bd4434e26b --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpMain.h @@ -0,0 +1,766 @@ +/** @file + Declaration of protocol interfaces in EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL. + It is the common head file for all Tcp*.c in TCP driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _TCP_MAIN_H_ +#define _TCP_MAIN_H_ + +#include +#include +#include +#include +#include + +#include "Socket.h" +#include "TcpProto.h" +#include "TcpDriver.h" +#include "TcpFunc.h" + +extern UINT16 mTcp4RandomPort; +extern UINT16 mTcp6RandomPort; +extern CHAR16 *mTcpStateName[]; +extern EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2; +extern EFI_UNICODE_STRING_TABLE *gTcpControllerNameTable; + +extern LIST_ENTRY mTcpRunQue; +extern LIST_ENTRY mTcpListenQue; +extern TCP_SEQNO mTcpGlobalIss; +extern UINT32 mTcpTick; + +/// +/// 30 seconds. +/// +#define TCP6_KEEP_NEIGHBOR_TIME 30 +/// +/// 5 seconds, since 1 tick equals 200ms. +/// +#define TCP6_REFRESH_NEIGHBOR_TICK 25 + +#define TCP_EXPIRE_TIME 65535 + +/// +/// The implementation selects the initial send sequence number and the unit to +/// be added when it is increased. +/// +#define TCP_BASE_ISS 0x4d7e980b +#define TCP_ISS_INCREMENT_1 2048 +#define TCP_ISS_INCREMENT_2 100 + +typedef union { + EFI_TCP4_CONFIG_DATA Tcp4CfgData; + EFI_TCP6_CONFIG_DATA Tcp6CfgData; +} TCP_CONFIG_DATA; + +typedef union { + EFI_TCP4_ACCESS_POINT Tcp4Ap; + EFI_TCP6_ACCESS_POINT Tcp6Ap; +} TCP_ACCESS_POINT; + +typedef struct _TCP4_MODE_DATA { + EFI_TCP4_CONNECTION_STATE *Tcp4State; + EFI_TCP4_CONFIG_DATA *Tcp4ConfigData; + EFI_IP4_MODE_DATA *Ip4ModeData; + EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData; + EFI_SIMPLE_NETWORK_MODE *SnpModeData; +} TCP4_MODE_DATA; + +typedef struct _TCP6_MODE_DATA { + EFI_TCP6_CONNECTION_STATE *Tcp6State; + EFI_TCP6_CONFIG_DATA *Tcp6ConfigData; + EFI_IP6_MODE_DATA *Ip6ModeData; + EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData; + EFI_SIMPLE_NETWORK_MODE *SnpModeData; +} TCP6_MODE_DATA; + +typedef struct _TCP4_ROUTE_INFO { + BOOLEAN DeleteRoute; + EFI_IPv4_ADDRESS *SubnetAddress; + EFI_IPv4_ADDRESS *SubnetMask; + EFI_IPv4_ADDRESS *GatewayAddress; +} TCP4_ROUTE_INFO; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +} TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +// +// EFI_TCP4_PROTOCOL definitions. +// + +/** + Get the current operational status. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[out] Tcp4State Pointer to the buffer to receive the current TCP + state. Optional parameter that may be NULL. + @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP + configuration. Optional parameter that may be NULL. + @param[out] Ip4ModeData Pointer to the buffer to receive the current + IPv4 configuration. Optional parameter that may be NULL. + @param[out] MnpConfigData Pointer to the buffer to receive the current MNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + @param[out] SnpModeData Pointer to the buffer to receive the current SNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp4GetModeData ( + IN EFI_TCP4_PROTOCOL *This, + OUT EFI_TCP4_CONNECTION_STATE *Tcp4State OPTIONAL, + OUT EFI_TCP4_CONFIG_DATA *Tcp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Initialize or brutally reset the operational parameters for + this EFI TCPv4 instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] TcpConfigData Pointer to the configure data to configure the + instance. Optional parameter that may be NULL. + + @retval EFI_SUCCESS The operational settings are set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED Configuring the TCP instance when it is already + configured. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + +**/ +EFI_STATUS +EFIAPI +Tcp4Configure ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL + ); + +/** + Add or delete routing entries. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] DeleteRoute If TRUE, delete the specified route from routing + table; if FALSE, add the specified route to + routing table. + @param[in] SubnetAddress The destination network. + @param[in] SubnetMask The subnet mask for the destination network. + @param[in] GatewayAddress The gateway address for this route. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the + entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table. + @retval EFI_ACCESS_DENIED This route is already in the routing table. + @retval EFI_UNSUPPORTED The TCP driver does not support this operation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Routes ( + IN EFI_TCP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ); + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when + the TCP three way handshake finishes. + + @retval EFI_SUCCESS The connection request is successfully + initiated. + @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instance is not configured as an active one + or it is not in Tcp4StateClosed state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to + initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Connect ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken + ); + +/** + Listen on the passive instance to accept an incoming connection request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when + operation finishes. + + @retval EFI_SUCCESS The listen token has been queued successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not + in Tcp4StateListen state, or a same listen token + has already existed in the listen token queue of + this TCP instance. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish + the operation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Accept ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_LISTEN_TOKEN *ListenToken + ); + +/** + Queues outgoing data into the transmit queue + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance + @param[in] Token Pointer to the completion token to queue to the + transmit queue + + @retval EFI_SUCCESS The data has been queued for transmission + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A transmit completion token with the same + Token-> CompletionToken.Event was already in the + transmission queue. * The current instance is in + Tcp4StateClosed state * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a + resource shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or + address. + +**/ +EFI_STATUS +EFIAPI +Tcp4Transmit ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + +/** + Place an asynchronous receive request into the receiving queue. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A receive completion token with the same + Token->CompletionToken.Event was already in the + receive queue. * The current instance is in + Tcp4StateClosed state. * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection + and there is no buffered data in the receive + buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp4Receive ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when + operation finishes. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: * + Configure() has been called with TcpConfigData + set to NULL and this function has not returned. + * Previous Close() call on this instance has not + finished. + @retval EFI_INVALID_PARAMETER One ore more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the + operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error + categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp4Close ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ); + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + Connect(), Accept(), Transmit() or Receive(). If + NULL, all pending tokens issued by the above four + functions will be aborted. + + @retval EFI_UNSUPPORTED The operation is not supported in the current + implementation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Cancel ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Poll to receive incoming data and transmit outgoing segments. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or + receive queue. Consider increasing the polling + rate. + +**/ +EFI_STATUS +EFIAPI +Tcp4Poll ( + IN EFI_TCP4_PROTOCOL *This + ); + +// +// EFI_TCP6_PROTOCOL definitions. +// + +/** + Get the current operational status. + + The GetModeData() function copies the current operational settings of this EFI TCPv6 + Protocol instance into user-supplied buffers. This function can also be used to retrieve + the operational setting of underlying drivers such as IPv6, MNP, or SNP. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[out] Tcp6State The buffer in which the current TCP state is + returned. Optional parameter that may be NULL. + @param[out] Tcp6ConfigData The buffer in which the current TCP configuration + is returned. Optional parameter that may be NULL. + @param[out] Ip6ModeData The buffer in which the current IPv6 configuration + data used by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] MnpConfigData The buffer in which the current MNP configuration + data used indirectly by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] SnpModeData The buffer in which the current SNP mode data + used indirectly by the TCP instance is returned. + Optional parameter that may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this instance hasn't + been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp6GetModeData ( + IN EFI_TCP6_PROTOCOL *This, + OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL, + OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Initialize or brutally reset the operational parameters for this EFI TCPv6 instance. + + The Configure() function does the following: + - Initialize this TCP instance, i.e., initialize the communication end settings and + specify active open or passive open for an instance. + - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush + transmission and receiving buffer directly without informing the communication peer. + + No other TCPv6 Protocol operation except Poll() can be executed by this instance until + it is configured properly. For an active TCP instance, after a proper configuration it + may call Connect() to initiates the three-way handshake. For a passive TCP instance, + its state will transit to Tcp6StateListen after configuration, and Accept() may be + called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL, + the instance is reset. Resetting process will be done brutally, the state machine will + be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed, + and no traffic is allowed through this instance. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance. + If Tcp6ConfigData is set to NULL, the instance is reset. + + @retval EFI_SUCCESS The operational settings were set, changed, or reset + successfully. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for + use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE: + - This is NULL. + - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor + one of the configured IP addresses in the underlying IPv6 driver. + - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast + IPv6 address. + - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or + Tcp6ConfigData->AccessPoint.RemotePort is zero when + Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE. + - A same access point has been configured in other TCP + instance properly. + @retval EFI_ACCESS_DENIED Configuring TCP instance when it is configured without + calling Configure() with NULL to reset it. + @retval EFI_UNSUPPORTED One or more of the control options are not supported in + the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Configure ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL + ); + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + The Connect() function will initiate an active open to the remote peer configured + in current TCP instance if it is configured active. If the connection succeeds or + fails due to an error, the ConnectionToken->CompletionToken.Event will be signaled, + and ConnectionToken->CompletionToken.Status will be updated accordingly. This + function can only be called for the TCP instance in Tcp6StateClosed state. The + instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS. + If TCP three-way handshake succeeds, its state will become Tcp6StateEstablished; + otherwise, the state will return to Tcp6StateClosed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when the TCP + three-way handshake finishes. + + @retval EFI_SUCCESS The connection request successfully initiated and the state of + this TCP instance has been changed to Tcp6StateSynSent. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - This instance is not configured as an active instance. + - This instance is not in Tcp6StateClosed state. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ConnectionToken is NULL. + - ConnectionToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Connect ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONNECTION_TOKEN *ConnectionToken + ); + +/** + Listen on the passive instance to accept an incoming connection request. This is a + nonblocking operation. + + The Accept() function initiates an asynchronous accept request to wait for an incoming + connection on the passive TCP instance. If a remote peer successfully establishes a + connection with this instance, a new TCP instance will be created and its handle will + be returned in ListenToken->NewChildHandle. The newly created instance is configured + by inheriting the passive instance's configuration, and is ready for use upon return. + The new instance is in the Tcp6StateEstablished state. + + The ListenToken->CompletionToken.Event will be signaled when a new connection is + accepted, user aborts the listen or connection is reset. + + This function only can be called when the current TCP instance is in Tcp6StateListen state. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when the operation finishes. + + + @retval EFI_SUCCESS The listen token was been queued successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - This instance is not a passive instance. + - This instance is not in Tcp6StateListen state. + - The same listen token has already existed in the listen + token queue of this TCP instance. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ListenToken is NULL. + - ListentToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error + categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Accept ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_LISTEN_TOKEN *ListenToken + ); + +/** + Queues outgoing data into the transmit queue. + + The Transmit() function queues a sending request to this TCP instance along with the + user data. The status of the token is updated and the event in the token will be + signaled once the data is sent out or some error occurs. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to the completion token to queue to the transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a + source address for this instance, but no source address was + available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.TxData is NULL. + - Token->Packet.FragmentCount is zero. + - Token->Packet.DataLength is not equal to the sum of fragment lengths. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - A transmit completion token with the same Token-> + CompletionToken.Event was already in the + transmission queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - User has called Close() to disconnect this connection. + @retval EFI_NOT_READY The completion token could not be queued because the + transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of a resource + shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address. + +**/ +EFI_STATUS +EFIAPI +Tcp6Transmit ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ); + +/** + Places an asynchronous receive request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. This + function is always asynchronous. The caller must allocate the Token->CompletionToken.Event + and the FragmentBuffer used to receive data. The caller also must fill the DataLength, which + represents the whole length of all FragmentBuffer. When the receive operation completes, the + EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData + fields, and the Token->CompletionToken.Event is signaled. If data is obtained, the data and its length + will be copied into the FragmentTable. At the same time the full length of received data will + be recorded in the DataLength fields. Providing a proper notification function and context + for the event enables the user to receive the notification and receiving status. That + notification function is guaranteed to not be re-entered. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the receive data + descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.RxData is NULL. + - Token->Packet.RxData->DataLength is 0. + - The Token->Packet.RxData->DataLength is not the + sum of all FragmentBuffer length in FragmentTable. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of + system resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI TCPv6 Protocol instance has been reset to startup defaults. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + - A receive completion token with the same Token->CompletionToken.Event + was already in the receive queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - The user has called Close() to disconnect this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection, and there is no + buffered data in the receive buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp6Receive ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ); + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a + nonblocking operation. + + Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered + transmission data will be sent by the TCP driver, and the current instance will have a graceful close + working flow described as RFC 793 if AbortOnClose is set to FALSE, otherwise, a rest packet + will be sent by TCP driver to fast disconnect this connection. When the close operation completes + successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous + operations are signaled, and any buffers used for TCP network traffic are flushed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when operation finishes. + + @retval EFI_SUCCESS The Close() was called successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - CloseToken or CloseToken->CompletionToken.Event is already in use. + - Previous Close() call on this instance has not finished. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - CloseToken is NULL. + - CloseToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Close ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CLOSE_TOKEN *CloseToken + ); + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + The Cancel() function aborts a pending connection, listen, transmit or + receive request. + + If Token is not NULL and the token is in the connection, listen, transmission + or receive queue when it is being cancelled, its Token->Status will be set + to EFI_ABORTED and then Token->Event will be signaled. + + If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. + + If Token is NULL all asynchronous token issued by Connect(), Accept(), + Transmit() and Receive() will be aborted. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_TCP6_PROTOCOL.Connect(), + EFI_TCP6_PROTOCOL.Accept(), + EFI_TCP6_PROTOCOL.Transmit() or + EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending + tokens issued by above four functions will be aborted. Type + EFI_TCP6_COMPLETION_TOKEN is defined in + EFI_TCP_PROTOCOL.Connect(). + + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +EFI_STATUS +EFIAPI +Tcp6Cancel ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Poll to receive incoming data and transmit outgoing segments. + + The Poll() function increases the rate that data is moved between the network + and application and can be called when the TCP instance is created successfully. + Its use is optional. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Tcp6Poll ( + IN EFI_TCP6_PROTOCOL *This + ); + +#endif diff --git a/Core/NetworkPkg/TcpDxe/TcpMisc.c b/Core/NetworkPkg/TcpDxe/TcpMisc.c new file mode 100644 index 0000000000..4096252888 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpMisc.c @@ -0,0 +1,1023 @@ +/** @file + Misc support routines for TCP driver. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +LIST_ENTRY mTcpRunQue = { + &mTcpRunQue, + &mTcpRunQue +}; + +LIST_ENTRY mTcpListenQue = { + &mTcpListenQue, + &mTcpListenQue +}; + +TCP_SEQNO mTcpGlobalIss = TCP_BASE_ISS; + +CHAR16 *mTcpStateName[] = { + L"TCP_CLOSED", + L"TCP_LISTEN", + L"TCP_SYN_SENT", + L"TCP_SYN_RCVD", + L"TCP_ESTABLISHED", + L"TCP_FIN_WAIT_1", + L"TCP_FIN_WAIT_2", + L"TCP_CLOSING", + L"TCP_TIME_WAIT", + L"TCP_CLOSE_WAIT", + L"TCP_LAST_ACK" +}; + + +/** + Initialize the Tcb local related members. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpInitTcbLocal ( + IN OUT TCP_CB *Tcb + ) +{ + // + // Compute the checksum of the fixed parts of pseudo header + // + if (Tcb->Sk->IpVersion == IP_VERSION_4) { + Tcb->HeadSum = NetPseudoHeadChecksum ( + Tcb->LocalEnd.Ip.Addr[0], + Tcb->RemoteEnd.Ip.Addr[0], + 0x06, + 0 + ); + } else { + Tcb->HeadSum = NetIp6PseudoHeadChecksum ( + &Tcb->LocalEnd.Ip.v6, + &Tcb->RemoteEnd.Ip.v6, + 0x06, + 0 + ); + } + + Tcb->Iss = TcpGetIss (); + Tcb->SndUna = Tcb->Iss; + Tcb->SndNxt = Tcb->Iss; + + Tcb->SndWl2 = Tcb->Iss; + Tcb->SndWnd = 536; + + Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk); + + // + // First window size is never scaled + // + Tcb->RcvWndScale = 0; + + Tcb->ProbeTimerOn = FALSE; +} + +/** + Initialize the peer related members. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Pointer to the segment that contains the peer's intial info. + @param[in] Opt Pointer to the options announced by the peer. + +**/ +VOID +TcpInitTcbPeer ( + IN OUT TCP_CB *Tcb, + IN TCP_SEG *Seg, + IN TCP_OPTION *Opt + ) +{ + UINT16 RcvMss; + + ASSERT ((Tcb != NULL) && (Seg != NULL) && (Opt != NULL)); + ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)); + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = Tcb->SndWnd; + Tcb->SndWl1 = Seg->Seq; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + Tcb->SndWl2 = Seg->Ack; + } else { + Tcb->SndWl2 = Tcb->Iss + 1; + } + + if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) { + Tcb->SndMss = (UINT16) MAX (64, Opt->Mss); + + RcvMss = TcpGetRcvMss (Tcb->Sk); + if (Tcb->SndMss > RcvMss) { + Tcb->SndMss = RcvMss; + } + + } else { + // + // One end doesn't support MSS option, use default. + // + Tcb->RcvMss = 536; + } + + Tcb->CWnd = Tcb->SndMss; + + Tcb->Irs = Seg->Seq; + Tcb->RcvNxt = Tcb->Irs + 1; + + Tcb->RcvWl2 = Tcb->RcvNxt; + + if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) { + + Tcb->SndWndScale = Opt->WndScale; + + Tcb->RcvWndScale = TcpComputeScale (Tcb); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS); + + } else { + // + // One end doesn't support window scale option. use zero. + // + Tcb->RcvWndScale = 0; + } + + if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS); + + Tcb->TsRecent = Opt->TSVal; + + // + // Compute the effective SndMss per RFC1122 + // section 4.2.2.6. If timestamp option is + // enabled, it will always occupy 12 bytes. + // + Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN; + } +} + +/** + Check whether one IP address equals the other. + + @param[in] Ip1 Pointer to IP address to be checked. + @param[in] Ip2 Pointer to IP address to be checked. + @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address, + IP_VERSION_6 indicates the IP address is an IPv6 address. + + @retval TRUE Ip1 equals Ip2. + @retval FALSE Ip1 does not equal Ip2. + +**/ +BOOLEAN +TcpIsIpEqual ( + IN EFI_IP_ADDRESS *Ip1, + IN EFI_IP_ADDRESS *Ip2, + IN UINT8 Version + ) +{ + ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6)); + + if (Version == IP_VERSION_4) { + return (BOOLEAN) (Ip1->Addr[0] == Ip2->Addr[0]); + } else { + return (BOOLEAN) EFI_IP6_EQUAL (&Ip1->v6, &Ip2->v6); + } +} + +/** + Check whether one IP address is filled with ZERO. + + @param[in] Ip Pointer to the IP address to be checked. + @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address, + IP_VERSION_6 indicates the IP address is an IPv6 address. + + @retval TRUE Ip is all zero address. + @retval FALSE Ip is not all zero address. + +**/ +BOOLEAN +TcpIsIpZero ( + IN EFI_IP_ADDRESS *Ip, + IN UINT8 Version + ) +{ + ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6)); + + if (Version == IP_VERSION_4) { + return (BOOLEAN) (Ip->Addr[0] == 0); + } else { + return (BOOLEAN) ((Ip->Addr[0] == 0) && (Ip->Addr[1] == 0) && + (Ip->Addr[2] == 0) && (Ip->Addr[3] == 0)); + } +} + +/** + Locate a listen TCB that matchs the Local and Remote. + + @param[in] Local Pointer to the local (IP, Port). + @param[in] Remote Pointer to the remote (IP, Port). + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + + @return Pointer to the TCP_CB with the least number of wildcards, + if NULL no match is found. + +**/ +TCP_CB * +TcpLocateListenTcb ( + IN TCP_PEER *Local, + IN TCP_PEER *Remote, + IN UINT8 Version + ) +{ + LIST_ENTRY *Entry; + TCP_CB *Node; + TCP_CB *Match; + INTN Last; + INTN Cur; + + Last = 4; + Match = NULL; + + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((Version != Node->Sk->IpVersion) || + (Local->Port != Node->LocalEnd.Port) || + !TCP_PEER_MATCH (Remote, &Node->RemoteEnd, Version) || + !TCP_PEER_MATCH (Local, &Node->LocalEnd, Version) + ) { + + continue; + } + + // + // Compute the number of wildcard + // + Cur = 0; + if (TcpIsIpZero (&Node->RemoteEnd.Ip, Version)) { + Cur++; + } + + if (Node->RemoteEnd.Port == 0) { + Cur++; + } + + if (TcpIsIpZero (&Node->LocalEnd.Ip, Version)) { + Cur++; + } + + if (Cur < Last) { + if (Cur == 0) { + return Node; + } + + Last = Cur; + Match = Node; + } + } + + return Match; +} + +/** + Try to find one Tcb whose equals to . + + @param[in] Addr Pointer to the IP address needs to match. + @param[in] Port The port number needs to match. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + + + @retval TRUE The Tcb which matches the pair exists. + @retval FALSE Otherwise + +**/ +BOOLEAN +TcpFindTcbByPeer ( + IN EFI_IP_ADDRESS *Addr, + IN TCP_PORTNO Port, + IN UINT8 Version + ) +{ + TCP_PORTNO LocalPort; + LIST_ENTRY *Entry; + TCP_CB *Tcb; + + ASSERT ((Addr != NULL) && (Port != 0)); + + LocalPort = HTONS (Port); + + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((Version == Tcb->Sk->IpVersion) && + TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) && + (LocalPort == Tcb->LocalEnd.Port) + ) { + + return TRUE; + } + } + + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((Version == Tcb->Sk->IpVersion) && + TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) && + (LocalPort == Tcb->LocalEnd.Port) + ) { + + return TRUE; + } + } + + return FALSE; +} + +/** + Locate the TCP_CB related to the socket pair. + + @param[in] LocalPort The local port number. + @param[in] LocalIp The local IP address. + @param[in] RemotePort The remote port number. + @param[in] RemoteIp The remote IP address. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + @param[in] Syn If TRUE, the listen sockets are searched. + + @return Pointer to the related TCP_CB. If NULL, no match is found. + +**/ +TCP_CB * +TcpLocateTcb ( + IN TCP_PORTNO LocalPort, + IN EFI_IP_ADDRESS *LocalIp, + IN TCP_PORTNO RemotePort, + IN EFI_IP_ADDRESS *RemoteIp, + IN UINT8 Version, + IN BOOLEAN Syn + ) +{ + TCP_PEER Local; + TCP_PEER Remote; + LIST_ENTRY *Entry; + TCP_CB *Tcb; + + Local.Port = LocalPort; + Remote.Port = RemotePort; + + CopyMem (&Local.Ip, LocalIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Remote.Ip, RemoteIp, sizeof (EFI_IP_ADDRESS)); + + // + // First check for exact match. + // + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((Version == Tcb->Sk->IpVersion) && + TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd, Version) && + TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd, Version) + ) { + + RemoveEntryList (&Tcb->List); + InsertHeadList (&mTcpRunQue, &Tcb->List); + + return Tcb; + } + } + + // + // Only check the listen queue when the SYN flag is on. + // + if (Syn) { + return TcpLocateListenTcb (&Local, &Remote, Version); + } + + return NULL; +} + +/** + Insert a Tcb into the proper queue. + + @param[in] Tcb Pointer to the TCP_CB to be inserted. + + @retval 0 The Tcb was inserted successfully. + @retval -1 Error condition occurred. + +**/ +INTN +TcpInsertTcb ( + IN TCP_CB *Tcb + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Head; + TCP_CB *Node; + + ASSERT ( + (Tcb != NULL) && + ( + (Tcb->State == TCP_LISTEN) || + (Tcb->State == TCP_SYN_SENT) || + (Tcb->State == TCP_SYN_RCVD) || + (Tcb->State == TCP_CLOSED) + ) + ); + + if (Tcb->LocalEnd.Port == 0) { + return -1; + } + + Head = &mTcpRunQue; + + if (Tcb->State == TCP_LISTEN) { + Head = &mTcpListenQue; + } + + // + // Check that the Tcb isn't already on the list. + // + NET_LIST_FOR_EACH (Entry, Head) { + Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd, Tcb->Sk->IpVersion) && + TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd, Tcb->Sk->IpVersion) + ) { + + return -1; + } + } + + InsertHeadList (Head, &Tcb->List); + + + return 0; +} + +/** + Clone a TCP_CB from Tcb. + + @param[in] Tcb Pointer to the TCP_CB to be cloned. + + @return Pointer to the new cloned TCP_CB; if NULL, error condition occurred. + +**/ +TCP_CB * +TcpCloneTcb ( + IN TCP_CB *Tcb + ) +{ + TCP_CB *Clone; + + Clone = AllocateZeroPool (sizeof (TCP_CB)); + + if (Clone == NULL) { + return NULL; + } + + CopyMem (Clone, Tcb, sizeof (TCP_CB)); + + // + // Increase the reference count of the shared IpInfo. + // + NET_GET_REF (Tcb->IpInfo); + + InitializeListHead (&Clone->List); + InitializeListHead (&Clone->SndQue); + InitializeListHead (&Clone->RcvQue); + + Clone->Sk = SockClone (Tcb->Sk); + if (Clone->Sk == NULL) { + DEBUG ((EFI_D_ERROR, "TcpCloneTcb: failed to clone a sock\n")); + FreePool (Clone); + return NULL; + } + + ((TCP_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone; + + return Clone; +} + +/** + Compute an ISS to be used by a new connection. + + @return The resulting ISS. + +**/ +TCP_SEQNO +TcpGetIss ( + VOID + ) +{ + mTcpGlobalIss += TCP_ISS_INCREMENT_1; + return mTcpGlobalIss; +} + +/** + Get the local mss. + + @param[in] Sock Pointer to the socket to get mss. + + @return The mss size. + +**/ +UINT16 +TcpGetRcvMss ( + IN SOCKET *Sock + ) +{ + EFI_IP4_MODE_DATA Ip4Mode; + EFI_IP6_MODE_DATA Ip6Mode; + EFI_IP4_PROTOCOL *Ip4; + EFI_IP6_PROTOCOL *Ip6; + TCP_PROTO_DATA *TcpProto; + + ASSERT (Sock != NULL); + + ZeroMem (&Ip4Mode, sizeof (EFI_IP4_MODE_DATA)); + ZeroMem (&Ip6Mode, sizeof (EFI_IP6_MODE_DATA)); + + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + + if (Sock->IpVersion == IP_VERSION_4) { + Ip4 = TcpProto->TcpService->IpIo->Ip.Ip4; + ASSERT (Ip4 != NULL); + Ip4->GetModeData (Ip4, &Ip4Mode, NULL, NULL); + + return (UINT16) (Ip4Mode.MaxPacketSize - sizeof (TCP_HEAD)); + } else { + Ip6 = TcpProto->TcpService->IpIo->Ip.Ip6; + ASSERT (Ip6 != NULL); + Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL); + + return (UINT16) (Ip6Mode.MaxPacketSize - sizeof (TCP_HEAD)); + } +} + +/** + Set the Tcb's state. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] State The state to be set. + +**/ +VOID +TcpSetState ( + IN TCP_CB *Tcb, + IN UINT8 State + ) +{ + ASSERT (Tcb->State < (sizeof (mTcpStateName) / sizeof (CHAR16 *))); + ASSERT (State < (sizeof (mTcpStateName) / sizeof (CHAR16 *))); + + DEBUG ( + (EFI_D_INFO, + "Tcb (%p) state %s --> %s\n", + Tcb, + mTcpStateName[Tcb->State], + mTcpStateName[State]) + ); + + Tcb->State = State; + + switch (State) { + case TCP_ESTABLISHED: + + SockConnEstablished (Tcb->Sk); + + if (Tcb->Parent != NULL) { + // + // A new connection is accepted by a listening socket. Install + // the device path. + // + TcpInstallDevicePath (Tcb->Sk); + } + + break; + + case TCP_CLOSED: + + SockConnClosed (Tcb->Sk); + + break; + default: + break; + } +} + +/** + Compute the TCP segment's checksum. + + @param[in] Nbuf Pointer to the buffer that contains the TCP segment. + @param[in] HeadSum The checksum value of the fixed part of pseudo header. + + @return The checksum value. + +**/ +UINT16 +TcpChecksum ( + IN NET_BUF *Nbuf, + IN UINT16 HeadSum + ) +{ + UINT16 Checksum; + + Checksum = NetbufChecksum (Nbuf); + Checksum = NetAddChecksum (Checksum, HeadSum); + + Checksum = NetAddChecksum ( + Checksum, + HTONS ((UINT16) Nbuf->TotalSize) + ); + + return (UINT16) (~Checksum); +} + +/** + Translate the information from the head of the received TCP + segment Nbuf contents and fill it into a TCP_SEG structure. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] Nbuf Pointer to the buffer contains the TCP segment. + + @return Pointer to the TCP_SEG that contains the translated TCP head information. + +**/ +TCP_SEG * +TcpFormatNetbuf ( + IN TCP_CB *Tcb, + IN OUT NET_BUF *Nbuf + ) +{ + TCP_SEG *Seg; + TCP_HEAD *Head; + + Seg = TCPSEG_NETBUF (Nbuf); + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + ASSERT (Head != NULL); + + Nbuf->Tcp = Head; + + Seg->Seq = NTOHL (Head->Seq); + Seg->Ack = NTOHL (Head->Ack); + Seg->End = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2)); + + Seg->Urg = NTOHS (Head->Urg); + Seg->Wnd = (NTOHS (Head->Wnd) << Tcb->SndWndScale); + Seg->Flag = Head->Flag; + + // + // SYN and FIN flag occupy one sequence space each. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + // + // RFC requires that the initial window not be scaled. + // + Seg->Wnd = NTOHS (Head->Wnd); + Seg->End++; + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + Seg->End++; + } + + return Seg; +} + +/** + Initialize an active connection. + + @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a + connection. + +**/ +VOID +TcpOnAppConnect ( + IN OUT TCP_CB *Tcb + ) +{ + TcpInitTcbLocal (Tcb); + TcpSetState (Tcb, TCP_SYN_SENT); + + TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout); + TcpToSendData (Tcb, 1); +} + +/** + Initiate the connection close procedure, called when + applications want to close the connection. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpOnAppClose ( + IN OUT TCP_CB *Tcb + ) +{ + ASSERT (Tcb != NULL); + + if (!IsListEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk) != 0) { + + DEBUG ( + (EFI_D_WARN, + "TcpOnAppClose: connection reset because data is lost for TCB %p\n", + Tcb) + ); + + TcpResetConnection (Tcb); + TcpClose (Tcb); + return; + } + + switch (Tcb->State) { + case TCP_CLOSED: + case TCP_LISTEN: + case TCP_SYN_SENT: + TcpSetState (Tcb, TCP_CLOSED); + break; + + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + TcpSetState (Tcb, TCP_FIN_WAIT_1); + break; + + case TCP_CLOSE_WAIT: + TcpSetState (Tcb, TCP_LAST_ACK); + break; + default: + break; + } + + TcpToSendData (Tcb, 1); +} + +/** + Check whether the application's newly delivered data can be sent out. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The data has been sent out successfully. + @retval -1 The Tcb is not in a state that data is permitted to + be sent out. + +**/ +INTN +TcpOnAppSend ( + IN OUT TCP_CB *Tcb + ) +{ + + switch (Tcb->State) { + case TCP_CLOSED: + return -1; + + case TCP_LISTEN: + return -1; + + case TCP_SYN_SENT: + case TCP_SYN_RCVD: + return 0; + + case TCP_ESTABLISHED: + case TCP_CLOSE_WAIT: + TcpToSendData (Tcb, 0); + return 0; + + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + return -1; + + default: + break; + } + + return 0; +} + +/** + Application has consumed some data. Check whether + to send a window update ack or a delayed ack. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpOnAppConsume ( + IN TCP_CB *Tcb + ) +{ + UINT32 TcpOld; + + switch (Tcb->State) { + case TCP_ESTABLISHED: + TcpOld = TcpRcvWinOld (Tcb); + if (TcpRcvWinNow (Tcb) > TcpOld) { + + if (TcpOld < Tcb->RcvMss) { + + DEBUG ( + (EFI_D_INFO, + "TcpOnAppConsume: send a window update for a window closed Tcb %p\n", + Tcb) + ); + + TcpSendAck (Tcb); + } else if (Tcb->DelayedAck == 0) { + + DEBUG ( + (EFI_D_INFO, + "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n", + Tcb) + ); + + Tcb->DelayedAck = 1; + } + } + + break; + + default: + break; + } +} + +/** + Abort the connection by sending a reset segment. Called + when the application wants to abort the connection. + + @param[in] Tcb Pointer to the TCP_CB of the TCP instance. + +**/ +VOID +TcpOnAppAbort ( + IN TCP_CB *Tcb + ) +{ + DEBUG ( + (EFI_D_WARN, + "TcpOnAppAbort: connection reset issued by application for TCB %p\n", + Tcb) + ); + + switch (Tcb->State) { + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSE_WAIT: + TcpResetConnection (Tcb); + break; + default: + break; + } + + TcpSetState (Tcb, TCP_CLOSED); +} + +/** + Reset the connection related with Tcb. + + @param[in] Tcb Pointer to the TCP_CB of the connection to be reset. + +**/ +VOID +TcpResetConnection ( + IN TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_HEAD *Nhead; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return ; + } + + Nhead = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_TAIL + ); + + ASSERT (Nhead != NULL); + + Nbuf->Tcp = Nhead; + + Nhead->Flag = TCP_FLG_RST; + Nhead->Seq = HTONL (Tcb->SndNxt); + Nhead->Ack = HTONL (Tcb->RcvNxt); + Nhead->SrcPort = Tcb->LocalEnd.Port; + Nhead->DstPort = Tcb->RemoteEnd.Port; + Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2); + Nhead->Res = 0; + Nhead->Wnd = HTONS (0xFFFF); + Nhead->Checksum = 0; + Nhead->Urg = 0; + Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum); + + TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion); + + NetbufFree (Nbuf); +} + +/** + Install the device path protocol on the TCP instance. + + @param[in] Sock Pointer to the socket representing the TCP instance. + + @retval EFI_SUCCESS The device path protocol was installed. + @retval other Failed to install the device path protocol. + +**/ +EFI_STATUS +TcpInstallDevicePath ( + IN SOCKET *Sock + ) +{ + TCP_PROTO_DATA *TcpProto; + TCP_SERVICE_DATA *TcpService; + TCP_CB *Tcb; + IPv4_DEVICE_PATH Ip4DPathNode; + IPv6_DEVICE_PATH Ip6DPathNode; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_STATUS Status; + TCP_PORTNO LocalPort; + TCP_PORTNO RemotePort; + + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + TcpService = TcpProto->TcpService; + Tcb = TcpProto->TcpPcb; + + LocalPort = NTOHS (Tcb->LocalEnd.Port); + RemotePort = NTOHS (Tcb->RemoteEnd.Port); + if (Sock->IpVersion == IP_VERSION_4) { + NetLibCreateIPv4DPathNode ( + &Ip4DPathNode, + TcpService->ControllerHandle, + Tcb->LocalEnd.Ip.Addr[0], + LocalPort, + Tcb->RemoteEnd.Ip.Addr[0], + RemotePort, + EFI_IP_PROTO_TCP, + Tcb->UseDefaultAddr + ); + + IP4_COPY_ADDRESS (&Ip4DPathNode.SubnetMask, &Tcb->SubnetMask); + + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode; + } else { + NetLibCreateIPv6DPathNode ( + &Ip6DPathNode, + TcpService->ControllerHandle, + &Tcb->LocalEnd.Ip.v6, + LocalPort, + &Tcb->RemoteEnd.Ip.v6, + RemotePort, + EFI_IP_PROTO_TCP + ); + + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip6DPathNode; + } + + Sock->DevicePath = AppendDevicePathNode (Sock->ParentDevicePath, DevicePath); + if (Sock->DevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->InstallProtocolInterface ( + &Sock->SockHandle, + &gEfiDevicePathProtocolGuid, + EFI_NATIVE_INTERFACE, + Sock->DevicePath + ); + if (EFI_ERROR (Status)) { + FreePool (Sock->DevicePath); + Sock->DevicePath = NULL; + } + + return Status; +} + diff --git a/Core/NetworkPkg/TcpDxe/TcpOption.c b/Core/NetworkPkg/TcpDxe/TcpOption.c new file mode 100644 index 0000000000..bacce1070d --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpOption.c @@ -0,0 +1,374 @@ +/** @file + Routines to process TCP option. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +/** + Get a UINT16 value from buffer. + + @param[in] Buf Pointer to input buffer. + + @return The UINT16 value obtained from the buffer. + +**/ +UINT16 +TcpGetUint16 ( + IN UINT8 *Buf + ) +{ + UINT16 Value; + CopyMem (&Value, Buf, sizeof (UINT16)); + return NTOHS (Value); +} + +/** + Get a UINT32 value from buffer. + + @param[in] Buf Pointer to input buffer. + + @return The UINT32 value obtained from the buffer. + +**/ +UINT32 +TcpGetUint32 ( + IN UINT8 *Buf + ) +{ + UINT32 Value; + CopyMem (&Value, Buf, sizeof (UINT32)); + return NTOHL (Value); +} + +/** + Put a UINT32 value in buffer. + + @param[out] Buf Pointer to the buffer. + @param[in] Data The UINT32 Date to put in the buffer. + +**/ +VOID +TcpPutUint32 ( + OUT UINT8 *Buf, + IN UINT32 Data + ) +{ + Data = HTONL (Data); + CopyMem (Buf, &Data, sizeof (UINT32)); +} + +/** + Compute the window scale value according to the given buffer size. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The scale value. + +**/ +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ) +{ + UINT8 Scale; + UINT32 BufSize; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + BufSize = GET_RCV_BUFFSIZE (Tcb->Sk); + + Scale = 0; + while ((Scale < TCP_OPTION_MAX_WS) && ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) { + + Scale++; + } + + return Scale; +} + +/** + Build the TCP option in three-way handshake. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpSynBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + UINT8 *Data; + UINT16 Len; + + ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); + + Len = 0; + + // + // Add a timestamp option if not disabled by the application + // and it is the first SYN segment, or the peer has sent + // us its timestamp. + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) && + (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS)) + ) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_TS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data != NULL); + Len += TCP_OPTION_TS_ALIGNED_LEN; + + TcpPutUint32 (Data, TCP_OPTION_TS_FAST); + TcpPutUint32 (Data + 4, mTcpTick); + TcpPutUint32 (Data + 8, 0); + } + + // + // Build window scale option, only when configured + // to send WS option, and either we are doing active + // open or we have received WS option from peer. + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) && + (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS)) + ) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_WS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data != NULL); + + Len += TCP_OPTION_WS_ALIGNED_LEN; + TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb)); + } + + // + // Build the MSS option. + // + Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1); + ASSERT (Data != NULL); + + Len += TCP_OPTION_MSS_LEN; + TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss); + + return Len; +} + +/** + Build the TCP option in synchronized states. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + UINT8 *Data; + UINT16 Len; + + ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); + Len = 0; + + // + // Build the Timestamp option. + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) && + !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST) + ) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_TS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data != NULL); + Len += TCP_OPTION_TS_ALIGNED_LEN; + + TcpPutUint32 (Data, TCP_OPTION_TS_FAST); + TcpPutUint32 (Data + 4, mTcpTick); + TcpPutUint32 (Data + 8, Tcb->TsRecent); + } + + return Len; +} + +/** + Parse the supported options. + + @param[in] Tcp Pointer to the TCP_CB of this TCP instance. + @param[in, out] Option Pointer to the TCP_OPTION used to store the + successfully pasrsed options. + + @retval 0 The options are successfully pasrsed. + @retval -1 Ilegal option was found. + +**/ +INTN +TcpParseOption ( + IN TCP_HEAD *Tcp, + IN OUT TCP_OPTION *Option + ) +{ + UINT8 *Head; + UINT8 TotalLen; + UINT8 Cur; + UINT8 Type; + UINT8 Len; + + ASSERT ((Tcp != NULL) && (Option != NULL)); + + Option->Flag = 0; + + TotalLen = (UINT8) ((Tcp->HeadLen << 2) - sizeof (TCP_HEAD)); + if (TotalLen <= 0) { + return 0; + } + + Head = (UINT8 *) (Tcp + 1); + + // + // Fast process of the timestamp option. + // + if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) { + + Option->TSVal = TcpGetUint32 (Head + 4); + Option->TSEcr = TcpGetUint32 (Head + 8); + Option->Flag = TCP_OPTION_RCVD_TS; + + return 0; + } + // + // Slow path to process the options. + // + Cur = 0; + + while (Cur < TotalLen) { + Type = Head[Cur]; + + switch (Type) { + case TCP_OPTION_MSS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_MSS_LEN) || (TotalLen - Cur < TCP_OPTION_MSS_LEN)) { + + return -1; + } + + Option->Mss = TcpGetUint16 (&Head[Cur + 2]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS); + + Cur += TCP_OPTION_MSS_LEN; + break; + + case TCP_OPTION_WS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_WS_LEN) || (TotalLen - Cur < TCP_OPTION_WS_LEN)) { + + return -1; + } + + Option->WndScale = (UINT8) MIN (14, Head[Cur + 2]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS); + + Cur += TCP_OPTION_WS_LEN; + break; + + case TCP_OPTION_TS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_TS_LEN) || (TotalLen - Cur < TCP_OPTION_TS_LEN)) { + + return -1; + } + + Option->TSVal = TcpGetUint32 (&Head[Cur + 2]); + Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS); + + Cur += TCP_OPTION_TS_LEN; + break; + + case TCP_OPTION_NOP: + Cur++; + break; + + case TCP_OPTION_EOP: + Cur = TotalLen; + break; + + default: + Len = Head[Cur + 1]; + + if ((TotalLen - Cur) < Len || Len < 2) { + return -1; + } + + Cur = (UINT8) (Cur + Len); + break; + } + + } + + return 0; +} + +/** + Check the segment against PAWS. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] TSVal The timestamp value. + + @retval 1 The segment passed the PAWS check. + @retval 0 The segment failed to pass the PAWS check. + +**/ +UINT32 +TcpPawsOK ( + IN TCP_CB *Tcb, + IN UINT32 TSVal + ) +{ + // + // PAWS as defined in RFC1323, buggy... + // + if (TCP_TIME_LT (TSVal, Tcb->TsRecent) && + TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick) + ) { + + return 0; + + } + + return 1; +} diff --git a/Core/NetworkPkg/TcpDxe/TcpOption.h b/Core/NetworkPkg/TcpDxe/TcpOption.h new file mode 100644 index 0000000000..0ccadb9536 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpOption.h @@ -0,0 +1,145 @@ +/** @file + Tcp option's routine header file. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _TCP_OPTION_H_ +#define _TCP_OPTION_H_ + +// +// Supported TCP option types and their length. +// +#define TCP_OPTION_EOP 0 ///< End Of oPtion +#define TCP_OPTION_NOP 1 ///< No-Option. +#define TCP_OPTION_MSS 2 ///< Maximum Segment Size +#define TCP_OPTION_WS 3 ///< Window scale +#define TCP_OPTION_TS 8 ///< Timestamp +#define TCP_OPTION_MSS_LEN 4 ///< Length of MSS option +#define TCP_OPTION_WS_LEN 3 ///< Length of window scale option +#define TCP_OPTION_TS_LEN 10 ///< Length of timestamp option +#define TCP_OPTION_WS_ALIGNED_LEN 4 ///< Length of window scale option, aligned +#define TCP_OPTION_TS_ALIGNED_LEN 12 ///< Length of timestamp option, aligned + +// +// recommend format of timestamp window scale +// option for fast process. +// +#define TCP_OPTION_TS_FAST ((TCP_OPTION_NOP << 24) | \ + (TCP_OPTION_NOP << 16) | \ + (TCP_OPTION_TS << 8) | \ + (TCP_OPTION_TS_LEN)) + +#define TCP_OPTION_WS_FAST ((TCP_OPTION_NOP << 24) | \ + (TCP_OPTION_WS << 16) | \ + (TCP_OPTION_WS_LEN << 8)) + +#define TCP_OPTION_MSS_FAST ((TCP_OPTION_MSS << 24) | (TCP_OPTION_MSS_LEN << 16)) + +// +// Other misc definations +// +#define TCP_OPTION_RCVD_MSS 0x01 +#define TCP_OPTION_RCVD_WS 0x02 +#define TCP_OPTION_RCVD_TS 0x04 +#define TCP_OPTION_MAX_WS 14 ///< Maxium window scale value +#define TCP_OPTION_MAX_WIN 0xffff ///< Max window size in TCP header + +/// +/// The structure to store the parse option value. +/// ParseOption only parses the options, doesn't process them. +/// +typedef struct _TCP_OPTION { + UINT8 Flag; ///< Flag such as TCP_OPTION_RCVD_MSS + UINT8 WndScale; ///< The WndScale received + UINT16 Mss; ///< The Mss received + UINT32 TSVal; ///< The TSVal field in a timestamp option + UINT32 TSEcr; ///< The TSEcr field in a timestamp option +} TCP_OPTION; + +/** + Compute the window scale value according to the given buffer size. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The scale value. + +**/ +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ); + +/** + Build the TCP option in three-way handshake. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpSynBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ); + +/** + Build the TCP option in synchronized states. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ); + +/** + Parse the supported options. + + @param[in] Tcp Pointer to the TCP_CB of this TCP instance. + @param[in, out] Option Pointer to the TCP_OPTION used to store the + successfully pasrsed options. + + @retval 0 The options successfully pasrsed. + @retval -1 Ilegal option was found. + +**/ +INTN +TcpParseOption ( + IN TCP_HEAD *Tcp, + IN OUT TCP_OPTION *Option + ); + +/** + Check the segment against PAWS. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] TSVal The timestamp value. + + @retval 1 The segment passed the PAWS check. + @retval 0 The segment failed to pass the PAWS check. + +**/ +UINT32 +TcpPawsOK ( + IN TCP_CB *Tcb, + IN UINT32 TSVal + ); + +#endif diff --git a/Core/NetworkPkg/TcpDxe/TcpOutput.c b/Core/NetworkPkg/TcpDxe/TcpOutput.c new file mode 100644 index 0000000000..c038213484 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpOutput.c @@ -0,0 +1,1219 @@ +/** @file + TCP output process routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +UINT8 mTcpOutFlag[] = { + 0, // TCP_CLOSED + 0, // TCP_LISTEN + TCP_FLG_SYN, // TCP_SYN_SENT + TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD + TCP_FLG_ACK, // TCP_ESTABLISHED + TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1 + TCP_FLG_ACK, // TCP_FIN_WAIT_2 + TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING + TCP_FLG_ACK, // TCP_TIME_WAIT + TCP_FLG_ACK, // TCP_CLOSE_WAIT + TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK +}; + +/** + Compute the sequence space left in the old receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence space left in the old receive window. + +**/ +UINT32 +TcpRcvWinOld ( + IN TCP_CB *Tcb + ) +{ + UINT32 OldWin; + + OldWin = 0; + + if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) { + + OldWin = TCP_SUB_SEQ ( + Tcb->RcvWl2 + Tcb->RcvWnd, + Tcb->RcvNxt + ); + } + + return OldWin; +} + +/** + Compute the current receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The size of the current receive window, in bytes. + +**/ +UINT32 +TcpRcvWinNow ( + IN TCP_CB *Tcb + ) +{ + SOCKET *Sk; + UINT32 Win; + UINT32 Increase; + UINT32 OldWin; + + Sk = Tcb->Sk; + ASSERT (Sk != NULL); + + OldWin = TcpRcvWinOld (Tcb); + + Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF); + + Increase = 0; + if (Win > OldWin) { + Increase = Win - OldWin; + } + + // + // Receiver's SWS: don't advertise a bigger window + // unless it can be increased by at least one Mss or + // half of the receive buffer. + // + if ((Increase > Tcb->SndMss) || (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) { + + return Win; + } + + return OldWin; +} + +/** + Compute the value to fill in the window size field of the outgoing segment. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Syn The flag to indicate whether the outgoing segment + is a SYN segment. + + @return The value of the local receive window size used to fill the outgoing segment. + +**/ +UINT16 +TcpComputeWnd ( + IN OUT TCP_CB *Tcb, + IN BOOLEAN Syn + ) +{ + UINT32 Wnd; + + // + // RFC requires that initial window not be scaled + // + if (Syn) { + + Wnd = GET_RCV_BUFFSIZE (Tcb->Sk); + } else { + + Wnd = TcpRcvWinNow (Tcb); + + Tcb->RcvWnd = Wnd; + } + + Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff); + return NTOHS ((UINT16) Wnd); +} + +/** + Get the maximum SndNxt. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence number of the maximum SndNxt. + +**/ +TCP_SEQNO +TcpGetMaxSndNxt ( + IN TCP_CB *Tcb + ) +{ + LIST_ENTRY *Entry; + NET_BUF *Nbuf; + + if (IsListEmpty (&Tcb->SndQue)) { + return Tcb->SndNxt; + } + + Entry = Tcb->SndQue.BackLink; + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt)); + return TCPSEG_NETBUF (Nbuf)->End; +} + +/** + Compute how much data to send. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, to ignore the sender's SWS avoidance algorithm and send + out data by force. + + @return The length of the data can be sent. If 0, no data can be sent. + +**/ +UINT32 +TcpDataToSend ( + IN TCP_CB *Tcb, + IN INTN Force + ) +{ + SOCKET *Sk; + UINT32 Win; + UINT32 Len; + UINT32 Left; + UINT32 Limit; + + Sk = Tcb->Sk; + ASSERT (Sk != NULL); + + // + // TCP should NOT send data beyond the send window + // and congestion window. The right edge of send + // window is defined as SND.WL2 + SND.WND. The right + // edge of congestion window is defined as SND.UNA + + // CWND. + // + Win = 0; + Limit = Tcb->SndWl2 + Tcb->SndWnd; + + if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) { + + Limit = Tcb->SndUna + Tcb->CWnd; + } + + if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) { + Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt); + } + + // + // The data to send contains two parts: the data on the + // socket send queue, and the data on the TCB's send + // buffer. The later can be non-zero if the peer shrinks + // its advertised window. + // + Left = GET_SND_DATASIZE (Sk) + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt); + + Len = MIN (Win, Left); + + if (Len > Tcb->SndMss) { + Len = Tcb->SndMss; + } + + if ((Force != 0)|| (Len == 0 && Left == 0)) { + return Len; + } + + if (Len == 0 && Left != 0) { + goto SetPersistTimer; + } + + // + // Sender's SWS avoidance: Don't send a small segment unless + // a)A full-sized segment can be sent, + // b)At least one-half of the maximum sized windows that + // the other end has ever advertised. + // c)It can send everything it has, and either it isn't + // expecting an ACK, or the Nagle algorithm is disabled. + // + if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) { + + return Len; + } + + if ((Len == Left) && + ((Tcb->SndNxt == Tcb->SndUna) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE)) + ) { + + return Len; + } + + // + // RFC1122 suggests to set a timer when SWSA forbids TCP + // sending more data, and combines it with a probe timer. + // +SetPersistTimer: + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) { + + DEBUG ( + (EFI_D_WARN, + "TcpDataToSend: enter persistent state for TCB %p\n", + Tcb) + ); + + if (!Tcb->ProbeTimerOn) { + TcpSetProbeTimer (Tcb); + } + } + + return 0; +} + +/** + Build the TCP header of the TCP segment and transmit the segment by IP. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer containing the segment to be + sent out. + + @retval 0 The segment was sent out successfully. + @retval -1 An error condition occurred. + +**/ +INTN +TcpTransmitSegment ( + IN OUT TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + UINT16 Len; + TCP_HEAD *Head; + TCP_SEG *Seg; + BOOLEAN Syn; + UINT32 DataLen; + + ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0)); + + DataLen = Nbuf->TotalSize; + + Seg = TCPSEG_NETBUF (Nbuf); + Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN); + + if (Syn) { + + Len = TcpSynBuildOption (Tcb, Nbuf); + } else { + + Len = TcpBuildOption (Tcb, Nbuf); + } + + ASSERT ((Len % 4 == 0) && (Len <= 40)); + + Len += sizeof (TCP_HEAD); + + Head = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_HEAD + ); + + ASSERT (Head != NULL); + + Nbuf->Tcp = Head; + + Head->SrcPort = Tcb->LocalEnd.Port; + Head->DstPort = Tcb->RemoteEnd.Port; + Head->Seq = NTOHL (Seg->Seq); + Head->Ack = NTOHL (Tcb->RcvNxt); + Head->HeadLen = (UINT8) (Len >> 2); + Head->Res = 0; + Head->Wnd = TcpComputeWnd (Tcb, Syn); + Head->Checksum = 0; + + // + // Check whether to set the PSH flag. + // + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH); + + if (DataLen != 0) { + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) && + TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End) + ) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH); + + } else if ((Seg->End == Tcb->SndNxt) && (GET_SND_DATASIZE (Tcb->Sk) == 0)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH); + } + } + + // + // Check whether to set the URG flag and the urgent pointer. + // + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG); + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_URG); + + if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) { + + Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq); + } else { + + Seg->Urg = (UINT16) MIN ( + TCP_SUB_SEQ (Tcb->SndUp, + Seg->Seq), + 0xffff + ); + } + } + + Head->Flag = Seg->Flag; + Head->Urg = NTOHS (Seg->Urg); + Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum); + + // + // Update the TCP session's control information. + // + Tcb->RcvWl2 = Tcb->RcvNxt; + if (Syn) { + Tcb->RcvWnd = NTOHS (Head->Wnd); + } + + // + // Clear the delayedack flag. + // + Tcb->DelayedAck = 0; + + return TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion); +} + +/** + Get a segment from the Tcb's SndQue. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment. + @param[in] Len The maximum length of the segment. + + @return Pointer to the segment. If NULL, some error occurred. + +**/ +NET_BUF * +TcpGetSegmentSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + LIST_ENTRY *Head; + LIST_ENTRY *Cur; + NET_BUF *Node; + TCP_SEG *Seg; + NET_BUF *Nbuf; + TCP_SEQNO End; + UINT8 *Data; + UINT8 Flag; + INT32 Offset; + INT32 CopyLen; + + ASSERT ((Tcb != NULL) && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0)); + + // + // Find the segment that contains the Seq. + // + Head = &Tcb->SndQue; + + Node = NULL; + Seg = NULL; + + NET_LIST_FOR_EACH (Cur, Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Seg = TCPSEG_NETBUF (Node); + + if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) { + + break; + } + } + + if ((Cur == Head) || (Seg == NULL) || (Node == NULL)) { + return NULL; + } + + // + // Return the buffer if it can be returned without + // adjustment: + // + if ((Seg->Seq == Seq) && + TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) && + !NET_BUF_SHARED (Node) + ) { + + NET_GET_REF (Node); + return Node; + } + + // + // Create a new buffer and copy data there. + // + Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return NULL; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + Flag = Seg->Flag; + End = Seg->End; + + if (TCP_SEQ_LT (Seq + Len, Seg->End)) { + End = Seq + Len; + } + + CopyLen = TCP_SUB_SEQ (End, Seq); + Offset = TCP_SUB_SEQ (Seq, Seg->Seq); + + // + // If SYN is set and out of the range, clear the flag. + // Becuase the sequence of the first byte is SEG.SEQ+1, + // adjust Offset by -1. If SYN is in the range, copy + // one byte less. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + if (TCP_SEQ_LT (Seg->Seq, Seq)) { + + TCP_CLEAR_FLG (Flag, TCP_FLG_SYN); + Offset--; + } else { + + CopyLen--; + } + } + + // + // If FIN is set and in the range, copy one byte less, + // and if it is out of the range, clear the flag. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + + if (Seg->End == End) { + + CopyLen--; + } else { + + TCP_CLEAR_FLG (Flag, TCP_FLG_FIN); + } + } + + ASSERT (CopyLen >= 0); + + // + // Copy data to the segment + // + if (CopyLen != 0) { + Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL); + ASSERT (Data != NULL); + + if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) { + goto OnError; + } + } + + CopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG)); + + TCPSEG_NETBUF (Nbuf)->Seq = Seq; + TCPSEG_NETBUF (Nbuf)->End = End; + TCPSEG_NETBUF (Nbuf)->Flag = Flag; + + return Nbuf; + +OnError: + NetbufFree (Nbuf); + return NULL; +} + +/** + Get a segment from the Tcb's socket buffer. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment. + @param[in] Len The maximum length of the segment. + + @return Pointer to the segment. If NULL, some error occurred. + +**/ +NET_BUF * +TcpGetSegmentSock ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_BUF *Nbuf; + UINT8 *Data; + UINT32 DataGet; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD); + + if (Nbuf == NULL) { + DEBUG ( + (EFI_D_ERROR, + "TcpGetSegmentSock: failed to allocate a netbuf for TCB %p\n", + Tcb) + ); + + return NULL; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + DataGet = 0; + + if (Len != 0) { + // + // copy data to the segment. + // + Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL); + ASSERT (Data != NULL); + + DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data); + } + + NET_GET_REF (Nbuf); + + TCPSEG_NETBUF (Nbuf)->Seq = Seq; + TCPSEG_NETBUF (Nbuf)->End = Seq + Len; + + InsertTailList (&(Tcb->SndQue), &(Nbuf->List)); + + if (DataGet != 0) { + + SockDataSent (Tcb->Sk, DataGet); + } + + return Nbuf; +} + +/** + Get a segment starting from sequence Seq of a maximum + length of Len. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment. + @param[in] Len The maximum length of the segment. + + @return Pointer to the segment. If NULL, some error occurred. + +**/ +NET_BUF * +TcpGetSegment ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_BUF *Nbuf; + + ASSERT (Tcb != NULL); + + // + // Compare the SndNxt with the max sequence number sent. + // + if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) { + + Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); + } else { + + Nbuf = TcpGetSegmentSock (Tcb, Seq, Len); + } + + ASSERT (TcpVerifySegment (Nbuf) != 0); + return Nbuf; +} + +/** + Retransmit the segment from sequence Seq. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment to be retransmitted. + + @retval 0 Retransmission succeeded. + @retval -1 Error condition occurred. + +**/ +INTN +TcpRetransmit ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq + ) +{ + NET_BUF *Nbuf; + UINT32 Len; + + // + // Compute the maxium length of retransmission. It is + // limited by three factors: + // 1. Less than SndMss + // 2. Must in the current send window + // 3. Will not change the boundaries of queued segments. + // + if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) { + DEBUG ( + (EFI_D_WARN, + "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n", + Tcb) + ); + + return 0; + } + + Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq); + Len = MIN (Len, Tcb->SndMss); + + Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); + if (Nbuf == NULL) { + return -1; + } + + ASSERT (TcpVerifySegment (Nbuf) != 0); + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + goto OnError; + } + + // + // The retransmitted buffer may be on the SndQue, + // trim TCP head because all the buffers on SndQue + // are headless. + // + ASSERT (Nbuf->Tcp != NULL); + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + return 0; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return -1; +} + +/** + Verify that all the segments in SndQue are in good shape. + + @param[in] Head Pointer to the head node of the SndQue. + + @retval 0 At least one segment is broken. + @retval 1 All segments in the specific queue are in good shape. + +**/ +INTN +TcpCheckSndQue ( + IN LIST_ENTRY *Head + ) +{ + LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + + if (IsListEmpty (Head)) { + return 1; + } + // + // Initialize the Seq. + // + Entry = Head->ForwardLink; + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Seq = TCPSEG_NETBUF (Nbuf)->Seq; + + NET_LIST_FOR_EACH (Entry, Head) { + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + if (TcpVerifySegment (Nbuf) == 0) { + return 0; + } + + // + // All the node in the SndQue should has: + // SEG.SEQ = LAST_SEG.END + // + if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) { + return 0; + } + + Seq = TCPSEG_NETBUF (Nbuf)->End; + } + + return 1; +} + +/** + Check whether to send data/SYN/FIN and piggyback an ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm + and send out data by force. + + @return The number of bytes sent. + +**/ +INTN +TcpToSendData ( + IN OUT TCP_CB *Tcb, + IN INTN Force + ) +{ + UINT32 Len; + INTN Sent; + UINT8 Flag; + NET_BUF *Nbuf; + TCP_SEG *Seg; + TCP_SEQNO Seq; + TCP_SEQNO End; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN)); + + Sent = 0; + + if ((Tcb->State == TCP_CLOSED) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) { + + return 0; + } + + do { + // + // Compute how much data can be sent + // + Len = TcpDataToSend (Tcb, Force); + Seq = Tcb->SndNxt; + + ASSERT ((Tcb->State) < (sizeof (mTcpOutFlag) / sizeof (mTcpOutFlag[0]))); + Flag = mTcpOutFlag[Tcb->State]; + + if ((Flag & TCP_FLG_SYN) != 0) { + + Seq = Tcb->Iss; + Len = 0; + } + + // + // Only send a segment without data if SYN or + // FIN is set. + // + if ((Len == 0) && ((Flag & (TCP_FLG_SYN | TCP_FLG_FIN)) == 0)) { + return Sent; + } + + Nbuf = TcpGetSegment (Tcb, Seq, Len); + + if (Nbuf == NULL) { + DEBUG ( + (EFI_D_ERROR, + "TcpToSendData: failed to get a segment for TCB %p\n", + Tcb) + ); + + goto OnError; + } + + Seg = TCPSEG_NETBUF (Nbuf); + + // + // Set the TcpSeg in Nbuf. + // + Len = Nbuf->TotalSize; + End = Seq + Len; + if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) { + End++; + } + + if ((Flag & TCP_FLG_FIN) != 0) { + // + // Send FIN if all data is sent, and FIN is + // in the window + // + if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) && + (GET_SND_DATASIZE (Tcb->Sk) == 0) && + TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2) + ) { + DEBUG ( + (EFI_D_INFO, + "TcpToSendData: send FIN to peer for TCB %p in state %s\n", + Tcb, + mTcpStateName[Tcb->State]) + ); + + End++; + } else { + TCP_CLEAR_FLG (Flag, TCP_FLG_FIN); + } + } + + Seg->Seq = Seq; + Seg->End = End; + Seg->Flag = Flag; + + ASSERT (TcpVerifySegment (Nbuf) != 0); + ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0); + + // + // Don't send an empty segment here. + // + if (Seg->End == Seg->Seq) { + DEBUG ( + (EFI_D_WARN, + "TcpToSendData: created a empty segment for TCB %p, free it now\n", + Tcb) + ); + + NetbufFree (Nbuf); + return Sent; + } + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + if ((Flag & TCP_FLG_FIN) != 0) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT); + } + + goto OnError; + } + + Sent += TCP_SUB_SEQ (End, Seq); + + // + // All the buffers in the SndQue are headless. + // + ASSERT (Nbuf->Tcp != NULL); + + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + + // + // Update the status in TCB. + // + Tcb->DelayedAck = 0; + + if ((Flag & TCP_FLG_FIN) != 0) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT); + } + + if (TCP_SEQ_GT (End, Tcb->SndNxt)) { + Tcb->SndNxt = End; + } + + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) { + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + } + + // + // Enable RTT measurement only if not in retransmit. + // Karn's algorithm requires not to update RTT when in loss. + // + if ((Tcb->CongestState == TCP_CONGEST_OPEN) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + DEBUG ( + (EFI_D_INFO, + "TcpToSendData: set RTT measure sequence %d for TCB %p\n", + Seq, + Tcb) + ); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + Tcb->RttSeq = Seq; + Tcb->RttMeasure = 0; + } + + } while (Len == Tcb->SndMss); + + return Sent; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return Sent; +} + +/** + Send an ACK immediately. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSendAck ( + IN OUT TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_SEG *Seg; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + Seg = TCPSEG_NETBUF (Nbuf); + Seg->Seq = Tcb->SndNxt; + Seg->End = Tcb->SndNxt; + Seg->Flag = TCP_FLG_ACK; + + if (TcpTransmitSegment (Tcb, Nbuf) == 0) { + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + Tcb->DelayedAck = 0; + } + + NetbufFree (Nbuf); +} + +/** + Send a zero probe segment. It can be used by keepalive and zero window probe. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The zero probe segment was sent out successfully. + @retval other An error condition occurred. + +**/ +INTN +TcpSendZeroProbe ( + IN OUT TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_SEG *Seg; + INTN Result; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return -1; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + // + // SndNxt-1 is out of window. The peer should respond + // with an ACK. + // + Seg = TCPSEG_NETBUF (Nbuf); + Seg->Seq = Tcb->SndNxt - 1; + Seg->End = Tcb->SndNxt - 1; + Seg->Flag = TCP_FLG_ACK; + + Result = TcpTransmitSegment (Tcb, Nbuf); + NetbufFree (Nbuf); + + return Result; +} + +/** + Check whether to send an ACK or delayed ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpToSendAck ( + IN OUT TCP_CB *Tcb + ) +{ + UINT32 TcpNow; + + // + // Generally, TCP should send a delayed ACK unless: + // 1. ACK at least every other FULL sized segment received. + // 2. Packets received out of order. + // 3. Receiving window is open. + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Tcb->DelayedAck >= 1)) { + TcpSendAck (Tcb); + return; + } + + TcpNow = TcpRcvWinNow (Tcb); + + if (TcpNow > TcpRcvWinOld (Tcb)) { + TcpSendAck (Tcb); + return; + } + + DEBUG ( + (EFI_D_INFO, + "TcpToSendAck: scheduled a delayed ACK for TCB %p\n", + Tcb) + ); + + // + // Schedule a delayed ACK. + // + Tcb->DelayedAck++; +} + +/** + Send a RESET segment in response to the segment received. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. May be NULL. + @param[in] Head TCP header of the segment that triggers the reset. + @param[in] Len Length of the segment that triggers the reset. + @param[in] Local Local IP address. + @param[in] Remote Remote peer's IP address. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + + @retval 0 A reset was sent or there is no need to send it. + @retval -1 No reset is sent. + +**/ +INTN +TcpSendReset ( + IN TCP_CB *Tcb, + IN TCP_HEAD *Head, + IN INT32 Len, + IN EFI_IP_ADDRESS *Local, + IN EFI_IP_ADDRESS *Remote, + IN UINT8 Version + ) +{ + NET_BUF *Nbuf; + TCP_HEAD *Nhead; + UINT16 HeadSum; + + // + // Don't respond to a Reset with reset. + // + if ((Head->Flag & TCP_FLG_RST) != 0) { + return 0; + } + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return -1; + } + + Nhead = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_TAIL + ); + + ASSERT (Nhead != NULL); + + Nbuf->Tcp = Nhead; + Nhead->Flag = TCP_FLG_RST; + + // + // Derive Seq/ACK from the segment if no TCB + // is associated with it, otherwise derive from the Tcb. + // + if (Tcb == NULL) { + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) { + Nhead->Seq = Head->Ack; + Nhead->Ack = 0; + } else { + Nhead->Seq = 0; + TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK); + Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len); + } + } else { + + Nhead->Seq = HTONL (Tcb->SndNxt); + Nhead->Ack = HTONL (Tcb->RcvNxt); + TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK); + } + + Nhead->SrcPort = Head->DstPort; + Nhead->DstPort = Head->SrcPort; + Nhead->HeadLen = (UINT8) (sizeof (TCP_HEAD) >> 2); + Nhead->Res = 0; + Nhead->Wnd = HTONS (0xFFFF); + Nhead->Checksum = 0; + Nhead->Urg = 0; + + if (Version == IP_VERSION_4) { + HeadSum = NetPseudoHeadChecksum (Local->Addr[0], Remote->Addr[0], 6, 0); + } else { + HeadSum = NetIp6PseudoHeadChecksum (&Local->v6, &Remote->v6, 6, 0); + } + + Nhead->Checksum = TcpChecksum (Nbuf, HeadSum); + + TcpSendIpPacket (Tcb, Nbuf, Local, Remote, Version); + + NetbufFree (Nbuf); + + return 0; +} + +/** + Verify that the segment is in good shape. + + @param[in] Nbuf The buffer that contains the segment to be checked. + + @retval 0 The segment is broken. + @retval 1 The segment is in good shape. + +**/ +INTN +TcpVerifySegment ( + IN NET_BUF *Nbuf + ) +{ + TCP_HEAD *Head; + TCP_SEG *Seg; + UINT32 Len; + + if (Nbuf == NULL) { + return 1; + } + + NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE); + + Seg = TCPSEG_NETBUF (Nbuf); + Len = Nbuf->TotalSize; + Head = Nbuf->Tcp; + + if (Head != NULL) { + if (Head->Flag != Seg->Flag) { + return 0; + } + + Len -= (Head->HeadLen << 2); + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + Len++; + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + Len++; + } + + if (Seg->Seq + Len != Seg->End) { + return 0; + } + + return 1; +} + diff --git a/Core/NetworkPkg/TcpDxe/TcpProto.h b/Core/NetworkPkg/TcpDxe/TcpProto.h new file mode 100644 index 0000000000..ee35134833 --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpProto.h @@ -0,0 +1,344 @@ +/** @file + TCP protocol header file. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _TCP_PROTO_H_ +#define _TCP_PROTO_H_ + +/// +/// Tcp states don't change their order. It is used as an +/// index to mTcpOutFlag and other macros. +/// +#define TCP_CLOSED 0 +#define TCP_LISTEN 1 +#define TCP_SYN_SENT 2 +#define TCP_SYN_RCVD 3 +#define TCP_ESTABLISHED 4 +#define TCP_FIN_WAIT_1 5 +#define TCP_FIN_WAIT_2 6 +#define TCP_CLOSING 7 +#define TCP_TIME_WAIT 8 +#define TCP_CLOSE_WAIT 9 +#define TCP_LAST_ACK 10 + + +/// +/// Flags in the TCP header +/// +#define TCP_FLG_FIN 0x01 +#define TCP_FLG_SYN 0x02 +#define TCP_FLG_RST 0x04 +#define TCP_FLG_PSH 0x08 +#define TCP_FLG_ACK 0x10 +#define TCP_FLG_URG 0x20 + + // + // mask for all the flags + // +#define TCP_FLG_FLAG 0x3F + + +#define TCP_CONNECT_REFUSED (-1) ///< TCP error status +#define TCP_CONNECT_RESET (-2) ///< TCP error status +#define TCP_CONNECT_CLOSED (-3) ///< TCP error status + +// +// Current congestion status as suggested by RFC3782. +// +#define TCP_CONGEST_RECOVER 1 ///< During the NewReno fast recovery. +#define TCP_CONGEST_LOSS 2 ///< Retxmit because of retxmit time out. +#define TCP_CONGEST_OPEN 3 ///< TCP is opening its congestion window. + +// +// TCP control flags +// +#define TCP_CTRL_NO_NAGLE 0x0001 ///< Disable Nagle algorithm +#define TCP_CTRL_NO_KEEPALIVE 0x0002 ///< Disable keepalive timer. +#define TCP_CTRL_NO_WS 0x0004 ///< Disable window scale option. +#define TCP_CTRL_RCVD_WS 0x0008 ///< Received a wnd scale option in syn. +#define TCP_CTRL_NO_TS 0x0010 ///< Disable Timestamp option. +#define TCP_CTRL_RCVD_TS 0x0020 ///< Received a Timestamp option in syn. +#define TCP_CTRL_SND_TS 0x0040 ///< Send Timestamp option to remote. +#define TCP_CTRL_SND_URG 0x0080 ///< In urgent send mode. +#define TCP_CTRL_RCVD_URG 0x0100 ///< In urgent receive mode. +#define TCP_CTRL_SND_PSH 0x0200 ///< In PUSH send mode. +#define TCP_CTRL_FIN_SENT 0x0400 ///< FIN is sent. +#define TCP_CTRL_FIN_ACKED 0x0800 ///< FIN is ACKed. +#define TCP_CTRL_TIMER_ON 0x1000 ///< At least one of the timer is on. +#define TCP_CTRL_RTT_ON 0x2000 ///< The RTT measurement is on. +#define TCP_CTRL_ACK_NOW 0x4000 ///< Send the ACK now, don't delay. + +// +// Timer related values +// +#define TCP_TIMER_CONNECT 0 ///< Connection establishment timer. +#define TCP_TIMER_REXMIT 1 ///< Retransmit timer. +#define TCP_TIMER_PROBE 2 ///< Window probe timer. +#define TCP_TIMER_KEEPALIVE 3 ///< Keepalive timer. +#define TCP_TIMER_FINWAIT2 4 ///< FIN_WAIT_2 timer. +#define TCP_TIMER_2MSL 5 ///< TIME_WAIT timer. +#define TCP_TIMER_NUMBER 6 ///< The total number of the TCP timer. +#define TCP_TICK 200 ///< Every TCP tick is 200ms. +#define TCP_TICK_HZ 5 ///< The frequence of TCP tick. +#define TCP_RTT_SHIFT 3 ///< SRTT & RTTVAR scaled by 8. +#define TCP_RTO_MIN TCP_TICK_HZ ///< The minium value of RTO. +#define TCP_RTO_MAX (TCP_TICK_HZ * 60) ///< The maxium value of RTO. +#define TCP_FOLD_RTT 4 ///< Timeout threshod to fold RTT. + +// +// Default values for some timers +// +#define TCP_MAX_LOSS 12 ///< Default max times to retxmit. +#define TCP_KEEPALIVE_IDLE_MIN (TCP_TICK_HZ * 60 * 60 * 2) ///< First keepalive. +#define TCP_KEEPALIVE_PERIOD (TCP_TICK_HZ * 60) +#define TCP_MAX_KEEPALIVE 8 +#define TCP_FIN_WAIT2_TIME (2 * TCP_TICK_HZ) +#define TCP_TIME_WAIT_TIME (2 * TCP_TICK_HZ) +#define TCP_PAWS_24DAY (24 * 24 * 60 * 60 * TCP_TICK_HZ) +#define TCP_CONNECT_TIME (75 * TCP_TICK_HZ) + +// +// The header space to be reserved before TCP data to accomodate : +// 60byte IP head + 60byte TCP head + link layer head +// +#define TCP_MAX_HEAD 192 + +// +// Value ranges for some control option +// +#define TCP_RCV_BUF_SIZE (2 * 1024 * 1024) +#define TCP_RCV_BUF_SIZE_MIN (8 * 1024) +#define TCP_SND_BUF_SIZE (2 * 1024 * 1024) +#define TCP_SND_BUF_SIZE_MIN (8 * 1024) +#define TCP_BACKLOG 10 +#define TCP_BACKLOG_MIN 5 +#define TCP_MAX_LOSS_MIN 6 +#define TCP_CONNECT_TIME_MIN (60 * TCP_TICK_HZ) +#define TCP_MAX_KEEPALIVE_MIN 4 +#define TCP_KEEPALIVE_IDLE_MAX (TCP_TICK_HZ * 60 * 60 * 4) +#define TCP_KEEPALIVE_PERIOD_MIN (TCP_TICK_HZ * 30) +#define TCP_FIN_WAIT2_TIME_MAX (4 * TCP_TICK_HZ) +#define TCP_TIME_WAIT_TIME_MAX (60 * TCP_TICK_HZ) + +/// +/// TCP_CONNECTED: both ends have synchronized their ISN. +/// +#define TCP_CONNECTED(state) ((state) > TCP_SYN_RCVD) + +#define TCP_FIN_RCVD(State) \ + ( \ + ((State) == TCP_CLOSE_WAIT) || \ + ((State) == TCP_LAST_ACK) || \ + ((State) == TCP_CLOSING) || \ + ((State) == TCP_TIME_WAIT) \ + ) + +#define TCP_LOCAL_CLOSED(State) \ + ( \ + ((State) == TCP_FIN_WAIT_1) || \ + ((State) == TCP_FIN_WAIT_2) || \ + ((State) == TCP_CLOSING) || \ + ((State) == TCP_TIME_WAIT) || \ + ((State) == TCP_LAST_ACK) \ + ) + +// +// Get the TCP_SEG point from a net buffer's ProtoData. +// +#define TCPSEG_NETBUF(NBuf) ((TCP_SEG *) ((NBuf)->ProtoData)) + +// +// Macros to compare sequence no +// +#define TCP_SEQ_LT(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) < 0) +#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0) +#define TCP_SEQ_GT(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) < 0) +#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0) + +// +// TCP_SEQ_BETWEEN return whether b <= m <= e +// +#define TCP_SEQ_BETWEEN(b, m, e) ((e) - (b) >= (m) - (b)) + +// +// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2 +// +#define TCP_SUB_SEQ(Seq1, Seq2) ((UINT32) ((Seq1) - (Seq2))) + +// +// Check whether Flag is on +// +#define TCP_FLG_ON(Value, Flag) ((BOOLEAN) (((Value) & (Flag)) != 0)) +// +// Set and Clear operation on a Flag +// +#define TCP_SET_FLG(Value, Flag) ((Value) |= (Flag)) +#define TCP_CLEAR_FLG(Value, Flag) ((Value) &= ~(Flag)) + +// +// Test whether two peers are equal +// +#define TCP_PEER_EQUAL(Pa, Pb, Ver) \ + (((Pa)->Port == (Pb)->Port) && TcpIsIpEqual(&((Pa)->Ip), &((Pb)->Ip), Ver)) + +// +// Test whether Pa matches Pb, or Pa is more specific +// than pb. Zero means wildcard. +// +#define TCP_PEER_MATCH(Pa, Pb, Ver) \ + ( \ + (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port)) && \ + (TcpIsIpZero (&((Pb)->Ip), Ver) || TcpIsIpEqual (&((Pb)->Ip), &((Pa)->Ip), Ver)) \ + ) + +#define TCP_TIMER_ON(Flag, Timer) ((Flag) & (1 << (Timer))) +#define TCP_SET_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) | (1 << (Timer)))) +#define TCP_CLEAR_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) & (~(1 << (Timer))))) + + +#define TCP_TIME_LT(Ta, Tb) ((INT32) ((Ta) - (Tb)) < 0) +#define TCP_TIME_LEQ(Ta, Tb) ((INT32) ((Ta) - (Tb)) <= 0) +#define TCP_SUB_TIME(Ta, Tb) ((UINT32) ((Ta) - (Tb))) + +#define TCP_MAX_WIN 0xFFFFU + +/// +/// TCP segmentation data. +/// +typedef struct _TCP_SEG { + TCP_SEQNO Seq; ///< Starting sequence number. + TCP_SEQNO End; ///< The sequence of the last byte + 1, include SYN/FIN. End-Seq = SEG.LEN. + TCP_SEQNO Ack; ///< ACK field in the segment. + UINT8 Flag; ///< TCP header flags. + UINT16 Urg; ///< Valid if URG flag is set. + UINT32 Wnd; ///< TCP window size field. +} TCP_SEG; + +/// +/// Network endpoint, IP plus Port structure. +/// +typedef struct _TCP_PEER { + EFI_IP_ADDRESS Ip; ///< IP address, in network byte order. + TCP_PORTNO Port; ///< Port number, in network byte order. +} TCP_PEER; + +typedef struct _TCP_CONTROL_BLOCK TCP_CB; + +/// +/// TCP control block: it includes various states. +/// +struct _TCP_CONTROL_BLOCK { + LIST_ENTRY List; ///< Back and forward link entry + TCP_CB *Parent; ///< The parent TCP_CB structure + + SOCKET *Sk; ///< The socket it controled. + TCP_PEER LocalEnd; ///< Local endpoint. + TCP_PEER RemoteEnd;///< Remote endpoint. + + LIST_ENTRY SndQue; ///< Retxmission queue. + LIST_ENTRY RcvQue; ///< Reassemble queue. + UINT32 CtrlFlag; ///< Control flags, such as NO_NAGLE. + INT32 Error; ///< Soft error status, such as TCP_CONNECT_RESET. + + // + // RFC793 and RFC1122 defined variables + // + UINT8 State; ///< TCP state, such as SYN_SENT, LISTEN. + UINT8 DelayedAck; ///< Number of delayed ACKs. + UINT16 HeadSum; ///< Checksum of the fixed parts of pesudo + ///< header: Src IP, Dst IP, 0, Protocol, + ///< do not include the TCP length. + + TCP_SEQNO Iss; ///< Initial Sending Sequence. + TCP_SEQNO SndUna; ///< First unacknowledged data. + TCP_SEQNO SndNxt; ///< Next data sequence to send. + TCP_SEQNO SndPsh; ///< Send PUSH point. + TCP_SEQNO SndUp; ///< Send urgent point. + UINT32 SndWnd; ///< Window advertised by the remote peer. + UINT32 SndWndMax; ///< Max send window advertised by the peer. + TCP_SEQNO SndWl1; ///< Seq number used for last window update. + TCP_SEQNO SndWl2; ///< Ack no of last window update. + UINT16 SndMss; ///< Max send segment size. + TCP_SEQNO RcvNxt; ///< Next sequence no to receive. + UINT32 RcvWnd; ///< Window advertised by the local peer. + TCP_SEQNO RcvWl2; ///< The RcvNxt (or ACK) of last window update. + ///< It is necessary because of delayed ACK. + + TCP_SEQNO RcvUp; ///< Urgent point; + TCP_SEQNO Irs; ///< Initial Receiving Sequence. + UINT16 RcvMss; ///< Max receive segment size. + UINT16 EnabledTimer; ///< Which timer is currently enabled. + UINT32 Timer[TCP_TIMER_NUMBER]; ///< When the timer will expire. + INT32 NextExpire; ///< Countdown offset for the nearest timer. + UINT32 Idle; ///< How long the connection is in idle. + UINT32 ProbeTime; ///< The time out value for current window prober. + BOOLEAN ProbeTimerOn;///< If TRUE, the probe time is on. + + // + // RFC1323 defined variables, about window scale, + // timestamp and PAWS + // + UINT8 SndWndScale; ///< Wndscale received from the peer. + UINT8 RcvWndScale; ///< Wndscale used to scale local buffer. + UINT32 TsRecent; ///< TsRecent to echo to the remote peer. + UINT32 TsRecentAge; ///< When this TsRecent is updated. + + // + // RFC2988 defined variables. about RTT measurement + // + TCP_SEQNO RttSeq; ///< The seq of measured segment now. + UINT32 RttMeasure; ///< Currently measured RTT in heartbeats. + UINT32 SRtt; ///< Smoothed RTT, scaled by 8. + UINT32 RttVar; ///< RTT variance, scaled by 8. + UINT32 Rto; ///< Current RTO, not scaled. + + // + // RFC2581, and 3782 variables. + // Congestion control + NewReno fast recovery. + // + UINT32 CWnd; ///< Sender's congestion window. + UINT32 Ssthresh; ///< Slow start threshold. + TCP_SEQNO Recover; ///< Recover point for NewReno. + UINT16 DupAck; ///< Number of duplicate ACKs. + UINT8 CongestState; ///< The current congestion state(RFC3782). + UINT8 LossTimes; ///< Number of retxmit timeouts in a row. + TCP_SEQNO LossRecover; ///< Recover point for retxmit. + + // + // configuration parameters, for EFI_TCP4_PROTOCOL specification + // + UINT32 KeepAliveIdle; ///< Idle time before sending first probe. + UINT32 KeepAlivePeriod; ///< Interval for subsequent keep alive probe. + UINT8 MaxKeepAlive; ///< Maxium keep alive probe times. + UINT8 KeepAliveProbes; ///< The number of keep alive probe. + UINT16 MaxRexmit; ///< The maxium number of retxmit before abort. + UINT32 FinWait2Timeout; ///< The FIN_WAIT_2 timeout. + UINT32 TimeWaitTimeout; ///< The TIME_WAIT timeout. + UINT32 ConnectTimeout; ///< The connect establishment timeout. + + // + // configuration for tcp provided by user + // + BOOLEAN UseDefaultAddr; + UINT8 Tos; + UINT8 Ttl; + EFI_IPv4_ADDRESS SubnetMask; + + + BOOLEAN RemoteIpZero; ///< RemoteEnd.Ip is ZERO when configured. + IP_IO_IP_INFO *IpInfo; ///< Pointer reference to Ip used to send pkt + UINT32 Tick; ///< 1 tick = 200ms +}; + +#endif diff --git a/Core/NetworkPkg/TcpDxe/TcpTimer.c b/Core/NetworkPkg/TcpDxe/TcpTimer.c new file mode 100644 index 0000000000..cc52ad924c --- /dev/null +++ b/Core/NetworkPkg/TcpDxe/TcpTimer.c @@ -0,0 +1,593 @@ +/** @file + TCP timer related functions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "TcpMain.h" + +UINT32 mTcpTick = 1000; + +/** + Connect timeout handler. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpConnectTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for TCP retransmission timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpRexmitTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for window probe timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpProbeTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for keepalive timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpKeepaliveTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for FIN_WAIT_2 timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpFinwait2Timeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for 2MSL timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +Tcp2MSLTimeout ( + IN OUT TCP_CB *Tcb + ); + +TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = { + TcpConnectTimeout, + TcpRexmitTimeout, + TcpProbeTimeout, + TcpKeepaliveTimeout, + TcpFinwait2Timeout, + Tcp2MSLTimeout, +}; + +/** + Close the TCP connection. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClose ( + IN OUT TCP_CB *Tcb + ) +{ + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); + + TcpSetState (Tcb, TCP_CLOSED); +} + +/** + Backoff the RTO. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpBackoffRto ( + IN OUT TCP_CB *Tcb + ) +{ + // + // Fold the RTT estimate if too many times, the estimate + // may be wrong, fold it. So the next time a valid + // measurement is sampled, we can start fresh. + // + if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) { + Tcb->RttVar += Tcb->SRtt >> 2; + Tcb->SRtt = 0; + } + + Tcb->Rto <<= 1; + + if (Tcb->Rto < TCP_RTO_MIN) { + + Tcb->Rto = TCP_RTO_MIN; + } else if (Tcb->Rto > TCP_RTO_MAX) { + + Tcb->Rto = TCP_RTO_MAX; + } +} + +/** + Connect timeout handler. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpConnectTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + if (!TCP_CONNECTED (Tcb->State)) { + DEBUG ( + (EFI_D_ERROR, + "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n", + Tcb) + ); + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + if (TCP_SYN_RCVD == Tcb->State) { + DEBUG ( + (EFI_D_WARN, + "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n", + Tcb) + ); + + TcpResetConnection (Tcb); + + } + + TcpClose (Tcb); + } +} + + +/** + Timeout handler for TCP retransmission timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpRexmitTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + UINT32 FlightSize; + + DEBUG ( + (EFI_D_WARN, + "TcpRexmitTimeout: transmission timeout for TCB %p\n", + Tcb) + ); + + // + // Set the congestion window. FlightSize is the + // amount of data that has been sent but not + // yet ACKed. + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + Tcb->Ssthresh = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2); + + Tcb->CWnd = Tcb->SndMss; + Tcb->LossRecover = Tcb->SndNxt; + + Tcb->LossTimes++; + if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) { + + DEBUG ( + (EFI_D_ERROR, + "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n", + Tcb) + ); + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + TcpClose (Tcb); + return ; + } + + TcpBackoffRto (Tcb); + TcpRetransmit (Tcb, Tcb->SndUna); + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + + Tcb->CongestState = TCP_CONGEST_LOSS; + + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); +} + +/** + Timeout handler for window probe timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpProbeTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + // + // This is the timer for sender's SWSA. RFC1122 requires + // a timer set for sender's SWSA, and suggest combine it + // with window probe timer. If data is sent, don't set + // the probe timer, since retransmit timer is on. + // + if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) { + + ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0); + Tcb->ProbeTimerOn = FALSE; + return ; + } + + TcpSendZeroProbe (Tcb); + TcpSetProbeTimer (Tcb); +} + +/** + Timeout handler for keepalive timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpKeepaliveTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + Tcb->KeepAliveProbes++; + + // + // Too many Keep-alive probes, drop the connection + // + if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) { + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + TcpClose (Tcb); + return ; + } + + TcpSendZeroProbe (Tcb); + TcpSetKeepaliveTimer (Tcb); +} + +/** + Timeout handler for FIN_WAIT_2 timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpFinwait2Timeout ( + IN OUT TCP_CB *Tcb + ) +{ + DEBUG ( + (EFI_D_WARN, + "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n", + Tcb) + ); + + TcpClose (Tcb); +} + +/** + Timeout handler for 2MSL timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +Tcp2MSLTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + DEBUG ( + (EFI_D_WARN, + "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n", + Tcb) + ); + + TcpClose (Tcb); +} + +/** + Update the timer status and the next expire time according to the timers + to expire in a specific future time slot. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpUpdateTimer ( + IN OUT TCP_CB *Tcb + ) +{ + UINT16 Index; + + // + // Don't use a too large value to init NextExpire + // since mTcpTick wraps around as sequence no does. + // + Tcb->NextExpire = TCP_EXPIRE_TIME; + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); + + for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { + + if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && + TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire) + ) { + + Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); + } + } +} + +/** + Enable a TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be enabled. + @param[in] TimeOut The timeout value of this timer. + +**/ +VOID +TcpSetTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer, + IN UINT32 TimeOut + ) +{ + TCP_SET_TIMER (Tcb->EnabledTimer, Timer); + Tcb->Timer[Timer] = mTcpTick + TimeOut; + + TcpUpdateTimer (Tcb); +} + +/** + Clear one TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be cleared. + +**/ +VOID +TcpClearTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer + ) +{ + TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer); + TcpUpdateTimer (Tcb); +} + +/** + Clear all TCP timers. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClearAllTimer ( + IN OUT TCP_CB *Tcb + ) +{ + Tcb->EnabledTimer = 0; + TcpUpdateTimer (Tcb); +} + +/** + Enable the window prober timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetProbeTimer ( + IN OUT TCP_CB *Tcb + ) +{ + if (!Tcb->ProbeTimerOn) { + Tcb->ProbeTime = Tcb->Rto; + Tcb->ProbeTimerOn = TRUE; + + } else { + Tcb->ProbeTime <<= 1; + } + + if (Tcb->ProbeTime < TCP_RTO_MIN) { + + Tcb->ProbeTime = TCP_RTO_MIN; + } else if (Tcb->ProbeTime > TCP_RTO_MAX) { + + Tcb->ProbeTime = TCP_RTO_MAX; + } + + TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime); +} + +/** + Enable the keepalive timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetKeepaliveTimer ( + IN OUT TCP_CB *Tcb + ) +{ + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) { + return ; + + } + + // + // Set the timer to KeepAliveIdle if either + // 1. the keepalive timer is off + // 2. The keepalive timer is on, but the idle + // is less than KeepAliveIdle, that means the + // connection is alive since our last probe. + // + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) || + (Tcb->Idle < Tcb->KeepAliveIdle) + ) { + + TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle); + Tcb->KeepAliveProbes = 0; + + } else { + + TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod); + } +} + +/** + Heart beat timer handler. + + @param[in] Context Context of the timer event, ignored. + +**/ +VOID +EFIAPI +TcpTickingDpc ( + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + TCP_CB *Tcb; + INT16 Index; + + mTcpTick++; + mTcpGlobalIss += TCP_ISS_INCREMENT_2; + + // + // Don't use LIST_FOR_EACH, which isn't delete safe. + // + for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) { + + Next = Entry->ForwardLink; + + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (Tcb->State == TCP_CLOSED) { + continue; + } + // + // The connection is doing RTT measurement. + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + Tcb->RttMeasure++; + } + + Tcb->Idle++; + + if (Tcb->DelayedAck != 0) { + TcpSendAck (Tcb); + } + + if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) { + Tcb->Tick--; + } + + // + // No timer is active or no timer expired + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) { + + continue; + } + + // + // Call the timeout handler for each expired timer. + // + for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { + + if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) { + // + // disable the timer before calling the handler + // in case the handler enables it again. + // + TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index); + mTcpTimerHandler[Index](Tcb); + + // + // The Tcb may have been deleted by the timer, or + // no other timer is set. + // + if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) { + break; + } + } + } + + // + // If the Tcb still exist or some timer is set, update the timer + // + if (Index == TCP_TIMER_NUMBER) { + TcpUpdateTimer (Tcb); + } + } +} + +/** + Heart beat timer handler, queues the DPC at TPL_CALLBACK. + + @param[in] Event Timer event signaled, ignored. + @param[in] Context Context of the timer event, ignored. + +**/ +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context); +} + diff --git a/Core/NetworkPkg/Udp6Dxe/ComponentName.c b/Core/NetworkPkg/Udp6Dxe/ComponentName.c new file mode 100644 index 0000000000..93c2003f34 --- /dev/null +++ b/Core/NetworkPkg/Udp6Dxe/ComponentName.c @@ -0,0 +1,429 @@ +/** @file + UEFI Component Name(2) protocol implementation for UDP6 driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Udp6Impl.h" + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Udp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Udp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUdp6ComponentName = { + Udp6ComponentNameGetDriverName, + Udp6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Udp6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Udp6ComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUdp6DriverNameTable[] = { + { + "eng;en", + L"UDP6 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gUdp6ControllerNameTable = NULL; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Udp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUdp6DriverNameTable, + DriverName, + (BOOLEAN) (This == &gUdp6ComponentName) + ); +} + +/** + Update the component name for the Udp6 child handle. + + @param Udp6[in] A pointer to the EFI_UDP6_PROTOCOL. + + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + +**/ +EFI_STATUS +UpdateName ( + IN EFI_UDP6_PROTOCOL *Udp6 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[64]; + EFI_UDP6_CONFIG_DATA Udp6ConfigData; + + if (Udp6 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer. + // + Status = Udp6->GetModeData (Udp6, &Udp6ConfigData, NULL, NULL, NULL); + if (!EFI_ERROR (Status)) { + UnicodeSPrint (HandleName, sizeof (HandleName), + L"UDPv6 (SrcPort=%d, DestPort=%d)", + Udp6ConfigData.StationPort, + Udp6ConfigData.RemotePort + ); + } else if (Status == EFI_NOT_STARTED) { + UnicodeSPrint (HandleName, sizeof (HandleName), L"UDPv6 (Not started)"); + } else { + UnicodeSPrint (HandleName, sizeof (HandleName), L"UDPv6 (%r)", Status); + } + + if (gUdp6ControllerNameTable != NULL) { + FreeUnicodeStringTable (gUdp6ControllerNameTable); + gUdp6ControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gUdp6ComponentName.SupportedLanguages, + &gUdp6ControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gUdp6ComponentName2.SupportedLanguages, + &gUdp6ControllerNameTable, + HandleName, + FALSE + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Udp6ComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_UDP6_PROTOCOL *Udp6; + + // + // Only provide names for child handles. + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiIp6ProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiUdp6ProtocolGuid, + (VOID **)&Udp6, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateName (Udp6); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gUdp6ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gUdp6ComponentName) + ); +} diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6Driver.c b/Core/NetworkPkg/Udp6Dxe/Udp6Driver.c new file mode 100644 index 0000000000..da2093962a --- /dev/null +++ b/Core/NetworkPkg/Udp6Dxe/Udp6Driver.c @@ -0,0 +1,623 @@ +/** @file + Driver Binding functions and Service Binding functions for the Network driver module. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Udp6Impl.h" + +EFI_DRIVER_BINDING_PROTOCOL gUdp6DriverBinding = { + Udp6DriverBindingSupported, + Udp6DriverBindingStart, + Udp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding = { + Udp6ServiceBindingCreateChild, + Udp6ServiceBindingDestroyChild +}; + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + // + // Test for the Udp6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + // + // Test for the Ip6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +/** + Start this driver on ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind the driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + UDP6_SERVICE_DATA *Udp6Service; + + // + // Allocate Private Context Data Structure. + // + Udp6Service = AllocateZeroPool (sizeof (UDP6_SERVICE_DATA)); + if (Udp6Service == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + Status = Udp6CreateService (Udp6Service, This->DriverBindingHandle, ControllerHandle); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Install the Udp6ServiceBindingProtocol on the ControllerHandle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Udp6Service->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + Udp6CleanService (Udp6Service); + goto EXIT; + } + +EXIT: + if (EFI_ERROR (Status)) { + if (Udp6Service != NULL) { + FreePool (Udp6Service); + } + } + return Status; +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +Udp6DestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + UDP6_INSTANCE_DATA *Instance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = NET_LIST_USER_STRUCT_S (Entry, UDP6_INSTANCE_DATA, Link, UDP6_INSTANCE_DATA_SIGNATURE); + ServiceBinding = ((UDP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((UDP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((UDP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (Instance->ChildHandle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle); +} + +/** + Stop this driver on ControllerHandle. + + This service is called by the EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop the driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If the number + of children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. It is optional. + + @retval EFI_SUCCES This driver is removed ControllerHandle. + @retval EFI_DEVICE_ERROR Can't find the NicHandle from the ControllerHandle and specified GUID. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UDP6_SERVICE_DATA *Udp6Service; + LIST_ENTRY *List; + UDP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + + // + // Find the NicHandle where UDP6 ServiceBinding Protocol is installed. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Retrieve the UDP6 ServiceBinding Protocol. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (ServiceBinding); + + if (NumberOfChildren != 0) { + // + // NumberOfChildren is not zero, destroy the children instances in ChildHandleBuffer. + // + List = &Udp6Service->ChildrenList; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + Udp6DestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } else if (IsListEmpty (&Udp6Service->ChildrenList)) { + gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Udp6Service->ServiceBinding, + NULL + ); + + Udp6CleanService (Udp6Service); + + FreePool (Udp6Service); + + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Udp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + EFI_STATUS Status; + UDP6_SERVICE_DATA *Udp6Service; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + VOID *Ip6; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This); + + // + // Allocate the instance private data structure. + // + Instance = AllocateZeroPool (sizeof (UDP6_INSTANCE_DATA)); + if (Instance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Udp6InitInstance (Udp6Service, Instance); + + // + // Add an IpInfo for this instance. + // + Instance->IpInfo = IpIoAddIp (Udp6Service->IpIo); + if (Instance->IpInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Install the Udp6Protocol for this instance. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiUdp6ProtocolGuid, + &Instance->Udp6Proto, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->ChildHandle = *ChildHandle; + + // + // Open the default Ip6 protocol in the IP_IO BY_CHILD. + // + Status = gBS->OpenProtocol ( + Udp6Service->IpIo->ChildHandle, + &gEfiIp6ProtocolGuid, + (VOID **) &Ip6, + gUdp6DriverBinding.DriverBindingHandle, + Instance->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open this instance's Ip6 protocol in the IpInfo BY_CHILD. + // + Status = gBS->OpenProtocol ( + Instance->IpInfo->ChildHandle, + &gEfiIp6ProtocolGuid, + (VOID **) &Ip6, + gUdp6DriverBinding.DriverBindingHandle, + Instance->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Link this instance into the service context data and increase the ChildrenNumber. + // + InsertTailList (&Udp6Service->ChildrenList, &Instance->Link); + Udp6Service->ChildrenNumber++; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_ERROR: + + if (Instance->ChildHandle != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + Instance->ChildHandle, + &gEfiUdp6ProtocolGuid, + &Instance->Udp6Proto, + NULL + ); + } + + if (Instance->IpInfo != NULL) { + IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo); + } + + Udp6CleanInstance (Instance); + + FreePool (Instance); + + return Status; +} + +/** + Destroys a child handle with a set of I/O services. + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Udp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + UDP6_SERVICE_DATA *Udp6Service; + EFI_UDP6_PROTOCOL *Udp6Proto; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Udp6Service = UDP6_SERVICE_DATA_FROM_THIS (This); + + // + // Try to get the Udp6 protocol from the ChildHandle. + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6Proto, + gUdp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (Udp6Proto); + + if (Instance->InDestroy) { + return EFI_SUCCESS; + } + + // + // Use the Destroyed flag to avoid the re-entering of the following code. + // + Instance->InDestroy = TRUE; + + // + // Close the Ip6 protocol on the default IpIo. + // + gBS->CloseProtocol ( + Udp6Service->IpIo->ChildHandle, + &gEfiIp6ProtocolGuid, + gUdp6DriverBinding.DriverBindingHandle, + Instance->ChildHandle + ); + // + // Close the Ip6 protocol on this instance's IpInfo. + // + gBS->CloseProtocol ( + Instance->IpInfo->ChildHandle, + &gEfiIp6ProtocolGuid, + gUdp6DriverBinding.DriverBindingHandle, + Instance->ChildHandle + ); + + // + // Uninstall the Udp6Protocol previously installed on the ChildHandle. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiUdp6ProtocolGuid, + (VOID *) &Instance->Udp6Proto, + NULL + ); + if (EFI_ERROR (Status)) { + Instance->InDestroy = FALSE; + return Status; + } + + // + // Reset the configuration in case the instance's consumer forgets to do this. + // + Udp6Proto->Configure (Udp6Proto, NULL); + + // + // Remove the IpInfo this instance consumes. + // + IpIoRemoveIp (Udp6Service->IpIo, Instance->IpInfo); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Remove this instance from the service context data's ChildrenList. + // + RemoveEntryList (&Instance->Link); + Udp6Service->ChildrenNumber--; + + // + // Clean the instance. + // + Udp6CleanInstance (Instance); + + gBS->RestoreTPL (OldTpl); + + FreePool (Instance); + + return EFI_SUCCESS; +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + The entry point for Udp6 driver that installs the driver binding + and component name protocol on its ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install the Udp6DriverBinding and Udp6ComponentName protocols. + // + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gUdp6DriverBinding, + ImageHandle, + &gUdp6ComponentName, + &gUdp6ComponentName2 + ); + if (!EFI_ERROR (Status)) { + // + // Initialize the UDP random port. + // + mUdp6RandomPort = (UINT16)( + ((UINT16) NetRandomInitSeed ()) % + UDP6_PORT_KNOWN + + UDP6_PORT_KNOWN + ); + } + + return Status; +} + + diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6Driver.h b/Core/NetworkPkg/Udp6Dxe/Udp6Driver.h new file mode 100644 index 0000000000..b40c6f91dc --- /dev/null +++ b/Core/NetworkPkg/Udp6Dxe/Udp6Driver.h @@ -0,0 +1,182 @@ +/** @file + Driver Binding functions and Service Binding functions for the Network driver module. + + Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UDP6_DRIVER_H_ +#define _UDP6_DRIVER_H_ + +#include +#include +#include + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind a driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCES This driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES The required system resource can't be allocated. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + This service is called by the EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop the driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. It is optional. + + @retval EFI_SUCCESS This driver removed ControllerHandle. + @retval EFI_DEVICE_ERROR Can't find the NicHandle from the ControllerHandle and specified GUID. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Udp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Udp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a set of I/O services. + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Udp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif + diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6Dxe.inf b/Core/NetworkPkg/Udp6Dxe/Udp6Dxe.inf new file mode 100644 index 0000000000..05dc5ef4fe --- /dev/null +++ b/Core/NetworkPkg/Udp6Dxe/Udp6Dxe.inf @@ -0,0 +1,69 @@ +## @file Udp6Dxe.inf +# UDP packet service based on IPv6 stack. +# +# This module produces EFI UDPv6 Protocol which provides simple packet-oriented +# services to transmit and receive UDP packets. +# +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Udp6Dxe + FILE_GUID = D912C7BC-F098-4367-92BA-E911083C7B0E + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = Udp6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = Udp6Dxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + Udp6Driver.h + Udp6Driver.c + Udp6Impl.c + Udp6Impl.h + ComponentName.c + Udp6Main.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + UefiLib + DebugLib + IpIoLib + NetLib + DpcLib + + +[Protocols] + gEfiIp6ProtocolGuid ## TO_START + gEfiIp6ServiceBindingProtocolGuid ## TO_START + gEfiUdp6ServiceBindingProtocolGuid ## BY_START + gEfiUdp6ProtocolGuid ## BY_START + +[UserExtensions.TianoCore."ExtraFiles"] + Udp6DxeExtra.uni diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6Dxe.uni b/Core/NetworkPkg/Udp6Dxe/Udp6Dxe.uni new file mode 100644 index 0000000000..9a970c50e4 Binary files /dev/null and b/Core/NetworkPkg/Udp6Dxe/Udp6Dxe.uni differ diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni b/Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni new file mode 100644 index 0000000000..e11d89a74e Binary files /dev/null and b/Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni differ diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6Impl.c b/Core/NetworkPkg/Udp6Dxe/Udp6Impl.c new file mode 100644 index 0000000000..40e3aff069 --- /dev/null +++ b/Core/NetworkPkg/Udp6Dxe/Udp6Impl.c @@ -0,0 +1,1965 @@ +/** @file + Udp6 driver's whole implementation. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Udp6Impl.h" + +UINT16 mUdp6RandomPort; + +/** + This function checks and timeouts the I/O datagrams holding by the corresponding + service context. + + @param[in] Event The event this function is registered to. + @param[in] Context The context data registered during the creation of + the Event. + +**/ +VOID +EFIAPI +Udp6CheckTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + This function finds the udp instance by the specified pair. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] Address Pointer to the specified IPv6 address. + @param[in] Port The udp port number. + + @retval TRUE The specified pair is found. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp6FindInstanceByPort ( + IN LIST_ENTRY *InstanceList, + IN EFI_IPv6_ADDRESS *Address, + IN UINT16 Port + ); + +/** + This function is the packet transmitting notify function registered to the IpIo + interface. It's called to signal the udp TxToken when the IpIo layer completes + transmitting of the udp datagram. + + @param[in] Status The completion status of the output udp datagram. + @param[in] Context Pointer to the context data. + @param[in] Sender Specify a EFI_IP6_PROTOCOL for sending. + @param[in] NotifyData Pointer to the notify data. + +**/ +VOID +EFIAPI +Udp6DgramSent ( + IN EFI_STATUS Status, + IN VOID *Context, + IN IP_IO_IP_PROTOCOL Sender, + IN VOID *NotifyData + ); + +/** + This function processes the received datagram passed up by the IpIo layer. + + @param[in] Status The status of this udp datagram. + @param[in] IcmpError The IcmpError code, only available when Status is + EFI_ICMP_ERROR. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA. + @param[in] Packet Pointer to the NET_BUF containing the received udp + datagram. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6DgramRcvd ( + IN EFI_STATUS Status, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet, + IN VOID *Context + ); + +/** + This function cancle the token specified by Arg in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled, if NULL, all + the tokens in this Map will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token + is not the same as that in the Item if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Udp6CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ); + +/** + This function check if the received udp datagram matches with the Instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted + from the received udp datagram. + + @retval TRUE The udp datagram matches the receiving requirements of the Instance. + @retval FALSE The udp datagram doe not match the receiving requirements of the Instance. + +**/ +BOOLEAN +Udp6MatchDgram ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_SESSION_DATA *Udp6Session + ); + +/** + This function removes the Wrap specified by Context and releases relevant resources. + + @param[in] Event The Event this notify function is registered to. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6RecycleRxDataWrap ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + This function wraps the Packet into RxData. + + @param[in] Instance Pointer to the instance context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this + datagram. + + @return Pointer to the structure wrapping the RxData and the Packet. + +**/ +UDP6_RXDATA_WRAP * +Udp6WrapRxData ( + IN UDP6_INSTANCE_DATA *Instance, + IN NET_BUF *Packet, + IN EFI_UDP6_RECEIVE_DATA *RxData + ); + +/** + This function enqueues the received datagram into the instances' receiving queues. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this + datagram. + + @return The times this datagram is enqueued. + +**/ +UINTN +Udp6EnqueueDgram ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN NET_BUF *Packet, + IN EFI_UDP6_RECEIVE_DATA *RxData + ); + +/** + This function delivers the datagrams enqueued in the instances. + + @param[in] Udp6Service Pointer to the udp service context data. + +**/ +VOID +Udp6DeliverDgram ( + IN UDP6_SERVICE_DATA *Udp6Service + ); + +/** + This function demultiplexes the received udp datagram to the apropriate instances. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from + the received datagram. + @param[in] Packet Pointer to the buffer containing the received udp + datagram. + +**/ +VOID +Udp6Demultiplex ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ); + +/** + This function handles the received Icmp Error message and demultiplexes it to the + instance. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] IcmpError The icmp error code. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted + from the received Icmp Error packet. + @param[in, out] Packet Pointer to the Icmp Error packet. + +**/ +VOID +Udp6IcmpHandler ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN OUT NET_BUF *Packet + ); + +/** + This function builds and sends out a icmp port unreachable message. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet + causes this icmp error message. + @param[in] Udp6Header Pointer to the udp header of the datagram causes + this icmp error message. + +**/ +VOID +Udp6SendPortUnreach ( + IN IP_IO *IpIo, + IN EFI_NET_SESSION_DATA *NetSession, + IN VOID *Udp6Header + ); + +/** + Find the key in the netmap + + @param[in] Map The netmap to search within. + @param[in] Key The key to search. + + @return The point to the item contains the Key, or NULL if Key isn't in the map. + +**/ +NET_MAP_ITEM * +Udp6MapMultiCastAddr ( + IN NET_MAP *Map, + IN VOID *Key + ); + +/** + Create the Udp service context data. + + @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. + @param[in] ImageHandle The image handle of this udp6 driver. + @param[in] ControllerHandle The controller handle this udp6 driver binds on. + + @retval EFI_SUCCESS The udp6 service context data was created and + initialized. + @retval EFI_OUT_OF_RESOURCES Cannot allocate memory. + @retval Others An error condition occurred. + +**/ +EFI_STATUS +Udp6CreateService ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_STATUS Status; + IP_IO_OPEN_DATA OpenData; + + ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA)); + + Udp6Service->Signature = UDP6_SERVICE_DATA_SIGNATURE; + Udp6Service->ServiceBinding = mUdp6ServiceBinding; + Udp6Service->ImageHandle = ImageHandle; + Udp6Service->ControllerHandle = ControllerHandle; + Udp6Service->ChildrenNumber = 0; + + InitializeListHead (&Udp6Service->ChildrenList); + + // + // Create the IpIo for this service context. + // + Udp6Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle, IP_VERSION_6); + if (Udp6Service->IpIo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the OpenData used to open the IpIo. + // + CopyMem ( + &OpenData.IpConfigData.Ip6CfgData, + &mIp6IoDefaultIpConfigData, + sizeof (EFI_IP6_CONFIG_DATA) + ); + OpenData.RcvdContext = (VOID *) Udp6Service; + OpenData.SndContext = NULL; + OpenData.PktRcvdNotify = Udp6DgramRcvd; + OpenData.PktSentNotify = Udp6DgramSent; + + // + // Configure and start the IpIo. + // + Status = IpIoOpen (Udp6Service->IpIo, &OpenData); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create the event for Udp timeout checking. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Udp6CheckTimeout, + Udp6Service, + &Udp6Service->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Start the timeout timer event. + // + Status = gBS->SetTimer ( + Udp6Service->TimeoutEvent, + TimerPeriodic, + UDP6_TIMEOUT_INTERVAL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (Udp6Service->TimeoutEvent != NULL) { + gBS->CloseEvent (Udp6Service->TimeoutEvent); + } + + IpIoDestroy (Udp6Service->IpIo); + Udp6Service->IpIo = NULL; + + return Status; +} + + +/** + Clean the Udp service context data. + + @param[in, out] Udp6Service Pointer to the UDP6_SERVICE_DATA. + +**/ +VOID +Udp6CleanService ( + IN OUT UDP6_SERVICE_DATA *Udp6Service + ) +{ + // + // Close the TimeoutEvent timer. + // + gBS->CloseEvent (Udp6Service->TimeoutEvent); + + // + // Destroy the IpIo. + // + IpIoDestroy (Udp6Service->IpIo); + Udp6Service->IpIo = NULL; + + ZeroMem (Udp6Service, sizeof (UDP6_SERVICE_DATA)); +} + + +/** + This function checks and times out the I/O datagrams listed in the + UDP6_SERVICE_DATA which is specified by the input parameter Context. + + + @param[in] Event The event this function registered to. + @param[in] Context The context data registered during the creation of + the Event. + +**/ +VOID +EFIAPI +Udp6CheckTimeout ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UDP6_SERVICE_DATA *Udp6Service; + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + LIST_ENTRY *WrapEntry; + LIST_ENTRY *NextEntry; + UDP6_RXDATA_WRAP *Wrap; + + Udp6Service = (UDP6_SERVICE_DATA *) Context; + NET_CHECK_SIGNATURE (Udp6Service, UDP6_SERVICE_DATA_SIGNATURE); + + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + // + // Iterate all the instances belonging to this service context. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + NET_CHECK_SIGNATURE (Instance, UDP6_INSTANCE_DATA_SIGNATURE); + + if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) { + // + // Skip this instance if it's not configured or no receive timeout. + // + continue; + } + + NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) { + // + // Iterate all the rxdatas belonging to this udp instance. + // + Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP6_RXDATA_WRAP, Link); + + if (Wrap->TimeoutTick < UDP6_TIMEOUT_INTERVAL / 10) { + // + // Remove this RxData if it timeouts. + // + Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap); + } else { + Wrap->TimeoutTick -= UDP6_TIMEOUT_INTERVAL / 10; + } + } + } +} + + +/** + This function intializes the new created udp instance. + + @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. + @param[in, out] Instance Pointer to the un-initialized UDP6_INSTANCE_DATA. + +**/ +VOID +Udp6InitInstance ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN OUT UDP6_INSTANCE_DATA *Instance + ) +{ + // + // Set the signature. + // + Instance->Signature = UDP6_INSTANCE_DATA_SIGNATURE; + + // + // Init the lists. + // + InitializeListHead (&Instance->Link); + InitializeListHead (&Instance->RcvdDgramQue); + InitializeListHead (&Instance->DeliveredDgramQue); + + // + // Init the NET_MAPs. + // + NetMapInit (&Instance->TxTokens); + NetMapInit (&Instance->RxTokens); + NetMapInit (&Instance->McastIps); + + // + // Save the pointer to the UDP6_SERVICE_DATA, and initialize other members. + // + Instance->Udp6Service = Udp6Service; + CopyMem (&Instance->Udp6Proto, &mUdp6Protocol, sizeof (EFI_UDP6_PROTOCOL)); + Instance->IcmpError = EFI_SUCCESS; + Instance->Configured = FALSE; + Instance->IsNoMapping = FALSE; + Instance->InDestroy = FALSE; +} + + +/** + This function cleans the udp instance. + + @param[in, out] Instance Pointer to the UDP6_INSTANCE_DATA to clean. + +**/ +VOID +Udp6CleanInstance ( + IN OUT UDP6_INSTANCE_DATA *Instance + ) +{ + NetMapClean (&Instance->McastIps); + NetMapClean (&Instance->RxTokens); + NetMapClean (&Instance->TxTokens); +} + + +/** + This function finds the udp instance by the specified pair. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] Address Pointer to the specified IPv6 address. + @param[in] Port The udp port number. + + @retval TRUE The specified pair is found. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp6FindInstanceByPort ( + IN LIST_ENTRY *InstanceList, + IN EFI_IPv6_ADDRESS *Address, + IN UINT16 Port + ) +{ + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + EFI_UDP6_CONFIG_DATA *ConfigData; + + NET_LIST_FOR_EACH (Entry, InstanceList) { + // + // Iterate all the udp instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + ConfigData = &Instance->ConfigData; + + if (!Instance->Configured || ConfigData->AcceptAnyPort) { + // + // If the instance is not configured, or the configdata of the instance indicates + // this instance accepts any port, skip it. + // + continue; + } + + if (EFI_IP6_EQUAL (&ConfigData->StationAddress, Address) && + (ConfigData->StationPort == Port) + ) { + // + // If both the address and the port are the same, return TRUE. + // + return TRUE; + } + } + + // + // Return FALSE when matching fails. + // + return FALSE; +} + + +/** + This function tries to bind the udp instance according to the configured port + allocation stragety. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] ConfigData Pointer to the ConfigData of the instance to be + bound. + + @retval EFI_SUCCESS The bound operation completed successfully. + @retval EFI_ACCESS_DENIED The specified by the ConfigData is + already used by other instance. + @retval EFI_OUT_OF_RESOURCES No available port resources. + +**/ +EFI_STATUS +Udp6Bind ( + IN LIST_ENTRY *InstanceList, + IN EFI_UDP6_CONFIG_DATA *ConfigData + ) +{ + EFI_IPv6_ADDRESS *StationAddress; + UINT16 StartPort; + + if (ConfigData->AcceptAnyPort) { + return EFI_SUCCESS; + } + + StationAddress = &ConfigData->StationAddress; + + if (ConfigData->StationPort != 0) { + + if (!ConfigData->AllowDuplicatePort && + Udp6FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort) + ) { + // + // Do not allow duplicate ports and the port is already used by other instance. + // + return EFI_ACCESS_DENIED; + } + } else { + // + // Select a random port for this instance. + // + if (ConfigData->AllowDuplicatePort) { + // + // Just pick up the random port if the instance allows duplicate port. + // + ConfigData->StationPort = mUdp6RandomPort; + } else { + + StartPort = mUdp6RandomPort; + + while (Udp6FindInstanceByPort (InstanceList, StationAddress, mUdp6RandomPort)) { + + mUdp6RandomPort++; + if (mUdp6RandomPort == 0) { + mUdp6RandomPort = UDP6_PORT_KNOWN; + } + + if (mUdp6RandomPort == StartPort) { + // + // No available port. + // + return EFI_OUT_OF_RESOURCES; + } + } + + ConfigData->StationPort = mUdp6RandomPort; + } + + mUdp6RandomPort++; + if (mUdp6RandomPort == 0) { + mUdp6RandomPort = UDP6_PORT_KNOWN; + } + } + return EFI_SUCCESS; +} + + +/** + This function is used to check whether the NewConfigData has any un-reconfigurable + parameters changed compared to the OldConfigData. + + @param[in] OldConfigData Pointer to the current ConfigData the udp instance + uses. + @param[in] NewConfigData Pointer to the new ConfigData. + + @retval TRUE The instance is reconfigurable according to the NewConfigData. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Udp6IsReconfigurable ( + IN EFI_UDP6_CONFIG_DATA *OldConfigData, + IN EFI_UDP6_CONFIG_DATA *NewConfigData + ) +{ + if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) || + (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) || + (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort) + ) { + // + // The receiving filter parameters cannot be changed. + // + return FALSE; + } + + if ((!NewConfigData->AcceptAnyPort) && + (NewConfigData->StationPort != OldConfigData->StationPort) + ) { + // + // The port is not changeable. + // + return FALSE; + } + + if (!EFI_IP6_EQUAL (&NewConfigData->StationAddress, &OldConfigData->StationAddress)) { + // + // The StationAddress is not the same. + // + return FALSE; + } + + + if (!EFI_IP6_EQUAL (&NewConfigData->RemoteAddress, &OldConfigData->RemoteAddress)) { + // + // The remoteaddress is not the same. + // + return FALSE; + } + + if (!NetIp6IsUnspecifiedAddr (&NewConfigData->RemoteAddress) && + (NewConfigData->RemotePort != OldConfigData->RemotePort) + ) { + // + // The RemotePort differs if it's designated in the configdata. + // + return FALSE; + } + + // + // All checks pass, return TRUE. + // + return TRUE; +} + + +/** + This function builds the Ip6 configdata from the Udp6ConfigData. + + @param[in] Udp6ConfigData Pointer to the EFI_UDP6_CONFIG_DATA. + @param[in, out] Ip6ConfigData Pointer to the EFI_IP6_CONFIG_DATA. + +**/ +VOID +Udp6BuildIp6ConfigData ( + IN EFI_UDP6_CONFIG_DATA *Udp6ConfigData, + IN OUT EFI_IP6_CONFIG_DATA *Ip6ConfigData + ) +{ + CopyMem ( + Ip6ConfigData, + &mIp6IoDefaultIpConfigData, + sizeof (EFI_IP6_CONFIG_DATA) + ); + Ip6ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP; + Ip6ConfigData->AcceptPromiscuous = Udp6ConfigData->AcceptPromiscuous; + IP6_COPY_ADDRESS (&Ip6ConfigData->StationAddress, &Udp6ConfigData->StationAddress); + IP6_COPY_ADDRESS (&Ip6ConfigData->DestinationAddress, &Udp6ConfigData->RemoteAddress); + // + // Use the -1 magic number to disable the receiving process of the ip instance. + // + Ip6ConfigData->ReceiveTimeout = (UINT32) (-1); +} + + +/** + This function validates the TxToken. It returns the error code according to the spec. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] TxToken Pointer to the token to be checked. + + @retval EFI_SUCCESS The TxToken is valid. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + Token.Event is NULL; + Token.Packet.TxData is NULL; + Token.Packet.TxData.FragmentCount is zero; + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths; + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentLength + fields is zero; + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentBuffer + fields is NULL; + UdpSessionData.DestinationAddress are not valid + unicast IPv6 addresses if the UdpSessionData is + not NULL; + UdpSessionData.DestinationPort and + ConfigData.RemotePort are all zero if the + UdpSessionData is not NULL. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. + +**/ +EFI_STATUS +Udp6ValidateTxToken ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_COMPLETION_TOKEN *TxToken + ) +{ + EFI_UDP6_TRANSMIT_DATA *TxData; + UINT32 Index; + UINT32 TotalLen; + EFI_UDP6_CONFIG_DATA *ConfigData; + EFI_UDP6_SESSION_DATA *UdpSessionData; + + + if (TxToken->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + TxData = TxToken->Packet.TxData; + + if ((TxData == NULL) || (TxData->FragmentCount == 0)) { + return EFI_INVALID_PARAMETER; + } + + TotalLen = 0; + for (Index = 0; Index < TxData->FragmentCount; Index++) { + + if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) || + (TxData->FragmentTable[Index].FragmentLength == 0) + ) { + // + // If the FragmentBuffer is NULL, or the FragmentLeng is zero. + // + return EFI_INVALID_PARAMETER; + } + + TotalLen += TxData->FragmentTable[Index].FragmentLength; + } + + if (TotalLen != TxData->DataLength) { + // + // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the + // DataLength. + // + return EFI_INVALID_PARAMETER; + } + + ConfigData = &Instance->ConfigData; + UdpSessionData = TxData->UdpSessionData; + + if (UdpSessionData != NULL) { + + if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) { + // + // Ambiguous; no avalaible DestinationPort for this token. + // + return EFI_INVALID_PARAMETER; + } + + if (NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) && + NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) + ) { + // + // The DestinationAddress is not specificed. + // + return EFI_INVALID_PARAMETER; + } + + if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress) && + !NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) + ) { + // + // The ConfigData.RemoteAddress is not zero and the UdpSessionData.DestinationAddress + // is not zero too. + // + return EFI_INVALID_PARAMETER; + } + } else if (NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress)) { + // + // The configured RemoteAddress is all zero, and the user doesn't override the + // destination address. + // + return EFI_INVALID_PARAMETER; + } + + if (TxData->DataLength > UDP6_MAX_DATA_SIZE) { + return EFI_BAD_BUFFER_SIZE; + } + + return EFI_SUCCESS; +} + + +/** + This function checks whether the specified Token duplicates the one in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to + the Token. + @param[in] Context Pointer to the Token to be checked. + + @retval EFI_SUCCESS The Token specified by Context differs from the + one in the Item. + @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item. + +**/ +EFI_STATUS +EFIAPI +Udp6TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_UDP6_COMPLETION_TOKEN *Token; + EFI_UDP6_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_UDP6_COMPLETION_TOKEN *) Context; + TokenInItem = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key; + + if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) { + // + // The Token duplicates with the TokenInItem in case either the two pointers are the + // same, or the Events of these two tokens are the same. + // + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + + +/** + This function calculates the checksum for the Packet, utilizing the pre-calculated + pseudo HeadSum to reduce some overhead. + + @param[in] Packet Pointer to the NET_BUF contains the udp datagram. + @param[in] HeadSum Checksum of the pseudo header, execpt the length + field. + + @return The 16-bit checksum of this udp datagram. + +**/ +UINT16 +Udp6Checksum ( + IN NET_BUF *Packet, + IN UINT16 HeadSum + ) +{ + UINT16 Checksum; + + Checksum = NetbufChecksum (Packet); + Checksum = NetAddChecksum (Checksum, HeadSum); + + Checksum = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize)); + Checksum = (UINT16) (~Checksum); + return Checksum; +} + + +/** + This function removes the specified Token from the TokenMap. + + @param[in] TokenMap Pointer to the NET_MAP containing the tokens. + @param[in] Token Pointer to the Token to be removed. + + @retval EFI_SUCCESS The specified Token is removed from the TokenMap. + @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap. + +**/ +EFI_STATUS +Udp6RemoveToken ( + IN NET_MAP *TokenMap, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ) +{ + NET_MAP_ITEM *Item; + + // + // Find the Token first. + // + Item = NetMapFindKey (TokenMap, (VOID *) Token); + + if (Item != NULL) { + // + // Remove the token if it's found in the map. + // + NetMapRemoveItem (TokenMap, Item, NULL); + + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; +} + + +/** + This function is the packet transmitting notify function registered to the IpIo + interface. It's called to signal the udp TxToken when IpIo layer completes the + transmitting of the udp datagram. + + @param[in] Status The completion status of the output udp datagram. + @param[in] Context Pointer to the context data. + @param[in] Sender Specify a EFI_IP6_PROTOCOL for sending. + @param[in] NotifyData Pointer to the notify data. + +**/ +VOID +EFIAPI +Udp6DgramSent ( + IN EFI_STATUS Status, + IN VOID *Context, + IN IP_IO_IP_PROTOCOL Sender, + IN VOID *NotifyData + ) +{ + UDP6_INSTANCE_DATA *Instance; + EFI_UDP6_COMPLETION_TOKEN *Token; + + Instance = (UDP6_INSTANCE_DATA *) Context; + Token = (EFI_UDP6_COMPLETION_TOKEN *) NotifyData; + + if (Udp6RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) { + // + // The token may be cancelled. Only signal it if the remove operation succeeds. + // + Token->Status = Status; + gBS->SignalEvent (Token->Event); + DispatchDpc (); + } +} + + +/** + This function processes the received datagram passed up by the IpIo layer. + + @param[in] Status The status of this udp datagram. + @param[in] IcmpError The IcmpError code, only available when Status is + EFI_ICMP_ERROR. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA. + @param[in] Packet Pointer to the NET_BUF containing the received udp + datagram. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6DgramRcvd ( + IN EFI_STATUS Status, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet, + IN VOID *Context + ) +{ + NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE); + + // + // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR. + // + if (Status == EFI_SUCCESS) { + + // + // Demultiplex the received datagram. + // + Udp6Demultiplex ((UDP6_SERVICE_DATA *) Context, NetSession, Packet); + } else { + // + // Handle the ICMP6 Error packet. + // + Udp6IcmpHandler ((UDP6_SERVICE_DATA *) Context, IcmpError, NetSession, Packet); + } + + // + // Dispatch the DPC queued by the NotifyFunction of the rx token's events + // that are signaled with received data. + // + DispatchDpc (); +} + + +/** + This function removes the multicast group specified by Arg from the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the Arg, it's the pointer to a + multicast IPv6 Address. This parameter is + optional and may be NULL. + + @retval EFI_SUCCESS The multicast address is removed. + @retval EFI_ABORTED The specified multicast address is removed, and the + Arg is not NULL. + +**/ +EFI_STATUS +EFIAPI +Udp6LeaveGroup ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + EFI_IPv6_ADDRESS *McastIp; + + McastIp = Arg; + + if ((McastIp != NULL) && + !EFI_IP6_EQUAL (McastIp, ((EFI_IPv6_ADDRESS *)Item->Key)) + ) { + // + // McastIp is not NULL and the multicast address contained in the Item + // is not the same as McastIp. + // + return EFI_SUCCESS; + } + + FreePool (Item->Key); + + // + // Remove this Item. + // + NetMapRemoveItem (Map, Item, NULL); + + if (McastIp != NULL) { + // + // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration. + // + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + This function cancle the token specified by Arg in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the token to be cancelled. If NULL, all + the tokens in this Map will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The token is cancelled if Arg is NULL, or the token + is not the same as that in the Item, if Arg is not + NULL. + @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is + cancelled. + +**/ +EFI_STATUS +EFIAPI +Udp6CancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ) +{ + EFI_UDP6_COMPLETION_TOKEN *TokenToCancel; + NET_BUF *Packet; + IP_IO *IpIo; + + if ((Arg != NULL) && (Item->Key != Arg)) { + return EFI_SUCCESS; + } + + if (Item->Value != NULL) { + // + // If the token is a transmit token, the corresponding Packet is recorded in + // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken + // will invoke Udp6DgramSent, the token will be signaled and this Item will + // be removed from the Map there. + // + Packet = (NET_BUF *) (Item->Value); + IpIo = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0])); + + IpIoCancelTxToken (IpIo, Packet); + } else { + // + // The token is a receive token. Abort it and remove it from the Map. + // + TokenToCancel = (EFI_UDP6_COMPLETION_TOKEN *) Item->Key; + NetMapRemoveItem (Map, Item, NULL); + + TokenToCancel->Status = EFI_ABORTED; + gBS->SignalEvent (TokenToCancel->Event); + } + + if (Arg != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + This function removes all the Wrap datas in the RcvdDgramQue. + + @param[in] Instance Pointer to the Udp6 Instance. + +**/ +VOID +Udp6FlushRcvdDgram ( + IN UDP6_INSTANCE_DATA *Instance + ) +{ + UDP6_RXDATA_WRAP *Wrap; + + while (!IsListEmpty (&Instance->RcvdDgramQue)) { + // + // Iterate all the Wraps in the RcvdDgramQue. + // + Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link); + + // + // The Wrap will be removed from the RcvdDgramQue by this function call. + // + Udp6RecycleRxDataWrap (NULL, (VOID *) Wrap); + } +} + + + +/** + Cancel Udp6 tokens from the Udp6 instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Token Pointer to the token to be canceled. If NULL, all + tokens in this instance will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Udp6InstanceCancelToken ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Cancel this token from the TxTokens map. + // + Status = NetMapIterate (&Instance->TxTokens, Udp6CancelTokens, Token); + + if ((Token != NULL) && (Status == EFI_ABORTED)) { + // + // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from + // the TxTokens and returns success. + // + return EFI_SUCCESS; + } + + // + // Try to cancel this token from the RxTokens map in condition either the Token + // is NULL or the specified Token is not in TxTokens. + // + Status = NetMapIterate (&Instance->RxTokens, Udp6CancelTokens, Token); + + if ((Token != NULL) && (Status == EFI_SUCCESS)) { + // + // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the + // TxTokens nor the RxTokens, or say, it's not found. + // + return EFI_NOT_FOUND; + } + + ASSERT ((Token != NULL) || + ((0 == NetMapGetCount (&Instance->TxTokens)) && + (0 == NetMapGetCount (&Instance->RxTokens))) + ); + + return EFI_SUCCESS; +} + + +/** + This function checks if the received udp datagram matches with the Instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Udp6Session Pointer to the EFI_UDP6_SESSION_DATA abstracted + from the received udp datagram. + + @retval TRUE The udp datagram matches the receiving requirements of the Instance. + @retval FALSE The udp datagram does not matche the receiving requirements of the Instance. + +**/ +BOOLEAN +Udp6MatchDgram ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_SESSION_DATA *Udp6Session + ) +{ + EFI_UDP6_CONFIG_DATA *ConfigData; + EFI_IPv6_ADDRESS Destination; + + ConfigData = &Instance->ConfigData; + + if (ConfigData->AcceptPromiscuous) { + // + // Always matches if this instance is in the promiscuous state. + // + return TRUE; + } + + if ((!ConfigData->AcceptAnyPort && (Udp6Session->DestinationPort != ConfigData->StationPort)) || + ((ConfigData->RemotePort != 0) && (Udp6Session->SourcePort != ConfigData->RemotePort)) + ) { + // + // The local port or the remote port doesn't match. + // + return FALSE; + } + + if (!NetIp6IsUnspecifiedAddr (&ConfigData->RemoteAddress) && + !EFI_IP6_EQUAL (&ConfigData->RemoteAddress, &Udp6Session->SourceAddress) + ) { + // + // This datagram doesn't come from the instance's specified sender. + // + return FALSE; + } + + if (NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress) || + EFI_IP6_EQUAL (&Udp6Session->DestinationAddress, &ConfigData->StationAddress) + ) { + // + // The instance is configured to receive datagrams destinated to any station IP or + // the destination address of this datagram matches the configured station IP. + // + return TRUE; + } + + IP6_COPY_ADDRESS (&Destination, &Udp6Session->DestinationAddress); + + if (IP6_IS_MULTICAST (&Destination) && + (NULL != Udp6MapMultiCastAddr (&Instance->McastIps, &Destination)) + ) { + // + // It's a multicast packet and the multicast address is accepted by this instance. + // + return TRUE; + } + + return FALSE; +} + + +/** + This function removes the Wrap specified by Context and release relevant resources. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6RecycleRxDataWrap ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UDP6_RXDATA_WRAP *Wrap; + + Wrap = (UDP6_RXDATA_WRAP *) Context; + + // + // Remove the Wrap from the list it belongs to. + // + RemoveEntryList (&Wrap->Link); + + // + // Free the Packet associated with this Wrap. + // + NetbufFree (Wrap->Packet); + + // + // Close the event. + // + gBS->CloseEvent (Wrap->RxData.RecycleSignal); + + FreePool (Wrap); +} + + +/** + This function wraps the Packet into RxData. + + @param[in] Instance Pointer to the instance context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this + datagram. + + @return Pointer to the structure wrapping the RxData and the Packet. + +**/ +UDP6_RXDATA_WRAP * +Udp6WrapRxData ( + IN UDP6_INSTANCE_DATA *Instance, + IN NET_BUF *Packet, + IN EFI_UDP6_RECEIVE_DATA *RxData + ) +{ + EFI_STATUS Status; + UDP6_RXDATA_WRAP *Wrap; + + // + // Allocate buffer for the Wrap. + // + Wrap = AllocateZeroPool (sizeof (UDP6_RXDATA_WRAP) + + (Packet->BlockOpNum - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA)); + if (Wrap == NULL) { + return NULL; + } + + InitializeListHead (&Wrap->Link); + + CopyMem (&Wrap->RxData, RxData, sizeof(EFI_UDP6_RECEIVE_DATA)); + // + // Create the Recycle event. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Udp6RecycleRxDataWrap, + Wrap, + &Wrap->RxData.RecycleSignal + ); + if (EFI_ERROR (Status)) { + FreePool (Wrap); + return NULL; + } + + Wrap->Packet = Packet; + Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout; + + return Wrap; +} + + +/** + This function enqueues the received datagram into the instances' receiving queues. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] Packet Pointer to the buffer containing the received + datagram. + @param[in] RxData Pointer to the EFI_UDP6_RECEIVE_DATA of this + datagram. + + @return The times this datagram is enqueued. + +**/ +UINTN +Udp6EnqueueDgram ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN NET_BUF *Packet, + IN EFI_UDP6_RECEIVE_DATA *RxData + ) +{ + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + UDP6_RXDATA_WRAP *Wrap; + UINTN Enqueued; + + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + // + // Iterate the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + if (Udp6MatchDgram (Instance, &RxData->UdpSession)) { + // + // Wrap the RxData and put this Wrap into the instances RcvdDgramQue. + // + Wrap = Udp6WrapRxData (Instance, Packet, RxData); + if (Wrap == NULL) { + continue; + } + + NET_GET_REF (Packet); + + InsertTailList (&Instance->RcvdDgramQue, &Wrap->Link); + + Enqueued++; + } + } + + return Enqueued; +} + + +/** + This function delivers the received datagrams to the specified instance. + + @param[in] Instance Pointer to the instance context data. + +**/ +VOID +Udp6InstanceDeliverDgram ( + IN UDP6_INSTANCE_DATA *Instance + ) +{ + UDP6_RXDATA_WRAP *Wrap; + EFI_UDP6_COMPLETION_TOKEN *Token; + NET_BUF *Dup; + EFI_UDP6_RECEIVE_DATA *RxData; + EFI_TPL OldTpl; + + if (!IsListEmpty (&Instance->RcvdDgramQue) && + !NetMapIsEmpty (&Instance->RxTokens) + ) { + + Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP6_RXDATA_WRAP, Link); + + if (NET_BUF_SHARED (Wrap->Packet)) { + // + // Duplicate the Packet if it is shared between instances. + // + Dup = NetbufDuplicate (Wrap->Packet, NULL, 0); + if (Dup == NULL) { + return; + } + + NetbufFree (Wrap->Packet); + + Wrap->Packet = Dup; + } + + NetListRemoveHead (&Instance->RcvdDgramQue); + + Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL); + + // + // Build the FragmentTable and set the FragmentCount in RxData. + // + RxData = &Wrap->RxData; + RxData->FragmentCount = Wrap->Packet->BlockOpNum; + + NetbufBuildExt ( + Wrap->Packet, + (NET_FRAGMENT *) RxData->FragmentTable, + &RxData->FragmentCount + ); + + Token->Status = EFI_SUCCESS; + Token->Packet.RxData = &Wrap->RxData; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Instance->DeliveredDgramQue, &Wrap->Link); + gBS->RestoreTPL (OldTpl); + + gBS->SignalEvent (Token->Event); + } +} + + +/** + This function delivers the datagrams enqueued in the instances. + + @param[in] Udp6Service Pointer to the udp service context data. + +**/ +VOID +Udp6DeliverDgram ( + IN UDP6_SERVICE_DATA *Udp6Service + ) +{ + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + // + // Iterate the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + // + // Deliver the datagrams of this instance. + // + Udp6InstanceDeliverDgram (Instance); + } +} + + +/** + This function demultiplexes the received udp datagram to the appropriate instances. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from + the received datagram. + @param[in] Packet Pointer to the buffer containing the received udp + datagram. + +**/ +VOID +Udp6Demultiplex ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Packet + ) +{ + EFI_UDP_HEADER *Udp6Header; + UINT16 HeadSum; + EFI_UDP6_RECEIVE_DATA RxData; + EFI_UDP6_SESSION_DATA *Udp6Session; + UINTN Enqueued; + + // + // Get the datagram header from the packet buffer. + // + Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Udp6Header != NULL); + + if (Udp6Header->Checksum != 0) { + // + // check the checksum. + // + HeadSum = NetIp6PseudoHeadChecksum ( + &NetSession->Source.v6, + &NetSession->Dest.v6, + EFI_IP_PROTO_UDP, + 0 + ); + + if (Udp6Checksum (Packet, HeadSum) != 0) { + // + // Wrong checksum. + // + return; + } + } + + Udp6Session = &RxData.UdpSession; + Udp6Session->SourcePort = NTOHS (Udp6Header->SrcPort); + Udp6Session->DestinationPort = NTOHS (Udp6Header->DstPort); + + IP6_COPY_ADDRESS (&Udp6Session->SourceAddress, &NetSession->Source); + IP6_COPY_ADDRESS (&Udp6Session->DestinationAddress, &NetSession->Dest); + + // + // Trim the UDP header. + // + NetbufTrim (Packet, UDP6_HEADER_SIZE, TRUE); + + RxData.DataLength = (UINT32) Packet->TotalSize; + + // + // Try to enqueue this datagram into the instances. + // + Enqueued = Udp6EnqueueDgram (Udp6Service, Packet, &RxData); + + if (Enqueued == 0) { + // + // Send the port unreachable ICMP packet before we free this NET_BUF + // + Udp6SendPortUnreach (Udp6Service->IpIo, NetSession, Udp6Header); + } + + // + // Try to free the packet before deliver it. + // + NetbufFree (Packet); + + if (Enqueued > 0) { + // + // Deliver the datagram. + // + Udp6DeliverDgram (Udp6Service); + } +} + + +/** + This function builds and sends out a icmp port unreachable message. + + @param[in] IpIo Pointer to the IP_IO instance. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA of the packet + causes this icmp error message. + @param[in] Udp6Header Pointer to the udp header of the datagram causes + this icmp error message. + +**/ +VOID +Udp6SendPortUnreach ( + IN IP_IO *IpIo, + IN EFI_NET_SESSION_DATA *NetSession, + IN VOID *Udp6Header + ) +{ + NET_BUF *Packet; + UINT32 Len; + IP6_ICMP_ERROR_HEAD *IcmpErrHdr; + UINT8 *Ptr; + IP_IO_OVERRIDE Override; + IP_IO_IP_INFO *IpSender; + EFI_IP6_MODE_DATA *Ip6ModeData; + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6Protocol; + + Ip6ModeData = NULL; + + // + // An ICMPv6 error message MUST NOT be originated as A packet destined to + // 1) an IPv6 multicast address 2) The IPv6 Unspecified Address + // + if (NetSession->IpVersion == IP_VERSION_6) { + if (NetIp6IsUnspecifiedAddr (&NetSession->Dest.v6) || + IP6_IS_MULTICAST (&NetSession->Dest.v6) + ) { + goto EXIT; + } + } + + + IpSender = IpIoFindSender (&IpIo, NetSession->IpVersion, &NetSession->Dest); + + // + // Get the Ipv6 Mode Data. + // + Ip6ModeData = AllocateZeroPool (sizeof (EFI_IP6_MODE_DATA)); + ASSERT (Ip6ModeData != NULL); + + // + // If not finding the related IpSender use the default IpIo to send out + // the port unreachable ICMP message. + // + if (IpSender == NULL) { + Ip6Protocol = IpIo->Ip.Ip6; + } else { + Ip6Protocol = IpSender->Ip.Ip6; + } + + Status = Ip6Protocol->GetModeData ( + Ip6Protocol, + Ip6ModeData, + NULL, + NULL + ); + + if (EFI_ERROR (Status)) { + goto EXIT; + } + // + // The ICMP6 packet length, includes whole invoking packet and ICMP6 error header. + // + Len = NetSession->IpHdrLen + + NTOHS(((EFI_UDP_HEADER *) Udp6Header)->Length) + + sizeof (IP6_ICMP_ERROR_HEAD); + + // + // If the ICMP6 packet length larger than IP MTU, adjust its length to MTU. + // + if (Ip6ModeData->MaxPacketSize < Len) { + Len = Ip6ModeData->MaxPacketSize; + } + + // + // Allocate buffer for the icmp error message. + // + Packet = NetbufAlloc (Len); + if (Packet == NULL) { + goto EXIT; + } + + // + // Allocate space for the IP6_ICMP_ERROR_HEAD. + // + IcmpErrHdr = (IP6_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE); + ASSERT (IcmpErrHdr != NULL); + + // + // Set the required fields for the icmp port unreachable message. + // + IcmpErrHdr->Head.Type = ICMP_V6_DEST_UNREACHABLE; + IcmpErrHdr->Head.Code = ICMP_V6_PORT_UNREACHABLE; + IcmpErrHdr->Head.Checksum = 0; + IcmpErrHdr->Fourth = 0; + + // + // Copy as much of invoking Packet as possible without the ICMPv6 packet + // exceeding the minimum Ipv6 MTU. The length of IP6_ICMP_ERROR_HEAD contains + // the length of EFI_IP6_HEADER, so when using the length of IP6_ICMP_ERROR_HEAD + // for pointer movement that fact should be considered. + // + Ptr = (VOID *) &IcmpErrHdr->Head; + Ptr = (UINT8 *) (UINTN) ((UINTN) Ptr + sizeof (IP6_ICMP_ERROR_HEAD) - sizeof (EFI_IP6_HEADER)); + CopyMem (Ptr, NetSession->IpHdr.Ip6Hdr, NetSession->IpHdrLen); + CopyMem ( + Ptr + NetSession->IpHdrLen, + Udp6Header, + Len - NetSession->IpHdrLen - sizeof (IP6_ICMP_ERROR_HEAD) + sizeof (EFI_IP6_HEADER) + ); + + // + // Set the checksum as zero, and IP6 driver will calcuate it with pseudo header. + // + IcmpErrHdr->Head.Checksum = 0; + + // + // Fill the override data. + // + Override.Ip6OverrideData.FlowLabel = 0; + Override.Ip6OverrideData.HopLimit = 255; + Override.Ip6OverrideData.Protocol = IP6_ICMP; + + // + // Send out this icmp packet. + // + IpIoSend (IpIo, Packet, IpSender, NULL, NULL, &NetSession->Source, &Override); + + NetbufFree (Packet); + +EXIT: + if (Ip6ModeData != NULL) { + FreePool (Ip6ModeData); + } +} + + +/** + This function handles the received Icmp Error message and de-multiplexes it to the + instance. + + @param[in] Udp6Service Pointer to the udp service context data. + @param[in] IcmpError The icmp error code. + @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted + from the received Icmp Error packet. + @param[in, out] Packet Pointer to the Icmp Error packet. + +**/ +VOID +Udp6IcmpHandler ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN UINT8 IcmpError, + IN EFI_NET_SESSION_DATA *NetSession, + IN OUT NET_BUF *Packet + ) +{ + EFI_UDP_HEADER *Udp6Header; + EFI_UDP6_SESSION_DATA Udp6Session; + LIST_ENTRY *Entry; + UDP6_INSTANCE_DATA *Instance; + + Udp6Header = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Udp6Header != NULL); + + IP6_COPY_ADDRESS (&Udp6Session.SourceAddress, &NetSession->Source); + IP6_COPY_ADDRESS (&Udp6Session.DestinationAddress, &NetSession->Dest); + + Udp6Session.SourcePort = NTOHS (Udp6Header->DstPort); + Udp6Session.DestinationPort = NTOHS (Udp6Header->SrcPort); + + NET_LIST_FOR_EACH (Entry, &Udp6Service->ChildrenList) { + // + // Iterate all the instances. + // + Instance = NET_LIST_USER_STRUCT (Entry, UDP6_INSTANCE_DATA, Link); + + if (!Instance->Configured) { + continue; + } + + if (Udp6MatchDgram (Instance, &Udp6Session)) { + // + // Translate the Icmp Error code according to the udp spec. + // + Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, IP_VERSION_6, NULL, NULL); + + if (IcmpError > ICMP_ERR_UNREACH_PORT) { + Instance->IcmpError = EFI_ICMP_ERROR; + } + + // + // Notify the instance with the received Icmp Error. + // + Udp6ReportIcmpError (Instance); + + break; + } + } + + NetbufFree (Packet); +} + + +/** + This function reports the received ICMP error. + + @param[in] Instance Pointer to the udp instance context data. + +**/ +VOID +Udp6ReportIcmpError ( + IN UDP6_INSTANCE_DATA *Instance + ) +{ + EFI_UDP6_COMPLETION_TOKEN *Token; + + if (NetMapIsEmpty (&Instance->RxTokens)) { + // + // There are no receive tokens to deliver the ICMP error. + // + return; + } + + if (EFI_ERROR (Instance->IcmpError)) { + // + // Try to get a RxToken from the RxTokens map. + // + Token = (EFI_UDP6_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL); + + if (Token != NULL) { + // + // Report the error through the Token. + // + Token->Status = Instance->IcmpError; + gBS->SignalEvent (Token->Event); + + // + // Clear the IcmpError. + // + Instance->IcmpError = EFI_SUCCESS; + } + } +} + + +/** + This function is a dummy ext-free function for the NET_BUF created for the output + udp datagram. + + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6NetVectorExtFree ( + IN VOID *Context + ) +{ +} + +/** + Find the key in the netmap. + + @param[in] Map The netmap to search within. + @param[in] Key The key to search. + + @return The point to the item contains the Key, or NULL, if Key isn't in the map. + +**/ +NET_MAP_ITEM * +Udp6MapMultiCastAddr ( + IN NET_MAP *Map, + IN VOID *Key + ) +{ + LIST_ENTRY *Entry; + NET_MAP_ITEM *Item; + EFI_IPv6_ADDRESS *Addr; + + ASSERT (Map != NULL); + NET_LIST_FOR_EACH (Entry, &Map->Used) { + Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link); + Addr = (EFI_IPv6_ADDRESS *) Item->Key; + if (EFI_IP6_EQUAL (Addr, Key)) { + return Item; + } + } + return NULL; +} + diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6Impl.h b/Core/NetworkPkg/Udp6Dxe/Udp6Impl.h new file mode 100644 index 0000000000..09f315f50b --- /dev/null +++ b/Core/NetworkPkg/Udp6Dxe/Udp6Impl.h @@ -0,0 +1,654 @@ +/** @file + Udp6 driver's whole implementation and internal data structures. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UDP6_IMPL_H_ +#define _UDP6_IMPL_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Udp6Driver.h" + +extern EFI_COMPONENT_NAME2_PROTOCOL gUdp6ComponentName2; +extern EFI_COMPONENT_NAME_PROTOCOL gUdp6ComponentName; +extern EFI_UNICODE_STRING_TABLE *gUdp6ControllerNameTable; +extern EFI_SERVICE_BINDING_PROTOCOL mUdp6ServiceBinding; +extern EFI_UDP6_PROTOCOL mUdp6Protocol; +extern UINT16 mUdp6RandomPort; + +// +// Define time out 50 milliseconds +// +#define UDP6_TIMEOUT_INTERVAL (50 * TICKS_PER_MS) +#define UDP6_HEADER_SIZE sizeof (EFI_UDP_HEADER) +#define UDP6_MAX_DATA_SIZE 65507 +#define UDP6_PORT_KNOWN 1024 + +#define UDP6_SERVICE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', '6') +#define UDP6_INSTANCE_DATA_SIGNATURE SIGNATURE_32 ('U', 'd', 'p', 'S') + +#define UDP6_SERVICE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + UDP6_SERVICE_DATA, \ + ServiceBinding, \ + UDP6_SERVICE_DATA_SIGNATURE \ + ) + +#define UDP6_INSTANCE_DATA_FROM_THIS(a) \ + CR ( \ + (a), \ + UDP6_INSTANCE_DATA, \ + Udp6Proto, \ + UDP6_INSTANCE_DATA_SIGNATURE \ + ) +// +// Udp6 service contest data +// +typedef struct _UDP6_SERVICE_DATA { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_HANDLE ImageHandle; + EFI_HANDLE ControllerHandle; + LIST_ENTRY ChildrenList; + UINTN ChildrenNumber; + IP_IO *IpIo; + EFI_EVENT TimeoutEvent; + } UDP6_SERVICE_DATA; + +typedef struct _UDP6_INSTANCE_DATA { + UINT32 Signature; + LIST_ENTRY Link; + UDP6_SERVICE_DATA *Udp6Service; + EFI_UDP6_PROTOCOL Udp6Proto; + EFI_UDP6_CONFIG_DATA ConfigData; + EFI_HANDLE ChildHandle; + BOOLEAN Configured; + BOOLEAN IsNoMapping; + NET_MAP TxTokens; + NET_MAP RxTokens; + NET_MAP McastIps; + LIST_ENTRY RcvdDgramQue; + LIST_ENTRY DeliveredDgramQue; + UINT16 HeadSum; + EFI_STATUS IcmpError; + IP_IO_IP_INFO *IpInfo; + BOOLEAN InDestroy; +} UDP6_INSTANCE_DATA; + +typedef struct _UDP6_RXDATA_WRAP { + LIST_ENTRY Link; + NET_BUF *Packet; + UINT32 TimeoutTick; + EFI_UDP6_RECEIVE_DATA RxData; +} UDP6_RXDATA_WRAP; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +} UDP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +/** + Clean the Udp service context data. + + @param[in, out] Udp6Service Pointer to the UDP6_SERVICE_DATA. + +**/ +VOID +Udp6CleanService ( + IN OUT UDP6_SERVICE_DATA *Udp6Service + ); + +/** + Create the Udp service context data. + + @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. + @param[in] ImageHandle The image handle of this udp6 driver. + @param[in] ControllerHandle The controller handle this udp6 driver binds on. + + @retval EFI_SUCCESS The udp6 service context data was created and + initialized. + @retval EFI_OUT_OF_RESOURCES Cannot allocate memory. + @retval Others An error condition occurred. + +**/ +EFI_STATUS +Udp6CreateService ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE ControllerHandle + ); + +/** + This function cleans the udp instance. + + @param[in, out] Instance Pointer to the UDP6_INSTANCE_DATA to clean. + +**/ +VOID +Udp6CleanInstance ( + IN OUT UDP6_INSTANCE_DATA *Instance + ); + +/** + This function intializes the new created udp instance. + + @param[in] Udp6Service Pointer to the UDP6_SERVICE_DATA. + @param[in, out] Instance Pointer to the un-initialized UDP6_INSTANCE_DATA. + +**/ +VOID +Udp6InitInstance ( + IN UDP6_SERVICE_DATA *Udp6Service, + IN OUT UDP6_INSTANCE_DATA *Instance + ); + +/** + This function reports the received ICMP error. + + @param[in] Instance Pointer to the udp instance context data. + +**/ +VOID +Udp6ReportIcmpError ( + IN UDP6_INSTANCE_DATA *Instance + ); + +/** + This function copies the current operational settings of this EFI UDPv6 Protocol + instance into user-supplied buffers. This function is used optionally to retrieve + the operational mode data of underlying networks or drivers. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[out] Udp6ConfigData The buffer in which the current UDP configuration + data is returned. This parameter is optional and + may be NULL. + @param[out] Ip6ModeData The buffer in which the current EFI IPv6 Protocol + mode data is returned. This parameter is optional + and may be NULL. + @param[out] MnpConfigData The buffer in which the current managed network + configuration data is returned. This parameter + is optional and may be NULL. + @param[out] SnpModeData The buffer in which the simple network mode data + is returned. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED When Udp6ConfigData is queried, no configuration + data is available because this instance has not + been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Udp6GetModeData ( + IN EFI_UDP6_PROTOCOL *This, + OUT EFI_UDP6_CONFIG_DATA *Udp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + This function is used to do the following: + Initialize and start this instance of the EFI UDPv6 Protocol. + Change the filtering rules and operational parameters. + Reset this instance of the EFI UDPv6 Protocol. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] UdpConfigData Pointer to the buffer to set the configuration + data. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The configuration settings were set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When the UdpConifgData.UseAnyStationAddress is set + to true and there is no address available for IP6 + driver to binding source address to this + instance. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + This is NULL. + UdpConfigData.StationAddress is not a valid + unicast IPv6 address. + UdpConfigData.RemoteAddress is not a valid unicast + IPv6 address, if it is not zero. + @retval EFI_ALREADY_STARTED The EFI UDPv6 Protocol instance is already + started/configured and must be stopped/reset + before it can be reconfigured. Only TrafficClass, + HopLimit, ReceiveTimeout, and TransmitTimeout can + be reconfigured without stopping the current + instance of the EFI UDPv6 Protocol. + @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE, and + UdpConfigData.StationPort is already used by another + instance. + @retval EFI_OUT_OF_RESOURCES The EFI UDPv6 Protocol driver cannot allocate + memory for this EFI UDPv6 Protocol instance. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred, and + this instance was not opened. + +**/ +EFI_STATUS +EFIAPI +Udp6Configure ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_CONFIG_DATA *UdpConfigData OPTIONAL + ); + +/** + This function places a sending request to this instance of the EFI UDPv6 Protocol, + alongside the transmit data that was filled by the user. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to the completion token that will be + placed into the transmit queue. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been + started. + @retval EFI_NO_MAPPING The under-layer IPv6 driver was responsible for + choosing a source address for this instance, but + no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + This is NULL. Token is NULL. Token.Event is NULL. + Token.Packet.TxData is NULL. + Token.Packet.TxData.FragmentCount is zero. + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths. + One or more of the + Token.Packet.TxData.FragmentTable[] + .FragmentLength fields is zero. + One or more of the + Token.Packet.TxData.FragmentTable[] + .FragmentBuffer fields is NULL. + One or more of the + Token.Packet.TxData.UdpSessionData. + DestinationAddres are not valid unicast IPv6 + addresses, if the UdpSessionData is not NULL. + Token.Packet.TxData.UdpSessionData. + DestinationAddres is NULL + Token.Packet.TxData.UdpSessionData. + DestinatioPort is zero. + Token.Packet.TxData.UdpSessionData is + NULL and this instance's + UdpConfigData.RemoteAddress is unspecified. + @retval EFI_ACCESS_DENIED The transmit completion token with the same + Token.Event is already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_NOT_FOUND There is no route to the destination network or + address. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. Or the length of the IP header + UDP + header + data length is greater than MTU if + DoNotFragment is TRUE. + +**/ +EFI_STATUS +EFIAPI +Udp6Transmit ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ); + +/** + This function places a completion token into the receive packet queue. This function + is always asynchronous. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token is cached. + @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been + started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources (usually + memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI UDPv6 Protocol instance has been reset to + startup defaults. + @retval EFI_ACCESS_DENIED A receive completion token with the same + Token.Event is already in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Udp6Receive ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ); + +/** + This function is used to abort a pending transmit or receive request. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_UDP6_PROTOCOL.Transmit() or + EFI_UDP6_PROTOCOL.Receive(). This parameter is + optional and may be NULL. + + @retval EFI_SUCCESS The asynchronous I/O request is aborted and + Token.Event is signaled. When Token is NULL, all + pending requests are aborted and their events are + signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O + request is not found in the transmit or receive + queue. It either completed or was not issued by + Transmit() or Receive(). + +**/ +EFI_STATUS +EFIAPI +Udp6Cancel ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + This function can be used by network drivers and applications to increase the rate that + data packets are moved between the communications device and the transmit/receive queues. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or + receive queue. + +**/ +EFI_STATUS +EFIAPI +Udp6Poll ( + IN EFI_UDP6_PROTOCOL *This + ); + +/** + This function is used to enable and disable the multicast group filtering. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join a multicast group. Set to + FALSE to leave one or all multicast groups. + @param[in] MulticastAddress Pointer to multicast group address to join or + leave. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI UDPv6 Protocol instance has not been + started. + @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. JoinFlag is TRUE and + MulticastAddress is NULL. JoinFlag is TRUE and + *MulticastAddress is not a valid multicast + address. + @retval EFI_ALREADY_STARTED The group address is already in the group table + (when JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when + JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Udp6Groups ( + IN EFI_UDP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *MulticastAddress OPTIONAL + ); + +/** + This function tries to bind the udp instance according to the configured port + allocation stragety. + + @param[in] InstanceList Pointer to the head of the list linking the udp + instances. + @param[in] ConfigData Pointer to the ConfigData of the instance to be + bound. + + @retval EFI_SUCCESS The bound operation completed successfully. + @retval EFI_ACCESS_DENIED The specified by the ConfigData is + already used by another instance. + @retval EFI_OUT_OF_RESOURCES No available port resources. + +**/ +EFI_STATUS +Udp6Bind ( + IN LIST_ENTRY *InstanceList, + IN EFI_UDP6_CONFIG_DATA *ConfigData + ); + +/** + This function builds the Ip6 configdata from the Udp6ConfigData. + + @param[in] Udp6ConfigData Pointer to the EFI_UDP6_CONFIG_DATA. + @param[in, out] Ip6ConfigData Pointer to the EFI_IP6_CONFIG_DATA. + +**/ +VOID +Udp6BuildIp6ConfigData ( + IN EFI_UDP6_CONFIG_DATA *Udp6ConfigData, + IN OUT EFI_IP6_CONFIG_DATA *Ip6ConfigData + ); + +/** + This function checks whether the specified Token duplicates with the one in the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM contain the pointer to + the Token. + @param[in] Context Pointer to the Token to be checked. + + @retval EFI_SUCCESS The Token specified by Context differs from the + one in the Item. + @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item. + +**/ +EFI_STATUS +EFIAPI +Udp6TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + This function removes the specified Token from the TokenMap. + + @param[in] TokenMap Pointer to the NET_MAP containing the tokens. + @param[in] Token Pointer to the Token to be removed. + + @retval EFI_SUCCESS The specified Token is removed from the TokenMap. + @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap. + +**/ +EFI_STATUS +Udp6RemoveToken ( + IN NET_MAP *TokenMap, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ); + +/** + This function is used to check whether the NewConfigData has any un-reconfigurable + parameters changed compared to the OldConfigData. + + @param[in] OldConfigData Pointer to the current ConfigData the udp instance + uses. + @param[in] NewConfigData Pointer to the new ConfigData. + + @retval TRUE The instance is reconfigurable according to NewConfigData. + @retval FALSE The instance is not reconfigurable according to NewConfigData. + +**/ +BOOLEAN +Udp6IsReconfigurable ( + IN EFI_UDP6_CONFIG_DATA *OldConfigData, + IN EFI_UDP6_CONFIG_DATA *NewConfigData + ); + +/** + This function removes the multicast group specified by Arg from the Map. + + @param[in] Map Pointer to the NET_MAP. + @param[in] Item Pointer to the NET_MAP_ITEM. + @param[in] Arg Pointer to the Arg. It is the pointer to a + multicast IPv6 Address. This parameter is + optional and may be NULL. + + @retval EFI_SUCCESS The multicast address is removed. + @retval EFI_ABORTED The specified multicast address is removed, and the + Arg is not NULL. + +**/ +EFI_STATUS +EFIAPI +Udp6LeaveGroup ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg OPTIONAL + ); + +/** + This function validates the TxToken, it returns the error code according to the spec. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] TxToken Pointer to the token to be checked. + + @retval EFI_SUCCESS The TxToken is valid. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + Token.Event is NULL. + Token.Packet.TxData is NULL. + Token.Packet.TxData.FragmentCount is zero. + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths. + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentLength + fields is zero. + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentBuffer + fields is NULL. + UdpSessionData.DestinationAddress are not valid + unicast IPv6 addresses if the UdpSessionData is + not NULL. + UdpSessionData.DestinationPort and + ConfigData.RemotePort are all zero if the + UdpSessionData is not NULL. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. + +**/ +EFI_STATUS +Udp6ValidateTxToken ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_COMPLETION_TOKEN *TxToken + ); + +/** + This function is a dummy ext-free function for the NET_BUF created for the output + udp datagram. + + @param[in] Context Pointer to the context data. + +**/ +VOID +EFIAPI +Udp6NetVectorExtFree ( + IN VOID *Context + ); + +/** + This function calculates the checksum for the Packet, utilizing the pre-calculated + pseudo header to reduce overhead. + + @param[in] Packet Pointer to the NET_BUF contains the udp datagram. + @param[in] HeadSum Checksum of the pseudo header execpt the length + field. + + @return The 16-bit checksum of this udp datagram. + +**/ +UINT16 +Udp6Checksum ( + IN NET_BUF *Packet, + IN UINT16 HeadSum + ); + +/** + This function delivers the received datagrams to the specified instance. + + @param[in] Instance Pointer to the instance context data. + +**/ +VOID +Udp6InstanceDeliverDgram ( + IN UDP6_INSTANCE_DATA *Instance + ); + +/** + Cancel Udp6 tokens from the Udp6 instance. + + @param[in] Instance Pointer to the udp instance context data. + @param[in] Token Pointer to the token to be canceled. If NULL, all + tokens in this instance will be cancelled. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The Token is cancelled. + @retval EFI_NOT_FOUND The Token is not found. + +**/ +EFI_STATUS +Udp6InstanceCancelToken ( + IN UDP6_INSTANCE_DATA *Instance, + IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + This function removes all the Wrap datas in the RcvdDgramQue. + + @param[in] Instance Pointer to the Udp6 Instance. + +**/ +VOID +Udp6FlushRcvdDgram ( + IN UDP6_INSTANCE_DATA *Instance + ); + +#endif + diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6Main.c b/Core/NetworkPkg/Udp6Dxe/Udp6Main.c new file mode 100644 index 0000000000..f3e99255f9 --- /dev/null +++ b/Core/NetworkPkg/Udp6Dxe/Udp6Main.c @@ -0,0 +1,853 @@ +/** @file + Contains all EFI_UDP6_PROTOCOL interfaces. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Udp6Impl.h" + +EFI_UDP6_PROTOCOL mUdp6Protocol = { + Udp6GetModeData, + Udp6Configure, + Udp6Groups, + Udp6Transmit, + Udp6Receive, + Udp6Cancel, + Udp6Poll +}; + + +/** + This function copies the current operational settings of this EFI UDPv6 Protocol + instance into user-supplied buffers. This function is used optionally to retrieve + the operational mode data of underlying networks or drivers. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[out] Udp6ConfigData The buffer in which the current UDP configuration + data is returned. This parameter is optional and + may be NULL. + @param[out] Ip6ModeData The buffer in which the current EFI IPv6 Protocol + mode data is returned. This parameter is optional + and may be NULL. + @param[out] MnpConfigData The buffer in which the current managed network + configuration data is returned. This parameter is + optional and may be NULL. + @param[out] SnpModeData The buffer in which the simple network mode data + is returned. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED When Udp6ConfigData is queried, no configuration + data is available because this instance has not + been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Udp6GetModeData ( + IN EFI_UDP6_PROTOCOL *This, + OUT EFI_UDP6_CONFIG_DATA *Udp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + UDP6_INSTANCE_DATA *Instance; + EFI_IP6_PROTOCOL *Ip; + EFI_TPL OldTpl; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured && (Udp6ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (Udp6ConfigData != NULL) { + // + // Set the Udp6ConfigData. + // + CopyMem (Udp6ConfigData, &Instance->ConfigData, sizeof (EFI_UDP6_CONFIG_DATA)); + } + + Ip = Instance->IpInfo->Ip.Ip6; + + // + // Get the underlying Ip6ModeData, MnpConfigData and SnpModeData. + // + Status = Ip->GetModeData (Ip, Ip6ModeData, MnpConfigData, SnpModeData); + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function is used to do the following: + Initialize and start this instance of the EFI UDPv6 Protocol. + Change the filtering rules and operational parameters. + Reset this instance of the EFI UDPv6 Protocol. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] UdpConfigData Pointer to the buffer to set the configuration + data. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The configuration settings were set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When the UdpConifgData.UseAnyStationAddress is set + to true and there is no address available for the IP6 + driver to bind a source address to this instance. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + This is NULL. + UdpConfigData.StationAddress is not a valid + unicast IPv6 address. + UdpConfigData.RemoteAddress is not a valid unicast + IPv6 address if it is not zero. + @retval EFI_ALREADY_STARTED The EFI UDPv6 Protocol instance is already + started/configured and must be stopped/reset + before it can be reconfigured. Only TrafficClass, + HopLimit, ReceiveTimeout, and TransmitTimeout can + be reconfigured without stopping the current + instance of the EFI UDPv6 Protocol. + @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE and + UdpConfigData.StationPort is already used by another + instance. + @retval EFI_OUT_OF_RESOURCES The EFI UDPv6 Protocol driver cannot allocate + memory for this EFI UDPv6 Protocol instance. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred, and + this instance was not opened. + +**/ +EFI_STATUS +EFIAPI +Udp6Configure ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_CONFIG_DATA *UdpConfigData OPTIONAL + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + UDP6_SERVICE_DATA *Udp6Service; + EFI_TPL OldTpl; + EFI_IPv6_ADDRESS StationAddress; + EFI_IPv6_ADDRESS RemoteAddress; + EFI_IP6_CONFIG_DATA Ip6ConfigData; + EFI_IPv6_ADDRESS LocalAddr; + EFI_IPv6_ADDRESS RemoteAddr; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured && (UdpConfigData == NULL)) { + return EFI_SUCCESS; + } + + Udp6Service = Instance->Udp6Service; + Status = EFI_SUCCESS; + ASSERT (Udp6Service != NULL); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (UdpConfigData != NULL) { + + IP6_COPY_ADDRESS (&StationAddress, &UdpConfigData->StationAddress); + IP6_COPY_ADDRESS (&RemoteAddress, &UdpConfigData->RemoteAddress); + + if ((!NetIp6IsUnspecifiedAddr (&StationAddress) && !NetIp6IsValidUnicast (&StationAddress)) || + (!NetIp6IsUnspecifiedAddr (&RemoteAddress) && !NetIp6IsValidUnicast (&RemoteAddress)) + ){ + // + // If not use default address, and StationAddress is not a valid unicast + // if it is not IPv6 address or RemoteAddress is not a valid unicast IPv6 + // address if it is not 0. + // + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if (Instance->Configured) { + // + // The instance is already configured, try to do the re-configuration. + // + if (!Udp6IsReconfigurable (&Instance->ConfigData, UdpConfigData)) { + // + // If the new configuration data wants to change some unreconfigurable + // settings, return EFI_ALREADY_STARTED. + // + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + } + + // + // Save the reconfigurable parameters. + // + Instance->ConfigData.TrafficClass = UdpConfigData->TrafficClass; + Instance->ConfigData.HopLimit = UdpConfigData->HopLimit; + Instance->ConfigData.ReceiveTimeout = UdpConfigData->ReceiveTimeout; + Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout; + } else { + // + // Construct the Ip configuration data from the UdpConfigData. + // + Udp6BuildIp6ConfigData (UdpConfigData, &Ip6ConfigData); + + // + // Configure the Ip instance wrapped in the IpInfo. + // + Status = IpIoConfigIp (Instance->IpInfo, &Ip6ConfigData); + if (EFI_ERROR (Status)) { + if (Status == EFI_NO_MAPPING) { + Instance->IsNoMapping = TRUE; + } + + goto ON_EXIT; + } + + Instance->IsNoMapping = FALSE; + + // + // Save the configuration data. + // + CopyMem ( + &Instance->ConfigData, + UdpConfigData, + sizeof (EFI_UDP6_CONFIG_DATA) + ); + IP6_COPY_ADDRESS (&Instance->ConfigData.StationAddress, &Ip6ConfigData.StationAddress); + // + // Try to allocate the required port resource. + // + Status = Udp6Bind (&Udp6Service->ChildrenList, &Instance->ConfigData); + if (EFI_ERROR (Status)) { + // + // Reset the ip instance if bind fails. + // + IpIoConfigIp (Instance->IpInfo, NULL); + goto ON_EXIT; + } + + // + // Pre calculate the checksum for the pseudo head, ignore the UDP length first. + // + IP6_COPY_ADDRESS (&LocalAddr, &Instance->ConfigData.StationAddress); + IP6_COPY_ADDRESS (&RemoteAddr, &Instance->ConfigData.RemoteAddress); + + Instance->HeadSum = NetIp6PseudoHeadChecksum ( + &LocalAddr, + &RemoteAddr, + EFI_IP_PROTO_UDP, + 0 + ); + + Instance->Configured = TRUE; + } + } else { + // + // UdpConfigData is NULL, reset the instance. + // + Instance->Configured = FALSE; + Instance->IsNoMapping = FALSE; + + // + // Reset the Ip instance wrapped in the IpInfo. + // + IpIoConfigIp (Instance->IpInfo, NULL); + + // + // Cancel all the user tokens. + // + Instance->Udp6Proto.Cancel (&Instance->Udp6Proto, NULL); + + // + // Remove the buffered RxData for this instance. + // + Udp6FlushRcvdDgram (Instance); + + ASSERT (IsListEmpty (&Instance->DeliveredDgramQue)); + } + +ON_EXIT: + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function is used to enable and disable the multicast group filtering. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join a multicast group. Set to + FALSE to leave one or all multicast groups. + @param[in] MulticastAddress Pointer to multicast group address to join or + leave. This parameter is optional and may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI UDPv6 Protocol instance has not been + started. + @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + JoinFlag is TRUE and MulticastAddress is NULL. + JoinFlag is TRUE and *MulticastAddress is not a + valid multicast address. + @retval EFI_ALREADY_STARTED The group address is already in the group table + (when JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when + JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Udp6Groups ( + IN EFI_UDP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *MulticastAddress OPTIONAL + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + EFI_IP6_PROTOCOL *Ip; + EFI_TPL OldTpl; + EFI_IPv6_ADDRESS *McastIp; + + if ((This == NULL) || (JoinFlag && (MulticastAddress == NULL))) { + return EFI_INVALID_PARAMETER; + } + + McastIp = NULL; + + if (JoinFlag) { + if (!IP6_IS_MULTICAST (MulticastAddress)) { + return EFI_INVALID_PARAMETER; + } + + McastIp = AllocateCopyPool (sizeof (EFI_IPv6_ADDRESS), MulticastAddress); + if (McastIp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + Ip = Instance->IpInfo->Ip.Ip6; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Invoke the Ip instance the Udp6 instance consumes to do the group operation. + // + Status = Ip->Groups (Ip, JoinFlag, MulticastAddress); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Keep a local copy of the configured multicast IPs because IpIo receives + // datagrams from the 0 station address IP instance and then UDP delivers to + // the matched instance. This copy of multicast IPs is used to avoid receive + // the mutlicast datagrams destinated to multicast IPs the other instances configured. + // + if (JoinFlag) { + + Status = NetMapInsertTail (&Instance->McastIps, (VOID *) McastIp, NULL); + } else { + + NetMapIterate (&Instance->McastIps, Udp6LeaveGroup, MulticastAddress); + } + +ON_EXIT: + + gBS->RestoreTPL (OldTpl); + + if (EFI_ERROR (Status)) { + if (McastIp != NULL) { + FreePool (McastIp); + } + } + + return Status; +} + + + +/** + This function places a sending request to this instance of the EFI UDPv6 Protocol, + alongside the transmit data that was filled by the user. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to the completion token that will be + placed into the transmit queue. + + @retval EFI_SUCCESS The data was queued for transmission. + @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been + started. + @retval EFI_NO_MAPPING The under-layer IPv6 driver was responsible for + choosing a source address for this instance, but + no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + This is NULL. + Token is NULL. Token.Event is NULL. + Token.Packet.TxData is NULL. + Token.Packet.TxData.FragmentCount is zero. + Token.Packet.TxData.DataLength is not equal to the + sum of fragment lengths. + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentLength + fields is zero. + One or more of the + Token.Packet.TxData.FragmentTable[].FragmentBuffer + fields is NULL. One or more of the + Token.Packet.TxData.UdpSessionData.DestinationAddres + are not valid unicast IPv6 + addresses if the UdpSessionData is not NULL. + Token.Packet.TxData.UdpSessionData. + DestinationAddress is NULL + Token.Packet.TxData.UdpSessionData. + DestinatioPort + is zero. + Token.Packet.TxData.UdpSessionData is NULL and this + instance's UdpConfigData.RemoteAddress is unspecified. + @retval EFI_ACCESS_DENIED The transmit completion token with the same + Token.Event is already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_NOT_FOUND There is no route to the destination network or + address. + @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP + packet size. Or, the length of the IP header + UDP + header + data length is greater than MTU if + DoNotFragment is TRUE. + +**/ +EFI_STATUS +EFIAPI +Udp6Transmit ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + NET_BUF *Packet; + EFI_UDP_HEADER *Udp6Header; + EFI_UDP6_CONFIG_DATA *ConfigData; + EFI_IPv6_ADDRESS Source; + EFI_IPv6_ADDRESS Destination; + EFI_UDP6_TRANSMIT_DATA *TxData; + EFI_UDP6_SESSION_DATA *UdpSessionData; + UDP6_SERVICE_DATA *Udp6Service; + IP_IO_OVERRIDE Override; + UINT16 HeadSum; + EFI_IP_ADDRESS IpDestAddr; + + if ((This == NULL) || (Token == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Validate the Token, if the token is invalid return the error code. + // + Status = Udp6ValidateTxToken (Instance, Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token)) || + EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token)) + ){ + // + // Try to find a duplicate token in the two token maps, if found, return + // EFI_ACCESS_DENIED. + // + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + TxData = Token->Packet.TxData; + + // + // Create a net buffer to hold the user buffer and the udp header. + // + Packet = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + UDP6_HEADER_SIZE, + 0, + Udp6NetVectorExtFree, + NULL + ); + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Store the IpIo in ProtoData. + // + Udp6Service = Instance->Udp6Service; + *((UINTN *) &Packet->ProtoData[0]) = (UINTN) (Udp6Service->IpIo); + + Udp6Header = (EFI_UDP_HEADER *) NetbufAllocSpace (Packet, UDP6_HEADER_SIZE, TRUE); + ASSERT (Udp6Header != NULL); + ConfigData = &Instance->ConfigData; + + // + // Fill the udp header. + // + Udp6Header->SrcPort = HTONS (ConfigData->StationPort); + Udp6Header->DstPort = HTONS (ConfigData->RemotePort); + Udp6Header->Length = HTONS ((UINT16) Packet->TotalSize); + Udp6Header->Checksum = 0; + // + // Set the UDP Header in NET_BUF, this UDP header is for IP6 can fast get the + // Udp header for pseudoHeadCheckSum. + // + Packet->Udp = Udp6Header; + UdpSessionData = TxData->UdpSessionData; + + if (UdpSessionData != NULL) { + // + // Set the Destination according to the specified + // UdpSessionData. + // + + if (UdpSessionData->DestinationPort != 0) { + Udp6Header->DstPort = HTONS (UdpSessionData->DestinationPort); + } + + IP6_COPY_ADDRESS (&Source, &ConfigData->StationAddress); + if (!NetIp6IsUnspecifiedAddr (&UdpSessionData->DestinationAddress)) { + IP6_COPY_ADDRESS (&Destination, &UdpSessionData->DestinationAddress); + } else { + IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress); + } + + // + //Calculate the pseudo head checksum using the overridden parameters. + // + if (!NetIp6IsUnspecifiedAddr (&ConfigData->StationAddress)) { + HeadSum = NetIp6PseudoHeadChecksum ( + &Source, + &Destination, + EFI_IP_PROTO_UDP, + 0 + ); + + // + // calculate the checksum. + // + Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum); + if (Udp6Header->Checksum == 0) { + // + // If the calculated checksum is 0, fill the Checksum field with all ones. + // + Udp6Header->Checksum = 0XFFFF; + } + } else { + // + // Set the checksum is zero if the ConfigData->StationAddress is unspcified + // and the Ipv6 will fill the correct value of this checksum. + // + Udp6Header->Checksum = 0; + + } + } else { + // + // UdpSessionData is NULL, use the address and port information previously configured. + // + IP6_COPY_ADDRESS (&Destination, &ConfigData->RemoteAddress); + + HeadSum = Instance->HeadSum; + // + // calculate the checksum. + // + Udp6Header->Checksum = Udp6Checksum (Packet, HeadSum); + if (Udp6Header->Checksum == 0) { + // + // If the calculated checksum is 0, fill the Checksum field with all ones. + // + Udp6Header->Checksum = 0xffff; + } + } + + + + // + // Fill the IpIo Override data. + // + Override.Ip6OverrideData.Protocol = EFI_IP_PROTO_UDP; + Override.Ip6OverrideData.HopLimit = ConfigData->HopLimit; + Override.Ip6OverrideData.FlowLabel = 0; + + // + // Save the token into the TxToken map. + // + Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet); + if (EFI_ERROR (Status)) { + goto FREE_PACKET; + } + + // + // Send out this datagram through IpIo. + // + if (UdpSessionData != NULL){ + IP6_COPY_ADDRESS (&(IpDestAddr.v6), &Destination); + } else { + ZeroMem (&IpDestAddr.v6, sizeof (EFI_IPv6_ADDRESS)); + } + + Status = IpIoSend ( + Udp6Service->IpIo, + Packet, + Instance->IpInfo, + Instance, + Token, + &IpDestAddr, + &Override + ); + if (EFI_ERROR (Status)) { + // + // Remove this token from the TxTokens. + // + Udp6RemoveToken (&Instance->TxTokens, Token); + } + +FREE_PACKET: + + NetbufFree (Packet); + +ON_EXIT: + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function places a completion token into the receive packet queue. This function + is always asynchronous. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached. + @retval EFI_NOT_STARTED This EFI UDPv6 Protocol instance has not been + started. + @retval EFI_NO_MAPPING When using a default address, configuration (DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. Token is NULL. Token.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources (usually + memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI UDPv6 Protocol instance has been reset to + startup defaults. + @retval EFI_ACCESS_DENIED A receive completion token with the same + Token.Event is already in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Udp6Receive ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp6TokenExist, Token)) || + EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp6TokenExist, Token)) + ){ + // + // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or + // RxTokens map. + // + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + Token->Packet.RxData = NULL; + + // + // Save the token into the RxTokens map. + // + Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL); + if (EFI_ERROR (Status)) { + Status = EFI_NOT_READY; + goto ON_EXIT; + } + + // + // If there is an icmp error, report it. + // + Udp6ReportIcmpError (Instance); + + // + // Try to delivered the received datagrams. + // + Udp6InstanceDeliverDgram (Instance); + + // + // Dispatch the DPC queued by the NotifyFunction of Token->Event. + // + DispatchDpc (); + +ON_EXIT: + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function is used to abort a pending transmit or receive request. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_UDP6_PROTOCOL.Transmit() or + EFI_UDP6_PROTOCOL.Receive(). This parameter is + optional and may be NULL. + + @retval EFI_SUCCESS The asynchronous I/O request was aborted, and + Token.Event was signaled. When Token is NULL, all + pending requests are aborted and their events are + signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O + request is not found in the transmit or receive + queue. It is either completed or not issued by + Transmit() or Receive(). + +**/ +EFI_STATUS +EFIAPI +Udp6Cancel ( + IN EFI_UDP6_PROTOCOL *This, + IN EFI_UDP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + UDP6_INSTANCE_DATA *Instance; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + + if (!Instance->Configured) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Cancle the tokens specified by Token for this instance. + // + Status = Udp6InstanceCancelToken (Instance, Token); + + // + // Dispatch the DPC queued by the NotifyFunction of the canceled token's events. + // + DispatchDpc (); + + gBS->RestoreTPL (OldTpl); + + return Status; +} + + +/** + This function can be used by network drivers and applications to increase the rate that + data packets are moved between the communications device and the transmit/receive queues. + + @param[in] This Pointer to the EFI_UDP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or + receive queue. + +**/ +EFI_STATUS +EFIAPI +Udp6Poll ( + IN EFI_UDP6_PROTOCOL *This + ) +{ + UDP6_INSTANCE_DATA *Instance; + EFI_IP6_PROTOCOL *Ip; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = UDP6_INSTANCE_DATA_FROM_THIS (This); + Ip = Instance->IpInfo->Ip.Ip6; + + // + // Invode the Ip instance consumed by the udp instance to do the poll operation. + // + return Ip->Poll (Ip); +} diff --git a/Core/NetworkPkg/UefiPxeBcDxe/ComponentName.c b/Core/NetworkPkg/UefiPxeBcDxe/ComponentName.c new file mode 100644 index 0000000000..6e48d4aa18 --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/ComponentName.c @@ -0,0 +1,358 @@ +/** @file + UEFI Component Name(2) protocol implementation for UefiPxeBc driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PxeBcImpl.h" + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName = { + PxeBcComponentNameGetDriverName, + PxeBcComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PxeBcComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PxeBcComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPxeBcDriverNameTable[] = { + { + "eng;en", + L"UEFI PXE Base Code Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPxeBcControllerNameTable[] = { + { + "eng;en", + L"PXE Controller" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2( + Language, + This->SupportedLanguages, + mPxeBcDriverNameTable, + DriverName, + (BOOLEAN)(This == &gPxeBcComponentName) + ); +} + + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param[in] ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PxeBcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + PXEBC_PRIVATE_PROTOCOL *Id; + + if (ControllerHandle == NULL || ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + NicHandle = PxeBcGetNicByIp4Children (ControllerHandle); + if (NicHandle == NULL) { + NicHandle = PxeBcGetNicByIp6Children (ControllerHandle); + if (NicHandle == NULL) { + return EFI_UNSUPPORTED; + } + } + + // + // Try to retrieve the private data by PxeBcPrivate protocol. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mPxeBcControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gPxeBcComponentName) + ); +} diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c new file mode 100644 index 0000000000..253115308e --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c @@ -0,0 +1,1259 @@ +/** @file + Boot functions implementation for UefiPxeBc Driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PxeBcImpl.h" + + +/** + Display the string of the boot item. + + If the length of the boot item string beyond 70 Char, just display 70 Char. + + @param[in] Str The pointer to the string. + @param[in] Len The length of the string. + +**/ +VOID +PxeBcDisplayBootItem ( + IN UINT8 *Str, + IN UINT8 Len + ) +{ + UINT8 Tmp; + + // + // Cut off the chars behind 70th. + // + Len = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len); + Tmp = Str[Len]; + Str[Len] = 0; + AsciiPrint ("%a \n", Str); + + // + // Restore the original 70th char. + // + Str[Len] = Tmp; +} + + +/** + Select and maintain the boot prompt if needed. + + @param[in] Private Pointer to PxeBc private data. + + @retval EFI_SUCCESS Selected boot prompt done. + @retval EFI_TIMEOUT Selected boot prompt timed out. + @retval EFI_NOT_FOUND The proxy offer is not Pxe10. + @retval EFI_ABORTED User cancelled the operation. + @retval EFI_NOT_READY Reading the input key from the keyboard has not finish. + +**/ +EFI_STATUS +PxeBcSelectBootPrompt ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + PXEBC_DHCP_PACKET_CACHE *Cache; + PXEBC_VENDOR_OPTION *VendorOpt; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_EVENT TimeoutEvent; + EFI_EVENT DescendEvent; + EFI_INPUT_KEY InputKey; + EFI_STATUS Status; + UINT32 OfferType; + UINT8 Timeout; + UINT8 *Prompt; + UINT8 PromptLen; + INT32 SecCol; + INT32 SecRow; + + TimeoutEvent = NULL; + DescendEvent = NULL; + Mode = Private->PxeBc.Mode; + Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck; + OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType; + + // + // Only DhcpPxe10 and ProxyPxe10 offer needs boot prompt. + // + if (OfferType != PxeOfferTypeProxyPxe10 && OfferType != PxeOfferTypeDhcpPxe10) { + return EFI_NOT_FOUND; + } + + // + // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec. + // + ASSERT (!Mode->UsingIpv6); + + VendorOpt = &Cache->Dhcp4.VendorOpt; + // + // According to the PXE specification 2.1, Table 2-1 PXE DHCP Options, + // we must not consider a boot prompt or boot menu if all of the following hold: + // - the PXE_DISCOVERY_CONTROL tag(6) is present inside the Vendor Options(43), and has bit 3 set + // - a boot file name has been presented in the initial DHCP or ProxyDHCP offer packet. + // + if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) && + Cache->Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + return EFI_ABORTED; + } + + if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) { + return EFI_TIMEOUT; + } + + Timeout = VendorOpt->MenuPrompt->Timeout; + Prompt = VendorOpt->MenuPrompt->Prompt; + PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1); + + // + // The valid scope of Timeout refers to PXE2.1 spec. + // + if (Timeout == 0) { + return EFI_TIMEOUT; + } + if (Timeout == 255) { + return EFI_SUCCESS; + } + + // + // Create and start a timer as timeout event. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer ( + TimeoutEvent, + TimerRelative, + Timeout * TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Create and start a periodic timer as descend event by second. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &DescendEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + DescendEvent, + TimerPeriodic, + TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Display the boot item and cursor on the screen. + // + SecCol = gST->ConOut->Mode->CursorColumn; + SecRow = gST->ConOut->Mode->CursorRow; + + PxeBcDisplayBootItem (Prompt, PromptLen); + + gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow); + AsciiPrint ("(%d) ", Timeout--); + + Status = EFI_TIMEOUT; + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) { + gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow); + AsciiPrint ("(%d) ", Timeout--); + } + if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) { + gBS->Stall (10 * TICKS_PER_MS); + continue; + } + // + // Parse the input key by user. + // If or + is pressed, return success to display the boot menu. + // + if (InputKey.ScanCode == 0) { + + switch (InputKey.UnicodeChar) { + + case CTRL ('c'): + Status = EFI_ABORTED; + break; + + case CTRL ('m'): + case 'm': + case 'M': + Status = EFI_SUCCESS; + break; + + default: + continue; + } + + } else { + + switch (InputKey.ScanCode) { + + case SCAN_F8: + Status = EFI_SUCCESS; + break; + + case SCAN_ESC: + Status = EFI_ABORTED; + break; + + default: + continue; + } + } + + break; + } + + // + // Reset the cursor on the screen. + // + gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1); + +ON_EXIT: + if (DescendEvent != NULL) { + gBS->CloseEvent (DescendEvent); + } + if (TimeoutEvent != NULL) { + gBS->CloseEvent (TimeoutEvent); + } + + return Status; +} + + +/** + Select the boot menu by user's input. + + @param[in] Private Pointer to PxeBc private data. + @param[out] Type The type of the menu. + @param[in] UseDefaultItem Use default item or not. + + @retval EFI_ABORTED User cancel operation. + @retval EFI_SUCCESS Select the boot menu success. + @retval EFI_NOT_READY Read the input key from the keybroad has not finish. + +**/ +EFI_STATUS +PxeBcSelectBootMenu ( + IN PXEBC_PRIVATE_DATA *Private, + OUT UINT16 *Type, + IN BOOLEAN UseDefaultItem + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + PXEBC_DHCP_PACKET_CACHE *Cache; + PXEBC_VENDOR_OPTION *VendorOpt; + EFI_INPUT_KEY InputKey; + UINT32 OfferType; + UINT8 MenuSize; + UINT8 MenuNum; + INT32 TopRow; + UINT16 Select; + UINT16 LastSelect; + UINT8 Index; + BOOLEAN Finish; + CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE]; + PXEBC_BOOT_MENU_ENTRY *MenuItem; + PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM]; + + Finish = FALSE; + Select = 0; + Index = 0; + *Type = 0; + Mode = Private->PxeBc.Mode; + Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck; + OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType; + + // + // There is no specified DhcpPxe10/ProxyPxe10 for IPv6 in PXE and UEFI spec. + // + ASSERT (!Mode->UsingIpv6); + ASSERT (OfferType == PxeOfferTypeProxyPxe10 || OfferType == PxeOfferTypeDhcpPxe10); + + VendorOpt = &Cache->Dhcp4.VendorOpt; + if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) { + return EFI_SUCCESS; + } + + // + // Display the boot menu on the screen. + // + SetMem (Blank, sizeof(Blank), ' '); + + MenuSize = VendorOpt->BootMenuLen; + MenuItem = VendorOpt->BootMenu; + + if (MenuSize == 0) { + return EFI_DEVICE_ERROR; + } + + while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) { + ASSERT (MenuItem != NULL); + MenuArray[Index] = MenuItem; + MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3)); + MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3); + Index++; + } + + if (UseDefaultItem) { + ASSERT (MenuArray[0] != NULL); + CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16)); + *Type = NTOHS (*Type); + return EFI_SUCCESS; + } + + MenuNum = Index; + + for (Index = 0; Index < MenuNum; Index++) { + ASSERT (MenuArray[Index] != NULL); + PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen); + } + + TopRow = gST->ConOut->Mode->CursorRow - MenuNum; + + // + // Select the boot item by user in the boot menu. + // + do { + // + // Highlight selected row. + // + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select); + ASSERT (Select < PXEBC_MENU_MAX_NUM); + ASSERT (MenuArray[Select] != NULL); + Blank[MenuArray[Select]->DescLen] = 0; + AsciiPrint ("%a\r", Blank); + PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum); + LastSelect = Select; + + while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) { + gBS->Stall (10 * TICKS_PER_MS); + } + + if (InputKey.ScanCode == 0) { + switch (InputKey.UnicodeChar) { + case CTRL ('c'): + InputKey.ScanCode = SCAN_ESC; + break; + + case CTRL ('j'): /* linefeed */ + case CTRL ('m'): /* return */ + Finish = TRUE; + break; + + case CTRL ('i'): /* tab */ + case ' ': + case 'd': + case 'D': + InputKey.ScanCode = SCAN_DOWN; + break; + + case CTRL ('h'): /* backspace */ + case 'u': + case 'U': + InputKey.ScanCode = SCAN_UP; + break; + + default: + InputKey.ScanCode = 0; + } + } + + switch (InputKey.ScanCode) { + case SCAN_LEFT: + case SCAN_UP: + if (Select != 0) { + Select--; + } + break; + + case SCAN_DOWN: + case SCAN_RIGHT: + if (++Select == MenuNum) { + Select--; + } + break; + + case SCAN_PAGE_UP: + case SCAN_HOME: + Select = 0; + break; + + case SCAN_PAGE_DOWN: + case SCAN_END: + Select = (UINT16) (MenuNum - 1); + break; + + case SCAN_ESC: + return EFI_ABORTED; + } + + // + // Unhighlight the last selected row. + // + gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect); + ASSERT (LastSelect < PXEBC_MENU_MAX_NUM); + ASSERT (MenuArray[LastSelect] != NULL); + Blank[MenuArray[LastSelect]->DescLen] = 0; + AsciiPrint ("%a\r", Blank); + PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum); + } while (!Finish); + + // + // Swap the byte order. + // + ASSERT (Select < PXEBC_MENU_MAX_NUM); + ASSERT (MenuArray[Select] != NULL); + CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16)); + *Type = NTOHS (*Type); + + return EFI_SUCCESS; +} + + +/** + Parse out the boot information from the last Dhcp4 reply packet. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] BufferSize Size of the boot file to be downloaded. + + @retval EFI_SUCCESS Successfully parsed out all the boot information. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcDhcp4BootInfo ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT UINT64 *BufferSize + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + PXEBC_DHCP4_PACKET_CACHE *Cache4; + UINT16 Value; + PXEBC_VENDOR_OPTION *VendorOpt; + PXEBC_BOOT_SVR_ENTRY *Entry; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Status = EFI_SUCCESS; + *BufferSize = 0; + + // + // Get the last received Dhcp4 reply packet. + // + if (Mode->PxeReplyReceived) { + Cache4 = &Private->PxeReply.Dhcp4; + } else if (Mode->ProxyOfferReceived) { + Cache4 = &Private->ProxyOffer.Dhcp4; + } else { + Cache4 = &Private->DhcpAck.Dhcp4; + } + + ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL); + + // + // Parse the boot server address. + // If prompt/discover is disabled, get the first boot server from the boot servers list. + // Otherwise, parse the boot server Ipv4 address from next server address field in DHCP header. + // If all these fields are not available, use option 54 instead. + // + VendorOpt = &Cache4->VendorOpt; + if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) && IS_VALID_BOOT_SERVERS (VendorOpt->BitMap)) { + Entry = VendorOpt->BootSvr; + if (VendorOpt->BootSvrLen >= sizeof (PXEBC_BOOT_SVR_ENTRY) && Entry->IpCnt > 0) { + CopyMem ( + &Private->ServerIp, + &Entry->IpAddr[0], + sizeof (EFI_IPv4_ADDRESS) + ); + } + } + if (Private->ServerIp.Addr[0] == 0) { + // + // ServerIp.Addr[0] equals zero means we failed to get IP address from boot server list. + // Try to use next server address field. + // + CopyMem ( + &Private->ServerIp, + &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr, + sizeof (EFI_IPv4_ADDRESS) + ); + } + if (Private->ServerIp.Addr[0] == 0) { + // + // Still failed , use the IP address from option 54. + // + CopyMem ( + &Private->ServerIp, + Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + // + // Parse the boot file name by option. + // + Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data; + + if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) { + // + // Parse the boot file size by option. + // + CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value)); + Value = NTOHS (Value); + // + // The field of boot file size is 512 bytes in unit. + // + *BufferSize = 512 * Value; + } else { + // + // Get the bootfile size by tftp command if no option available. + // + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + FALSE, + BufferSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + } + + // + // Save the value of boot file size. + // + Private->BootFileSize = (UINTN) *BufferSize; + + // + // Display all the information: boot server address, boot file name and boot file size. + // + AsciiPrint ("\n Server IP address is "); + PxeBcShowIp4Addr (&Private->ServerIp.v4); + AsciiPrint ("\n NBP filename is %a", Private->BootFileName); + AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize); + + return Status; +} + + +/** + Parse out the boot information from the last Dhcp6 reply packet. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] BufferSize Size of the boot file to be downloaded. + + @retval EFI_SUCCESS Successfully parsed out all the boot information. + @retval EFI_BUFFER_TOO_SMALL + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcDhcp6BootInfo ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT UINT64 *BufferSize + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + PXEBC_DHCP6_PACKET_CACHE *Cache6; + UINT16 Value; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Status = EFI_SUCCESS; + *BufferSize = 0; + + // + // Get the last received Dhcp6 reply packet. + // + if (Mode->PxeReplyReceived) { + Cache6 = &Private->PxeReply.Dhcp6; + } else if (Mode->ProxyOfferReceived) { + Cache6 = &Private->ProxyOffer.Dhcp6; + } else { + Cache6 = &Private->DhcpAck.Dhcp6; + } + + ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + + // + // Parse (m)tftp server ip address and bootfile name. + // + Status = PxeBcExtractBootFileUrl ( + &Private->BootFileName, + &Private->ServerIp.v6, + (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data), + NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set the station address to IP layer. + // + Status = PxeBcSetIp6Address (Private); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Parse the value of boot file size. + // + if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) { + // + // Parse it out if have the boot file parameter option. + // + Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value); + if (EFI_ERROR (Status)) { + return Status; + } + // + // The field of boot file size is 512 bytes in unit. + // + *BufferSize = 512 * Value; + } else { + // + // Send get file size command by tftp if option unavailable. + // + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + FALSE, + BufferSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + } + + // + // Save the value of boot file size. + // + Private->BootFileSize = (UINTN) *BufferSize; + + // + // Display all the information: boot server address, boot file name and boot file size. + // + AsciiPrint ("\n Server IP address is "); + PxeBcShowIp6Addr (&Private->ServerIp.v6); + AsciiPrint ("\n NBP filename is %a", Private->BootFileName); + AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize); + + return Status; +} + + +/** + Extract the discover information and boot server entry from the + cached packets if unspecified. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type The type of bootstrap to perform. + @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO. + @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY. + @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully extracted the information. + @retval EFI_DEVICE_ERROR Failed to extract the information. + +**/ +EFI_STATUS +PxeBcExtractDiscoverInfo ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO **DiscoverInfo, + OUT PXEBC_BOOT_SVR_ENTRY **BootEntry, + OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + PXEBC_DHCP4_PACKET_CACHE *Cache4; + PXEBC_VENDOR_OPTION *VendorOpt; + PXEBC_BOOT_SVR_ENTRY *Entry; + BOOLEAN IsFound; + EFI_PXE_BASE_CODE_DISCOVER_INFO *Info; + UINT16 Index; + + Mode = Private->PxeBc.Mode; + Info = *DiscoverInfo; + + if (Mode->UsingIpv6) { + Info->IpCnt = 1; + Info->UseUCast = TRUE; + + Info->SrvList[0].Type = Type; + Info->SrvList[0].AcceptAnyResponse = FALSE; + + // + // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet. + // + CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + *SrvList = Info->SrvList; + } else { + Entry = NULL; + IsFound = FALSE; + Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4; + VendorOpt = &Cache4->VendorOpt; + + if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) { + // + // Address is not acquired or no discovery options. + // + return EFI_INVALID_PARAMETER; + } + + // + // Parse the boot server entry from the vendor option in the last cached packet. + // + Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl); + Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl); + Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl); + Info->UseUCast = (BOOLEAN) IS_VALID_BOOT_SERVERS (VendorOpt->BitMap); + + if (Info->UseMCast) { + // + // Get the multicast discover ip address from vendor option if has. + // + CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS)); + } + + Info->IpCnt = 0; + + if (Info->UseUCast) { + Entry = VendorOpt->BootSvr; + + while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) { + if (Entry->Type == HTONS (Type)) { + IsFound = TRUE; + break; + } + Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry); + } + + if (!IsFound) { + return EFI_DEVICE_ERROR; + } + + Info->IpCnt = Entry->IpCnt; + if (Info->IpCnt >= 1) { + *DiscoverInfo = AllocatePool (sizeof (*Info) + (Info->IpCnt - 1) * sizeof (**SrvList)); + if (*DiscoverInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (*DiscoverInfo, Info, sizeof (*Info)); + Info = *DiscoverInfo; + } + + for (Index = 0; Index < Info->IpCnt; Index++) { + CopyMem (&Info->SrvList[Index].IpAddr, &Entry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS)); + Info->SrvList[Index].AcceptAnyResponse = !Info->MustUseList; + Info->SrvList[Index].Type = NTOHS (Entry->Type); + } + } + + *BootEntry = Entry; + *SrvList = Info->SrvList; + } + + return EFI_SUCCESS; +} + + +/** + Build the discover packet and send out for boot server. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the destination address. + @param[in] IpCount The count of the server address. + @param[in] SrvList Pointer to the server address list. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDiscoverBootServer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcDhcp6Discover ( + Private, + Type, + Layer, + UseBis, + DestIp + ); + } else { + return PxeBcDhcp4Discover ( + Private, + Type, + Layer, + UseBis, + DestIp, + IpCount, + SrvList + ); + } +} + + +/** + Discover all the boot information for boot file. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] BufferSize Size of the boot file to be downloaded. + + @retval EFI_SUCCESS Successfully obtained all the boot information . + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_ABORTED User cancel current operation. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcDiscoverBootFile ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT UINT64 *BufferSize + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + UINT16 Type; + UINT16 Layer; + BOOLEAN UseBis; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP; + Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL; + + // + // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and + // other pxe boot information. + // + Status = PxeBc->Dhcp (PxeBc, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select a boot server from boot server list. + // + Status = PxeBcSelectBootPrompt (Private); + + if (Status == EFI_SUCCESS) { + // + // Choose by user's input. + // + Status = PxeBcSelectBootMenu (Private, &Type, FALSE); + } else if (Status == EFI_TIMEOUT) { + // + // Choose by default item. + // + Status = PxeBcSelectBootMenu (Private, &Type, TRUE); + } + + if (!EFI_ERROR (Status)) { + + if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) { + // + // Local boot(PXE bootstrap server) need abort + // + return EFI_ABORTED; + } + + // + // Start to discover the boot server to get (m)tftp server ip address, bootfile + // name and bootfile size. + // + UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected); + Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Mode->PxeReplyReceived && !Mode->ProxyOfferReceived) { + // + // Some network boot loader only search the packet in Mode.ProxyOffer to get its server + // IP address, so we need to store a copy of Mode.PxeReply packet into Mode.ProxyOffer. + // + if (Mode->UsingIpv6) { + CopyMem ( + &Mode->ProxyOffer.Dhcpv6, + &Mode->PxeReply.Dhcpv6, + Private->PxeReply.Dhcp6.Packet.Ack.Length + ); + } else { + CopyMem ( + &Mode->ProxyOffer.Dhcpv4, + &Mode->PxeReply.Dhcpv4, + Private->PxeReply.Dhcp4.Packet.Ack.Length + ); + } + Mode->ProxyOfferReceived = TRUE; + } + } + + // + // Parse the boot information. + // + if (Mode->UsingIpv6) { + Status = PxeBcDhcp6BootInfo (Private, BufferSize); + } else { + Status = PxeBcDhcp4BootInfo (Private, BufferSize); + } + + return Status; +} + + +/** + Install PxeBaseCodeCallbackProtocol if not installed before. + + @param[in, out] Private Pointer to PxeBc private data. + @param[out] NewMakeCallback If TRUE, it is a new callback. + Otherwise, it is not new callback. + @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully. + @retval Others Failed to install PxeBaseCodeCallbackProtocol. + +**/ +EFI_STATUS +PxeBcInstallCallback ( + IN OUT PXEBC_PRIVATE_DATA *Private, + OUT BOOLEAN *NewMakeCallback + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_STATUS Status; + + // + // Check whether PxeBaseCodeCallbackProtocol already installed. + // + PxeBc = &Private->PxeBc; + Status = gBS->HandleProtocol ( + Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID **) &Private->PxeBcCallback + ); + if (Status == EFI_UNSUPPORTED) { + + CopyMem ( + &Private->LoadFileCallback, + &gPxeBcCallBackTemplate, + sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL) + ); + + // + // Install a default callback if user didn't offer one. + // + Status = gBS->InstallProtocolInterface ( + &Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + EFI_NATIVE_INTERFACE, + &Private->LoadFileCallback + ); + + (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS); + + Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback); + if (EFI_ERROR (Status)) { + PxeBc->Stop (PxeBc); + return Status; + } + } + + return EFI_SUCCESS; +} + + +/** + Uninstall PxeBaseCodeCallbackProtocol. + + @param[in] Private Pointer to PxeBc private data. + @param[in] NewMakeCallback If TRUE, it is a new callback. + Otherwise, it is not new callback. + +**/ +VOID +PxeBcUninstallCallback ( + IN PXEBC_PRIVATE_DATA *Private, + IN BOOLEAN NewMakeCallback + ) +{ + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + + PxeBc = &Private->PxeBc; + + if (NewMakeCallback) { + + NewMakeCallback = FALSE; + + PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback); + + gBS->UninstallProtocolInterface ( + Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + &Private->LoadFileCallback + ); + } +} + + +/** + Download one of boot file in the list, and it's special for IPv6. + + @param[in] Private Pointer to PxeBc private data. + @param[in, out] BufferSize Size of user buffer for input; + required buffer size for output. + @param[in] Buffer Pointer to user buffer. + + @retval EFI_SUCCESS Read one of boot file in the list successfully. + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_NOT_FOUND There is no proper boot file available. + @retval Others Failed to download boot file in the list. + +**/ +EFI_STATUS +PxeBcReadBootFileList ( + IN PXEBC_PRIVATE_DATA *Private, + IN OUT UINT64 *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + + PxeBc = &Private->PxeBc; + + // + // Try to download the boot file if everything is ready. + // + if (Buffer != NULL) { + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + Buffer, + FALSE, + BufferSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + + + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + return Status; +} + + +/** + Load boot file into user buffer. + + @param[in] Private Pointer to PxeBc private data. + @param[in, out] BufferSize Size of user buffer for input; + required buffer size for output. + @param[in] Buffer Pointer to user buffer. + + @retval EFI_SUCCESS Get all the boot information successfully. + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_ABORTED User cancelled the current operation. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcLoadBootFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + BOOLEAN NewMakeCallback; + UINT64 RequiredSize; + UINT64 CurrentSize; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_MODE *PxeBcMode; + + NewMakeCallback = FALSE; + PxeBc = &Private->PxeBc; + PxeBcMode = &Private->Mode; + CurrentSize = *BufferSize; + RequiredSize = 0; + + // + // Install pxebc callback protocol if hasn't been installed yet. + // + Status = PxeBcInstallCallback (Private, &NewMakeCallback); + if (EFI_ERROR(Status)) { + return Status; + } + + if (Private->BootFileSize == 0) { + // + // Discover the boot information about the bootfile if hasn't. + // + Status = PxeBcDiscoverBootFile (Private, &RequiredSize); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) { + // + // It's error if the required buffer size is beyond the system scope. + // + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } else if (RequiredSize > 0) { + // + // Get the right buffer size of the bootfile required. + // + if (CurrentSize < RequiredSize || Buffer == NULL) { + // + // It's buffer too small if the size of user buffer is smaller than the required. + // + CurrentSize = RequiredSize; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + CurrentSize = RequiredSize; + } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) { + // + // Try to download another bootfile in list if failed to get the filesize of the last one. + // It's special for the case of IPv6. + // + Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer); + goto ON_EXIT; + } + } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) { + // + // It's buffer too small if the size of user buffer is smaller than the required. + // + CurrentSize = Private->BootFileSize; + Status = EFI_BUFFER_TOO_SMALL; + goto ON_EXIT; + } + + // + // Begin to download the bootfile if everything is ready. + // + AsciiPrint ("\n Downloading NBP file...\n"); + if (PxeBcMode->UsingIpv6) { + Status = PxeBcReadBootFileList ( + Private, + &CurrentSize, + Buffer + ); + } else { + Status = PxeBc->Mtftp ( + PxeBc, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + Buffer, + FALSE, + &CurrentSize, + &Private->BlockSize, + &Private->ServerIp, + Private->BootFileName, + NULL, + FALSE + ); + } + +ON_EXIT: + *BufferSize = (UINTN) CurrentSize; + PxeBcUninstallCallback(Private, NewMakeCallback); + + if (Status == EFI_SUCCESS) { + AsciiPrint ("\n Succeed to download NBP file.\n"); + return EFI_SUCCESS; + } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) { + AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n"); + } else if (Status == EFI_DEVICE_ERROR) { + AsciiPrint ("\n PXE-E07: Network device error.\n"); + } else if (Status == EFI_OUT_OF_RESOURCES) { + AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n"); + } else if (Status == EFI_NO_MEDIA) { + AsciiPrint ("\n PXE-E12: Could not detect network connection.\n"); + } else if (Status == EFI_NO_RESPONSE) { + AsciiPrint ("\n PXE-E16: No offer received.\n"); + } else if (Status == EFI_TIMEOUT) { + AsciiPrint ("\n PXE-E18: Server response timeout.\n"); + } else if (Status == EFI_ABORTED) { + AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n"); + } else if (Status == EFI_ICMP_ERROR) { + AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n"); + } else if (Status == EFI_TFTP_ERROR) { + AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n"); + } else if (Status == EFI_NOT_FOUND) { + AsciiPrint ("\n PXE-E53: No boot filename received.\n"); + } else if (Status != EFI_BUFFER_TOO_SMALL) { + AsciiPrint ("\n PXE-E99: Unexpected network error.\n"); + } + + return Status; +} + diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h new file mode 100644 index 0000000000..d998200ce0 --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h @@ -0,0 +1,100 @@ +/** @file + Boot functions declaration for UefiPxeBc Driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_PXEBC_BOOT_H__ +#define __EFI_PXEBC_BOOT_H__ + +#define PXEBC_DISPLAY_MAX_LINE 70 +#define PXEBC_DEFAULT_UDP_OVERHEAD_SIZE 8 +#define PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE 4 + +#define PXEBC_IS_SIZE_OVERFLOWED(x) ((sizeof (UINTN) < sizeof (UINT64)) && ((x) > 0xFFFFFFFF)) + + +/** + Extract the discover information and boot server entry from the + cached packets if unspecified. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type The type of bootstrap to perform. + @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO. + @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY. + @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully extracted the information. + @retval EFI_DEVICE_ERROR Failed to extract the information. + +**/ +EFI_STATUS +PxeBcExtractDiscoverInfo ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO **DiscoverInfo, + OUT PXEBC_BOOT_SVR_ENTRY **BootEntry, + OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList + ); + + +/** + Build the discover packet and send out for boot. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the server address. + @param[in] IpCount The total count of the server address. + @param[in] SrvList Pointer to the server address list. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDiscoverBootServer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ); + + +/** + Load boot file into user buffer. + + @param[in] Private Pointer to PxeBc private data. + @param[in, out] BufferSize Size of user buffer for input; + required buffer size for output. + @param[in] Buffer Pointer to user buffer. + + @retval EFI_SUCCESS Successfully obtained all the boot information. + @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file. + @retval EFI_ABORTED User cancelled the current operation. + @retval Others Failed to parse out the boot information. + +**/ +EFI_STATUS +PxeBcLoadBootFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ); + +#endif diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c new file mode 100644 index 0000000000..587566d4e0 --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c @@ -0,0 +1,1672 @@ +/** @file + Functions implementation related with DHCPv4 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PxeBcImpl.h" + +// +// This is a map from the interested DHCP4 option tags' index to the tag value. +// +UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = { + PXEBC_DHCP4_TAG_BOOTFILE_LEN, + PXEBC_DHCP4_TAG_VENDOR, + PXEBC_DHCP4_TAG_OVERLOAD, + PXEBC_DHCP4_TAG_MSG_TYPE, + PXEBC_DHCP4_TAG_SERVER_ID, + PXEBC_DHCP4_TAG_CLASS_ID, + PXEBC_DHCP4_TAG_BOOTFILE +}; + +// +// There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec. +// +UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32}; + + +/** + Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer. + + @param[in] Buffer Pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag Tag of the required option. + + @retval NULL Failed to find the required option. + @retval Others The position of the required option. + +**/ +EFI_DHCP4_PACKET_OPTION * +PxeBcParseDhcp4Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT8 OptTag + ) +{ + EFI_DHCP4_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP4_PACKET_OPTION *) Buffer; + Offset = 0; + + while (Offset < Length && Option->OpCode != PXEBC_DHCP4_TAG_EOP) { + + if (Option->OpCode == OptTag) { + // + // Found the required option. + // + return Option; + } + + // + // Skip the current option to the next. + // + if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) { + Offset++; + } else { + Offset += Option->Length + 2; + } + + Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; +} + + +/** + Parse the PXE vender options and extract the information from them. + + @param[in] Dhcp4Option Pointer to vendor options in buffer. + @param[in] VendorOption Pointer to structure to store information in vendor options. + +**/ +VOID +PxeBcParseVendorOptions ( + IN EFI_DHCP4_PACKET_OPTION *Dhcp4Option, + IN PXEBC_VENDOR_OPTION *VendorOption + ) +{ + UINT32 *BitMap; + UINT8 VendorOptionLen; + EFI_DHCP4_PACKET_OPTION *PxeOption; + UINT8 Offset; + + BitMap = VendorOption->BitMap; + VendorOptionLen = Dhcp4Option->Length; + PxeOption = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0]; + Offset = 0; + + ASSERT (PxeOption != NULL); + + while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) { + // + // Parse all the interesting PXE vendor options one by one. + // + switch (PxeOption->OpCode) { + + case PXEBC_VENDOR_TAG_MTFTP_IP: + + CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); + break; + + case PXEBC_VENDOR_TAG_MTFTP_CPORT: + + CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort)); + break; + + case PXEBC_VENDOR_TAG_MTFTP_SPORT: + + CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort)); + break; + + case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT: + + VendorOption->MtftpTimeout = *PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_MTFTP_DELAY: + + VendorOption->MtftpDelay = *PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_DISCOVER_CTRL: + + VendorOption->DiscoverCtrl = *PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_DISCOVER_MCAST: + + CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); + break; + + case PXEBC_VENDOR_TAG_BOOT_SERVERS: + + VendorOption->BootSvrLen = PxeOption->Length; + VendorOption->BootSvr = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_BOOT_MENU: + + VendorOption->BootMenuLen = PxeOption->Length; + VendorOption->BootMenu = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_MENU_PROMPT: + + VendorOption->MenuPromptLen = PxeOption->Length; + VendorOption->MenuPrompt = (PXEBC_MENU_PROMPT *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_MCAST_ALLOC: + + CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock)); + CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange)); + break; + + case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES: + + VendorOption->CredTypeLen = PxeOption->Length; + VendorOption->CredType = (UINT32 *) PxeOption->Data; + break; + + case PXEBC_VENDOR_TAG_BOOT_ITEM: + + CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType)); + CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer)); + break; + + default: + // + // Not interesting PXE vendor options. + // + break; + } + + // + // Set the bit map for the special PXE options. + // + SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode); + + // + // Continue to the next option. + // + if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) { + Offset++; + } else { + Offset = (UINT8) (Offset + PxeOption->Length + 2); + } + + PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset); + } +} + + +/** + Build the options buffer for the DHCPv4 request packet. + + @param[in] Private Pointer to PxeBc private data. + @param[out] OptList Pointer to the option pointer array. + @param[in] Buffer Pointer to the buffer to contain the option list. + @param[in] NeedMsgType If TRUE, it is necessary to include the Msg type option. + Otherwise, it is not necessary. + + @return Index The count of the built-in options. + +**/ +UINT32 +PxeBcBuildDhcp4Options ( + IN PXEBC_PRIVATE_DATA *Private, + OUT EFI_DHCP4_PACKET_OPTION **OptList, + IN UINT8 *Buffer, + IN BOOLEAN NeedMsgType + ) +{ + UINT32 Index; + PXEBC_DHCP4_OPTION_ENTRY OptEnt; + UINT16 Value; + + Index = 0; + OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer; + + if (NeedMsgType) { + // + // Append message type. + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MSG_TYPE; + OptList[Index]->Length = 1; + OptEnt.Mesg = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data; + OptEnt.Mesg->Type = PXEBC_DHCP4_MSG_TYPE_REQUEST; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append max message size. + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MAXMSG; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE); + OptEnt.MaxMesgSize = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data; + Value = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8); + CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + } + + // + // Append parameter request list option. + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_PARA_LIST; + OptList[Index]->Length = 35; + OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data; + OptEnt.Para->ParaList[0] = PXEBC_DHCP4_TAG_NETMASK; + OptEnt.Para->ParaList[1] = PXEBC_DHCP4_TAG_TIME_OFFSET; + OptEnt.Para->ParaList[2] = PXEBC_DHCP4_TAG_ROUTER; + OptEnt.Para->ParaList[3] = PXEBC_DHCP4_TAG_TIME_SERVER; + OptEnt.Para->ParaList[4] = PXEBC_DHCP4_TAG_NAME_SERVER; + OptEnt.Para->ParaList[5] = PXEBC_DHCP4_TAG_DNS_SERVER; + OptEnt.Para->ParaList[6] = PXEBC_DHCP4_TAG_HOSTNAME; + OptEnt.Para->ParaList[7] = PXEBC_DHCP4_TAG_BOOTFILE_LEN; + OptEnt.Para->ParaList[8] = PXEBC_DHCP4_TAG_DOMAINNAME; + OptEnt.Para->ParaList[9] = PXEBC_DHCP4_TAG_ROOTPATH; + OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH; + OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU; + OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL; + OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST; + OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN; + OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER; + OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER; + OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR; + OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP; + OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE; + OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID; + OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1; + OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2; + OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID; + OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP; + OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE; + OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID; + OptEnt.Para->ParaList[27] = 0x80; + OptEnt.Para->ParaList[28] = 0x81; + OptEnt.Para->ParaList[29] = 0x82; + OptEnt.Para->ParaList[30] = 0x83; + OptEnt.Para->ParaList[31] = 0x84; + OptEnt.Para->ParaList[32] = 0x85; + OptEnt.Para->ParaList[33] = 0x86; + OptEnt.Para->ParaList[34] = 0x87; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append UUID/Guid-based client identifier option + // + OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UUID; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID); + OptEnt.Uuid = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data; + OptEnt.Uuid->Type = 0; + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID)); + } + + // + // Append client network device interface option + // + OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UNDI; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI); + OptEnt.Undi = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data; + + if (Private->Nii != NULL) { + OptEnt.Undi->Type = Private->Nii->Type; + OptEnt.Undi->MajorVer = Private->Nii->MajorVer; + OptEnt.Undi->MinorVer = Private->Nii->MinorVer; + } else { + OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; + OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; + OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; + } + + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_ARCH; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH); + OptEnt.Arch = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data; + Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE); + CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); + + // + // Append vendor class identify option + // + OptList[Index]->OpCode = PXEBC_DHCP4_TAG_CLASS_ID; + OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID); + OptEnt.Clid = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data; + CopyMem ( + OptEnt.Clid, + DEFAULT_CLASS_ID_DATA, + sizeof (PXEBC_DHCP4_OPTION_CLID) + ); + PxeBcUintnToAscDecWithFormat ( + EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.Clid->ArchitectureType, + sizeof (OptEnt.Clid->ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName)); + PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor)); + PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor)); + } + + Index++; + + return Index; +} + + +/** + Create a template DHCPv4 packet as a seed. + + @param[out] Seed Pointer to the seed packet. + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + +**/ +VOID +PxeBcSeedDhcp4Packet ( + OUT EFI_DHCP4_PACKET *Seed, + IN EFI_UDP4_PROTOCOL *Udp4 + ) +{ + EFI_SIMPLE_NETWORK_MODE Mode; + EFI_DHCP4_HEADER *Header; + + // + // Get IfType and HwAddressSize from SNP mode data. + // + Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode); + + Seed->Size = sizeof (EFI_DHCP4_PACKET); + Seed->Length = sizeof (Seed->Dhcp4); + Header = &Seed->Dhcp4.Header; + ZeroMem (Header, sizeof (EFI_DHCP4_HEADER)); + Header->OpCode = PXEBC_DHCP4_OPCODE_REQUEST; + Header->HwType = Mode.IfType; + Header->HwAddrLen = (UINT8) Mode.HwAddressSize; + CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen); + + Seed->Dhcp4.Magik = PXEBC_DHCP4_MAGIC; + Seed->Dhcp4.Option[0] = PXEBC_DHCP4_TAG_EOP; +} + + +/** + Cache the DHCPv4 packet. + + @param[in] Dst Pointer to the cache buffer for DHCPv4 packet. + @param[in] Src Pointer to the DHCPv4 packet to be cached. + +**/ +VOID +PxeBcCacheDhcp4Packet ( + IN EFI_DHCP4_PACKET *Dst, + IN EFI_DHCP4_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); + Dst->Length = Src->Length; +} + + +/** + Parse the cached DHCPv4 packet, including all the options. + + @param[in] Cache4 Pointer to cached DHCPv4 packet. + + @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. + +**/ +EFI_STATUS +PxeBcParseDhcp4Packet ( + IN PXEBC_DHCP4_PACKET_CACHE *Cache4 + ) +{ + EFI_DHCP4_PACKET *Offer; + EFI_DHCP4_PACKET_OPTION **Options; + EFI_DHCP4_PACKET_OPTION *Option; + PXEBC_OFFER_TYPE OfferType; + UINTN Index; + BOOLEAN IsProxyOffer; + BOOLEAN IsPxeOffer; + UINT8 *Ptr8; + + IsProxyOffer = FALSE; + IsPxeOffer = FALSE; + + ZeroMem (Cache4->OptList, sizeof (Cache4->OptList)); + ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt)); + + Offer = &Cache4->Packet.Offer; + Options = Cache4->OptList; + + // + // Parse DHCPv4 options in this offer, and store the pointers. + // First, try to parse DHCPv4 options from the DHCP optional parameters field. + // + for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { + Options[Index] = PxeBcParseDhcp4Options ( + Offer->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Offer), + mInterestedDhcp4Tags[Index] + ); + } + // + // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132. + // If yes, try to parse options from the BootFileName field, then ServerName field. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD]; + if (Option != NULL) { + if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) { + for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { + if (Options[Index] == NULL) { + Options[Index] = PxeBcParseDhcp4Options ( + (UINT8 *) Offer->Dhcp4.Header.BootFileName, + sizeof (Offer->Dhcp4.Header.BootFileName), + mInterestedDhcp4Tags[Index] + ); + } + } + } + if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_SERVER_NAME) != 0) { + for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { + if (Options[Index] == NULL) { + Options[Index] = PxeBcParseDhcp4Options ( + (UINT8 *) Offer->Dhcp4.Header.ServerName, + sizeof (Offer->Dhcp4.Header.ServerName), + mInterestedDhcp4Tags[Index] + ); + } + } + } + } + + // + // The offer with zero "yiaddr" is a proxy offer. + // + if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) { + IsProxyOffer = TRUE; + } + + // + // The offer with "PXEClient" is a PXE offer. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID]; + if ((Option != NULL) && (Option->Length >= 9) && + (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) { + IsPxeOffer = TRUE; + } + + // + // Parse PXE vendor options in this offer, and store the contents/pointers. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR]; + if (IsPxeOffer && Option != NULL) { + PxeBcParseVendorOptions (Option, &Cache4->VendorOpt); + } + + // + // Parse PXE boot file name: + // According to PXE spec, boot file name should be read from DHCP option 67 (bootfile name) if present. + // Otherwise, read from boot file field in DHCP header. + // + if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + // + // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null + // terminated string. So force to append null terminated character at the end of string. + // + Ptr8 = (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0]; + Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length; + if (*(Ptr8 - 1) != '\0') { + *Ptr8 = '\0'; + } + } else if (Offer->Dhcp4.Header.BootFileName[0] != 0) { + // + // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it. + // Do not count dhcp option header here, or else will destroy the serverhostname. + // + Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) + (&Offer->Dhcp4.Header.BootFileName[0] - + OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0])); + + } + + // + // Determine offer type of the DHCPv4 packet. + // + Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE]; + if (Option == NULL || Option->Data[0] == 0) { + // + // It's a Bootp offer. + // + OfferType = PxeOfferTypeBootp; + + Option = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]; + if (Option == NULL) { + // + // If the Bootp offer without bootfilename, discard it. + // + return EFI_DEVICE_ERROR; + } + } else { + + if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) { + // + // It's a PXE10 offer with PXEClient and discover vendor option. + // + OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10; + } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) { + // + // It's a WFM11a offer with PXEClient and mtftp vendor option. + // But multi-cast download is not supported currently, so discard it. + // + return EFI_DEVICE_ERROR; + } else if (IsPxeOffer) { + // + // It's a BINL offer only with PXEClient. + // + OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl; + } else { + // + // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet. + // + OfferType = PxeOfferTypeDhcpOnly; + } + } + + Cache4->OfferType = OfferType; + + return EFI_SUCCESS; +} + + +/** + Cache the DHCPv4 ack packet, and parse it on demand. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Ack Pointer to the DHCPv4 ack packet. + @param[in] Verified If TRUE, parse the ACK packet and store info into mode data. + +**/ +VOID +PxeBcCopyDhcp4Ack ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PACKET *Ack, + IN BOOLEAN Verified + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + + Mode = Private->PxeBc.Mode; + + PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack); + + if (Verified) { + // + // Parse the ack packet and store it into mode data if needed. + // + PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4); + CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length); + Mode->DhcpAckReceived = TRUE; + } +} + + +/** + Cache the DHCPv4 proxy offer packet according to the received order. + + @param[in] Private Pointer to PxeBc private data. + @param[in] OfferIndex The received order of offer packets. + +**/ +VOID +PxeBcCopyProxyOffer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 OfferIndex + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP4_PACKET *Offer; + + ASSERT (OfferIndex < Private->OfferNum); + ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM); + + Mode = Private->PxeBc.Mode; + Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer; + + // + // Cache the proxy offer packet and parse it. + // + PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer); + PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4); + + // + // Store this packet into mode data. + // + CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length); + Mode->ProxyOfferReceived = TRUE; +} + + +/** + Retry to request bootfile name by the BINL offer. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Index The received order of offer packets. + + @retval EFI_SUCCESS Successfully retried to request bootfile name. + @retval EFI_DEVICE_ERROR Failed to retry bootfile name. + +**/ +EFI_STATUS +PxeBcRetryBinlOffer ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 Index + ) +{ + EFI_DHCP4_PACKET *Offer; + EFI_IP_ADDRESS ServerIp; + EFI_STATUS Status; + PXEBC_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET *Reply; + + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl || + Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl); + + Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; + + // + // Prefer to siaddr in header as next server address. If it's zero, then use option 54. + // + if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) { + CopyMem ( + &ServerIp.Addr[0], + Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data, + sizeof (EFI_IPv4_ADDRESS) + ); + } else { + CopyMem ( + &ServerIp.Addr[0], + &Offer->Dhcp4.Header.ServerAddr, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + Private->IsDoDiscover = FALSE; + Cache4 = &Private->ProxyOffer.Dhcp4; + Reply = &Cache4->Packet.Offer; + + // + // Send another request packet for bootfile name. + // + Status = PxeBcDhcp4Discover ( + Private, + 0, + NULL, + FALSE, + &ServerIp, + 0, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Parse the reply for the last request packet. + // + Status = PxeBcParseDhcp4Packet (Cache4); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Cache4->OfferType != PxeOfferTypeProxyPxe10 && + Cache4->OfferType != PxeOfferTypeProxyWfm11a && + Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + // + // This BINL ack doesn't have discovery option set or multicast option set + // or bootfile name specified. + // + return EFI_DEVICE_ERROR; + } + + // + // Store the reply into mode data. + // + Private->PxeBc.Mode->ProxyOfferReceived = TRUE; + CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length); + + return EFI_SUCCESS; +} + + +/** + Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount. + + @param[in] Private Pointer to PxeBc private data. + @param[in] RcvdOffer Pointer to the received offer packet. + +**/ +VOID +PxeBcCacheDhcp4Offer ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PACKET *RcvdOffer + ) +{ + PXEBC_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET *Offer; + PXEBC_OFFER_TYPE OfferType; + + ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM); + Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4; + Offer = &Cache4->Packet.Offer; + + // + // Cache the content of DHCPv4 packet firstly. + // + PxeBcCacheDhcp4Packet (Offer, RcvdOffer); + + // + // Validate the DHCPv4 packet, and parse the options and offer type. + // + if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) { + return; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache4->OfferType; + ASSERT (OfferType < PxeOfferTypeMax); + + if (OfferType == PxeOfferTypeBootp) { + // + // It's a Bootp offer, only cache the first one, and discard the others. + // + if (Private->OfferCount[OfferType] == 0) { + Private->OfferIndex[OfferType][0] = Private->OfferNum; + Private->OfferCount[OfferType] = 1; + } else { + return; + } + } else { + ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM); + if (IS_PROXY_DHCP_OFFER (Offer)) { + // + // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer. + // + Private->IsProxyRecved = TRUE; + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Cache all proxy BINL offers. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } else if (Private->OfferCount[OfferType] > 0) { + // + // Only cache the first PXE10/WFM11a offer, and discard the others. + // + Private->OfferIndex[OfferType][0] = Private->OfferNum; + Private->OfferCount[OfferType] = 1; + } else { + return ; + } + } else { + // + // It's a DHCPv4 offer with yiaddr, and cache them all. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } + } + + Private->OfferNum++; +} + + +/** + Select an DHCPv4 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private Pointer to PxeBc private data. + +**/ +VOID +PxeBcSelectDhcp4Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + UINT32 Index; + UINT32 OfferIndex; + EFI_DHCP4_PACKET *Offer; + + Private->SelectIndex = 0; + + if (Private->IsOfferSorted) { + // + // Select offer by default policy. + // + if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) { + // + // 1. DhcpPxe10 offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) { + // + // 2. DhcpWfm11a offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) { + // + // 3. DhcpOnly offer and ProxyPxe10 offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyPxe10; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) { + // + // 4. DhcpOnly offer and ProxyWfm11a offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyWfm11a; + + } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) { + // + // 5. DhcpBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyBinl] > 0) { + // + // 6. DhcpOnly offer and ProxyBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyBinl; + + } else { + // + // 7. DhcpOnly offer with bootfilename. + // + for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) { + OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index]; + if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + Private->SelectIndex = OfferIndex + 1; + break; + } + } + // + // 8. Bootp offer with bootfilename. + // + OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0]; + if (Private->SelectIndex == 0 && + Private->OfferCount[PxeOfferTypeBootp] > 0 && + Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { + Private->SelectIndex = OfferIndex + 1; + } + } + } else { + // + // Select offer by received order. + // + for (Index = 0; Index < Private->OfferNum; Index++) { + + Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; + + if (IS_PROXY_DHCP_OFFER (Offer)) { + // + // Skip proxy offers + // + continue; + } + + if (!Private->IsProxyRecved && + Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly && + Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + // + // Skip if DhcpOnly offer without any other proxy offers or bootfilename. + // + continue; + } + + // + // Record the index of the select offer. + // + Private->SelectIndex = Index + 1; + break; + } + } +} + + +/** + Handle the DHCPv4 offer packet. + + @param[in] Private Pointer to PxeBc private data. + + @retval EFI_SUCCESS Handled the DHCPv4 offer packet successfully. + @retval EFI_NO_RESPONSE No response to the following request packet. + @retval EFI_NOT_FOUND No boot filename received. + +**/ +EFI_STATUS +PxeBcHandleDhcp4Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + PXEBC_DHCP4_PACKET_CACHE *Cache4; + EFI_DHCP4_PACKET_OPTION **Options; + UINT32 Index; + EFI_DHCP4_PACKET *Offer; + PXEBC_OFFER_TYPE OfferType; + UINT32 ProxyIndex; + UINT32 SelectIndex; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP4_PACKET *Ack; + + ASSERT (Private->SelectIndex > 0); + SelectIndex = (UINT32) (Private->SelectIndex - 1); + ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM); + Cache4 = &Private->OfferBuffer[SelectIndex].Dhcp4; + Options = Cache4->OptList; + Status = EFI_SUCCESS; + + if (Cache4->OfferType == PxeOfferTypeDhcpBinl) { + // + // DhcpBinl offer is selected, so need try to request bootfilename by this offer. + // + if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) { + Status = EFI_NO_RESPONSE; + } + } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) { + + if (Private->IsProxyRecved) { + // + // DhcpOnly offer is selected, so need try to request bootfile name. + // + ProxyIndex = 0; + if (Private->IsOfferSorted) { + // + // The proxy offer should be determined if select by default policy. + // IsOfferSorted means all offers are labeled by OfferIndex. + // + ASSERT (Private->SelectProxyType < PxeOfferTypeMax); + ASSERT (Private->OfferCount[Private->SelectProxyType] > 0); + + if (Private->SelectProxyType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfile name. + // + for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) { + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index]; + if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) { + break; + } + } + if (Index == Private->OfferCount[Private->SelectProxyType]) { + Status = EFI_NO_RESPONSE; + } + } else { + // + // For other proxy offers, only one is buffered. + // + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + } + } else { + // + // The proxy offer should not be determined if select by received order. + // + Status = EFI_NO_RESPONSE; + + for (Index = 0; Index < Private->OfferNum; Index++) { + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; + OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType; + if (!IS_PROXY_DHCP_OFFER (Offer)) { + // + // Skip non proxy DHCPv4 offers. + // + continue; + } + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfile name. + // + if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) { + continue; + } + } + + Private->SelectProxyType = OfferType; + ProxyIndex = Index; + Status = EFI_SUCCESS; + break; + } + } + + if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) { + // + // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it. + // + PxeBcCopyProxyOffer (Private, ProxyIndex); + } + } else { + // + // Othewise, the bootfile name must be included in DhcpOnly offer. + // + if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { + Status = EFI_NOT_FOUND; + } + } + } + + if (!EFI_ERROR (Status)) { + // + // All PXE boot information is ready by now. + // + Mode = Private->PxeBc.Mode; + Offer = &Cache4->Packet.Offer; + Ack = &Private->DhcpAck.Dhcp4.Packet.Ack; + if (Cache4->OfferType == PxeOfferTypeBootp) { + // + // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply + // should be taken as ack. + // + Ack = Offer; + } + + PxeBcCopyDhcp4Ack (Private, Ack, TRUE); + Mode->DhcpDiscoverValid = TRUE; + } + + return Status; +} + + +/** + EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This Pointer to the EFI DHCPv4 Protocol. + @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver. + @param[in] Dhcp4Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv4 packet that is going to be sent or already received. + @param[out] NewPacket The packet that is used to replace the above Packet. + + @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol + driver will continue to wait for more DHCPOFFER packets until the + retry timeout expires. + @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process + and return to the Dhcp4Init or Dhcp4InitReboot state. + +**/ +EFI_STATUS +EFIAPI +PxeBcDhcp4CallBack ( + IN EFI_DHCP4_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP4_STATE CurrentState, + IN EFI_DHCP4_EVENT Dhcp4Event, + IN EFI_DHCP4_PACKET *Packet OPTIONAL, + OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_DHCP4_PACKET_OPTION *MaxMsgSize; + UINT16 Value; + EFI_STATUS Status; + BOOLEAN Received; + + if ((Dhcp4Event != Dhcp4RcvdOffer) && + (Dhcp4Event != Dhcp4SelectOffer) && + (Dhcp4Event != Dhcp4SendDiscover) && + (Dhcp4Event != Dhcp4RcvdAck)) { + return EFI_SUCCESS; + } + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = Private->PxeBc.Mode; + Callback = Private->PxeBcCallback; + + // + // Override the Maximum DHCP Message Size. + // + MaxMsgSize = PxeBcParseDhcp4Options ( + Packet->Dhcp4.Option, + GET_OPTION_BUFFER_LEN (Packet), + PXEBC_DHCP4_TAG_MAXMSG + ); + if (MaxMsgSize != NULL) { + Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8); + CopyMem (MaxMsgSize->Data, &Value, sizeof (Value)); + } + + // + // Callback to user if any packets sent or received. + // + if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) { + Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck); + Status = Callback->Callback ( + Callback, + Private->Function, + Received, + Packet->Length, + (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4 + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + return EFI_ABORTED; + } + } + + Status = EFI_SUCCESS; + + switch (Dhcp4Event) { + + case Dhcp4SendDiscover: + // + // Cache the DHCPv4 discover packet to mode data directly. + // It need to check SendGuid as well as Dhcp4SendRequest. + // + CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length); + + case Dhcp4SendRequest: + if (Mode->SendGUID) { + // + // Send the system Guid instead of the MAC address as the hardware address if required. + // + if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID)); + } + Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID); + } + break; + + case Dhcp4RcvdOffer: + Status = EFI_NOT_READY; + if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) { + // + // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + PxeBcCacheDhcp4Offer (Private, Packet); + } + break; + + case Dhcp4SelectOffer: + // + // Select offer by the default policy or by order, and record the SelectIndex + // and SelectProxyType. + // + PxeBcSelectDhcp4Offer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer; + } + break; + + case Dhcp4RcvdAck: + // + // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data + // without verification. + // + ASSERT (Private->SelectIndex != 0); + + PxeBcCopyDhcp4Ack (Private, Packet, FALSE); + break; + + default: + break; + } + + return Status; +} + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the server address. + @param[in] IpCount The total count of the server address. + @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDhcp4Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT Sport; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN Token; + BOOLEAN IsBCast; + EFI_STATUS Status; + UINT16 RepIndex; + UINT16 SrvIndex; + UINT16 TryIndex; + EFI_DHCP4_LISTEN_POINT ListenPoint; + EFI_DHCP4_PACKET *Response; + UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE]; + EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM]; + UINT32 OptCount; + EFI_DHCP4_PACKET_OPTION *PxeOpt; + PXEBC_OPTION_BOOT_ITEM *PxeBootItem; + UINT8 VendorOptLen; + UINT32 Xid; + + Mode = Private->PxeBc.Mode; + Dhcp4 = Private->Dhcp4; + Status = EFI_SUCCESS; + + ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN)); + + // + // Use broadcast if destination address not specified. + // + if (DestIp == NULL) { + Sport = PXEBC_DHCP4_S_PORT; + IsBCast = TRUE; + } else { + Sport = PXEBC_BS_DISCOVER_PORT; + IsBCast = FALSE; + } + + if (!UseBis && Layer != NULL) { + *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK; + } + + // + // Build all the options for the request packet. + // + OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE); + + if (Private->IsDoDiscover) { + // + // Add vendor option of PXE_BOOT_ITEM + // + VendorOptLen = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1); + OptList[OptCount] = AllocateZeroPool (VendorOptLen); + if (OptList[OptCount] == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OptList[OptCount]->OpCode = PXEBC_DHCP4_TAG_VENDOR; + OptList[OptCount]->Length = (UINT8) (VendorOptLen - 2); + PxeOpt = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data; + PxeOpt->OpCode = PXEBC_VENDOR_TAG_BOOT_ITEM; + PxeOpt->Length = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM); + PxeBootItem = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data; + PxeBootItem->Type = HTONS (Type); + PxeOpt->Data[PxeOpt->Length] = PXEBC_DHCP4_TAG_EOP; + + if (Layer != NULL) { + PxeBootItem->Layer = HTONS (*Layer); + } + + OptCount++; + } + + // + // Build the request packet with seed packet and option list. + // + Status = Dhcp4->Build ( + Dhcp4, + &Private->SeedPacket, + 0, + NULL, + OptCount, + OptList, + &Token.Packet + ); + // + // Free the vendor option of PXE_BOOT_ITEM. + // + if (Private->IsDoDiscover) { + FreePool (OptList[OptCount - 1]); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Mode->SendGUID) { + if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) { + // + // Zero the Guid to indicate NOT programable if failed to get system Guid. + // + ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID)); + } + Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID); + } + + // + // Set fields of the token for the request packet. + // + Xid = NET_RANDOM (NetRandomInitSeed ()); + Token.Packet->Dhcp4.Header.Xid = HTONL (Xid); + Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0)); + CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); + + Token.RemotePort = Sport; + + if (IsBCast) { + SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff); + } else { + CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS)); + } + + CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS)); + + if (!IsBCast) { + Token.ListenPointCount = 1; + Token.ListenPoints = &ListenPoint; + Token.ListenPoints[0].ListenPort = PXEBC_BS_DISCOVER_PORT; + CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS)); + CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS)); + } + + // + // Send out the request packet to discover the bootfile. + // + for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) { + + Token.TimeoutValue = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex); + Token.Packet->Dhcp4.Header.Seconds = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1)); + + Status = Dhcp4->TransmitReceive (Dhcp4, &Token); + if (Token.Status != EFI_TIMEOUT) { + break; + } + } + + if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) { + // + // No server response our PXE request + // + Status = EFI_TIMEOUT; + } + + if (!EFI_ERROR (Status)) { + + RepIndex = 0; + SrvIndex = 0; + Response = Token.ResponseList; + // + // Find the right PXE Reply according to server address. + // + while (RepIndex < Token.ResponseCount) { + + while (SrvIndex < IpCount) { + if (SrvList[SrvIndex].AcceptAnyResponse) { + break; + } + if ((SrvList[SrvIndex].Type == Type) && + EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &SrvList[SrvIndex].IpAddr)) { + break; + } + SrvIndex++; + } + + if ((IpCount != SrvIndex) || (IpCount == 0)) { + break; + } + + SrvIndex = 0; + RepIndex++; + + Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size); + } + + if (RepIndex < Token.ResponseCount) { + // + // Cache the right PXE reply packet here, set valid flag later. + // Especially for PXE discover packet, store it into mode data here. + // + if (Private->IsDoDiscover) { + PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response); + CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length); + } else { + PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response); + } + } else { + // + // Not found the right PXE reply packet. + // + Status = EFI_NOT_FOUND; + } + if (Token.ResponseList != NULL) { + FreePool (Token.ResponseList); + } + } + + FreePool (Token.Packet); + return Status; +} + +/** + Switch the Ip4 policy to static. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The policy is already configured to static. + @retval Others Other error as indicated.. + +**/ +EFI_STATUS +PxeBcSetIp4Policy ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + EFI_IP4_CONFIG2_POLICY Policy; + UINTN DataSize; + + Ip4Config2 = Private->Ip4Config2; + DataSize = sizeof (EFI_IP4_CONFIG2_POLICY); + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + &DataSize, + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Policy != Ip4Config2PolicyStatic) { + Policy = Ip4Config2PolicyStatic; + Status= Ip4Config2->SetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + sizeof (EFI_IP4_CONFIG2_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +PxeBcDhcp4Dora ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PROTOCOL *Dhcp4 + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeMode; + EFI_DHCP4_CONFIG_DATA Config; + EFI_DHCP4_MODE_DATA Mode; + EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM]; + UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE]; + UINT32 OptCount; + EFI_STATUS Status; + + ASSERT (Dhcp4 != NULL); + + Status = EFI_SUCCESS; + PxeMode = Private->PxeBc.Mode; + + // + // Build option list for the request packet. + // + OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE); + ASSERT (OptCount> 0); + + ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA)); + ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); + + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp4Callback = PxeBcDhcp4CallBack; + Config.CallbackContext = Private; + Config.DiscoverTryCount = PXEBC_DHCP_RETRIES; + Config.DiscoverTimeout = mPxeDhcpTimeout; + + // + // Configure the DHCPv4 instance for PXE boot. + // + Status = Dhcp4->Configure (Dhcp4, &Config); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Initialize the record fields for DHCPv4 offer in private data. + // + Private->IsProxyRecved = FALSE; + Private->OfferNum = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + // + // Start DHCPv4 D.O.R.A. process to acquire IPv4 address. This may + // have already been done, thus do not leave in error if the return + // code is EFI_ALREADY_STARTED. + // + Status = Dhcp4->Start (Dhcp4, NULL); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + if (Status == EFI_ICMP_ERROR) { + PxeMode->IcmpErrorReceived = TRUE; + } + goto ON_EXIT; + } + + // + // Get the acquired IPv4 address and store them. + // + Status = Dhcp4->GetModeData (Dhcp4, &Mode); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + ASSERT (Mode.State == Dhcp4Bound); + + CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + + Status = PxeBcFlushStationIp (Private, &Private->StationIp, &Private->SubnetMask); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Check the selected offer whether BINL retry is needed. + // + Status = PxeBcHandleDhcp4Offer (Private); + + AsciiPrint ("\n Station IP address is "); + + PxeBcShowIp4Addr (&Private->StationIp.v4); + AsciiPrint ("\n"); + +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp4->Stop (Dhcp4); + Dhcp4->Configure (Dhcp4, NULL); + } else { + ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); + Dhcp4->Configure (Dhcp4, &Config); + Private->IsAddressOk = TRUE; + } + + return Status; +} diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h new file mode 100644 index 0000000000..248dc60d2c --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h @@ -0,0 +1,409 @@ +/** @file + Functions declaration related with DHCPv4 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_PXEBC_DHCP4_H__ +#define __EFI_PXEBC_DHCP4_H__ + +#define PXEBC_DHCP4_OPTION_MAX_NUM 16 +#define PXEBC_DHCP4_OPTION_MAX_SIZE 312 +#define PXEBC_DHCP4_PACKET_MAX_SIZE 1472 +#define PXEBC_DHCP4_S_PORT 67 +#define PXEBC_DHCP4_C_PORT 68 +#define PXEBC_BS_DOWNLOAD_PORT 69 +#define PXEBC_BS_DISCOVER_PORT 4011 +#define PXEBC_DHCP4_OPCODE_REQUEST 1 +#define PXEBC_DHCP4_OPCODE_REPLY 2 +#define PXEBC_DHCP4_MSG_TYPE_REQUEST 3 +#define PXEBC_DHCP4_MAGIC 0x63538263 // network byte order + +// +// Dhcp Options +// +#define PXEBC_DHCP4_TAG_PAD 0 // Pad Option +#define PXEBC_DHCP4_TAG_EOP 255 // End Option +#define PXEBC_DHCP4_TAG_NETMASK 1 // Subnet Mask +#define PXEBC_DHCP4_TAG_TIME_OFFSET 2 // Time Offset from UTC +#define PXEBC_DHCP4_TAG_ROUTER 3 // Router option, +#define PXEBC_DHCP4_TAG_TIME_SERVER 4 // Time Server +#define PXEBC_DHCP4_TAG_NAME_SERVER 5 // Name Server +#define PXEBC_DHCP4_TAG_DNS_SERVER 6 // Domain Name Server +#define PXEBC_DHCP4_TAG_HOSTNAME 12 // Host Name +#define PXEBC_DHCP4_TAG_BOOTFILE_LEN 13 // Boot File Size +#define PXEBC_DHCP4_TAG_DUMP 14 // Merit Dump File +#define PXEBC_DHCP4_TAG_DOMAINNAME 15 // Domain Name +#define PXEBC_DHCP4_TAG_ROOTPATH 17 // Root path +#define PXEBC_DHCP4_TAG_EXTEND_PATH 18 // Extensions Path +#define PXEBC_DHCP4_TAG_EMTU 22 // Maximum Datagram Reassembly Size +#define PXEBC_DHCP4_TAG_TTL 23 // Default IP Time-to-live +#define PXEBC_DHCP4_TAG_BROADCAST 28 // Broadcast Address +#define PXEBC_DHCP4_TAG_NIS_DOMAIN 40 // Network Information Service Domain +#define PXEBC_DHCP4_TAG_NIS_SERVER 41 // Network Information Servers +#define PXEBC_DHCP4_TAG_NTP_SERVER 42 // Network Time Protocol Servers +#define PXEBC_DHCP4_TAG_VENDOR 43 // Vendor Specific Information +#define PXEBC_DHCP4_TAG_REQUEST_IP 50 // Requested IP Address +#define PXEBC_DHCP4_TAG_LEASE 51 // IP Address Lease Time +#define PXEBC_DHCP4_TAG_OVERLOAD 52 // Option Overload +#define PXEBC_DHCP4_TAG_MSG_TYPE 53 // DHCP Message Type +#define PXEBC_DHCP4_TAG_SERVER_ID 54 // Server Identifier +#define PXEBC_DHCP4_TAG_PARA_LIST 55 // Parameter Request List +#define PXEBC_DHCP4_TAG_MAXMSG 57 // Maximum DHCP Message Size +#define PXEBC_DHCP4_TAG_T1 58 // Renewal (T1) Time Value +#define PXEBC_DHCP4_TAG_T2 59 // Rebinding (T2) Time Value +#define PXEBC_DHCP4_TAG_CLASS_ID 60 // Vendor class identifier +#define PXEBC_DHCP4_TAG_CLIENT_ID 61 // Client-identifier +#define PXEBC_DHCP4_TAG_TFTP 66 // TFTP server name +#define PXEBC_DHCP4_TAG_BOOTFILE 67 // Bootfile name +#define PXEBC_PXE_DHCP4_TAG_ARCH 93 +#define PXEBC_PXE_DHCP4_TAG_UNDI 94 +#define PXEBC_PXE_DHCP4_TAG_UUID 97 +// +// Sub-Options in Dhcp Vendor Option +// +#define PXEBC_VENDOR_TAG_MTFTP_IP 1 +#define PXEBC_VENDOR_TAG_MTFTP_CPORT 2 +#define PXEBC_VENDOR_TAG_MTFTP_SPORT 3 +#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT 4 +#define PXEBC_VENDOR_TAG_MTFTP_DELAY 5 +#define PXEBC_VENDOR_TAG_DISCOVER_CTRL 6 +#define PXEBC_VENDOR_TAG_DISCOVER_MCAST 7 +#define PXEBC_VENDOR_TAG_BOOT_SERVERS 8 +#define PXEBC_VENDOR_TAG_BOOT_MENU 9 +#define PXEBC_VENDOR_TAG_MENU_PROMPT 10 +#define PXEBC_VENDOR_TAG_MCAST_ALLOC 11 +#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES 12 +#define PXEBC_VENDOR_TAG_BOOT_ITEM 71 + +#define PXEBC_BOOT_REQUEST_TIMEOUT 1 +#define PXEBC_BOOT_REQUEST_RETRIES 4 + +#define PXEBC_DHCP4_OVERLOAD_FILE 1 +#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME 2 + + +// +// The array index of the DHCP4 option tag interested +// +#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN 0 +#define PXEBC_DHCP4_TAG_INDEX_VENDOR 1 +#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD 2 +#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE 3 +#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID 4 +#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID 5 +#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE 6 +#define PXEBC_DHCP4_TAG_INDEX_MAX 7 + +// +// Dhcp4 and Dhcp6 share this definition, and corresponding +// relatioinship is as follows: +// +// Dhcp4Discover <> Dhcp6Solicit +// Dhcp4Offer <> Dhcp6Advertise +// Dhcp4Request <> Dhcp6Request +// Dhcp4Ack <> DHcp6Reply +// +typedef enum { + PxeOfferTypeDhcpOnly, + PxeOfferTypeDhcpPxe10, + PxeOfferTypeDhcpWfm11a, + PxeOfferTypeDhcpBinl, + PxeOfferTypeProxyPxe10, + PxeOfferTypeProxyWfm11a, + PxeOfferTypeProxyBinl, + PxeOfferTypeBootp, + PxeOfferTypeMax +} PXEBC_OFFER_TYPE; + +#define BIT(x) (1 << x) +#define CTRL(x) (0x1F & (x)) +#define DEFAULT_CLASS_ID_DATA "PXEClient:Arch:xxxxx:UNDI:003000" +#define DEFAULT_UNDI_TYPE 1 +#define DEFAULT_UNDI_MAJOR 3 +#define DEFAULT_UNDI_MINOR 0 + +#define MTFTP_VENDOR_OPTION_BIT_MAP \ + (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \ + BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY)) + +#define DISCOVER_VENDOR_OPTION_BIT_MAP \ + (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \ + BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \ + BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \ + BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \ + BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) + +#define IS_VALID_BOOT_SERVERS(x) \ + ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS)) \ + == BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS)) + +#define IS_VALID_BOOT_PROMPT(x) \ + ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) \ + == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) + +#define IS_VALID_BOOT_MENU(x) \ + ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) \ + == BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) + +#define IS_VALID_MTFTP_VENDOR_OPTION(x) \ + (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) \ + == MTFTP_VENDOR_OPTION_BIT_MAP) + +#define IS_VALID_DISCOVER_VENDOR_OPTION(x) \ + (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0) + +#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \ + (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) \ + == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) + +#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \ + (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & \ + BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \ + == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) + +#define SET_VENDOR_OPTION_BIT_MAP(x, y) \ + (*(x + ((y) / 32)) = (UINT32) ((UINT32) ((x)[(y) / 32]) | BIT ((y) % 32))) + +#define GET_NEXT_DHCP_OPTION(Opt) \ + (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1) + +#define GET_OPTION_BUFFER_LEN(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4) + +#define GET_NEXT_BOOT_SVR_ENTRY(Ent) \ + (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + \ + ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS)) + +#define IS_PROXY_DHCP_OFFER(Offer) \ + EFI_IP4_EQUAL (&(Offer)->Dhcp4.Header.YourAddr, &mZeroIp4Addr) + +#define IS_DISABLE_BCAST_DISCOVER(x) \ + (((x) & BIT (0)) == BIT (0)) + +#define IS_DISABLE_MCAST_DISCOVER(x) \ + (((x) & BIT (1)) == BIT (1)) + +#define IS_ENABLE_USE_SERVER_LIST(x) \ + (((x) & BIT (2)) == BIT (2)) + +#define IS_DISABLE_PROMPT_MENU(x) \ + (((x) & BIT (3)) == BIT (3)) + + +#pragma pack(1) +typedef struct { + UINT8 ParaList[135]; +} PXEBC_DHCP4_OPTION_PARA; + +typedef struct { + UINT16 Size; +} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; +} PXEBC_DHCP4_OPTION_UNDI; + +typedef struct { + UINT8 Type; +} PXEBC_DHCP4_OPTION_MESG; + +typedef struct { + UINT16 Type; +} PXEBC_DHCP4_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[10]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} PXEBC_DHCP4_OPTION_CLID; + +typedef struct { + UINT8 Type; + UINT8 Guid[16]; +} PXEBC_DHCP4_OPTION_UUID; + +typedef struct { + UINT16 Type; + UINT16 Layer; +} PXEBC_OPTION_BOOT_ITEM; + +#pragma pack() + +typedef union { + PXEBC_DHCP4_OPTION_PARA *Para; + PXEBC_DHCP4_OPTION_UNDI *Undi; + PXEBC_DHCP4_OPTION_ARCH *Arch; + PXEBC_DHCP4_OPTION_CLID *Clid; + PXEBC_DHCP4_OPTION_UUID *Uuid; + PXEBC_DHCP4_OPTION_MESG *Mesg; + PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *MaxMesgSize; +} PXEBC_DHCP4_OPTION_ENTRY; + +#pragma pack(1) +typedef struct { + UINT16 Type; + UINT8 IpCnt; + EFI_IPv4_ADDRESS IpAddr[1]; +} PXEBC_BOOT_SVR_ENTRY; + +typedef struct { + UINT16 Type; + UINT8 DescLen; + UINT8 DescStr[1]; +} PXEBC_BOOT_MENU_ENTRY; + +typedef struct { + UINT8 Timeout; + UINT8 Prompt[1]; +} PXEBC_MENU_PROMPT; +#pragma pack() + +typedef struct { + UINT32 BitMap[8]; + EFI_IPv4_ADDRESS MtftpIp; + UINT16 MtftpCPort; + UINT16 MtftpSPort; + UINT8 MtftpTimeout; + UINT8 MtftpDelay; + UINT8 DiscoverCtrl; + EFI_IPv4_ADDRESS DiscoverMcastIp; + EFI_IPv4_ADDRESS McastIpBase; + UINT16 McastIpBlock; + UINT16 McastIpRange; + UINT16 BootSrvType; + UINT16 BootSrvLayer; + PXEBC_BOOT_SVR_ENTRY *BootSvr; + UINT8 BootSvrLen; + PXEBC_BOOT_MENU_ENTRY *BootMenu; + UINT8 BootMenuLen; + PXEBC_MENU_PROMPT *MenuPrompt; + UINT8 MenuPromptLen; + UINT32 *CredType; + UINT8 CredTypeLen; +} PXEBC_VENDOR_OPTION; + +typedef union { + EFI_DHCP4_PACKET Offer; + EFI_DHCP4_PACKET Ack; + UINT8 Buffer[PXEBC_DHCP4_PACKET_MAX_SIZE]; +} PXEBC_DHCP4_PACKET; + +typedef struct { + PXEBC_DHCP4_PACKET Packet; + PXEBC_OFFER_TYPE OfferType; + EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_TAG_INDEX_MAX]; + PXEBC_VENDOR_OPTION VendorOpt; +} PXEBC_DHCP4_PACKET_CACHE; + + +/** + Create a template DHCPv4 packet as a seed. + + @param[out] Seed Pointer to the seed packet. + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + +**/ +VOID +PxeBcSeedDhcp4Packet ( + OUT EFI_DHCP4_PACKET *Seed, + IN EFI_UDP4_PROTOCOL *Udp4 + ); + + +/** + Parse the cached DHCPv4 packet, including all the options. + + @param[in] Cache4 Pointer to cached DHCPv4 packet. + + @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. + +**/ +EFI_STATUS +PxeBcParseDhcp4Packet ( + IN PXEBC_DHCP4_PACKET_CACHE *Cache4 + ); + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer Pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp Pointer to the server address. + @param[in] IpCount The total count of the server address. + @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. + + @retval EFI_SUCCESS Successfully discovered boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDhcp4Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp, + IN UINT16 IpCount, + IN EFI_PXE_BASE_CODE_SRVLIST *SrvList + ); + +/** + Switch the Ip4 policy to static. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The policy is already configured to static. + @retval Others Other error as indicated.. + +**/ +EFI_STATUS +PxeBcSetIp4Policy ( + IN PXEBC_PRIVATE_DATA *Private + ); + + +/** + Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL + + @retval EFI_SUCCESS The D.O.R.A process successfully finished. + @retval Others Failed to finish the D.O.R.A process. + +**/ +EFI_STATUS +PxeBcDhcp4Dora ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP4_PROTOCOL *Dhcp4 + ); + +#endif + diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c new file mode 100644 index 0000000000..6ad5f5f1ac --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c @@ -0,0 +1,2096 @@ +/** @file + Functions implementation related with DHCPv6 for UefiPxeBc Driver. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PxeBcImpl.h" + +// +// Well-known multi-cast address defined in section-24.1 of rfc-3315 +// +// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2 +// +EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}}; + +/** + Parse out a DHCPv6 option by OptTag, and find the position in buffer. + + @param[in] Buffer The pointer to the option buffer. + @param[in] Length Length of the option buffer. + @param[in] OptTag The required option tag. + + @retval NULL Failed to parse the required option. + @retval Others The postion of the required option in buffer. + +**/ +EFI_DHCP6_PACKET_OPTION * +PxeBcParseDhcp6Options ( + IN UINT8 *Buffer, + IN UINT32 Length, + IN UINT16 OptTag + ) +{ + EFI_DHCP6_PACKET_OPTION *Option; + UINT32 Offset; + + Option = (EFI_DHCP6_PACKET_OPTION *) Buffer; + Offset = 0; + + // + // OpLen and OpCode here are both stored in network order. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == OptTag) { + + return Option; + } + + Offset += (NTOHS(Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset); + } + + return NULL; +} + + +/** + Build the options buffer for the DHCPv6 request packet. + + @param[in] Private The pointer to PxeBc private data. + @param[out] OptList The pointer to the option pointer array. + @param[in] Buffer The pointer to the buffer to contain the option list. + + @return Index The count of the built-in options. + +**/ +UINT32 +PxeBcBuildDhcp6Options ( + IN PXEBC_PRIVATE_DATA *Private, + OUT EFI_DHCP6_PACKET_OPTION **OptList, + IN UINT8 *Buffer + ) +{ + PXEBC_DHCP6_OPTION_ENTRY OptEnt; + UINT32 Index; + UINT16 Value; + + Index = 0; + OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer; + + // + // Append client option request option + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ORO); + OptList[Index]->OpLen = HTONS (4); + OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data; + OptEnt.Oro->OpCode[0] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL); + OptEnt.Oro->OpCode[1] = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client network device interface option + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_UNDI); + OptList[Index]->OpLen = HTONS ((UINT16)3); + OptEnt.Undi = (PXEBC_DHCP6_OPTION_UNDI *) OptList[Index]->Data; + + if (Private->Nii != NULL) { + OptEnt.Undi->Type = Private->Nii->Type; + OptEnt.Undi->MajorVer = Private->Nii->MajorVer; + OptEnt.Undi->MinorVer = Private->Nii->MinorVer; + } else { + OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; + OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; + OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; + } + + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append client system architecture option + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_ARCH); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH)); + OptEnt.Arch = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data; + Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE); + CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); + Index++; + OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]); + + // + // Append vendor class option to store the PXE class identifier. + // + OptList[Index]->OpCode = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS); + OptList[Index]->OpLen = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS)); + OptEnt.VendorClass = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data; + OptEnt.VendorClass->Vendor = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM); + OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID)); + CopyMem ( + &OptEnt.VendorClass->ClassId, + DEFAULT_CLASS_ID_DATA, + sizeof (PXEBC_CLASS_ID) + ); + PxeBcUintnToAscDecWithFormat ( + EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, + OptEnt.VendorClass->ClassId.ArchitectureType, + sizeof (OptEnt.VendorClass->ClassId.ArchitectureType) + ); + + if (Private->Nii != NULL) { + CopyMem ( + OptEnt.VendorClass->ClassId.InterfaceName, + Private->Nii->StringId, + sizeof (OptEnt.VendorClass->ClassId.InterfaceName) + ); + PxeBcUintnToAscDecWithFormat ( + Private->Nii->MajorVer, + OptEnt.VendorClass->ClassId.UndiMajor, + sizeof (OptEnt.VendorClass->ClassId.UndiMajor) + ); + PxeBcUintnToAscDecWithFormat ( + Private->Nii->MinorVer, + OptEnt.VendorClass->ClassId.UndiMinor, + sizeof (OptEnt.VendorClass->ClassId.UndiMinor) + ); + } + + Index++; + + return Index; +} + + +/** + Cache the DHCPv6 packet. + + @param[in] Dst The pointer to the cache buffer for DHCPv6 packet. + @param[in] Src The pointer to the DHCPv6 packet to be cached. + +**/ +VOID +PxeBcCacheDhcp6Packet ( + IN EFI_DHCP6_PACKET *Dst, + IN EFI_DHCP6_PACKET *Src + ) +{ + ASSERT (Dst->Size >= Src->Length); + + CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length); + Dst->Length = Src->Length; +} + + +/** + Free all the nodes in the list for boot file. + + @param[in] Head The pointer to the head of list. + +**/ +VOID +PxeBcFreeBootFileOption ( + IN LIST_ENTRY *Head + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + PXEBC_DHCP6_OPTION_NODE *Node; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) { + Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link); + RemoveEntryList (Entry); + FreePool (Node); + } +} + + +/** + Parse the Boot File URL option. + + @param[out] FileName The pointer to the boot file name. + @param[in, out] SrvAddr The pointer to the boot server address. + @param[in] BootFile The pointer to the boot file URL option data. + @param[in] Length The length of the boot file URL option data. + + @retval EFI_ABORTED User cancel operation. + @retval EFI_SUCCESS Selected the boot menu successfully. + @retval EFI_NOT_READY Read the input key from the keybroad has not finish. + +**/ +EFI_STATUS +PxeBcExtractBootFileUrl ( + OUT UINT8 **FileName, + IN OUT EFI_IPv6_ADDRESS *SrvAddr, + IN CHAR8 *BootFile, + IN UINT16 Length + ) +{ + UINT16 PrefixLen; + CHAR8 *BootFileNamePtr; + CHAR8 *BootFileName; + UINT16 BootFileNameLen; + CHAR8 *TmpStr; + CHAR8 TmpChar; + CHAR8 *ServerAddressOption; + CHAR8 *ServerAddress; + CHAR8 *ModeStr; + EFI_STATUS Status; + + // + // The format of the Boot File URL option is: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | OPT_BOOTFILE_URL | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . bootfile-url (variable length) . + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // Based upon RFC 5970 and UEFI 2.3 Errata D specification, bootfile-url format + // is tftp://[SERVER_ADDRESS]/BOOTFILE_NAME + // As an example where the BOOTFILE_NAME is the EFI loader and + // SERVER_ADDRESS is the ASCII encoding of an IPV6 address. + // + PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX); + + if (Length <= PrefixLen || + CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) { + return EFI_NOT_FOUND; + } + + BootFile = BootFile + PrefixLen; + Length = (UINT16) (Length - PrefixLen); + + TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1); + if (TmpStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (TmpStr, BootFile, Length); + TmpStr[Length] = '\0'; + + // + // Get the part of SERVER_ADDRESS string. + // + ServerAddressOption = TmpStr; + if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + ServerAddressOption ++; + ServerAddress = ServerAddressOption; + while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) { + ServerAddress++; + } + + if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + *ServerAddress = '\0'; + + // + // Convert the string of server address to Ipv6 address format and store it. + // + Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr); + if (EFI_ERROR (Status)) { + FreePool (TmpStr); + return Status; + } + + // + // Get the part of BOOTFILE_NAME string. + // + BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1); + if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) { + FreePool (TmpStr); + return EFI_INVALID_PARAMETER; + } + + ++BootFileNamePtr; + BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1); + if (BootFileNameLen != 0 || FileName != NULL) { + // + // Remove trailing mode=octet if present and ignore. All other modes are + // invalid for netboot6, so reject them. + // + ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet"); + if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') { + *ModeStr = '\0'; + } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Extract boot file name from URL. + // + BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen); + if (BootFileName == NULL) { + FreePool (TmpStr); + return EFI_OUT_OF_RESOURCES; + } + *FileName = (UINT8*) BootFileName; + + // + // Decode percent-encoding in boot file name. + // + while (*BootFileNamePtr != '\0') { + if (*BootFileNamePtr == '%') { + TmpChar = *(BootFileNamePtr+ 3); + *(BootFileNamePtr+ 3) = '\0'; + *BootFileName = (UINT8) AsciiStrHexToUintn ((CHAR8*)(BootFileNamePtr + 1)); + BootFileName++; + *(BootFileNamePtr+ 3) = TmpChar; + BootFileNamePtr += 3; + } else { + *BootFileName = *BootFileNamePtr; + BootFileName++; + BootFileNamePtr++; + } + } + *BootFileName = '\0'; + } + + FreePool (TmpStr); + + return EFI_SUCCESS; +} + + +/** + Parse the Boot File Parameter option. + + @param[in] BootFilePara The pointer to boot file parameter option data. + @param[out] BootFileSize The pointer to the parsed boot file size. + + @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option. + @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option. + +**/ +EFI_STATUS +PxeBcExtractBootFileParam ( + IN CHAR8 *BootFilePara, + OUT UINT16 *BootFileSize + ) +{ + UINT16 Length; + UINT8 Index; + UINT8 Digit; + UINT32 Size; + + CopyMem (&Length, BootFilePara, sizeof (UINT16)); + Length = NTOHS (Length); + + // + // The BootFile Size should be 1~5 byte ASCII strings + // + if (Length < 1 || Length > 5) { + return EFI_NOT_FOUND; + } + + // + // Extract the value of BootFile Size. + // + BootFilePara = BootFilePara + sizeof (UINT16); + Size = 0; + for (Index = 0; Index < Length; Index++) { + if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) { + return EFI_NOT_FOUND; + } + + Size = (Size + Digit) * 10; + } + + Size = Size / 10; + if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) { + return EFI_NOT_FOUND; + } + + *BootFileSize = (UINT16) Size; + return EFI_SUCCESS; +} + + +/** + Parse the cached DHCPv6 packet, including all the options. + + @param[in] Cache6 The pointer to a cached DHCPv6 packet. + + @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet. + +**/ +EFI_STATUS +PxeBcParseDhcp6Packet ( + IN PXEBC_DHCP6_PACKET_CACHE *Cache6 + ) +{ + EFI_DHCP6_PACKET *Offer; + EFI_DHCP6_PACKET_OPTION **Options; + EFI_DHCP6_PACKET_OPTION *Option; + PXEBC_OFFER_TYPE OfferType; + BOOLEAN IsProxyOffer; + BOOLEAN IsPxeOffer; + UINT32 Offset; + UINT32 Length; + UINT32 EnterpriseNum; + + IsProxyOffer = TRUE; + IsPxeOffer = FALSE; + Offer = &Cache6->Packet.Offer; + Options = Cache6->OptList; + + ZeroMem (Cache6->OptList, sizeof (Cache6->OptList)); + + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option); + Offset = 0; + Length = GET_DHCP6_OPTION_SIZE (Offer); + + // + // OpLen and OpCode here are both stored in network order, since they are from original packet. + // + while (Offset < Length) { + + if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) { + Options[PXEBC_DHCP6_IDX_IA_NA] = Option; + } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) { + // + // The server sends this option to inform the client about an URL to a boot file. + // + Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option; + } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) { + Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option; + } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) { + Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option; + } + + Offset += (NTOHS (Option->OpLen) + 4); + Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset); + } + + // + // The offer with assigned client address is NOT a proxy offer. + // An ia_na option, embeded with valid ia_addr option and a status_code of success. + // + Option = Options[PXEBC_DHCP6_IDX_IA_NA]; + if (Option != NULL) { + Option = PxeBcParseDhcp6Options ( + Option->Data + 12, + NTOHS (Option->OpLen), + PXEBC_DHCP6_OPT_STATUS_CODE + ); + if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) { + IsProxyOffer = FALSE; + } + } + + // + // The offer with "PXEClient" is a pxe offer. + // + Option = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS]; + EnterpriseNum = HTONL(PXEBC_DHCP6_ENTERPRISE_NUM); + + if (Option != NULL && + NTOHS(Option->OpLen) >= 13 && + CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 && + CompareMem (&Option->Data[6], DEFAULT_CLASS_ID_DATA, 9) == 0) { + IsPxeOffer = TRUE; + } + + // + // Determine offer type of the dhcp6 packet. + // + if (IsPxeOffer) { + // + // It's a binl offer only with PXEClient. + // + OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl; + } else { + // + // It's a dhcp only offer, which is a pure dhcp6 offer packet. + // + OfferType = PxeOfferTypeDhcpOnly; + } + + Cache6->OfferType = OfferType; + + return EFI_SUCCESS; +} + + +/** + Cache the DHCPv6 ack packet, and parse it on demand. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Ack The pointer to the DHCPv6 ack packet. + @param[in] Verified If TRUE, parse the ACK packet and store info into mode data. + +**/ +VOID +PxeBcCopyDhcp6Ack ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PACKET *Ack, + IN BOOLEAN Verified + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + + Mode = Private->PxeBc.Mode; + + PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack); + + if (Verified) { + // + // Parse the ack packet and store it into mode data if needed. + // + PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6); + CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length); + Mode->DhcpAckReceived = TRUE; + } +} + + +/** + Cache the DHCPv6 proxy offer packet according to the received order. + + @param[in] Private The pointer to PxeBc private data. + @param[in] OfferIndex The received order of offer packets. + +**/ +VOID +PxeBcCopyDhcp6Proxy ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 OfferIndex + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_DHCP6_PACKET *Offer; + + ASSERT (OfferIndex < Private->OfferNum); + ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM); + + Mode = Private->PxeBc.Mode; + Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer; + + // + // Cache the proxy offer packet and parse it. + // + PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer); + PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6); + + // + // Store this packet into mode data. + // + CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length); + Mode->ProxyOfferReceived = TRUE; +} + +/** + Seek the address of the first byte of the option header. + + @param[in] Buf The pointer to the buffer. + @param[in] SeekLen The length to seek. + @param[in] OptType The option type. + + @retval NULL If it failed to seek the option. + @retval others The position to the option. + +**/ +UINT8 * +PxeBcDhcp6SeekOption ( + IN UINT8 *Buf, + IN UINT32 SeekLen, + IN UINT16 OptType + ) +{ + UINT8 *Cursor; + UINT8 *Option; + UINT16 DataLen; + UINT16 OpCode; + + Option = NULL; + Cursor = Buf; + + while (Cursor < Buf + SeekLen) { + OpCode = ReadUnaligned16 ((UINT16 *) Cursor); + if (OpCode == HTONS (OptType)) { + Option = Cursor; + break; + } + DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2))); + Cursor += (DataLen + 4); + } + + return Option; +} + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Index PxeBc option boot item type. + + @retval EFI_SUCCESS Successfully discovered the boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover the boot file. + +**/ +EFI_STATUS +PxeBcRequestBootService ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 Index + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT SrcPort; + EFI_PXE_BASE_CODE_UDP_PORT DestPort; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover; + UINTN DiscoverLen; + EFI_DHCP6_PACKET *Request; + UINTN RequestLen; + EFI_DHCP6_PACKET *Reply; + UINT8 *RequestOpt; + UINT8 *DiscoverOpt; + UINTN ReadSize; + UINT16 OpFlags; + UINT16 OpCode; + UINT16 OpLen; + EFI_STATUS Status; + EFI_DHCP6_PACKET *ProxyOffer; + UINT8 *Option; + + PxeBc = &Private->PxeBc; + Request = Private->Dhcp6Request; + ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer; + SrcPort = PXEBC_BS_DISCOVER_PORT; + DestPort = PXEBC_BS_DISCOVER_PORT; + OpFlags = 0; + + if (Request == NULL) { + return EFI_DEVICE_ERROR; + } + + Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET)); + if (Discover == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Build the request packet by the cached request packet before. + // + Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId; + Discover->MessageType = Request->Dhcp6.Header.MessageType; + RequestOpt = Request->Dhcp6.Option; + DiscoverOpt = Discover->DhcpOptions; + DiscoverLen = sizeof (EFI_DHCP6_HEADER); + RequestLen = DiscoverLen; + + // + // Find Server ID Option from ProxyOffer. + // + Option = PxeBcDhcp6SeekOption ( + ProxyOffer->Dhcp6.Option, + ProxyOffer->Length - 4, + PXEBC_DHCP6_OPT_SERVER_ID + ); + if (Option == NULL) { + return EFI_NOT_FOUND; + } + + // + // Add Server ID Option. + // + OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen); + CopyMem (DiscoverOpt, Option, OpLen + 4); + DiscoverOpt += (OpLen + 4); + DiscoverLen += (OpLen + 4); + + while (RequestLen < Request->Length) { + OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode); + OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen); + if (OpCode != EFI_DHCP6_IA_TYPE_NA && + OpCode != EFI_DHCP6_IA_TYPE_TA && + OpCode != PXEBC_DHCP6_OPT_SERVER_ID + ) { + // + // Copy all the options except IA option and Server ID + // + CopyMem (DiscoverOpt, RequestOpt, OpLen + 4); + DiscoverOpt += (OpLen + 4); + DiscoverLen += (OpLen + 4); + } + RequestOpt += (OpLen + 4); + RequestLen += (OpLen + 4); + } + + // + // Update Elapsed option in the package + // + Option = PxeBcDhcp6SeekOption ( + Discover->DhcpOptions, + (UINT32)(RequestLen - 4), + PXEBC_DHCP6_OPT_ELAPSED_TIME + ); + if (Option != NULL) { + CalcElapsedTime (Private); + WriteUnaligned16 ((UINT16*)(Option + 4), HTONS((UINT16) Private->ElapsedTime)); + } + + Status = PxeBc->UdpWrite ( + PxeBc, + OpFlags, + &Private->ServerIp, + &DestPort, + NULL, + &Private->StationIp, + &SrcPort, + NULL, + NULL, + &DiscoverLen, + (VOID *) Discover + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Cache the right PXE reply packet here, set valid flag later. + // Especially for PXE discover packet, store it into mode data here. + // + Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer; + ReadSize = (UINTN) Reply->Size; + + // + // Start Udp6Read instance + // + Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PxeBc->UdpRead ( + PxeBc, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP, + NULL, + &SrcPort, + &Private->ServerIp, + &DestPort, + NULL, + NULL, + &ReadSize, + (VOID *) &Reply->Dhcp6 + ); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update length + // + Reply->Length = (UINT32) ReadSize; + + return EFI_SUCCESS; +} + + +/** + Retry to request bootfile name by the BINL offer. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Index The received order of offer packets. + + @retval EFI_SUCCESS Successfully retried a request for the bootfile name. + @retval EFI_DEVICE_ERROR Failed to retry the bootfile name. + +**/ +EFI_STATUS +PxeBcRetryDhcp6Binl ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT32 Index + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + PXEBC_DHCP6_PACKET_CACHE *Offer; + PXEBC_DHCP6_PACKET_CACHE *Cache6; + EFI_STATUS Status; + + ASSERT (Index < PXEBC_OFFER_MAX_NUM); + ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl || + Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl); + + Mode = Private->PxeBc.Mode; + Private->IsDoDiscover = FALSE; + Offer = &Private->OfferBuffer[Index].Dhcp6; + if (Offer->OfferType == PxeOfferTypeDhcpBinl) { + // + // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead. + // + CopyMem ( + &Private->ServerIp.v6, + &mAllDhcpRelayAndServersAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + // + // Parse out the next server address from the last offer, and store it + // + Status = PxeBcExtractBootFileUrl ( + &Private->BootFileName, + &Private->ServerIp.v6, + (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data), + NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen) + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer. + // + Status = PxeBcRequestBootService (Private, Index); + + if (EFI_ERROR (Status)) { + return Status; + } + + Cache6 = &Private->ProxyOffer.Dhcp6; + Status = PxeBcParseDhcp6Packet (Cache6); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Cache6->OfferType != PxeOfferTypeProxyPxe10 && + Cache6->OfferType != PxeOfferTypeProxyWfm11a && + Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) { + // + // This BINL ack doesn't have discovery option set or multicast option set + // or bootfile name specified. + // + return EFI_DEVICE_ERROR; + } + + Mode->ProxyOfferReceived = TRUE; + CopyMem ( + &Mode->ProxyOffer.Dhcpv6, + &Cache6->Packet.Offer.Dhcp6, + Cache6->Packet.Offer.Length + ); + + return EFI_SUCCESS; +} + + +/** + Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + @param[in] RcvdOffer The pointer to the received offer packet. + +**/ +VOID +PxeBcCacheDhcp6Offer ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PACKET *RcvdOffer + ) +{ + PXEBC_DHCP6_PACKET_CACHE *Cache6; + EFI_DHCP6_PACKET *Offer; + PXEBC_OFFER_TYPE OfferType; + + Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6; + Offer = &Cache6->Packet.Offer; + + // + // Cache the content of DHCPv6 packet firstly. + // + PxeBcCacheDhcp6Packet (Offer, RcvdOffer); + + // + // Validate the DHCPv6 packet, and parse the options and offer type. + // + if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) { + return ; + } + + // + // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. + // + OfferType = Cache6->OfferType; + ASSERT (OfferType < PxeOfferTypeMax); + ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM); + + if (IS_PROXY_OFFER (OfferType)) { + // + // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer. + // + Private->IsProxyRecved = TRUE; + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Cache all proxy BINL offers. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } else if (Private->OfferCount[OfferType] > 0) { + // + // Only cache the first PXE10/WFM11a offer, and discard the others. + // + Private->OfferIndex[OfferType][0] = Private->OfferNum; + Private->OfferCount[OfferType] = 1; + } else { + return; + } + } else { + // + // It's a DHCPv6 offer with yiaddr, and cache them all. + // + Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; + Private->OfferCount[OfferType]++; + } + + Private->OfferNum++; +} + + +/** + Select an DHCPv6 offer, and record SelectIndex and SelectProxyType. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcSelectDhcp6Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + UINT32 Index; + UINT32 OfferIndex; + PXEBC_OFFER_TYPE OfferType; + + Private->SelectIndex = 0; + + if (Private->IsOfferSorted) { + // + // Select offer by default policy. + // + if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) { + // + // 1. DhcpPxe10 offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) { + // + // 2. DhcpWfm11a offer + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) { + // + // 3. DhcpOnly offer and ProxyPxe10 offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyPxe10; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) { + // + // 4. DhcpOnly offer and ProxyWfm11a offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyWfm11a; + + } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) { + // + // 5. DhcpBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1; + + } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && + Private->OfferCount[PxeOfferTypeProxyBinl] > 0) { + // + // 6. DhcpOnly offer and ProxyBinl offer. + // + Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; + Private->SelectProxyType = PxeOfferTypeProxyBinl; + + } else { + // + // 7. DhcpOnly offer with bootfilename. + // + for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) { + OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index]; + if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) { + Private->SelectIndex = OfferIndex + 1; + break; + } + } + } + } else { + // + // Select offer by received order. + // + for (Index = 0; Index < Private->OfferNum; Index++) { + + OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType; + + if (IS_PROXY_OFFER (OfferType)) { + // + // Skip proxy offers + // + continue; + } + + if (!Private->IsProxyRecved && + OfferType == PxeOfferTypeDhcpOnly && + Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) { + // + // Skip if DhcpOnly offer without any other proxy offers or bootfilename. + // + continue; + } + + Private->SelectIndex = Index + 1; + break; + } + } +} + + +/** + Handle the DHCPv6 offer packet. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Handled the DHCPv6 offer packet successfully. + @retval EFI_NO_RESPONSE No response to the following request packet. + +**/ +EFI_STATUS +PxeBcHandleDhcp6Offer ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + PXEBC_DHCP6_PACKET_CACHE *Cache6; + EFI_STATUS Status; + PXEBC_OFFER_TYPE OfferType; + UINT32 ProxyIndex; + UINT32 SelectIndex; + UINT32 Index; + + ASSERT (Private->SelectIndex > 0); + SelectIndex = (UINT32) (Private->SelectIndex - 1); + ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM); + Cache6 = &Private->OfferBuffer[SelectIndex].Dhcp6; + Status = EFI_SUCCESS; + + if (Cache6->OfferType == PxeOfferTypeDhcpBinl) { + // + // DhcpBinl offer is selected, so need try to request bootfilename by this offer. + // + if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) { + Status = EFI_NO_RESPONSE; + } + } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) { + + if (Private->IsProxyRecved) { + // + // DhcpOnly offer is selected, so need try to request bootfilename. + // + ProxyIndex = 0; + if (Private->IsOfferSorted) { + // + // The proxy offer should be determined if select by default policy. + // IsOfferSorted means all offers are labeled by OfferIndex. + // + ASSERT (Private->OfferCount[Private->SelectProxyType] > 0); + + if (Private->SelectProxyType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfilename. + // + for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) { + + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index]; + if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) { + break; + } + } + if (Index == Private->OfferCount[Private->SelectProxyType]) { + Status = EFI_NO_RESPONSE; + } + } else { + // + // For other proxy offers (pxe10 or wfm11a), only one is buffered. + // + ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; + } + } else { + // + // The proxy offer should not be determined if select by received order. + // + Status = EFI_NO_RESPONSE; + + for (Index = 0; Index < Private->OfferNum; Index++) { + + OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType; + + if (!IS_PROXY_OFFER (OfferType)) { + // + // Skip non proxy dhcp offers. + // + continue; + } + + if (OfferType == PxeOfferTypeProxyBinl) { + // + // Try all the cached ProxyBinl offer one by one to request bootfilename. + // + if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) { + continue; + } + } + + Private->SelectProxyType = OfferType; + ProxyIndex = Index; + Status = EFI_SUCCESS; + break; + } + } + + if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) { + // + // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it. + // + PxeBcCopyDhcp6Proxy (Private, ProxyIndex); + } + } else { + // + // Othewise, the bootfilename must be included in DhcpOnly offer. + // + ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); + } + } + + if (!EFI_ERROR (Status)) { + // + // All PXE boot information is ready by now. + // + PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE); + Private->PxeBc.Mode->DhcpDiscoverValid = TRUE; + } + + return Status; +} + + +/** + Unregister the address by Ip6Config protocol. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcUnregisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) { + // + // PXE driver change the policy of IP6 driver, it's a chance to recover. + // Keep the point and there is no enough requirements to do recovery. + // + } +} + +/** + Check whether IP driver could route the message which will be sent to ServerIp address. + + This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid + route is found in IP6 route table, the address will be filed in GatewayAddr and return. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + @param[in] TimeOutInSecond Timeout value in seconds. + @param[out] GatewayAddr Pointer to store the gateway IP address. + + @retval EFI_SUCCESS Found a valid gateway address successfully. + @retval EFI_TIMEOUT The operation is time out. + @retval Other Unexpect error happened. + +**/ +EFI_STATUS +PxeBcCheckRouteTable ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINTN TimeOutInSecond, + OUT EFI_IPv6_ADDRESS *GatewayAddr + ) +{ + EFI_STATUS Status; + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_MODE_DATA Ip6ModeData; + UINTN Index; + EFI_EVENT TimeOutEvt; + UINTN RetryCount; + BOOLEAN GatewayIsFound; + + ASSERT (GatewayAddr != NULL); + ASSERT (Private != NULL); + + Ip6 = Private->Ip6; + GatewayIsFound = FALSE; + RetryCount = 0; + TimeOutEvt = NULL; + ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS)); + + while (TRUE) { + Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Find out the gateway address which can route the message which send to ServerIp. + // + for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) { + if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) { + IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway); + GatewayIsFound = TRUE; + break; + } + } + + if (Ip6ModeData.AddressList != NULL) { + FreePool (Ip6ModeData.AddressList); + } + if (Ip6ModeData.GroupTable != NULL) { + FreePool (Ip6ModeData.GroupTable); + } + if (Ip6ModeData.RouteTable != NULL) { + FreePool (Ip6ModeData.RouteTable); + } + if (Ip6ModeData.NeighborCache != NULL) { + FreePool (Ip6ModeData.NeighborCache); + } + if (Ip6ModeData.PrefixTable != NULL) { + FreePool (Ip6ModeData.PrefixTable); + } + if (Ip6ModeData.IcmpTypeList != NULL) { + FreePool (Ip6ModeData.IcmpTypeList); + } + + if (GatewayIsFound || RetryCount == TimeOutInSecond) { + break; + } + + RetryCount++; + + // + // Delay 1 second then recheck it again. + // + if (TimeOutEvt == NULL) { + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeOutEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + + Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) { + Ip6->Poll (Ip6); + } + } + +ON_EXIT: + if (TimeOutEvt != NULL) { + gBS->CloseEvent (TimeOutEvt); + } + + if (GatewayIsFound) { + Status = EFI_SUCCESS; + } else if (RetryCount == TimeOutInSecond) { + Status = EFI_TIMEOUT; + } + + return Status; +} + +/** + Register the ready station address and gateway by Ip6Config protocol. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + @param[in] Address The pointer to the ready address. + + @retval EFI_SUCCESS Registered the address succesfully. + @retval Others Failed to register the address. + +**/ +EFI_STATUS +PxeBcRegisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_IPv6_ADDRESS *Address + ) +{ + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr; + EFI_IPv6_ADDRESS GatewayAddr; + UINTN DataSize; + EFI_EVENT MappedEvt; + EFI_STATUS Status; + BOOLEAN NoGateway; + EFI_IPv6_ADDRESS *Ip6Addr; + UINTN Index; + + Status = EFI_SUCCESS; + MappedEvt = NULL; + Ip6Addr = NULL; + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + Ip6Cfg = Private->Ip6Cfg; + Ip6 = Private->Ip6; + NoGateway = FALSE; + + ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS)); + + Status = Ip6->Configure (Ip6, &Private->Ip6CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Retrieve the gateway address from IP6 route table. + // + Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr); + if (EFI_ERROR (Status)) { + NoGateway = TRUE; + } + + // + // There is no channel between IP6 and PXE driver about address setting, + // so it has to set the new address by Ip6ConfigProtocol manually. + // + Policy = Ip6ConfigPolicyManual; + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + // + // There is no need to recover later. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + goto ON_EXIT; + } + + // + // Create a notify event to set address flag when DAD if IP6 driver succeeded. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &Private->IsAddressOk, + &MappedEvt + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Private->IsAddressOk = FALSE; + Status = Ip6Cfg->RegisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + if (EFI_ERROR(Status)) { + goto ON_EXIT; + } + + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS), + &CfgAddr + ); + if (EFI_ERROR(Status) && Status != EFI_NOT_READY) { + goto ON_EXIT; + } else if (Status == EFI_NOT_READY) { + // + // Poll the network until the asynchronous process is finished. + // + while (!Private->IsAddressOk) { + Ip6->Poll (Ip6); + } + // + // Check whether the IP6 address setting is successed. + // + DataSize = 0; + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + &DataSize, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + Ip6Addr = AllocatePool (DataSize); + if (Ip6Addr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + &DataSize, + (VOID*) Ip6Addr + ); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + + for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) { + if (CompareMem (Ip6Addr + Index, Address, sizeof (EFI_IPv6_ADDRESS)) == 0) { + break; + } + } + if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) { + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Set the default gateway address back if needed. + // + if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) { + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeGateway, + sizeof (EFI_IPv6_ADDRESS), + &GatewayAddr + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + +ON_EXIT: + if (MappedEvt != NULL) { + Ip6Cfg->UnregisterDataNotify ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + MappedEvt + ); + gBS->CloseEvent (MappedEvt); + } + if (Ip6Addr != NULL) { + FreePool (Ip6Addr); + } + return Status; +} + +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +PxeBcSetIp6Policy ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_IP6_CONFIG_POLICY Policy; + EFI_STATUS Status; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + UINTN DataSize; + + Ip6Cfg = Private->Ip6Cfg; + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + + // + // Get and store the current policy of IP6 driver. + // + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + &DataSize, + &Private->Ip6Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Private->Ip6Policy == Ip6ConfigPolicyManual) { + Policy = Ip6ConfigPolicyAutomatic; + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypePolicy, + sizeof(EFI_IP6_CONFIG_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + // + // There is no need to recover later. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + } + } + + return Status; +} + +/** + This function will register the station IP address and flush IP instance to start using the new IP address. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +PxeBcSetIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PROTOCOL *Dhcp6; + + Dhcp6 = Private->Dhcp6; + + CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + + Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL); + if (EFI_ERROR (Status)) { + PxeBcUnregisterIp6Address (Private); + Dhcp6->Stop (Dhcp6); + return Status; + } + + AsciiPrint ("\n Station IP address is "); + PxeBcShowIp6Addr (&Private->StationIp.v6); + + return EFI_SUCCESS; +} + +/** + EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver + to intercept events that occurred in the configuration process. + + @param[in] This The pointer to the EFI DHCPv6 Protocol. + @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure(). + @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver. + @param[in] Dhcp6Event The event that occurs in the current state, which usually means a + state transition. + @param[in] Packet The DHCPv6 packet that is going to be sent or was already received. + @param[out] NewPacket The packet that is used to replace the Packet above. + + @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process. + @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol + driver will continue to wait for more packets. + @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process. + +**/ +EFI_STATUS +EFIAPI +PxeBcDhcp6CallBack ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_STATE CurrentState, + IN EFI_DHCP6_EVENT Dhcp6Event, + IN EFI_DHCP6_PACKET *Packet, + OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_DHCP6_PACKET *SelectAd; + EFI_STATUS Status; + BOOLEAN Received; + + if ((Dhcp6Event != Dhcp6RcvdAdvertise) && + (Dhcp6Event != Dhcp6SelectAdvertise) && + (Dhcp6Event != Dhcp6SendSolicit) && + (Dhcp6Event != Dhcp6SendRequest) && + (Dhcp6Event != Dhcp6RcvdReply)) { + return EFI_SUCCESS; + } + + ASSERT (Packet != NULL); + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = Private->PxeBc.Mode; + Callback = Private->PxeBcCallback; + + // + // Callback to user when any traffic ocurred if has. + // + if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) { + Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply); + Status = Callback->Callback ( + Callback, + Private->Function, + Received, + Packet->Length, + (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6 + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + return EFI_ABORTED; + } + } + + Status = EFI_SUCCESS; + + switch (Dhcp6Event) { + + case Dhcp6SendSolicit: + // + // Record the first Solicate msg time + // + if (Private->SolicitTimes == 0) { + CalcElapsedTime (Private); + Private->SolicitTimes++; + } + // + // Cache the dhcp discover packet to mode data directly. + // + CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length); + break; + + case Dhcp6RcvdAdvertise: + Status = EFI_NOT_READY; + if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) { + // + // Cache the dhcp offers to OfferBuffer[] for select later, and record + // the OfferIndex and OfferCount. + // + PxeBcCacheDhcp6Offer (Private, Packet); + } + break; + + case Dhcp6SendRequest: + // + // Store the request packet as seed packet for discover. + // + if (Private->Dhcp6Request != NULL) { + FreePool (Private->Dhcp6Request); + } + Private->Dhcp6Request = AllocateZeroPool (Packet->Size); + if (Private->Dhcp6Request != NULL) { + CopyMem (Private->Dhcp6Request, Packet, Packet->Size); + } + break; + + case Dhcp6SelectAdvertise: + // + // Select offer by the default policy or by order, and record the SelectIndex + // and SelectProxyType. + // + PxeBcSelectDhcp6Offer (Private); + + if (Private->SelectIndex == 0) { + Status = EFI_ABORTED; + } else { + ASSERT (NewPacket != NULL); + SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer; + *NewPacket = AllocateZeroPool (SelectAd->Size); + ASSERT (*NewPacket != NULL); + CopyMem (*NewPacket, SelectAd, SelectAd->Size); + } + break; + + case Dhcp6RcvdReply: + // + // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data + // without verification. + // + ASSERT (Private->SelectIndex != 0); + PxeBcCopyDhcp6Ack (Private, Packet, FALSE); + break; + + default: + ASSERT (0); + } + + return Status; +} + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer The pointer to option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp The pointer to the server address. + + @retval EFI_SUCCESS Successfully discovered the boot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover the boot file. + +**/ +EFI_STATUS +PxeBcDhcp6Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp + ) +{ + EFI_PXE_BASE_CODE_UDP_PORT SrcPort; + EFI_PXE_BASE_CODE_UDP_PORT DestPort; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + EFI_PXE_BASE_CODE_DHCPV6_PACKET *Discover; + UINTN DiscoverLen; + EFI_DHCP6_PACKET *Request; + UINTN RequestLen; + EFI_DHCP6_PACKET *Reply; + UINT8 *RequestOpt; + UINT8 *DiscoverOpt; + UINTN ReadSize; + UINT16 OpCode; + UINT16 OpLen; + UINT32 Xid; + EFI_STATUS Status; + + PxeBc = &Private->PxeBc; + Mode = PxeBc->Mode; + Request = Private->Dhcp6Request; + SrcPort = PXEBC_BS_DISCOVER_PORT; + DestPort = PXEBC_BS_DISCOVER_PORT; + + if (!UseBis && Layer != NULL) { + *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK; + } + + if (Request == NULL) { + return EFI_DEVICE_ERROR; + } + + Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET)); + if (Discover == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Build the discover packet by the cached request packet before. + // + Xid = NET_RANDOM (NetRandomInitSeed ()); + Discover->TransactionId = HTONL (Xid); + Discover->MessageType = Request->Dhcp6.Header.MessageType; + RequestOpt = Request->Dhcp6.Option; + DiscoverOpt = Discover->DhcpOptions; + DiscoverLen = sizeof (EFI_DHCP6_HEADER); + RequestLen = DiscoverLen; + + while (RequestLen < Request->Length) { + OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode); + OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen); + if (OpCode != EFI_DHCP6_IA_TYPE_NA && + OpCode != EFI_DHCP6_IA_TYPE_TA) { + // + // Copy all the options except IA option. + // + CopyMem (DiscoverOpt, RequestOpt, OpLen + 4); + DiscoverOpt += (OpLen + 4); + DiscoverLen += (OpLen + 4); + } + RequestOpt += (OpLen + 4); + RequestLen += (OpLen + 4); + } + + Status = PxeBc->UdpWrite ( + PxeBc, + 0, + &Private->ServerIp, + &DestPort, + NULL, + &Private->StationIp, + &SrcPort, + NULL, + NULL, + &DiscoverLen, + (VOID *) Discover + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Cache the right PXE reply packet here, set valid flag later. + // Especially for PXE discover packet, store it into mode data here. + // + if (Private->IsDoDiscover) { + CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen); + Reply = &Private->PxeReply.Dhcp6.Packet.Ack; + } else { + Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer; + } + ReadSize = (UINTN) Reply->Size; + + // + // Start Udp6Read instance + // + Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PxeBc->UdpRead ( + PxeBc, + EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP, + NULL, + &SrcPort, + &Private->ServerIp, + &DestPort, + NULL, + NULL, + &ReadSize, + (VOID *) &Reply->Dhcp6 + ); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information. + + @param[in] Private The pointer to PxeBc private data. + @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL + + @retval EFI_SUCCESS The S.A.R.R. process successfully finished. + @retval Others Failed to finish the S.A.R.R. process. + +**/ +EFI_STATUS +PxeBcDhcp6Sarr ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PROTOCOL *Dhcp6 + ) +{ + EFI_PXE_BASE_CODE_MODE *PxeMode; + EFI_DHCP6_CONFIG_DATA Config; + EFI_DHCP6_MODE_DATA Mode; + EFI_DHCP6_RETRANSMISSION *Retransmit; + EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_OPTION_MAX_NUM]; + UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE]; + UINT32 OptCount; + EFI_STATUS Status; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_STATUS TimerStatus; + EFI_EVENT Timer; + UINT64 GetMappingTimeOut; + UINTN DataSize; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + + Status = EFI_SUCCESS; + PxeMode = Private->PxeBc.Mode; + Ip6Cfg = Private->Ip6Cfg; + Timer = NULL; + + // + // Build option list for the request packet. + // + OptCount = PxeBcBuildDhcp6Options (Private, OptList, Buffer); + ASSERT (OptCount> 0); + + Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION)); + if (Retransmit == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA)); + ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA)); + + Config.OptionCount = OptCount; + Config.OptionList = OptList; + Config.Dhcp6Callback = PxeBcDhcp6CallBack; + Config.CallbackContext = Private; + Config.IaInfoEvent = NULL; + Config.RapidCommit = FALSE; + Config.ReconfigureAccept = FALSE; + Config.IaDescriptor.IaId = Private->IaId; + Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; + Config.SolicitRetransmission = Retransmit; + Retransmit->Irt = 4; + Retransmit->Mrc = 4; + Retransmit->Mrt = 32; + Retransmit->Mrd = 60; + + // + // Configure the DHCPv6 instance for PXE boot. + // + Status = Dhcp6->Configure (Dhcp6, &Config); + FreePool (Retransmit); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Initialize the record fields for DHCPv6 offer in private data. + // + Private->IsProxyRecved = FALSE; + Private->OfferNum = 0; + Private->SelectIndex = 0; + ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); + ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); + + + // + // Start DHCPv6 S.A.R.R. process to acquire IPv6 address. + // + Status = Dhcp6->Start (Dhcp6); + if (Status == EFI_NO_MAPPING) { + // + // IP6 Linklocal address is not available for use, so stop current Dhcp process + // and wait for duplicate address detection to finish. + // + Dhcp6->Stop (Dhcp6); + + // + // Get Duplicate Address Detection Transmits count. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Cfg->GetData ( + Ip6Cfg, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &DadXmits + ); + if (EFI_ERROR (Status)) { + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); + if (EFI_ERROR (Status)) { + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY; + Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Timer); + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + do { + + TimerStatus = gBS->CheckEvent (Timer); + if (!EFI_ERROR (TimerStatus)) { + Status = Dhcp6->Start (Dhcp6); + } + } while (TimerStatus == EFI_NOT_READY); + + gBS->CloseEvent (Timer); + } + if (EFI_ERROR (Status)) { + if (Status == EFI_ICMP_ERROR) { + PxeMode->IcmpErrorReceived = TRUE; + } + Dhcp6->Configure (Dhcp6, NULL); + return Status; + } + + // + // Get the acquired IPv6 address and store them. + // + Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + ASSERT (Mode.Ia->State == Dhcp6Bound); + // + // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the + // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when + // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as + // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery + // to find a valid router address. + // + CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS)); + + // + // Check the selected offer whether BINL retry is needed. + // + Status = PxeBcHandleDhcp6Offer (Private); + if (EFI_ERROR (Status)) { + Dhcp6->Stop (Dhcp6); + return Status; + } + + return EFI_SUCCESS; +} diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h new file mode 100644 index 0000000000..38bf26564d --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h @@ -0,0 +1,303 @@ +/** @file + Functions declaration related with DHCPv6 for UefiPxeBc Driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_PXEBC_DHCP6_H__ +#define __EFI_PXEBC_DHCP6_H__ + +#define PXEBC_DHCP6_OPTION_MAX_NUM 16 +#define PXEBC_DHCP6_OPTION_MAX_SIZE 312 +#define PXEBC_DHCP6_PACKET_MAX_SIZE 1472 +#define PXEBC_IP6_POLICY_MAX 0xff +#define PXEBC_IP6_ROUTE_TABLE_TIMEOUT 10 + +#define PXEBC_DHCP6_S_PORT 547 +#define PXEBC_DHCP6_C_PORT 546 + +#define PXEBC_DHCP6_OPT_CLIENT_ID 1 +#define PXEBC_DHCP6_OPT_SERVER_ID 2 +#define PXEBC_DHCP6_OPT_IA_NA 3 +#define PXEBC_DHCP6_OPT_IA_TA 4 +#define PXEBC_DHCP6_OPT_IAADDR 5 +#define PXEBC_DHCP6_OPT_ORO 6 +#define PXEBC_DHCP6_OPT_PREFERENCE 7 +#define PXEBC_DHCP6_OPT_ELAPSED_TIME 8 +#define PXEBC_DHCP6_OPT_REPLAY_MSG 9 +#define PXEBC_DHCP6_OPT_AUTH 11 +#define PXEBC_DHCP6_OPT_UNICAST 12 +#define PXEBC_DHCP6_OPT_STATUS_CODE 13 +#define PXEBC_DHCP6_OPT_RAPID_COMMIT 14 +#define PXEBC_DHCP6_OPT_USER_CLASS 15 +#define PXEBC_DHCP6_OPT_VENDOR_CLASS 16 +#define PXEBC_DHCP6_OPT_VENDOR_OPTS 17 +#define PXEBC_DHCP6_OPT_INTERFACE_ID 18 +#define PXEBC_DHCP6_OPT_RECONFIG_MSG 19 +#define PXEBC_DHCP6_OPT_RECONFIG_ACCEPT 20 +#define PXEBC_DHCP6_OPT_BOOT_FILE_URL 59 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_OPT_BOOT_FILE_PARAM 60 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_OPT_ARCH 61 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_OPT_UNDI 62 // Assigned by IANA, RFC 5970 +#define PXEBC_DHCP6_ENTERPRISE_NUM 343 // TODO: IANA TBD: temporarily using Intel's +#define PXEBC_DHCP6_MAX_BOOT_FILE_SIZE 65535 // It's a limitation of bit length, 65535*512 bytes. + + +#define PXEBC_DHCP6_IDX_IA_NA 0 +#define PXEBC_DHCP6_IDX_BOOT_FILE_URL 1 +#define PXEBC_DHCP6_IDX_BOOT_FILE_PARAM 2 +#define PXEBC_DHCP6_IDX_VENDOR_CLASS 3 +#define PXEBC_DHCP6_IDX_MAX 4 + +#define PXEBC_DHCP6_BOOT_FILE_URL_PREFIX "tftp://" +#define PXEBC_TFTP_URL_SEPARATOR '/' +#define PXEBC_ADDR_START_DELIMITER '[' +#define PXEBC_ADDR_END_DELIMITER ']' + +#define GET_NEXT_DHCP6_OPTION(Opt) \ + (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \ + sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1) + +#define GET_DHCP6_OPTION_SIZE(Pkt) \ + ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER)) + +#define IS_PROXY_OFFER(Type) \ + ((Type) == PxeOfferTypeProxyBinl || \ + (Type) == PxeOfferTypeProxyPxe10 || \ + (Type) == PxeOfferTypeProxyWfm11a) + + +#pragma pack(1) +typedef struct { + UINT16 OpCode[256]; +} PXEBC_DHCP6_OPTION_ORO; + +typedef struct { + UINT8 Type; + UINT8 MajorVer; + UINT8 MinorVer; +} PXEBC_DHCP6_OPTION_UNDI; + +typedef struct { + UINT16 Type; +} PXEBC_DHCP6_OPTION_ARCH; + +typedef struct { + UINT8 ClassIdentifier[10]; + UINT8 ArchitecturePrefix[5]; + UINT8 ArchitectureType[5]; + UINT8 Lit3[1]; + UINT8 InterfaceName[4]; + UINT8 Lit4[1]; + UINT8 UndiMajor[3]; + UINT8 UndiMinor[3]; +} PXEBC_CLASS_ID; + +typedef struct { + UINT32 Vendor; + UINT16 ClassLen; + PXEBC_CLASS_ID ClassId; +} PXEBC_DHCP6_OPTION_VENDOR_CLASS; + +#pragma pack() + +typedef union { + PXEBC_DHCP6_OPTION_ORO *Oro; + PXEBC_DHCP6_OPTION_UNDI *Undi; + PXEBC_DHCP6_OPTION_ARCH *Arch; + PXEBC_DHCP6_OPTION_VENDOR_CLASS *VendorClass; +} PXEBC_DHCP6_OPTION_ENTRY; + +typedef struct { + LIST_ENTRY Link; + EFI_DHCP6_PACKET_OPTION *Option; + UINT8 Precedence; +} PXEBC_DHCP6_OPTION_NODE; + +typedef union { + EFI_DHCP6_PACKET Offer; + EFI_DHCP6_PACKET Ack; + UINT8 Buffer[PXEBC_DHCP6_PACKET_MAX_SIZE]; +} PXEBC_DHCP6_PACKET; + +typedef struct { + PXEBC_DHCP6_PACKET Packet; + PXEBC_OFFER_TYPE OfferType; + EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_IDX_MAX]; +} PXEBC_DHCP6_PACKET_CACHE; + + +/** + Free all the nodes in the boot file list. + + @param[in] Head The pointer to the head of the list. + +**/ +VOID +PxeBcFreeBootFileOption ( + IN LIST_ENTRY *Head + ); + + +/** + Parse the Boot File URL option. + + @param[out] FileName The pointer to the boot file name. + @param[in, out] SrvAddr The pointer to the boot server address. + @param[in] BootFile The pointer to the boot file URL option data. + @param[in] Length Length of the boot file URL option data. + + @retval EFI_ABORTED User canceled the operation. + @retval EFI_SUCCESS Selected the boot menu successfully. + @retval EFI_NOT_READY Read the input key from the keybroad has not finish. + +**/ +EFI_STATUS +PxeBcExtractBootFileUrl ( + OUT UINT8 **FileName, + IN OUT EFI_IPv6_ADDRESS *SrvAddr, + IN CHAR8 *BootFile, + IN UINT16 Length + ); + + +/** + Parse the Boot File Parameter option. + + @param[in] BootFilePara The pointer to the boot file parameter option data. + @param[out] BootFileSize The pointer to the parsed boot file size. + + @retval EFI_SUCCESS Successfully obtained the boot file size from parameter option. + @retval EFI_NOT_FOUND Failed to extract the boot file size from parameter option. + +**/ +EFI_STATUS +PxeBcExtractBootFileParam ( + IN CHAR8 *BootFilePara, + OUT UINT16 *BootFileSize + ); + + +/** + Parse the cached DHCPv6 packet, including all the options. + + @param[in] Cache6 The pointer to a cached DHCPv6 packet. + + @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully. + @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. + +**/ +EFI_STATUS +PxeBcParseDhcp6Packet ( + IN PXEBC_DHCP6_PACKET_CACHE *Cache6 + ); + + +/** + Register the ready address by Ip6Config protocol. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] Address The pointer to the ready address. + + @retval EFI_SUCCESS Registered the address succesfully. + @retval Others Failed to register the address. + +**/ +EFI_STATUS +PxeBcRegisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_IPv6_ADDRESS *Address + ); + + +/** + Unregister the address by Ip6Config protocol. + + @param[in] Private The pointer to the PxeBc private data. + +**/ +VOID +PxeBcUnregisterIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ); + + +/** + Build and send out the request packet for the bootfile, and parse the reply. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] Type PxeBc option boot item type. + @param[in] Layer The pointer to the option boot item layer. + @param[in] UseBis Use BIS or not. + @param[in] DestIp The pointer to the server address. + + @retval EFI_SUCCESS Successfully discovered theboot file. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_NOT_FOUND Can't get the PXE reply packet. + @retval Others Failed to discover boot file. + +**/ +EFI_STATUS +PxeBcDhcp6Discover ( + IN PXEBC_PRIVATE_DATA *Private, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_IP_ADDRESS *DestIp + ); + +/** + Set the IP6 policy to Automatic. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS Switch the IP policy succesfully. + @retval Others Unexpect error happened. + +**/ +EFI_STATUS +PxeBcSetIp6Policy ( + IN PXEBC_PRIVATE_DATA *Private + ); + +/** + This function will register the station IP address and flush IP instance to start using the new IP address. + + @param[in] Private The pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The new IP address has been configured successfully. + @retval Others Failed to configure the address. + +**/ +EFI_STATUS +PxeBcSetIp6Address ( + IN PXEBC_PRIVATE_DATA *Private + ); + +/** + Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] Dhcp6 The pointer to EFI_DHCP6_PROTOCOL. + + @retval EFI_SUCCESS The S.A.R.R. process successfully finished. + @retval Others Failed to finish the S.A.R.R. process. + +**/ +EFI_STATUS +PxeBcDhcp6Sarr ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_DHCP6_PROTOCOL *Dhcp6 + ); + +#endif + diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c new file mode 100644 index 0000000000..a6f66682f3 --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c @@ -0,0 +1,1825 @@ +/** @file + Driver Binding functions implementationfor for UefiPxeBc Driver. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.
+ Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PxeBcImpl.h" + + +EFI_DRIVER_BINDING_PROTOCOL gPxeBcIp4DriverBinding = { + PxeBcIp4DriverBindingSupported, + PxeBcIp4DriverBindingStart, + PxeBcIp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_DRIVER_BINDING_PROTOCOL gPxeBcIp6DriverBinding = { + PxeBcIp6DriverBindingSupported, + PxeBcIp6DriverBindingStart, + PxeBcIp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +/** + Get the Nic handle using any child handle in the IPv4 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv4. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp4Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + } + } + } + + return NicHandle; +} + + +/** + Get the Nic handle using any child handle in the IPv6 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv6. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp6Children ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_HANDLE NicHandle; + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp6ProtocolGuid); + if (NicHandle == NULL) { + return NULL; + } + } + } + } + + return NicHandle; +} + + +/** + Destroy the opened instances based on IPv4. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcDestroyIp4Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + ASSERT(Private != NULL); + + if (Private->ArpChild != NULL) { + // + // Close Arp for PxeBc->Arp and destroy the instance. + // + gBS->CloseProtocol ( + Private->ArpChild, + &gEfiArpProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiArpServiceBindingProtocolGuid, + Private->ArpChild + ); + } + + if (Private->Ip4Child != NULL) { + // + // Close Ip4 for background ICMP error message and destroy the instance. + // + gBS->CloseProtocol ( + Private->Ip4Child, + &gEfiIp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiIp4ServiceBindingProtocolGuid, + Private->Ip4Child + ); + } + + if (Private->Udp4WriteChild != NULL) { + // + // Close Udp4 for PxeBc->UdpWrite and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp4WriteChild, + &gEfiUdp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + Private->Udp4WriteChild + ); + } + + if (Private->Udp4ReadChild != NULL) { + // + // Close Udp4 for PxeBc->UdpRead and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp4ReadChild, + &gEfiUdp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + Private->Udp4ReadChild + ); + } + + if (Private->Mtftp4Child != NULL) { + // + // Close Mtftp4 for PxeBc->Mtftp4 and destroy the instance. + // + gBS->CloseProtocol ( + Private->Mtftp4Child, + &gEfiMtftp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + Private->Mtftp4Child + ); + } + + if (Private->Dhcp4Child != NULL) { + // + // Close Dhcp4 for PxeBc->Dhcp4 and destroy the instance. + // + gBS->CloseProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + Private->Dhcp4Child + ); + } + + if (Private->Ip4Nic != NULL) { + // + // Close PxeBcPrivate from the parent Nic handle and destroy the virtual handle. + // + gBS->CloseProtocol ( + Private->Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Private->Ip4Nic->Controller + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip4Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc, + NULL + ); + + if (Private->Snp != NULL) { + // + // Close SNP from the child virtual handle + // + gBS->CloseProtocol ( + Private->Ip4Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Private->Ip4Nic->Controller + ); + + gBS->UninstallProtocolInterface ( + Private->Ip4Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + Private->Snp + ); + } + FreePool (Private->Ip4Nic); + } + + Private->ArpChild = NULL; + Private->Ip4Child = NULL; + Private->Udp4WriteChild = NULL; + Private->Udp4ReadChild = NULL; + Private->Mtftp4Child = NULL; + Private->Dhcp4Child = NULL; + Private->Ip4Nic = NULL; +} + + +/** + Destroy the opened instances based on IPv6. + + @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL. + @param[in] Private Pointer to PXEBC_PRIVATE_DATA. + +**/ +VOID +PxeBcDestroyIp6Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + ASSERT(Private != NULL); + + if (Private->Ip6Child != NULL) { + // + // Close Ip6 for Ip6->Ip6Config and destroy the instance. + // + gBS->CloseProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + Private->Ip6Child + ); + } + + if (Private->Udp6WriteChild != NULL) { + // + // Close Udp6 for PxeBc->UdpWrite and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp6WriteChild, + &gEfiUdp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + Private->Udp6WriteChild + ); + } + + if (Private->Udp6ReadChild != NULL) { + // + // Close Udp6 for PxeBc->UdpRead and destroy the instance. + // + gBS->CloseProtocol ( + Private->Udp6ReadChild, + &gEfiUdp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + Private->Udp6ReadChild + ); + } + + if (Private->Mtftp6Child != NULL) { + // + // Close Mtftp6 for PxeBc->Mtftp and destroy the instance. + // + gBS->CloseProtocol ( + Private->Mtftp6Child, + &gEfiMtftp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + Private->Mtftp6Child + ); + } + + if (Private->Dhcp6Child != NULL) { + // + // Close Dhcp6 for PxeBc->Dhcp and destroy the instance. + // + gBS->CloseProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + This->DriverBindingHandle, + Private->Controller + ); + + NetLibDestroyServiceChild ( + Private->Controller, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + Private->Dhcp6Child + ); + } + + if (Private->Ip6Nic != NULL) { + // + // Close PxeBcPrivate from the parent Nic handle and destroy the virtual handle. + // + gBS->CloseProtocol ( + Private->Controller, + &gEfiCallerIdGuid, + This->DriverBindingHandle, + Private->Ip6Nic->Controller + ); + + gBS->UninstallMultipleProtocolInterfaces ( + Private->Ip6Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc, + NULL + ); + if (Private->Snp != NULL) { + // + // Close SNP from the child virtual handle + // + gBS->CloseProtocol ( + Private->Ip6Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + This->DriverBindingHandle, + Private->Ip6Nic->Controller + ); + gBS->UninstallProtocolInterface ( + Private->Ip6Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + Private->Snp + ); + } + FreePool (Private->Ip6Nic); + } + + Private->Ip6Child = NULL; + Private->Udp6WriteChild = NULL; + Private->Udp6ReadChild = NULL; + Private->Mtftp6Child = NULL; + Private->Dhcp6Child = NULL; + Private->Ip6Nic = NULL; + Private->Mode.Ipv6Available = FALSE; +} + +/** + Check whether UNDI protocol supports IPv6. + + @param[in] ControllerHandle Controller handle. + @param[in] Private Pointer to PXEBC_PRIVATE_DATA. + @param[out] Ipv6Support TRUE if UNDI supports IPv6. + + @retval EFI_SUCCESS Get the result whether UNDI supports IPv6 by NII or AIP protocol successfully. + @retval EFI_NOT_FOUND Don't know whether UNDI supports IPv6 since NII or AIP is not available. + +**/ +EFI_STATUS +PxeBcCheckIpv6Support ( + IN EFI_HANDLE ControllerHandle, + IN PXEBC_PRIVATE_DATA *Private, + OUT BOOLEAN *Ipv6Support + ) +{ + EFI_HANDLE Handle; + EFI_ADAPTER_INFORMATION_PROTOCOL *Aip; + EFI_STATUS Status; + EFI_GUID *InfoTypesBuffer; + UINTN InfoTypeBufferCount; + UINTN TypeIndex; + BOOLEAN Supported; + VOID *InfoBlock; + UINTN InfoBlockSize; + + ASSERT (Private != NULL && Ipv6Support != NULL); + + // + // Check whether the UNDI supports IPv6 by NII protocol. + // + if (Private->Nii != NULL) { + *Ipv6Support = Private->Nii->Ipv6Supported; + return EFI_SUCCESS; + } + + // + // Check whether the UNDI supports IPv6 by AIP protocol. + // + + // + // Get the NIC handle by SNP protocol. + // + Handle = NetLibGetSnpHandle (ControllerHandle, NULL); + if (Handle == NULL) { + return EFI_NOT_FOUND; + } + + Aip = NULL; + Status = gBS->HandleProtocol ( + Handle, + &gEfiAdapterInformationProtocolGuid, + (VOID *) &Aip + ); + if (EFI_ERROR (Status) || Aip == NULL) { + return EFI_NOT_FOUND; + } + + InfoTypesBuffer = NULL; + InfoTypeBufferCount = 0; + Status = Aip->GetSupportedTypes (Aip, &InfoTypesBuffer, &InfoTypeBufferCount); + if (EFI_ERROR (Status) || InfoTypesBuffer == NULL) { + FreePool (InfoTypesBuffer); + return EFI_NOT_FOUND; + } + + Supported = FALSE; + for (TypeIndex = 0; TypeIndex < InfoTypeBufferCount; TypeIndex++) { + if (CompareGuid (&InfoTypesBuffer[TypeIndex], &gEfiAdapterInfoUndiIpv6SupportGuid)) { + Supported = TRUE; + break; + } + } + + FreePool (InfoTypesBuffer); + if (!Supported) { + return EFI_NOT_FOUND; + } + + // + // We now have adapter information block. + // + InfoBlock = NULL; + InfoBlockSize = 0; + Status = Aip->GetInformation (Aip, &gEfiAdapterInfoUndiIpv6SupportGuid, &InfoBlock, &InfoBlockSize); + if (EFI_ERROR (Status) || InfoBlock == NULL) { + FreePool (InfoBlock); + return EFI_NOT_FOUND; + } + + *Ipv6Support = ((EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT *) InfoBlock)->Ipv6Support; + FreePool (InfoBlock); + return EFI_SUCCESS; + +} + +/** + Create the opened instances based on IPv4. + + @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL. + @param[in] ControllerHandle Handle of the child to destroy. + @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The instances based on IPv4 were all created successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +PxeBcCreateIp4Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + IPv4_DEVICE_PATH Ip4Node; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_CONFIG_DATA *Udp4CfgData; + EFI_IP4_CONFIG_DATA *Ip4CfgData; + EFI_IP4_MODE_DATA Ip4ModeData; + PXEBC_PRIVATE_PROTOCOL *Id; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + + if (Private->Ip4Nic != NULL) { + // + // Already created before. + // + return EFI_SUCCESS; + } + + // + // Create Dhcp4 child and open Dhcp4 protocol for PxeBc->Dhcp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + &Private->Dhcp4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp4Child, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Private->Dhcp4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Mtftp4 child and open Mtftp4 protocol for PxeBc->Mtftp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + &Private->Mtftp4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Mtftp4Child, + &gEfiMtftp4ProtocolGuid, + (VOID **) &Private->Mtftp4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp4 child and open Udp4 protocol for PxeBc->UdpRead. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + &Private->Udp4ReadChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp4ReadChild, + &gEfiUdp4ProtocolGuid, + (VOID **) &Private->Udp4Read, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp4 child and open Udp4 protocol for PxeBc->UdpWrite. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp4ServiceBindingProtocolGuid, + &Private->Udp4WriteChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp4WriteChild, + &gEfiUdp4ProtocolGuid, + (VOID **) &Private->Udp4Write, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Arp child and open Arp protocol for PxeBc->Arp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiArpServiceBindingProtocolGuid, + &Private->ArpChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->ArpChild, + &gEfiArpProtocolGuid, + (VOID **) &Private->Arp, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Ip4 child and open Ip4 protocol for background ICMP packets. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiIp4ServiceBindingProtocolGuid, + &Private->Ip4Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip4Child, + &gEfiIp4ProtocolGuid, + (VOID **) &Private->Ip4, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get max packet size from Ip4 to calculate block size for Tftp later. + // + Status = Private->Ip4->GetModeData (Private->Ip4, &Ip4ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Private->Ip4MaxPacketSize = Ip4ModeData.MaxPacketSize; + + Private->Ip4Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC)); + if (Private->Ip4Nic == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Ip4Nic->Private = Private; + Private->Ip4Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE; + + // + // Locate Ip4->Ip4Config2 and store it for set IPv4 Policy. + // + Status = gBS->HandleProtocol ( + ControllerHandle, + &gEfiIp4Config2ProtocolGuid, + (VOID **) &Private->Ip4Config2 + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create a device path node for Ipv4 virtual nic, and append it. + // + ZeroMem (&Ip4Node, sizeof (IPv4_DEVICE_PATH)); + Ip4Node.Header.Type = MESSAGING_DEVICE_PATH; + Ip4Node.Header.SubType = MSG_IPv4_DP; + Ip4Node.StaticIpAddress = FALSE; + + SetDevicePathNodeLength (&Ip4Node.Header, sizeof (Ip4Node)); + + Private->Ip4Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip4Node.Header); + + if (Private->Ip4Nic->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + CopyMem ( + &Private->Ip4Nic->LoadFile, + &gLoadFileProtocolTemplate, + sizeof (EFI_LOAD_FILE_PROTOCOL) + ); + + // + // Create a new handle for IPv4 virtual nic, + // and install PxeBaseCode, LoadFile and DevicePath protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip4Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip4Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip4Nic->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (Private->Snp != NULL) { + // + // Install SNP protocol on purpose is for some OS loader backward + // compatibility consideration. + // + Status = gBS->InstallProtocolInterface ( + &Private->Ip4Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + EFI_NATIVE_INTERFACE, + Private->Snp + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open SNP on the child handle BY_DRIVER. It will prevent any additionally + // layering to perform the experiment. + // + Status = gBS->OpenProtocol ( + Private->Ip4Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Snp, + This->DriverBindingHandle, + Private->Ip4Nic->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Open PxeBaseCodePrivate protocol by child to setup a parent-child relationship between + // real NIC handle and the virtual IPv4 NIC handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + Private->Ip4Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Set default configure data for Udp4Read and Ip4 instance. + // + Mode = Private->PxeBc.Mode; + Udp4CfgData = &Private->Udp4CfgData; + Ip4CfgData = &Private->Ip4CfgData; + + Udp4CfgData->AcceptBroadcast = FALSE; + Udp4CfgData->AcceptAnyPort = TRUE; + Udp4CfgData->AllowDuplicatePort = TRUE; + Udp4CfgData->TypeOfService = Mode->ToS; + Udp4CfgData->TimeToLive = Mode->TTL; + Udp4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Udp4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + Ip4CfgData->AcceptIcmpErrors = TRUE; + Ip4CfgData->DefaultProtocol = EFI_IP_PROTO_ICMP; + Ip4CfgData->TypeOfService = Mode->ToS; + Ip4CfgData->TimeToLive = Mode->TTL; + Ip4CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Ip4CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + return EFI_SUCCESS; + +ON_ERROR: + PxeBcDestroyIp4Children (This, Private); + return Status; +} + + +/** + Create the opened instances based on IPv6. + + @param[in] This Pointer to EFI_DRIVER_BINDING_PROTOCOL. + @param[in] ControllerHandle Handle of the child to destroy. + @param[in] Private Handle Pointer to PXEBC_PRIVATE_DATA. + + @retval EFI_SUCCESS The instances based on IPv6 were all created successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +PxeBcCreateIp6Children ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + IPv6_DEVICE_PATH Ip6Node; + EFI_UDP6_CONFIG_DATA *Udp6CfgData; + EFI_IP6_CONFIG_DATA *Ip6CfgData; + EFI_IP6_MODE_DATA Ip6ModeData; + PXEBC_PRIVATE_PROTOCOL *Id; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + UINTN Index; + + if (Private->Ip6Nic != NULL) { + // + // Already created before. + // + return EFI_SUCCESS; + } + + Private->Ip6Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC)); + + if (Private->Ip6Nic == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Ip6Nic->Private = Private; + Private->Ip6Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE; + + // + // Create Dhcp6 child and open Dhcp6 protocol for PxeBc->Dhcp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Private->Dhcp6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Dhcp6Child, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Private->Dhcp6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Generate a random IAID for the Dhcp6 assigned address. + // + Private->IaId = NET_RANDOM (NetRandomInitSeed ()); + if (Private->Snp != NULL) { + for (Index = 0; Index < Private->Snp->Mode->HwAddressSize; Index++) { + Private->IaId |= (Private->Snp->Mode->CurrentAddress.Addr[Index] << ((Index << 3) & 31)); + } + } + + // + // Create Mtftp6 child and open Mtftp6 protocol for PxeBc->Mtftp. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + &Private->Mtftp6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Mtftp6Child, + &gEfiMtftp6ProtocolGuid, + (VOID **) &Private->Mtftp6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp6 child and open Udp6 protocol for PxeBc->UdpRead. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Private->Udp6ReadChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp6ReadChild, + &gEfiUdp6ProtocolGuid, + (VOID **) &Private->Udp6Read, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Udp6 child and open Udp6 protocol for PxeBc->UdpWrite. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiUdp6ServiceBindingProtocolGuid, + &Private->Udp6WriteChild + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Udp6WriteChild, + &gEfiUdp6ProtocolGuid, + (VOID **) &Private->Udp6Write, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create Ip6 child and open Ip6 protocol for background ICMP6 packets. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiIp6ServiceBindingProtocolGuid, + &Private->Ip6Child + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + Private->Ip6Child, + &gEfiIp6ProtocolGuid, + (VOID **) &Private->Ip6, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get max packet size from Ip6 to calculate block size for Tftp later. + // + Status = Private->Ip6->GetModeData (Private->Ip6, &Ip6ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Private->Ip6MaxPacketSize = Ip6ModeData.MaxPacketSize; + + // + // Locate Ip6->Ip6Config and store it for set IPv6 address. + // + Status = gBS->HandleProtocol ( + ControllerHandle, + &gEfiIp6ConfigProtocolGuid, + (VOID **) &Private->Ip6Cfg + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create a device path node for Ipv6 virtual nic, and append it. + // + ZeroMem (&Ip6Node, sizeof (IPv6_DEVICE_PATH)); + Ip6Node.Header.Type = MESSAGING_DEVICE_PATH; + Ip6Node.Header.SubType = MSG_IPv6_DP; + Ip6Node.PrefixLength = IP6_PREFIX_LENGTH; + + SetDevicePathNodeLength (&Ip6Node.Header, sizeof (Ip6Node)); + + Private->Ip6Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip6Node.Header); + + if (Private->Ip6Nic->DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + CopyMem ( + &Private->Ip6Nic->LoadFile, + &gLoadFileProtocolTemplate, + sizeof (EFI_LOAD_FILE_PROTOCOL) + ); + + // + // Create a new handle for IPv6 virtual nic, + // and install PxeBaseCode, LoadFile and DevicePath protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Ip6Nic->Controller, + &gEfiDevicePathProtocolGuid, + Private->Ip6Nic->DevicePath, + &gEfiLoadFileProtocolGuid, + &Private->Ip6Nic->LoadFile, + &gEfiPxeBaseCodeProtocolGuid, + &Private->PxeBc, + NULL + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (Private->Snp != NULL) { + // + // Install SNP protocol on purpose is for some OS loader backward + // compatibility consideration. + // + Status = gBS->InstallProtocolInterface ( + &Private->Ip6Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + EFI_NATIVE_INTERFACE, + Private->Snp + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open SNP on the child handle BY_DRIVER. It will prevent any additionally + // layering to perform the experiment. + // + Status = gBS->OpenProtocol ( + Private->Ip6Nic->Controller, + &gEfiSimpleNetworkProtocolGuid, + (VOID **) &Snp, + This->DriverBindingHandle, + Private->Ip6Nic->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Open PxeBaseCodePrivate protocol by child to setup a parent-child relationship between + // real NIC handle and the virtual IPv6 NIC handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + Private->Ip6Nic->Controller, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Set IPv6 avaiable flag and set default configure data for + // Udp6Read and Ip6 instance. + // + Status = PxeBcCheckIpv6Support (ControllerHandle, Private, &Private->Mode.Ipv6Available); + if (EFI_ERROR (Status)) { + // + // Fail to get the data whether UNDI supports IPv6. Set default value. + // + Private->Mode.Ipv6Available = TRUE; + } + + if (!Private->Mode.Ipv6Available) { + goto ON_ERROR; + } + + Udp6CfgData = &Private->Udp6CfgData; + Ip6CfgData = &Private->Ip6CfgData; + + Udp6CfgData->AcceptAnyPort = TRUE; + Udp6CfgData->AllowDuplicatePort = TRUE; + Udp6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT; + Udp6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Udp6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + Ip6CfgData->AcceptIcmpErrors = TRUE; + Ip6CfgData->DefaultProtocol = IP6_ICMP; + Ip6CfgData->HopLimit = PXEBC_DEFAULT_HOPLIMIT; + Ip6CfgData->ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Ip6CfgData->TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + + return EFI_SUCCESS; + +ON_ERROR: + PxeBcDestroyIp6Children (This, Private); + return Status; +} + + +/** + The entry point for UefiPxeBc driver that installs the driver + binding and component name protocol on its image. + + @param[in] ImageHandle The Image handle of the driver. + @param[in] SystemTable The system table. + + @return EFI_SUCCESS + @return Others + +**/ +EFI_STATUS +EFIAPI +PxeBcDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gPxeBcIp4DriverBinding, + ImageHandle, + &gPxeBcComponentName, + &gPxeBcComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gPxeBcIp6DriverBinding, + NULL, + &gPxeBcComponentName, + &gPxeBcComponentName2 + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiDriverBindingProtocolGuid, + &gPxeBcIp4DriverBinding, + &gEfiComponentName2ProtocolGuid, + &gPxeBcComponentName2, + &gEfiComponentNameProtocolGuid, + &gPxeBcComponentName, + NULL + ); + } + + return Status; +} + +/** + Test to see if this driver supports ControllerHandle. This is the worker function for + PxeBcIp4(6)DriverBindingSupported. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *DhcpServiceBindingGuid; + EFI_GUID *MtftpServiceBindingGuid; + + if (IpVersion == IP_VERSION_4) { + DhcpServiceBindingGuid = &gEfiDhcp4ServiceBindingProtocolGuid; + MtftpServiceBindingGuid = &gEfiMtftp4ServiceBindingProtocolGuid; + } else { + DhcpServiceBindingGuid = &gEfiDhcp6ServiceBindingProtocolGuid; + MtftpServiceBindingGuid = &gEfiMtftp6ServiceBindingProtocolGuid; + } + + // + // Try to open the Mtftp and Dhcp protocol to test whether IP stack is ready. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + DhcpServiceBindingGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + ControllerHandle, + MtftpServiceBindingGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + } + + // + // It's unsupported case if IP stack are not ready. + // + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Start this driver on ControllerHandle. This is the worker function for + PxeBcIp4(6)DriverBindingStart. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL, + IN UINT8 IpVersion + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_STATUS Status; + PXEBC_PRIVATE_PROTOCOL *Id; + BOOLEAN FirstStart; + + FirstStart = FALSE; + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // Skip the initialization if the driver has been started already. + // + Private = PXEBC_PRIVATE_DATA_FROM_ID (Id); + } else { + FirstStart = TRUE; + // + // If the driver has not been started yet, it should do initialization. + // + Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem ( + &Private->PxeBc, + &gPxeBcProtocolTemplate, + sizeof (EFI_PXE_BASE_CODE_PROTOCOL) + ); + + Private->Signature = PXEBC_PRIVATE_DATA_SIGNATURE; + Private->Controller = ControllerHandle; + Private->Image = This->ImageHandle; + Private->PxeBc.Mode = &Private->Mode; + Private->Mode.Ipv6Supported = TRUE; + Private->Mode.AutoArp = TRUE; + Private->Mode.TTL = DEFAULT_TTL; + Private->Mode.ToS = DEFAULT_ToS; + + // + // Open device path to prepare for appending virtual NIC node. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &Private->DevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Get the NII interface if it exists, it's not required. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &Private->Nii, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Private->Nii = NULL; + } + + // + // Install PxeBaseCodePrivate protocol onto the real NIC handler. + // PxeBaseCodePrivate protocol is only used to keep the relationship between + // NIC handle and virtual child handles. + // gEfiCallerIdGuid will be used as its protocol guid. + // + Status = gBS->InstallProtocolInterface ( + &ControllerHandle, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + &Private->Id + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Try to locate SNP protocol. + // + NetLibGetSnpHandle(ControllerHandle, &Private->Snp); + } + + if (IpVersion == IP_VERSION_4) { + // + // Try to create virtual NIC handle for IPv4. + // + Status = PxeBcCreateIp4Children (This, ControllerHandle, Private); + } else { + // + // Try to create virtual NIC handle for IPv6. + // + Status = PxeBcCreateIp6Children (This, ControllerHandle, Private); + } + if (EFI_ERROR (Status)) { + // + // Failed to start PXE driver if IPv4 and IPv6 stack are both not available. + // + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + if (FirstStart) { + gBS->UninstallProtocolInterface ( + ControllerHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + } + + if (IpVersion == IP_VERSION_4) { + PxeBcDestroyIp4Children (This, Private); + } else { + PxeBcDestroyIp6Children (This, Private); + } + + if (FirstStart && Private != NULL) { + FreePool (Private); + } + + return Status; +} + + +/** + Stop this driver on ControllerHandle. This is the worker function for + PxeBcIp4(6)DriverBindingStop. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_SUCCESS This driver was removed ControllerHandle. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +PxeBcStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer, + IN UINT8 IpVersion + ) +{ + PXEBC_PRIVATE_DATA *Private; + PXEBC_VIRTUAL_NIC *VirtualNic; + EFI_LOAD_FILE_PROTOCOL *LoadFile; + EFI_STATUS Status; + EFI_HANDLE NicHandle; + PXEBC_PRIVATE_PROTOCOL *Id; + + Private = NULL; + NicHandle = NULL; + VirtualNic = NULL; + LoadFile = NULL; + Id = NULL; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiLoadFileProtocolGuid, + (VOID **) &LoadFile, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // Get the Nic handle by any pass-over service child handle. + // + if (IpVersion == IP_VERSION_4) { + NicHandle = PxeBcGetNicByIp4Children (ControllerHandle); + } else { + NicHandle = PxeBcGetNicByIp6Children (ControllerHandle); + } + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Try to retrieve the private data by PxeBcPrivate protocol. + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiCallerIdGuid, + (VOID **) &Id, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private = PXEBC_PRIVATE_DATA_FROM_ID (Id); + + } else { + // + // It's a virtual handle with LoadFileProtocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiLoadFileProtocolGuid, + (VOID **) &LoadFile, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (LoadFile); + Private = VirtualNic->Private; + NicHandle = Private->Controller; + } + + // + // Stop functionality of PXE Base Code protocol + // + Status = Private->PxeBc.Stop (&Private->PxeBc); + if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) { + return Status; + } + + + if (Private->Ip4Nic != NULL && IpVersion == IP_VERSION_4) { + PxeBcDestroyIp4Children (This, Private); + } + + if (Private->Ip6Nic != NULL && IpVersion == IP_VERSION_6) { + PxeBcDestroyIp6Children (This, Private); + } + + if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) { + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiCallerIdGuid, + &Private->Id + ); + FreePool (Private); + } + + return EFI_SUCCESS; +} + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return PxeBcSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return PxeBcStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_4 + ); +} + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return PxeBcStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_4 + ); +} + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return PxeBcSupported ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); +} + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + return PxeBcStart ( + This, + ControllerHandle, + RemainingDevicePath, + IP_VERSION_6 + ); +} + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return PxeBcStop ( + This, + ControllerHandle, + NumberOfChildren, + ChildHandleBuffer, + IP_VERSION_6 + ); +} diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h new file mode 100644 index 0000000000..f22f0f6bb3 --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h @@ -0,0 +1,181 @@ +/** @file + Driver Binding functions declaration for UefiPxeBc Driver. + + Copyright (c) 2007 - 2013, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_PXEBC_DRIVER_H__ +#define __EFI_PXEBC_DRIVER_H__ + +extern EFI_COMPONENT_NAME_PROTOCOL gPxeBcComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2; + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be tested. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_UNSUPPORTED This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This The pointer to the driver binding protocol. + @param[in] ControllerHandle The handle of device to be started. + @param[in] RemainingDevicePath Optional parameter used to pick a specific child + device to be started. + + @retval EFI_SUCCESS This driver is installed to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PxeBcIp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); +#endif + diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c new file mode 100644 index 0000000000..12e5566a79 --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c @@ -0,0 +1,2411 @@ +/** @file + This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL. + + Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PxeBcImpl.h" + + +/** + Enables the use of the PXE Base Code Protocol functions. + + This function enables the use of the PXE Base Code Protocol functions. If the + Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then + EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted + addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted + addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported + field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will + be returned. If there is not enough memory or other resources to start the PXE + Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the + PXE Base Code Protocol will be started. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] UseIpv6 Specifies the type of IP addresses that are to be + used during the session that is being started. + Set to TRUE for IPv6, and FALSE for IPv4. + + @retval EFI_SUCCESS The PXE Base Code Protocol was started. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_UNSUPPORTED UseIpv6 is TRUE, but the Ipv6Supported field of the + EFI_PXE_BASE_CODE_MODE structure is FALSE. + @retval EFI_ALREADY_STARTED The PXE Base Code Protocol is already in the started state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory or other resources to start the + PXE Base Code Protocol. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcStart ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN UseIpv6 + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + UINTN Index; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (Mode->Started) { + return EFI_ALREADY_STARTED; + } + + // + // Detect whether using IPv6 or not, and set it into mode data. + // + if (UseIpv6 && Mode->Ipv6Available && Mode->Ipv6Supported && Private->Ip6Nic != NULL) { + Mode->UsingIpv6 = TRUE; + } else if (!UseIpv6 && Private->Ip4Nic != NULL) { + Mode->UsingIpv6 = FALSE; + } else { + return EFI_UNSUPPORTED; + } + + if (Mode->UsingIpv6) { + AsciiPrint ("\n>>Start PXE over IPv6"); + // + // Configure udp6 instance to receive data. + // + Status = Private->Udp6Read->Configure ( + Private->Udp6Read, + &Private->Udp6CfgData + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Configure block size for TFTP as a default value to handle all link layers. + // + Private->BlockSize = (UINTN) (Private->Ip6MaxPacketSize - + PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE); + + // + // PXE over IPv6 starts here, initialize the fields and list header. + // + Private->Ip6Policy = PXEBC_IP6_POLICY_MAX; + Private->ProxyOffer.Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + Private->DhcpAck.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + Private->PxeReply.Dhcp6.Packet.Ack.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + + for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE; + } + + // + // Create event and set status for token to capture ICMP6 error message. + // + Private->Icmp6Token.Status = EFI_NOT_READY; + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcIcmp6ErrorUpdate, + Private, + &Private->Icmp6Token.Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Set Ip6 policy to Automatic to start the IP6 router discovery. + // + Status = PxeBcSetIp6Policy (Private); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } else { + AsciiPrint ("\n>>Start PXE over IPv4"); + // + // Configure udp4 instance to receive data. + // + Status = Private->Udp4Read->Configure ( + Private->Udp4Read, + &Private->Udp4CfgData + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Configure block size for TFTP as a default value to handle all link layers. + // + Private->BlockSize = (UINTN) (Private->Ip4MaxPacketSize - + PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE); + + // + // PXE over IPv4 starts here, initialize the fields. + // + Private->ProxyOffer.Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + Private->DhcpAck.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + Private->PxeReply.Dhcp4.Packet.Ack.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + + for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) { + Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE; + } + + PxeBcSeedDhcp4Packet (&Private->SeedPacket, Private->Udp4Read); + + // + // Create the event for Arp cache update. + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + PxeBcArpCacheUpdate, + Private, + &Private->ArpUpdateEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Start a periodic timer by second to update Arp cache. + // + Status = gBS->SetTimer ( + Private->ArpUpdateEvent, + TimerPeriodic, + TICKS_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Create event and set status for token to capture ICMP error message. + // + Private->Icmp6Token.Status = EFI_NOT_READY; + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcIcmpErrorUpdate, + Private, + &Private->IcmpToken.Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + //DHCP4 service allows only one of its children to be configured in + //the active state, If the DHCP4 D.O.R.A started by IP4 auto + //configuration and has not been completed, the Dhcp4 state machine + //will not be in the right state for the PXE to start a new round D.O.R.A. + //so we need to switch it's policy to static. + // + Status = PxeBcSetIp4Policy (Private); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // If PcdTftpBlockSize is set to non-zero, override the default value. + // + if (PcdGet64 (PcdTftpBlockSize) != 0) { + Private->BlockSize = (UINTN) PcdGet64 (PcdTftpBlockSize); + } + + // + // Create event for UdpRead/UdpWrite timeout since they are both blocking API. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Private->UdpTimeOutEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Private->IsAddressOk = FALSE; + Mode->Started = TRUE; + + return EFI_SUCCESS; + +ON_ERROR: + if (Mode->UsingIpv6) { + if (Private->Icmp6Token.Event != NULL) { + gBS->CloseEvent (Private->Icmp6Token.Event); + Private->Icmp6Token.Event = NULL; + } + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + Private->Ip6->Configure (Private->Ip6, NULL); + } else { + if (Private->ArpUpdateEvent != NULL) { + gBS->CloseEvent (Private->ArpUpdateEvent); + Private->ArpUpdateEvent = NULL; + } + if (Private->IcmpToken.Event != NULL) { + gBS->CloseEvent (Private->IcmpToken.Event); + Private->IcmpToken.Event = NULL; + } + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + Private->Ip4->Configure (Private->Ip4, NULL); + } + return Status; +} + + +/** + Disable the use of the PXE Base Code Protocol functions. + + This function stops all activity on the network device. All the resources allocated + in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is + set to FALSE, and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE + structure is already FALSE, then EFI_NOT_STARTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + + @retval EFI_SUCCESS The PXE Base Code Protocol was stopped. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is already in the stopped state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval Others + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcStop ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + BOOLEAN Ipv6Supported; + BOOLEAN Ipv6Available; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Ipv6Supported = Mode->Ipv6Supported; + Ipv6Available = Mode->Ipv6Available; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6) { + // + // Configure all the instances for IPv6 as NULL. + // + ZeroMem (&Private->Udp6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&Private->Ip6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS)); + Private->Dhcp6->Stop (Private->Dhcp6); + Private->Dhcp6->Configure (Private->Dhcp6, NULL); + Private->Udp6Write->Configure (Private->Udp6Write, NULL); + Private->Udp6Read->Groups (Private->Udp6Read, FALSE, NULL); + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token); + Private->Ip6->Configure (Private->Ip6, NULL); + PxeBcUnregisterIp6Address (Private); + if (Private->Icmp6Token.Event != NULL) { + gBS->CloseEvent (Private->Icmp6Token.Event); + Private->Icmp6Token.Event = NULL; + } + if (Private->Dhcp6Request != NULL) { + FreePool (Private->Dhcp6Request); + Private->Dhcp6Request = NULL; + } + if (Private->BootFileName != NULL) { + FreePool (Private->BootFileName); + Private->BootFileName = NULL; + } + } else { + // + // Configure all the instances for IPv4 as NULL. + // + ZeroMem (&Private->Udp4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Private->Udp4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Private->Ip4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Private->Ip4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + Private->Dhcp4->Stop (Private->Dhcp4); + Private->Dhcp4->Configure (Private->Dhcp4, NULL); + Private->Udp4Write->Configure (Private->Udp4Write, NULL); + Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL); + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken); + Private->Ip4->Configure (Private->Ip4, NULL); + if (Private->ArpUpdateEvent != NULL) { + gBS->CloseEvent (Private->ArpUpdateEvent); + Private->ArpUpdateEvent = NULL; + } + if (Private->IcmpToken.Event != NULL) { + gBS->CloseEvent (Private->IcmpToken.Event); + Private->IcmpToken.Event = NULL; + } + Private->BootFileName = NULL; + } + + gBS->CloseEvent (Private->UdpTimeOutEvent); + Private->CurSrcPort = 0; + Private->BootFileSize = 0; + Private->SolicitTimes = 0; + Private->ElapsedTime = 0; + ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS)); + ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + // + // Reset the mode data. + // + ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE)); + Mode->Ipv6Available = Ipv6Available; + Mode->Ipv6Supported = Ipv6Supported; + Mode->AutoArp = TRUE; + Mode->TTL = DEFAULT_TTL; + Mode->ToS = DEFAULT_ToS; + + return EFI_SUCCESS; +} + + +/** + Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6 + S.A.R.R (solicit / advertise / request / reply) sequence. + + If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before + they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will + be tried in the order in which they are received. Please see the Preboot Execution + Environment (PXE) Specification and Unified Extensible Firmware Interface (UEFI) + Specification for additional details on the implementation of DHCP. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the DHCP sequence will be stopped and EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] SortOffers TRUE if the offers received should be sorted. Set to FALSE to + try the offers in the order that they are received. + + @retval EFI_SUCCESS Valid DHCP has completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete the DHCP Protocol. + @retval EFI_ABORTED The callback function aborted the DHCP Protocol. + @retval EFI_TIMEOUT The DHCP Protocol timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the DHCP session. + @retval EFI_NO_RESPONSE Valid PXE offer was not received. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcDhcp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN SortOffers + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Mode->IcmpErrorReceived = FALSE; + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DHCP; + Private->IsOfferSorted = SortOffers; + Private->SolicitTimes = 0; + Private->ElapsedTime = 0; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6) { + + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + + // + // Start S.A.R.R. process to get a IPv6 address and other boot information. + // + Status = PxeBcDhcp6Sarr (Private, Private->Dhcp6); + } else { + + // + // Stop Udp4Read instance + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + + // + // Start D.O.R.A. process to get a IPv4 address and other boot information. + // + Status = PxeBcDhcp4Dora (Private, Private->Dhcp4); + } + + // + // Reconfigure the UDP instance with the default configuration. + // + if (Mode->UsingIpv6) { + Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + } else { + Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData); + } + // + // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP + // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + // + ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + This->SetIpFilter (This, &IpFilter); + + return Status; +} + + +/** + Attempts to complete the PXE Boot Server and/or boot image discovery sequence. + + This function attempts to complete the PXE Boot Server and/or boot image discovery + sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the + PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the + EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the + PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure + will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE. + In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[], + has two uses: It is the Boot Server IP address list used for unicast discovery + (if the UseUCast field is TRUE), and it is the list used for Boot Server verification + (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure + is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot + Server reply of that type will be accepted. If the AcceptAnyResponse field is + FALSE, only responses from Boot Servers with matching IP addresses will be accepted. + This function can take at least 10 seconds to timeout and return control to the + caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be + returned. Please see the Preboot Execution Environment (PXE) Specification for + additional details on the implementation of the Discovery sequence. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the Discovery sequence is stopped and EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] Type The type of bootstrap to perform. + @param[in] Layer Pointer to the boot server layer number to discover, which must be + PXE_BOOT_LAYER_INITIAL when a new server type is being + discovered. + @param[in] UseBis TRUE if Boot Integrity Services are to be used. FALSE otherwise. + @param[in] Info Pointer to a data structure that contains additional information + on the type of discovery operation that is to be performed. + It is optional. + + @retval EFI_SUCCESS The Discovery sequence has been completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough memory to complete Discovery. + @retval EFI_ABORTED The callback function aborted the Discovery sequence. + @retval EFI_TIMEOUT The Discovery sequence timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the PXE discovery + session. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcDiscover ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 Type, + IN UINT16 *Layer, + IN BOOLEAN UseBis, + IN EFI_PXE_BASE_CODE_DISCOVER_INFO *Info OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo; + EFI_PXE_BASE_CODE_SRVLIST *SrvList; + PXEBC_BOOT_SVR_ENTRY *BootSvrEntry; + UINT16 Index; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + EFI_PXE_BASE_CODE_DISCOVER_INFO *NewCreatedInfo; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Mode->IcmpErrorReceived = FALSE; + BootSvrEntry = NULL; + SrvList = NULL; + Status = EFI_DEVICE_ERROR; + Private->Function = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER; + NewCreatedInfo = NULL; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + // + // Station address should be ready before do discover. + // + if (!Private->IsAddressOk) { + return EFI_INVALID_PARAMETER; + } + + if (Mode->UsingIpv6) { + + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + } else { + + // + // Stop Udp4Read instance + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + } + + // + // There are 3 methods to get the information for discover. + // + ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO)); + if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) { + // + // 1. Take the previous setting as the discover info. + // + if (!Mode->PxeDiscoverValid || + !Mode->PxeReplyReceived || + (!Mode->PxeBisReplyReceived && UseBis)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Info = &DefaultInfo; + Info->IpCnt = 1; + Info->UseUCast = TRUE; + SrvList = Info->SrvList; + SrvList[0].Type = Type; + SrvList[0].AcceptAnyResponse = FALSE; + + CopyMem (&SrvList->IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + + } else if (Info == NULL) { + // + // 2. Extract the discover information from the cached packets if unspecified. + // + NewCreatedInfo = &DefaultInfo; + Status = PxeBcExtractDiscoverInfo (Private, Type, &NewCreatedInfo, &BootSvrEntry, &SrvList); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + ASSERT (NewCreatedInfo != NULL); + Info = NewCreatedInfo; + } else { + // + // 3. Take the pass-in information as the discover info, and validate the server list. + // + SrvList = Info->SrvList; + + if (!SrvList[0].AcceptAnyResponse) { + for (Index = 1; Index < Info->IpCnt; Index++) { + if (SrvList[Index].AcceptAnyResponse) { + break; + } + } + if (Index != Info->IpCnt) { + // + // It's invalid if the first server doesn't accecpt any response + // but any of the other servers does accept any response. + // + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + } + + // + // Info and BootSvrEntry/SrvList are all ready by now, so execute discover by UniCast/BroadCast/MultiCast. + // + if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) || + (Info->MustUseList && Info->IpCnt == 0)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + Private->IsDoDiscover = TRUE; + + if (Info->UseMCast) { + // + // Do discover by multicast. + // + Status = PxeBcDiscoverBootServer ( + Private, + Type, + Layer, + UseBis, + &Info->ServerMCastIp, + Info->IpCnt, + SrvList + ); + + } else if (Info->UseBCast) { + // + // Do discover by broadcast, but only valid for IPv4. + // + ASSERT (!Mode->UsingIpv6); + Status = PxeBcDiscoverBootServer ( + Private, + Type, + Layer, + UseBis, + NULL, + Info->IpCnt, + SrvList + ); + + } else if (Info->UseUCast) { + // + // Do discover by unicast. + // + for (Index = 0; Index < Info->IpCnt; Index++) { + if (BootSvrEntry == NULL) { + CopyMem (&Private->ServerIp, &SrvList[Index].IpAddr, sizeof (EFI_IP_ADDRESS)); + } else { + ASSERT (!Mode->UsingIpv6); + ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Private->ServerIp, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS)); + } + + Status = PxeBcDiscoverBootServer ( + Private, + Type, + Layer, + UseBis, + &Private->ServerIp, + Info->IpCnt, + SrvList + ); + } + } + + if (!EFI_ERROR (Status)) { + // + // Parse the cached PXE reply packet, and store it into mode data if valid. + // + if (Mode->UsingIpv6) { + Status = PxeBcParseDhcp6Packet (&Private->PxeReply.Dhcp6); + if (!EFI_ERROR (Status)) { + CopyMem ( + &Mode->PxeReply.Dhcpv6, + &Private->PxeReply.Dhcp6.Packet.Ack.Dhcp6, + Private->PxeReply.Dhcp6.Packet.Ack.Length + ); + Mode->PxeReplyReceived = TRUE; + Mode->PxeDiscoverValid = TRUE; + } + } else { + Status = PxeBcParseDhcp4Packet (&Private->PxeReply.Dhcp4); + if (!EFI_ERROR (Status)) { + CopyMem ( + &Mode->PxeReply.Dhcpv4, + &Private->PxeReply.Dhcp4.Packet.Ack.Dhcp4, + Private->PxeReply.Dhcp4.Packet.Ack.Length + ); + Mode->PxeReplyReceived = TRUE; + Mode->PxeDiscoverValid = TRUE; + } + } + } + +ON_EXIT: + + if (NewCreatedInfo != NULL && NewCreatedInfo != &DefaultInfo) { + FreePool (NewCreatedInfo); + } + + if (Mode->UsingIpv6) { + Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + } else { + Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData); + } + + // + // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP + // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + // + ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + This->SetIpFilter (This, &IpFilter); + + return Status; +} + + +/** + Used to perform TFTP and MTFTP services. + + This function is used to perform TFTP and MTFTP services. This includes the + TFTP operations to get the size of a file, read a directory, read a file, and + write a file. It also includes the MTFTP operations to get the size of a file, + read a directory, and read a file. The type of operation is specified by Operation. + If the callback function that is invoked during the TFTP/MTFTP operation does + not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will + be returned. + For read operations, the return data will be placed in the buffer specified by + BufferPtr. If BufferSize is too small to contain the entire downloaded file, + then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero, + or the size of the requested file. (NOTE: the size of the requested file is only returned + if the TFTP server supports TFTP options). If BufferSize is large enough for the + read operation, then BufferSize will be set to the size of the downloaded file, + and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services + should use the get-file-size operations to determine the size of the downloaded + file prior to using the read-file operations-especially when downloading large + (greater than 64 MB) files-instead of making two calls to the read-file operation. + Following this recommendation will save time if the file is larger than expected + and the TFTP server does not support TFTP option extensions. Without TFTP option + extension support, the client must download the entire file, counting and discarding + the received packets, to determine the file size. + For write operations, the data to be sent is in the buffer specified by BufferPtr. + BufferSize specifies the number of bytes to send. If the write operation completes + successfully, then EFI_SUCCESS will be returned. + For TFTP "get file size" operations, the size of the requested file or directory + is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server + does not support options, the file will be downloaded into a bit bucket and the + length of the downloaded file will be returned. For MTFTP "get file size" operations, + if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED + will be returned. + This function can take up to 10 seconds to timeout and return control to the caller. + If the TFTP sequence does not complete, EFI_TIMEOUT will be returned. + If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then the TFTP sequence is stopped and EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] Operation The type of operation to perform. + @param[in, out] BufferPtr A pointer to the data buffer. + @param[in] Overwrite Only used on write file operations. TRUE if a file on a remote + server can be overwritten. + @param[in, out] BufferSize For get-file-size operations, *BufferSize returns the size of the + requested file. + @param[in] BlockSize The requested block size to be used during a TFTP transfer. + @param[in] ServerIp The TFTP / MTFTP server IP address. + @param[in] Filename A Null-terminated ASCII string that specifies a directory name + or a file name. + @param[in] Info Pointer to the MTFTP information. + @param[in] DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation. + + @retval EFI_SUCCESS The TFTP/MTFTP operation was completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_BUFFER_TOO_SMALL The buffer is not large enough to complete the read operation. + @retval EFI_ABORTED The callback function aborted the TFTP/MTFTP operation. + @retval EFI_TIMEOUT The TFTP/MTFTP operation timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the MTFTP session. + @retval EFI_TFTP_ERROR A TFTP error packet was received during the MTFTP session. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcMtftp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_TFTP_OPCODE Operation, + IN OUT VOID *BufferPtr OPTIONAL, + IN BOOLEAN Overwrite, + IN OUT UINT64 *BufferSize, + IN UINTN *BlockSize OPTIONAL, + IN EFI_IP_ADDRESS *ServerIp, + IN UINT8 *Filename, + IN EFI_PXE_BASE_CODE_MTFTP_INFO *Info OPTIONAL, + IN BOOLEAN DontUseBuffer + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_MTFTP4_CONFIG_DATA Mtftp4Config; + EFI_MTFTP6_CONFIG_DATA Mtftp6Config; + VOID *Config; + EFI_STATUS Status; + EFI_PXE_BASE_CODE_IP_FILTER IpFilter; + + + if ((This == NULL) || + (Filename == NULL) || + (BufferSize == NULL) || + (ServerIp == NULL) || + ((BufferPtr == NULL) && DontUseBuffer) || + ((BlockSize != NULL) && (*BlockSize < PXE_MTFTP_DEFAULT_BLOCK_SIZE)) || + (!NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 0) && !NetIp6IsValidUnicast (&ServerIp->v6))) { + return EFI_INVALID_PARAMETER; + } + + Config = NULL; + Status = EFI_DEVICE_ERROR; + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (Mode->UsingIpv6) { + // + // Set configuration data for Mtftp6 instance. + // + ZeroMem (&Mtftp6Config, sizeof (EFI_MTFTP6_CONFIG_DATA)); + Config = &Mtftp6Config; + Mtftp6Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT; + Mtftp6Config.TryCount = PXEBC_MTFTP_RETRIES; + CopyMem (&Mtftp6Config.StationIp, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Mtftp6Config.ServerIp, &ServerIp->v6, sizeof (EFI_IPv6_ADDRESS)); + // + // Stop Udp6Read instance + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + } else { + // + // Set configuration data for Mtftp4 instance. + // + ZeroMem (&Mtftp4Config, sizeof (EFI_MTFTP4_CONFIG_DATA)); + Config = &Mtftp4Config; + Mtftp4Config.UseDefaultSetting = FALSE; + Mtftp4Config.TimeoutValue = PXEBC_MTFTP_TIMEOUT; + Mtftp4Config.TryCount = PXEBC_MTFTP_RETRIES; + CopyMem (&Mtftp4Config.StationIp, &Private->StationIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4Config.SubnetMask, &Private->SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4Config.GatewayIp, &Private->GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4Config.ServerIp, &ServerIp->v4, sizeof (EFI_IPv4_ADDRESS)); + // + // Stop Udp4Read instance + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + } + + Mode->TftpErrorReceived = FALSE; + Mode->IcmpErrorReceived = FALSE; + + switch (Operation) { + + case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE: + // + // Send TFTP request to get file size. + // + Status = PxeBcTftpGetFileSize ( + Private, + Config, + Filename, + BlockSize, + BufferSize + ); + + break; + + case EFI_PXE_BASE_CODE_TFTP_READ_FILE: + // + // Send TFTP request to read file. + // + Status = PxeBcTftpReadFile ( + Private, + Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + + break; + + case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE: + // + // Send TFTP request to write file. + // + Status = PxeBcTftpWriteFile ( + Private, + Config, + Filename, + Overwrite, + BlockSize, + BufferPtr, + BufferSize + ); + + break; + + case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY: + // + // Send TFTP request to read directory. + // + Status = PxeBcTftpReadDirectory ( + Private, + Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + + break; + + case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE: + case EFI_PXE_BASE_CODE_MTFTP_READ_FILE: + case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY: + Status = EFI_UNSUPPORTED; + + break; + + default: + Status = EFI_INVALID_PARAMETER; + + break; + } + + if (Status == EFI_ICMP_ERROR) { + Mode->IcmpErrorReceived = TRUE; + } + + // + // Reconfigure the UDP instance with the default configuration. + // + if (Mode->UsingIpv6) { + Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData); + } else { + Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData); + } + // + // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP + // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + // + ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER)); + IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP; + This->SetIpFilter (This, &IpFilter); + + return Status; +} + + +/** + Writes a UDP packet to the network interface. + + This function writes a UDP packet specified by the (optional HeaderPtr and) + BufferPtr parameters to the network interface. The UDP header is automatically + built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp, + SrcIp, and SrcPort to build this header. If the packet is successfully built and + transmitted through the network interface, then EFI_SUCCESS will be returned. + If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will + be returned. If an ICMP error occurs during the transmission of the packet, then + the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and + EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return + EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] OpFlags The UDP operation flags. + @param[in] DestIp The destination IP address. + @param[in] DestPort The destination UDP port number. + @param[in] GatewayIp The gateway IP address. + @param[in] SrcIp The source IP address. + @param[in, out] SrcPort The source UDP port number. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS The UDP Write operation completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_BAD_BUFFER_SIZE The buffer is too long to be transmitted. + @retval EFI_ABORTED The callback function aborted the UDP Write operation. + @retval EFI_TIMEOUT The UDP Write operation timed out. + @retval EFI_ICMP_ERROR An ICMP error packet was received during the UDP write session. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcUdpWrite ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN EFI_IP_ADDRESS *DestIp, + IN EFI_PXE_BASE_CODE_UDP_PORT *DestPort, + IN EFI_IP_ADDRESS *GatewayIp OPTIONAL, + IN EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_SESSION_DATA Udp4Session; + EFI_UDP6_SESSION_DATA Udp6Session; + EFI_STATUS Status; + BOOLEAN DoNotFragment; + + if (This == NULL || DestIp == NULL || DestPort == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT) != 0) { + DoNotFragment = FALSE; + } else { + DoNotFragment = TRUE; + } + + if (!Mode->UsingIpv6 && GatewayIp != NULL && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 0)) { + // + // Gateway is provided but it's not a unicast IPv4 address, while it will be ignored for IPv6. + // + return EFI_INVALID_PARAMETER; + } + + if (HeaderSize != NULL && (*HeaderSize == 0 || HeaderPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == NULL || (*BufferSize != 0 && BufferPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (!Private->IsAddressOk && SrcIp == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Private->CurSrcPort == 0 || + (SrcPort != NULL && *SrcPort != Private->CurSrcPort)) { + // + // Reconfigure UDPv4/UDPv6 for UdpWrite if the source port changed. + // + if (SrcPort != NULL) { + Private->CurSrcPort = *SrcPort; + } + } + + if (Mode->UsingIpv6) { + Status = PxeBcConfigUdp6Write ( + Private->Udp6Write, + &Private->StationIp.v6, + &Private->CurSrcPort + ); + } else { + // + // Configure the UDPv4 instance with gateway information from DHCP server as default. + // + Status = PxeBcConfigUdp4Write ( + Private->Udp4Write, + &Private->StationIp.v4, + &Private->SubnetMask.v4, + &Private->GatewayIp.v4, + &Private->CurSrcPort, + DoNotFragment + ); + } + + if (EFI_ERROR (Status)) { + Private->CurSrcPort = 0; + return EFI_INVALID_PARAMETER; + } else if (SrcPort != NULL) { + *SrcPort = Private->CurSrcPort; + } + + // + // Start a timer as timeout event for this blocking API. + // + gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT); + + if (Mode->UsingIpv6) { + // + // Construct UDPv6 session data. + // + ZeroMem (&Udp6Session, sizeof (EFI_UDP6_SESSION_DATA)); + CopyMem (&Udp6Session.DestinationAddress, DestIp, sizeof (EFI_IPv6_ADDRESS)); + Udp6Session.DestinationPort = *DestPort; + if (SrcIp != NULL) { + CopyMem (&Udp6Session.SourceAddress, SrcIp, sizeof (EFI_IPv6_ADDRESS)); + } + if (SrcPort != NULL) { + Udp6Session.SourcePort = *SrcPort; + } + + Status = PxeBcUdp6Write ( + Private->Udp6Write, + &Udp6Session, + Private->UdpTimeOutEvent, + HeaderSize, + HeaderPtr, + BufferSize, + BufferPtr + ); + } else { + // + // Construct UDPv4 session data. + // + ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA)); + CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS)); + Udp4Session.DestinationPort = *DestPort; + if (SrcIp != NULL) { + CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS)); + } + if (SrcPort != NULL) { + Udp4Session.SourcePort = *SrcPort; + } + // + // Override the gateway information if user specified. + // + Status = PxeBcUdp4Write ( + Private->Udp4Write, + &Udp4Session, + Private->UdpTimeOutEvent, + (EFI_IPv4_ADDRESS *) GatewayIp, + HeaderSize, + HeaderPtr, + BufferSize, + BufferPtr + ); + } + + gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0); + + + // + // Reset the UdpWrite instance. + // + if (Mode->UsingIpv6) { + Private->Udp6Write->Configure (Private->Udp6Write, NULL); + } else { + Private->Udp4Write->Configure (Private->Udp4Write, NULL); + } + + return Status; +} + + +/** + Reads a UDP packet from the network interface. ++ + This function reads a UDP packet from a network interface. The data contents + are returned in (the optional HeaderPtr and) BufferPtr, and the size of the + buffer received is returned in BufferSize . If the input BufferSize is smaller + than the UDP packet received (less optional HeaderSize), it will be set to the + required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the + contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is + successfully received, then EFI_SUCCESS will be returned, and the information + from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if + they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort, + SrcIp, and SrcPort input values, different types of UDP packet receive filtering + will be performed. The following tables summarize these receive filter operations. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] OpFlags The UDP operation flags. + @param[in, out] DestIp The destination IP address. + @param[in, out] DestPort The destination UDP port number. + @param[in, out] SrcIp The source IP address. + @param[in, out] SrcPort The source UDP port number. + @param[in] HeaderSize An optional field which may be set to the length of a + header at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in, out] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be read. + + @retval EFI_SUCCESS The UDP Read operation was completed. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_BUFFER_TOO_SMALL The packet is larger than Buffer can hold. + @retval EFI_ABORTED The callback function aborted the UDP Read operation. + @retval EFI_TIMEOUT The UDP Read operation timed out. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcUdpRead ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN UINT16 OpFlags, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN OUT UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_COMPLETION_TOKEN Udp4Token; + EFI_UDP6_COMPLETION_TOKEN Udp6Token; + EFI_UDP4_RECEIVE_DATA *Udp4Rx; + EFI_UDP6_RECEIVE_DATA *Udp6Rx; + EFI_STATUS Status; + BOOLEAN IsDone; + BOOLEAN IsMatched; + UINTN CopiedLen; + UINTN HeaderLen; + UINTN HeaderCopiedLen; + UINTN BufferCopiedLen; + UINT32 FragmentLength; + UINTN FragmentIndex; + UINT8 *FragmentBuffer; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + IsDone = FALSE; + IsMatched = FALSE; + Udp4Rx = NULL; + Udp6Rx = NULL; + + if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) == 0 && DestPort == NULL) || + ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) == 0 && SrcIp == NULL) || + ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) == 0 && SrcPort == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((HeaderSize != NULL && *HeaderSize == 0) || (HeaderSize != NULL && HeaderPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((BufferSize == NULL) || (BufferPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + ZeroMem (&Udp6Token, sizeof (EFI_UDP6_COMPLETION_TOKEN)); + ZeroMem (&Udp4Token, sizeof (EFI_UDP4_COMPLETION_TOKEN)); + + if (Mode->UsingIpv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Udp6Token.Event + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Udp4Token.Event + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + } + + // + // Start a timer as timeout event for this blocking API. + // + gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT); + Mode->IcmpErrorReceived = FALSE; + + // + // Read packet by Udp4Read/Udp6Read until matched or timeout. + // + while (!IsMatched && !EFI_ERROR (Status)) { + if (Mode->UsingIpv6) { + Status = PxeBcUdp6Read ( + Private->Udp6Read, + &Udp6Token, + Mode, + Private->UdpTimeOutEvent, + OpFlags, + &IsDone, + &IsMatched, + DestIp, + DestPort, + SrcIp, + SrcPort + ); + } else { + Status = PxeBcUdp4Read ( + Private->Udp4Read, + &Udp4Token, + Mode, + Private->UdpTimeOutEvent, + OpFlags, + &IsDone, + &IsMatched, + DestIp, + DestPort, + SrcIp, + SrcPort + ); + } + } + + if (Status == EFI_ICMP_ERROR || + Status == EFI_NETWORK_UNREACHABLE || + Status == EFI_HOST_UNREACHABLE || + Status == EFI_PROTOCOL_UNREACHABLE || + Status == EFI_PORT_UNREACHABLE) { + // + // Get different return status for icmp error from Udp, refers to UEFI spec. + // + Mode->IcmpErrorReceived = TRUE; + } + gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0); + + if (IsMatched) { + // + // Copy the rececived packet to user if matched by filter. + // + if (Mode->UsingIpv6) { + Udp6Rx = Udp6Token.Packet.RxData; + ASSERT (Udp6Rx != NULL); + + HeaderLen = 0; + if (HeaderSize != NULL) { + HeaderLen = MIN (*HeaderSize, Udp6Rx->DataLength); + } + + if (Udp6Rx->DataLength - HeaderLen > *BufferSize) { + Status = EFI_BUFFER_TOO_SMALL; + } else { + if (HeaderSize != NULL) { + *HeaderSize = HeaderLen; + } + *BufferSize = Udp6Rx->DataLength - HeaderLen; + + HeaderCopiedLen = 0; + BufferCopiedLen = 0; + for (FragmentIndex = 0; FragmentIndex < Udp6Rx->FragmentCount; FragmentIndex++) { + FragmentLength = Udp6Rx->FragmentTable[FragmentIndex].FragmentLength; + FragmentBuffer = Udp6Rx->FragmentTable[FragmentIndex].FragmentBuffer; + if (HeaderCopiedLen + FragmentLength < HeaderLen) { + // + // Copy the header part of received data. + // + CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, FragmentLength); + HeaderCopiedLen += FragmentLength; + } else if (HeaderCopiedLen < HeaderLen) { + // + // Copy the header part of received data. + // + CopiedLen = HeaderLen - HeaderCopiedLen; + CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, CopiedLen); + HeaderCopiedLen += CopiedLen; + + // + // Copy the other part of received data. + // + CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer + CopiedLen, FragmentLength - CopiedLen); + BufferCopiedLen += (FragmentLength - CopiedLen); + } else { + // + // Copy the other part of received data. + // + CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer, FragmentLength); + BufferCopiedLen += FragmentLength; + } + } + } + // + // Recycle the receiving buffer after copy to user. + // + gBS->SignalEvent (Udp6Rx->RecycleSignal); + } else { + Udp4Rx = Udp4Token.Packet.RxData; + ASSERT (Udp4Rx != NULL); + + HeaderLen = 0; + if (HeaderSize != NULL) { + HeaderLen = MIN (*HeaderSize, Udp4Rx->DataLength); + } + + if (Udp4Rx->DataLength - HeaderLen > *BufferSize) { + Status = EFI_BUFFER_TOO_SMALL; + } else { + if (HeaderSize != NULL) { + *HeaderSize = HeaderLen; + } + *BufferSize = Udp4Rx->DataLength - HeaderLen; + + HeaderCopiedLen = 0; + BufferCopiedLen = 0; + for (FragmentIndex = 0; FragmentIndex < Udp4Rx->FragmentCount; FragmentIndex++) { + FragmentLength = Udp4Rx->FragmentTable[FragmentIndex].FragmentLength; + FragmentBuffer = Udp4Rx->FragmentTable[FragmentIndex].FragmentBuffer; + if (HeaderCopiedLen + FragmentLength < HeaderLen) { + // + // Copy the header part of received data. + // + CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, FragmentLength); + HeaderCopiedLen += FragmentLength; + } else if (HeaderCopiedLen < HeaderLen) { + // + // Copy the header part of received data. + // + CopiedLen = HeaderLen - HeaderCopiedLen; + CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, CopiedLen); + HeaderCopiedLen += CopiedLen; + + // + // Copy the other part of received data. + // + CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer + CopiedLen, FragmentLength - CopiedLen); + BufferCopiedLen += (FragmentLength - CopiedLen); + } else { + // + // Copy the other part of received data. + // + CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer, FragmentLength); + BufferCopiedLen += FragmentLength; + } + } + } + // + // Recycle the receiving buffer after copy to user. + // + gBS->SignalEvent (Udp4Rx->RecycleSignal); + } + } + + if (Mode->UsingIpv6) { + Private->Udp6Read->Cancel (Private->Udp6Read, &Udp6Token); + gBS->CloseEvent (Udp6Token.Event); + } else { + Private->Udp4Read->Cancel (Private->Udp4Read, &Udp4Token); + gBS->CloseEvent (Udp4Token.Event); + } + + return Status; +} + + +/** + Updates the IP receive filters of a network device and enables software filtering. + + The NewFilter field is used to modify the network device's current IP receive + filter settings and to enable a software filter. This function updates the IpFilter + field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter. + The software filter is used when the USE_FILTER in OpFlags is set to UdpRead(). + The current hardware filter remains in effect no matter what the settings of OpFlags. + This is so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those + packets whose reception is enabled in hardware-physical NIC address (unicast), + broadcast address, logical address or addresses (multicast), or all (promiscuous). + UdpRead() does not modify the IP filter settings. + Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive + filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP. + If an application or driver wishes to preserve the IP receive filter settings, + it will have to preserve the IP receive filter settings before these calls, and + use SetIpFilter() to restore them after the calls. If incompatible filtering is + requested (for example, PROMISCUOUS with anything else), or if the device does not + support a requested filter setting and it cannot be accommodated in software + (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned. + The IPlist field is used to enable IPs other than the StationIP. They may be + multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP, + then both the StationIP and the IPs from the IPlist will be used. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewFilter Pointer to the new set of IP receive filters. + + @retval EFI_SUCCESS The IP receive filter settings were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetIpFilter ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_IP_FILTER *NewFilter + ) +{ + EFI_STATUS Status; + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_UDP4_CONFIG_DATA *Udp4Cfg; + EFI_UDP6_CONFIG_DATA *Udp6Cfg; + UINTN Index; + BOOLEAN NeedPromiscuous; + BOOLEAN AcceptPromiscuous; + BOOLEAN AcceptBroadcast; + BOOLEAN MultiCastUpdate; + + if (This == NULL || NewFilter == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Status = EFI_SUCCESS; + NeedPromiscuous = FALSE; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + for (Index = 0; Index < NewFilter->IpCnt; Index++) { + ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT); + if (!Mode->UsingIpv6 && + IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) { + // + // IPv4 broadcast address should not be in IP filter. + // + return EFI_INVALID_PARAMETER; + } + if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 && + (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) || + NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6))) { + // + // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IPv4/IPv6 address + // is in IpList, promiscuous mode is needed. + // + NeedPromiscuous = TRUE; + } + } + + AcceptPromiscuous = FALSE; + AcceptBroadcast = FALSE; + MultiCastUpdate = FALSE; + + if (NeedPromiscuous || + (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0 || + (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) { + // + // Configure UDPv4/UDPv6 as promiscuous mode to receive all packets. + // + AcceptPromiscuous = TRUE; + } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) { + // + // Configure UDPv4 to receive all broadcast packets. + // + AcceptBroadcast = TRUE; + } + + // + // In multicast condition when Promiscuous FALSE and IpCnt no-zero. + // Here check if there is any update of the multicast ip address. If yes, + // we need leave the old multicast group (by Config UDP instance to NULL), + // and join the new multicast group. + // + if (!AcceptPromiscuous) { + if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) { + if (Mode->IpFilter.IpCnt != NewFilter->IpCnt) { + MultiCastUpdate = TRUE; + } else if (CompareMem (Mode->IpFilter.IpList, NewFilter->IpList, NewFilter->IpCnt * sizeof (EFI_IP_ADDRESS)) != 0 ) { + MultiCastUpdate = TRUE; + } + } + } + + if (!Mode->UsingIpv6) { + // + // Check whether we need reconfigure the UDP4 instance. + // + Udp4Cfg = &Private->Udp4CfgData; + if ((AcceptPromiscuous != Udp4Cfg->AcceptPromiscuous) || + (AcceptBroadcast != Udp4Cfg->AcceptBroadcast) || MultiCastUpdate) { + // + // Clear the UDP4 instance configuration, all joined groups will be left + // during the operation. + // + Private->Udp4Read->Configure (Private->Udp4Read, NULL); + + // + // Configure the UDP instance with the new configuration. + // + Udp4Cfg->AcceptPromiscuous = AcceptPromiscuous; + Udp4Cfg->AcceptBroadcast = AcceptBroadcast; + Status = Private->Udp4Read->Configure (Private->Udp4Read, Udp4Cfg); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // In not Promiscuous mode, need to join the new multicast group. + // + if (!AcceptPromiscuous) { + for (Index = 0; Index < NewFilter->IpCnt; ++Index) { + if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) { + // + // Join the mutilcast group. + // + Status = Private->Udp4Read->Groups (Private->Udp4Read, TRUE, &NewFilter->IpList[Index].v4); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + } + } + } else { + // + // Check whether we need reconfigure the UDP6 instance. + // + Udp6Cfg = &Private->Udp6CfgData; + if ((AcceptPromiscuous != Udp6Cfg->AcceptPromiscuous) || MultiCastUpdate) { + // + // Clear the UDP6 instance configuration, all joined groups will be left + // during the operation. + // + Private->Udp6Read->Configure (Private->Udp6Read, NULL); + + // + // Configure the UDP instance with the new configuration. + // + Udp6Cfg->AcceptPromiscuous = AcceptPromiscuous; + Status = Private->Udp6Read->Configure (Private->Udp6Read, Udp6Cfg); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // In not Promiscuous mode, need to join the new multicast group. + // + if (!AcceptPromiscuous) { + for (Index = 0; Index < NewFilter->IpCnt; ++Index) { + if (IP6_IS_MULTICAST (&NewFilter->IpList[Index].v6)) { + // + // Join the mutilcast group. + // + Status = Private->Udp6Read->Groups (Private->Udp6Read, TRUE, &NewFilter->IpList[Index].v6); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + } + } + } + + // + // Save the new IP filter into mode data. + // + CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter)); + + return Status; +} + + +/** + Uses the ARP protocol to resolve a MAC address. It is not supported for IPv6. + + This function uses the ARP protocol to resolve a MAC address. The IP address specified + by IpAddr is used to resolve a MAC address. If the ARP protocol succeeds in resolving + the specified address, then the ArpCacheEntries and ArpCache fields of the mode data + are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved + MAC address is placed there as well. If the PXE Base Code protocol is in the + stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters + a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is + returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, + then EFI_ABORTED is returned. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] IpAddr Pointer to the IP address that is used to resolve a MAC address. + @param[in] MacAddr If not NULL, a pointer to the MAC address that was resolved with the + ARP protocol. + + @retval EFI_SUCCESS The IP or MAC address was resolved. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval EFI_ICMP_ERROR An error occur with the ICMP packet message. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcArp ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *IpAddr, + IN EFI_MAC_ADDRESS *MacAddr OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_EVENT ResolvedEvent; + EFI_STATUS Status; + EFI_MAC_ADDRESS TempMac; + EFI_MAC_ADDRESS ZeroMac; + BOOLEAN IsResolved; + + if (This == NULL || IpAddr == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + ResolvedEvent = NULL; + Status = EFI_SUCCESS; + IsResolved = FALSE; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6) { + return EFI_UNSUPPORTED; + } + + // + // Station address should be ready before do arp. + // + if (!Private->IsAddressOk) { + return EFI_INVALID_PARAMETER; + } + + Mode->IcmpErrorReceived = FALSE; + ZeroMem (&TempMac, sizeof (EFI_MAC_ADDRESS)); + ZeroMem (&ZeroMac, sizeof (EFI_MAC_ADDRESS)); + + if (!Mode->AutoArp) { + // + // If AutoArp is FALSE, only search in the current Arp cache. + // + PxeBcArpCacheUpdate (NULL, Private); + if (!PxeBcCheckArpCache (Mode, &IpAddr->v4, &TempMac)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsResolved, + &ResolvedEvent + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // If AutoArp is TRUE, try to send Arp request on initiative. + // + Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, ResolvedEvent, &TempMac); + if (EFI_ERROR (Status) && Status != EFI_NOT_READY) { + goto ON_EXIT; + } + + while (!IsResolved) { + if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) { + break; + } + } + if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) { + Status = EFI_SUCCESS; + } else { + Status = EFI_TIMEOUT; + } + } + + // + // Copy the Mac address to user if needed. + // + if (MacAddr != NULL && !EFI_ERROR (Status)) { + CopyMem (MacAddr, &TempMac, sizeof (EFI_MAC_ADDRESS)); + } + +ON_EXIT: + if (ResolvedEvent != NULL) { + gBS->CloseEvent (ResolvedEvent); + } + return Status; +} + + +/** + Updates the parameters that affect the operation of the PXE Base Code Protocol. + + This function sets parameters that affect the operation of the PXE Base Code Protocol. + The parameter specified by NewAutoArp is used to control the generation of ARP + protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated + as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP + Protocol packets will be generated. In this case, the only mappings that are + available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure. + If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol + service, then the service will fail. This function updates the AutoArp field of + the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp. + The SetParameters() call must be invoked after a Callback Protocol is installed + to enable the use of callbacks. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewAutoArp If not NULL, a pointer to a value that specifies whether to replace the + current value of AutoARP. + @param[in] NewSendGUID If not NULL, a pointer to a value that specifies whether to replace the + current value of SendGUID. + @param[in] NewTTL If not NULL, a pointer to be used in place of the current value of TTL, + the "time to live" field of the IP header. + @param[in] NewToS If not NULL, a pointer to be used in place of the current value of ToS, + the "type of service" field of the IP header. + @param[in] NewMakeCallback If not NULL, a pointer to a value that specifies whether to replace the + current value of the MakeCallback field of the Mode structure. + + @retval EFI_SUCCESS The new parameters values were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetParameters ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN *NewAutoArp OPTIONAL, + IN BOOLEAN *NewSendGUID OPTIONAL, + IN UINT8 *NewTTL OPTIONAL, + IN UINT8 *NewToS OPTIONAL, + IN BOOLEAN *NewMakeCallback OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_GUID SystemGuid; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (NewMakeCallback != NULL) { + if (*NewMakeCallback) { + // + // Update the previous PxeBcCallback protocol. + // + Status = gBS->HandleProtocol ( + Private->Controller, + &gEfiPxeBaseCodeCallbackProtocolGuid, + (VOID **) &Private->PxeBcCallback + ); + + if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) { + return EFI_INVALID_PARAMETER; + } + } else { + Private->PxeBcCallback = NULL; + } + Mode->MakeCallbacks = *NewMakeCallback; + } + + if (NewSendGUID != NULL) { + if (*NewSendGUID && EFI_ERROR (NetLibGetSystemGuid (&SystemGuid))) { + return EFI_INVALID_PARAMETER; + } + Mode->SendGUID = *NewSendGUID; + } + + if (NewAutoArp != NULL) { + Mode->AutoArp = *NewAutoArp; + } + + if (NewTTL != NULL) { + Mode->TTL = *NewTTL; + } + + if (NewToS != NULL) { + Mode->ToS = *NewToS; + } + + return EFI_SUCCESS; +} + + +/** + Updates the station IP address and/or subnet mask values of a network device. + + This function updates the station IP address and/or subnet mask values of a network + device. The NewStationIp field is used to modify the network device's current IP address. + If NewStationIP is NULL, then the current IP address will not be modified. Otherwise, + this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure + with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet + mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified. + Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE + structure with NewSubnetMask. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewStationIp Pointer to the new IP address to be used by the network device. + @param[in] NewSubnetMask Pointer to the new subnet mask to be used by the network device. + + @retval EFI_SUCCESS The new station IP address and/or subnet mask were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetStationIP ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN EFI_IP_ADDRESS *NewStationIp OPTIONAL, + IN EFI_IP_ADDRESS *NewSubnetMask OPTIONAL + ) +{ + EFI_STATUS Status; + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_ARP_CONFIG_DATA ArpConfigData; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (NewStationIp != NULL && + (!NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0) && + !NetIp6IsValidUnicast (&NewStationIp->v6))) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + Status = EFI_SUCCESS; + + if (!Mode->UsingIpv6 && + NewSubnetMask != NULL && + !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) { + return EFI_INVALID_PARAMETER; + } + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (Mode->UsingIpv6 && NewStationIp != NULL) { + // + // Set the IPv6 address by Ip6Config protocol. + // + Status = PxeBcRegisterIp6Address (Private, &NewStationIp->v6); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } else if (!Mode->UsingIpv6 && NewStationIp != NULL) { + // + // Configure the corresponding ARP with the IPv4 address. + // + ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA)); + + ArpConfigData.SwAddressType = 0x0800; + ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS); + ArpConfigData.StationAddress = &NewStationIp->v4; + + Private->Arp->Configure (Private->Arp, NULL); + Private->Arp->Configure (Private->Arp, &ArpConfigData); + + if (NewSubnetMask != NULL) { + Mode->RouteTableEntries = 1; + Mode->RouteTable[0].IpAddr.Addr[0] = NewStationIp->Addr[0] & NewSubnetMask->Addr[0]; + Mode->RouteTable[0].SubnetMask.Addr[0] = NewSubnetMask->Addr[0]; + Mode->RouteTable[0].GwAddr.Addr[0] = 0; + } + + Private->IsAddressOk = TRUE; + } + + if (NewStationIp != NULL) { + CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS)); + } + + if (!Mode->UsingIpv6 && NewSubnetMask != NULL) { + CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS)); + } + + Status = PxeBcFlushStationIp (Private, NewStationIp, NewSubnetMask); +ON_EXIT: + return Status; +} + + +/** + Updates the contents of the cached DHCP and Discover packets. + + The pointers to the new packets are used to update the contents of the cached + packets in the EFI_PXE_BASE_CODE_MODE structure. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance. + @param[in] NewDhcpDiscoverValid Pointer to a value that will replace the current + DhcpDiscoverValid field. + @param[in] NewDhcpAckReceived Pointer to a value that will replace the current + DhcpAckReceived field. + @param[in] NewProxyOfferReceived Pointer to a value that will replace the current + ProxyOfferReceived field. + @param[in] NewPxeDiscoverValid Pointer to a value that will replace the current + ProxyOfferReceived field. + @param[in] NewPxeReplyReceived Pointer to a value that will replace the current + PxeReplyReceived field. + @param[in] NewPxeBisReplyReceived Pointer to a value that will replace the current + PxeBisReplyReceived field. + @param[in] NewDhcpDiscover Pointer to the new cached DHCP Discover packet contents. + @param[in] NewDhcpAck Pointer to the new cached DHCP Ack packet contents. + @param[in] NewProxyOffer Pointer to the new cached Proxy Offer packet contents. + @param[in] NewPxeDiscover Pointer to the new cached PXE Discover packet contents. + @param[in] NewPxeReply Pointer to the new cached PXE Reply packet contents. + @param[in] NewPxeBisReply Pointer to the new cached PXE BIS Reply packet contents. + + @retval EFI_SUCCESS The cached packet contents were updated. + @retval EFI_NOT_STARTED The PXE Base Code Protocol is in the stopped state. + @retval EFI_INVALID_PARAMETER This is NULL or does not point to a valid + EFI_PXE_BASE_CODE_PROTOCOL structure. + +**/ +EFI_STATUS +EFIAPI +EfiPxeBcSetPackets ( + IN EFI_PXE_BASE_CODE_PROTOCOL *This, + IN BOOLEAN *NewDhcpDiscoverValid OPTIONAL, + IN BOOLEAN *NewDhcpAckReceived OPTIONAL, + IN BOOLEAN *NewProxyOfferReceived OPTIONAL, + IN BOOLEAN *NewPxeDiscoverValid OPTIONAL, + IN BOOLEAN *NewPxeReplyReceived OPTIONAL, + IN BOOLEAN *NewPxeBisReplyReceived OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewDhcpDiscover OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewDhcpAck OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewProxyOffer OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewPxeDiscover OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewPxeReply OPTIONAL, + IN EFI_PXE_BASE_CODE_PACKET *NewPxeBisReply OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This); + Mode = Private->PxeBc.Mode; + + if (!Mode->Started) { + return EFI_NOT_STARTED; + } + + if (NewDhcpDiscoverValid != NULL) { + Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid; + } + + if (NewDhcpAckReceived != NULL) { + Mode->DhcpAckReceived = *NewDhcpAckReceived; + } + + if (NewProxyOfferReceived != NULL) { + Mode->ProxyOfferReceived = *NewProxyOfferReceived; + } + + if (NewPxeDiscoverValid != NULL) { + Mode->PxeDiscoverValid = *NewPxeDiscoverValid; + } + + if (NewPxeReplyReceived != NULL) { + Mode->PxeReplyReceived = *NewPxeReplyReceived; + } + + if (NewPxeBisReplyReceived != NULL) { + Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived; + } + + if (NewDhcpDiscover != NULL) { + CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewDhcpAck != NULL) { + CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewProxyOffer != NULL) { + CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewPxeDiscover != NULL) { + CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewPxeReply != NULL) { + CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + if (NewPxeBisReply != NULL) { + CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET)); + } + + return EFI_SUCCESS; +} + +EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate = { + EFI_PXE_BASE_CODE_PROTOCOL_REVISION, + EfiPxeBcStart, + EfiPxeBcStop, + EfiPxeBcDhcp, + EfiPxeBcDiscover, + EfiPxeBcMtftp, + EfiPxeBcUdpWrite, + EfiPxeBcUdpRead, + EfiPxeBcSetIpFilter, + EfiPxeBcArp, + EfiPxeBcSetParameters, + EfiPxeBcSetStationIP, + EfiPxeBcSetPackets, + NULL +}; + + +/** + Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has + received, or is waiting to receive a packet. + + This function is invoked when the PXE Base Code Protocol is about to transmit, has received, + or is waiting to receive a packet. Parameters Function and Received specify the type of event. + Parameters PacketLen and Packet specify the packet that generated the event. If these fields + are zero and NULL respectively, then this is a status update callback. If the operation specified + by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation + specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to + the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms. + The SetParameters() function must be called after a Callback Protocol is installed to enable the + use of callbacks. + + @param[in] This Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance. + @param[in] Function The PXE Base Code Protocol function that is waiting for an event. + @param[in] Received TRUE if the callback is being invoked due to a receive event. FALSE if + the callback is being invoked due to a transmit event. + @param[in] PacketLength The length, in bytes, of Packet. This field will have a value of zero if + this is a wait for receive event. + @param[in] PacketPtr If Received is TRUE, a pointer to the packet that was just received; + otherwise a pointer to the packet that is about to be transmitted. + + @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE If Function specifies a continue operation. + @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT If Function specifies an abort operation. + +**/ +EFI_PXE_BASE_CODE_CALLBACK_STATUS +EFIAPI +EfiPxeLoadFileCallback ( + IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *This, + IN EFI_PXE_BASE_CODE_FUNCTION Function, + IN BOOLEAN Received, + IN UINT32 PacketLength, + IN EFI_PXE_BASE_CODE_PACKET *PacketPtr OPTIONAL + ) +{ + EFI_INPUT_KEY Key; + EFI_STATUS Status; + + // + // Catch Ctrl-C or ESC to abort. + // + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + + if (!EFI_ERROR (Status)) { + + if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) { + + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT; + } + } + // + // No print if receive packet + // + if (Received) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + // + // Print only for three functions + // + switch (Function) { + + case EFI_PXE_BASE_CODE_FUNCTION_MTFTP: + // + // Print only for open MTFTP packets, not every MTFTP packets + // + if (PacketLength != 0 && PacketPtr != NULL) { + if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) { + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + } + break; + + case EFI_PXE_BASE_CODE_FUNCTION_DHCP: + case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER: + break; + + default: + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; + } + + if (PacketLength != 0 && PacketPtr != NULL) { + // + // Print '.' when transmit a packet + // + AsciiPrint ("."); + } + + return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE; +} + +EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate = { + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION, + EfiPxeLoadFileCallback +}; + + +/** + Causes the driver to load a specified file. + + @param[in] This Protocol instance pointer. + @param[in] FilePath The device specific path of the file to load. + @param[in] BootPolicy If TRUE, indicates that the request originates from the + boot manager is attempting to load FilePath as a boot + selection. If FALSE, then FilePath must match an exact file + to be loaded. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[in] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then no the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy. + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NO_MEDIA No medium was present to load the file. + @retval EFI_DEVICE_ERROR The file was not loaded due to a device error. + @retval EFI_NO_RESPONSE The remote system did not respond. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_ABORTED The file load process was manually cancelled. + +**/ +EFI_STATUS +EFIAPI +EfiPxeLoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + PXEBC_PRIVATE_DATA *Private; + PXEBC_VIRTUAL_NIC *VirtualNic; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBc; + BOOLEAN UsingIpv6; + EFI_STATUS Status; + BOOLEAN MediaPresent; + + if (FilePath == NULL || !IsDevicePathEnd (FilePath)) { + return EFI_INVALID_PARAMETER; + } + + VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (This); + Private = VirtualNic->Private; + PxeBc = &Private->PxeBc; + UsingIpv6 = FALSE; + Status = EFI_DEVICE_ERROR; + + if (This == NULL || BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Only support BootPolicy + // + if (!BootPolicy) { + return EFI_UNSUPPORTED; + } + + // + // Check media status before PXE start + // + MediaPresent = TRUE; + NetLibDetectMedia (Private->Controller, &MediaPresent); + if (!MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Check whether the virtual nic is using IPv6 or not. + // + if (VirtualNic == Private->Ip6Nic) { + UsingIpv6 = TRUE; + } + + // + // Start Pxe Base Code to initialize PXE boot. + // + Status = PxeBc->Start (PxeBc, UsingIpv6); + if (Status == EFI_ALREADY_STARTED && UsingIpv6 != PxeBc->Mode->UsingIpv6) { + // + // PxeBc protocol has already been started but not on the required IP version, restart it. + // + Status = PxeBc->Stop (PxeBc); + if (!EFI_ERROR (Status)) { + Status = PxeBc->Start (PxeBc, UsingIpv6); + } + } + if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) { + Status = PxeBcLoadBootFile (Private, BufferSize, Buffer); + } + + if (Status != EFI_SUCCESS && + Status != EFI_UNSUPPORTED && + Status != EFI_BUFFER_TOO_SMALL) { + // + // There are three cases, which needn't stop pxebc here. + // 1. success to download file. + // 2. success to get file size. + // 3. unsupported. + // + PxeBc->Stop (PxeBc); + } else { + // + // The DHCP4 can have only one configured child instance so we need to stop + // reset the DHCP4 child before we return. Otherwise these programs which + // also need to use DHCP4 will be impacted. + // + if (!PxeBc->Mode->UsingIpv6) { + Private->Dhcp4->Stop (Private->Dhcp4); + Private->Dhcp4->Configure (Private->Dhcp4, NULL); + } + } + + return Status; +} + +EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate = { EfiPxeLoadFile }; + diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h new file mode 100644 index 0000000000..ac7dc8781a --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h @@ -0,0 +1,225 @@ +/** @file + This EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL. + interfaces declaration. + + Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_PXEBC_IMPL_H__ +#define __EFI_PXEBC_IMPL_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _PXEBC_PRIVATE_DATA PXEBC_PRIVATE_DATA; +typedef struct _PXEBC_PRIVATE_PROTOCOL PXEBC_PRIVATE_PROTOCOL; +typedef struct _PXEBC_VIRTUAL_NIC PXEBC_VIRTUAL_NIC; + +#include "PxeBcDriver.h" +#include "PxeBcDhcp4.h" +#include "PxeBcDhcp6.h" +#include "PxeBcMtftp.h" +#include "PxeBcBoot.h" +#include "PxeBcSupport.h" + +#define PXEBC_DEFAULT_HOPLIMIT 64 +#define PXEBC_DEFAULT_LIFETIME 50000 // 50 ms, unit is microsecond +#define PXEBC_UDP_TIMEOUT 30000000 // 3 seconds, unit is 100nanosecond +#define PXEBC_DAD_ADDITIONAL_DELAY 30000000 // 3 seconds +#define PXEBC_MTFTP_TIMEOUT 4 +#define PXEBC_MTFTP_RETRIES 6 +#define PXEBC_DHCP_RETRIES 4 // refers to mPxeDhcpTimeout, also by PXE2.1 spec. +#define PXEBC_MENU_MAX_NUM 24 +#define PXEBC_OFFER_MAX_NUM 16 + +#define PXEBC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'P') +#define PXEBC_VIRTUAL_NIC_SIGNATURE SIGNATURE_32 ('P', 'X', 'E', 'V') +#define PXEBC_PRIVATE_DATA_FROM_PXEBC(a) CR (a, PXEBC_PRIVATE_DATA, PxeBc, PXEBC_PRIVATE_DATA_SIGNATURE) +#define PXEBC_PRIVATE_DATA_FROM_ID(a) CR (a, PXEBC_PRIVATE_DATA, Id, PXEBC_PRIVATE_DATA_SIGNATURE) +#define PXEBC_VIRTUAL_NIC_FROM_LOADFILE(a) CR (a, PXEBC_VIRTUAL_NIC, LoadFile, PXEBC_VIRTUAL_NIC_SIGNATURE) + +typedef union { + PXEBC_DHCP4_PACKET_CACHE Dhcp4; + PXEBC_DHCP6_PACKET_CACHE Dhcp6; +} PXEBC_DHCP_PACKET_CACHE; + +struct _PXEBC_PRIVATE_PROTOCOL { + UINT64 Reserved; +}; + +struct _PXEBC_VIRTUAL_NIC { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + PXEBC_PRIVATE_DATA *Private; +}; + +struct _PXEBC_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Controller; + EFI_HANDLE Image; + + PXEBC_PRIVATE_PROTOCOL Id; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + + PXEBC_VIRTUAL_NIC *Ip4Nic; + PXEBC_VIRTUAL_NIC *Ip6Nic; + + EFI_HANDLE ArpChild; + EFI_HANDLE Ip4Child; + EFI_HANDLE Dhcp4Child; + EFI_HANDLE Mtftp4Child; + EFI_HANDLE Udp4ReadChild; + EFI_HANDLE Udp4WriteChild; + + EFI_ARP_PROTOCOL *Arp; + EFI_IP4_PROTOCOL *Ip4; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_UDP4_PROTOCOL *Udp4Read; + EFI_UDP4_PROTOCOL *Udp4Write; + + EFI_HANDLE Ip6Child; + EFI_HANDLE Dhcp6Child; + EFI_HANDLE Mtftp6Child; + EFI_HANDLE Udp6ReadChild; + EFI_HANDLE Udp6WriteChild; + + EFI_IP6_PROTOCOL *Ip6; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_UDP6_PROTOCOL *Udp6Read; + EFI_UDP6_PROTOCOL *Udp6Write; + + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii; + EFI_PXE_BASE_CODE_PROTOCOL PxeBc; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL LoadFileCallback; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *PxeBcCallback; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + EFI_PXE_BASE_CODE_MODE Mode; + EFI_PXE_BASE_CODE_FUNCTION Function; + UINT32 Ip6Policy; + UINT32 SolicitTimes; + UINT64 ElapsedTime; + + EFI_UDP4_CONFIG_DATA Udp4CfgData; + EFI_UDP6_CONFIG_DATA Udp6CfgData; + EFI_IP4_CONFIG_DATA Ip4CfgData; + EFI_IP6_CONFIG_DATA Ip6CfgData; + + EFI_EVENT UdpTimeOutEvent; + EFI_EVENT ArpUpdateEvent; + EFI_IP4_COMPLETION_TOKEN IcmpToken; + EFI_IP6_COMPLETION_TOKEN Icmp6Token; + + BOOLEAN IsAddressOk; + BOOLEAN IsOfferSorted; + BOOLEAN IsProxyRecved; + BOOLEAN IsDoDiscover; + + EFI_IP_ADDRESS TmpStationIp; + EFI_IP_ADDRESS StationIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS GatewayIp; + EFI_IP_ADDRESS ServerIp; + UINT16 CurSrcPort; + UINT32 IaId; + + UINT32 Ip4MaxPacketSize; + UINT32 Ip6MaxPacketSize; + UINT8 *BootFileName; + UINTN BootFileSize; + UINTN BlockSize; + + PXEBC_DHCP_PACKET_CACHE ProxyOffer; + PXEBC_DHCP_PACKET_CACHE DhcpAck; + PXEBC_DHCP_PACKET_CACHE PxeReply; + EFI_DHCP6_PACKET *Dhcp6Request; + EFI_DHCP4_PACKET SeedPacket; + + // + // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer. + // + // It supposed that + // + // OfferNum: 8 + // OfferBuffer: [ProxyBinl, ProxyBinl, DhcpOnly, ProxyPxe10, DhcpOnly, DhcpPxe10, DhcpBinl, ProxyBinl] + // (OfferBuffer is 0-based.) + // + // And assume that (DhcpPxe10 is the first priority actually.) + // + // SelectIndex: 2 + // SelectProxyType: PXEBC_OFFER_TYPE_PROXY_BINL + // (SelectIndex is 1-based, and 0 means no one is selected.) + // + // So it should be + // + // DhcpOnly DhcpPxe10 DhcpWfm11a DhcpBinl ProxyPxe10 ProxyWfm11a ProxyBinl Bootp + // OfferCount: [ 2(n), 1(n), 0(n), 1(n), 1(1), 0(1), 3(n), 1(1)] + // + // OfferIndex: {[ 2, 5, 0, 6, 3, 0, *0, 0] + // [ 4, 0, 0, 0, 0, 0, 1, 0] + // [ 0, 0, 0, 0, 0, 0, 7, 0] + // ... ]} + // (OfferIndex is 0-based.) + // + // + UINT32 SelectIndex; + UINT32 SelectProxyType; + PXEBC_DHCP_PACKET_CACHE OfferBuffer[PXEBC_OFFER_MAX_NUM]; + UINT32 OfferNum; + UINT32 OfferCount[PxeOfferTypeMax]; + UINT32 OfferIndex[PxeOfferTypeMax][PXEBC_OFFER_MAX_NUM]; +}; + +extern EFI_PXE_BASE_CODE_PROTOCOL gPxeBcProtocolTemplate; +extern EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate; +extern EFI_LOAD_FILE_PROTOCOL gLoadFileProtocolTemplate; + +#endif diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c new file mode 100644 index 0000000000..270190d42e --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c @@ -0,0 +1,1113 @@ +/** @file + Functions implementation related with Mtftp for UefiPxeBc Driver. + + Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PxeBcImpl.h" + +CHAR8 *mMtftpOptions[PXE_MTFTP_OPTION_MAXIMUM_INDEX] = { + "blksize", + "timeout", + "tsize", + "multicast" +}; + + +/** + This is a callback function when packets are received or transmitted in Mtftp driver. + + A callback function that is provided by the caller to intercept + the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP6_OPCODE_DATA8 packets processed in the + EFI_MTFTP6_PROTOCOL.ReadFile() function, and alternatively to intercept + EFI_MTFTP6_OPCODE_OACK or EFI_MTFTP6_OPCODE_ERROR packets during a call to + EFI_MTFTP6_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory(). + + @param[in] This Pointer to EFI_MTFTP6_PROTOCOL. + @param[in] Token Pointer to EFI_MTFTP6_TOKEN. + @param[in] PacketLen Length of EFI_MTFTP6_PACKET. + @param[in] Packet Pointer to EFI_MTFTP6_PACKET to be checked. + + @retval EFI_SUCCESS The current operation succeeded. + @retval EFI_ABORTED Abort the current transfer process. + +**/ +EFI_STATUS +EFIAPI +PxeBcMtftp6CheckPacket ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP6_PACKET *Packet + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_STATUS Status; + + Private = (PXEBC_PRIVATE_DATA *) Token->Context; + Callback = Private->PxeBcCallback; + Status = EFI_SUCCESS; + + if (Packet->OpCode == EFI_MTFTP6_OPCODE_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpyS ( + Private->Mode.TftpError.ErrorString, + PXE_MTFTP_ERROR_STRING_LENGTH, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH - 1 + ); + Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; + } + + if (Callback != NULL) { + // + // Callback to user if has when received any tftp packet. + // + Status = Callback->Callback ( + Callback, + Private->Function, + TRUE, + PacketLen, + (EFI_PXE_BASE_CODE_PACKET *) Packet + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + // + // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_ABORTED; + } else { + // + // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_SUCCESS; + } + } + + return Status; +} + + +/** + This function is to get the size of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Sucessfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp ptions failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Has not obtained the size of the file. + +**/ +EFI_STATUS +PxeBcMtftp6GetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_OPTION ReqOpt[2]; + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_OPTION *Option; + UINT32 PktLen; + UINT8 OptBuf[128]; + UINT32 OptCnt; + EFI_STATUS Status; + + *BufferSize = 0; + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + Packet = NULL; + Option = NULL; + PktLen = 0; + OptCnt = 1; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Build the required options for get info. + // + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX]; + PxeBcUintnToAscDec (0, OptBuf, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + ReqOpt[0].ValueStr = OptBuf; + + if (BlockSize != NULL) { + ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1); + PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX - (AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1)); + OptCnt++; + } + + Status = Mtftp6->GetInfo ( + Mtftp6, + NULL, + Filename, + NULL, + (UINT8) OptCnt, + ReqOpt, + &PktLen, + &Packet + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_TFTP_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpyS ( + Private->Mode.TftpError.ErrorString, + PXE_MTFTP_ERROR_STRING_LENGTH, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH - 1 + ); + Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; + } + goto ON_ERROR; + } + + // + // Parse the options in the reply packet. + // + OptCnt = 0; + Status = Mtftp6->ParseOptions ( + Mtftp6, + PktLen, + Packet, + (UINT32 *) &OptCnt, + &Option + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Parse out the value of "tsize" option. + // + Status = EFI_NOT_FOUND; + while (OptCnt != 0) { + if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) { + *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr)); + Status = EFI_SUCCESS; + } + OptCnt--; + } + FreePool (Option); + +ON_ERROR: + if (Packet != NULL) { + FreePool (Packet); + } + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This function is to get data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether with a receive buffer. + + @retval EFI_SUCCESS Successfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcMtftp6ReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_TOKEN Token; + EFI_MTFTP6_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp6->ReadFile (Mtftp6, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This function is used to write the data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicate whether with overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully wrote the data into a special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcMtftp6WriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_TOKEN Token; + EFI_MTFTP6_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + Token.CheckPacket = PxeBcMtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp6->WriteFile (Mtftp6, &Token); + // + // Get the real size of transmitted buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This function is to read the data (file) from a directory using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP6_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Successfully obtained the data from the file included in directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcMtftp6ReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP6_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_MTFTP6_TOKEN Token; + EFI_MTFTP6_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp6 = Private->Mtftp6; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp6->Configure (Mtftp6, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp6->ReadDirectory (Mtftp6, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp6->Configure (Mtftp6, NULL); + + return Status; +} + + +/** + This is a callback function when packets are received or transmitted in Mtftp driver. + + A callback function that is provided by the caller to intercept + the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the + EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept + EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to + EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory(). + + @param[in] This Pointer to EFI_MTFTP4_PROTOCOL. + @param[in] Token Pointer to EFI_MTFTP4_TOKEN. + @param[in] PacketLen Length of EFI_MTFTP4_PACKET. + @param[in] Packet Pointer to EFI_MTFTP4_PACKET to be checked. + + @retval EFI_SUCCESS The current operation succeeeded. + @retval EFI_ABORTED Abort the current transfer process. + +**/ +EFI_STATUS +EFIAPI +PxeBcMtftp4CheckPacket ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP4_PACKET *Packet + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; + EFI_STATUS Status; + + Private = (PXEBC_PRIVATE_DATA *) Token->Context; + Callback = Private->PxeBcCallback; + Status = EFI_SUCCESS; + + if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpyS ( + Private->Mode.TftpError.ErrorString, + PXE_MTFTP_ERROR_STRING_LENGTH, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH - 1 + ); + Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; + } + + if (Callback != NULL) { + // + // Callback to user if has when received any tftp packet. + // + Status = Callback->Callback ( + Callback, + Private->Function, + TRUE, + PacketLen, + (EFI_PXE_BASE_CODE_PACKET *) Packet + ); + if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { + // + // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_ABORTED; + } else { + // + // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE. + // + Status = EFI_SUCCESS; + } + } + + return Status; +} + + +/** + This function is to get size of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp options failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Did not obtain the size of the file. + +**/ +EFI_STATUS +PxeBcMtftp4GetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_OPTION ReqOpt[2]; + EFI_MTFTP4_PACKET *Packet; + EFI_MTFTP4_OPTION *Option; + UINT32 PktLen; + UINT8 OptBuf[128]; + UINT32 OptCnt; + EFI_STATUS Status; + + *BufferSize = 0; + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + Packet = NULL; + Option = NULL; + PktLen = 0; + OptCnt = 1; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Build the required options for get info. + // + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX]; + PxeBcUintnToAscDec (0, OptBuf, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + ReqOpt[0].ValueStr = OptBuf; + + if (BlockSize != NULL) { + ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[1].ValueStr = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1); + PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX - (AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1)); + OptCnt++; + } + + Status = Mtftp4->GetInfo ( + Mtftp4, + NULL, + Filename, + NULL, + (UINT8) OptCnt, + ReqOpt, + &PktLen, + &Packet + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_TFTP_ERROR) { + // + // Store the tftp error message into mode data and set the received flag. + // + Private->Mode.TftpErrorReceived = TRUE; + Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode; + AsciiStrnCpyS ( + Private->Mode.TftpError.ErrorString, + PXE_MTFTP_ERROR_STRING_LENGTH, + (CHAR8 *) Packet->Error.ErrorMessage, + PXE_MTFTP_ERROR_STRING_LENGTH - 1 + ); + Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0'; + } + goto ON_ERROR; + } + + // + // Parse the options in the reply packet. + // + OptCnt = 0; + Status = Mtftp4->ParseOptions ( + Mtftp4, + PktLen, + Packet, + (UINT32 *) &OptCnt, + &Option + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Parse out the value of "tsize" option. + // + Status = EFI_NOT_FOUND; + while (OptCnt != 0) { + if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) { + *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr)); + Status = EFI_SUCCESS; + } + OptCnt--; + } + FreePool (Option); + +ON_ERROR: + if (Packet != NULL) { + FreePool (Packet); + } + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is to read the data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Successfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcMtftp4ReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_TOKEN Token; + EFI_MTFTP4_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp4CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp4->ReadFile (Mtftp4, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is to write the data of a file using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicates whether to use the overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully write the data into the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcMtftp4WriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_TOKEN Token; + EFI_MTFTP4_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + Token.CheckPacket = PxeBcMtftp4CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp4->WriteFile (Mtftp4, &Token); + // + // Get the real size of transmitted buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is to get data (file) from a directory using Tftp. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to EFI_MTFTP4_CONFIG_DATA. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCES Successfully obtained the data from the file included in the directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcMtftp4ReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN EFI_MTFTP4_CONFIG_DATA *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_MTFTP4_TOKEN Token; + EFI_MTFTP4_OPTION ReqOpt[1]; + UINT32 OptCnt; + UINT8 OptBuf[128]; + EFI_STATUS Status; + + Status = EFI_DEVICE_ERROR; + Mtftp4 = Private->Mtftp4; + OptCnt = 0; + Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT; + + Status = Mtftp4->Configure (Mtftp4, Config); + if (EFI_ERROR (Status)) { + return Status; + } + + if (BlockSize != NULL) { + ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX]; + ReqOpt[0].ValueStr = OptBuf; + PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX); + OptCnt++; + } + + Token.Event = NULL; + Token.OverrideData = NULL; + Token.Filename = Filename; + Token.ModeStr = NULL; + Token.OptionCount = OptCnt; + Token.OptionList = ReqOpt; + Token.Context = Private; + + if (DontUseBuffer) { + Token.BufferSize = 0; + Token.Buffer = NULL; + } else { + Token.BufferSize = *BufferSize; + Token.Buffer = BufferPtr; + } + + Token.CheckPacket = PxeBcMtftp4CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + Status = Mtftp4->ReadDirectory (Mtftp4, &Token); + // + // Get the real size of received buffer. + // + *BufferSize = Token.BufferSize; + + Mtftp4->Configure (Mtftp4, NULL); + + return Status; +} + + +/** + This function is wrapper to get the file size using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to configure data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp options failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Did not obtain the size of the file. + +**/ +EFI_STATUS +PxeBcTftpGetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6GetFileSize ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferSize + ); + } else { + return PxeBcMtftp4GetFileSize ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferSize + ); + } +} + + +/** + This function is a wrapper to get file using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Sucessfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcTftpReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6ReadFile ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } else { + return PxeBcMtftp4ReadFile ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } +} + + +/** + This function is a wrapper to write file using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicate whether with overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully wrote the data into a special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcTftpWriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6WriteFile ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + Overwrite, + BlockSize, + BufferPtr, + BufferSize + ); + } else { + return PxeBcMtftp4WriteFile ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + Overwrite, + BlockSize, + BufferPtr, + BufferSize + ); + } +} + + +/** + This function is a wrapper to get the data (file) from a directory using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicatse whether to use a receive buffer. + + @retval EFI_SUCCES Successfully obtained the data from the file included in the directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcTftpReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ) +{ + if (Private->PxeBc.Mode->UsingIpv6) { + return PxeBcMtftp6ReadDirectory ( + Private, + (EFI_MTFTP6_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } else { + return PxeBcMtftp4ReadDirectory ( + Private, + (EFI_MTFTP4_CONFIG_DATA *) Config, + Filename, + BlockSize, + BufferPtr, + BufferSize, + DontUseBuffer + ); + } +} + diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h new file mode 100644 index 0000000000..f1150762c6 --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h @@ -0,0 +1,137 @@ +/** @file + Functions declaration related with Mtftp for UefiPxeBc Driver. + + Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_PXEBC_MTFTP_H__ +#define __EFI_PXEBC_MTFTP_H__ + +#define PXE_MTFTP_OPTION_BLKSIZE_INDEX 0 +#define PXE_MTFTP_OPTION_TIMEOUT_INDEX 1 +#define PXE_MTFTP_OPTION_TSIZE_INDEX 2 +#define PXE_MTFTP_OPTION_MULTICAST_INDEX 3 +#define PXE_MTFTP_OPTION_MAXIMUM_INDEX 4 +#define PXE_MTFTP_OPTBUF_MAXNUM_INDEX 128 + +#define PXE_MTFTP_ERROR_STRING_LENGTH 127 // refer to definition of struct EFI_PXE_BASE_CODE_TFTP_ERROR. +#define PXE_MTFTP_DEFAULT_BLOCK_SIZE 512 // refer to rfc-1350. + + +/** + This function is wrapper to get the file size using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to configure data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully obtained the size of file. + @retval EFI_NOT_FOUND Parse the tftp ptions failed. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Did not obtain the size of the file. + +**/ +EFI_STATUS +PxeBcTftpGetFileSize ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN OUT UINT64 *BufferSize + ); + + +/** + This function is a wrapper to get a file using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether to use a receive buffer. + + @retval EFI_SUCCESS Successfully read the data from the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Read data from file failed. + +**/ +EFI_STATUS +PxeBcTftpReadFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ); + + +/** + This function is a wrapper to put file with TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] Overwrite Indicates whether to use an overwrite attribute. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + + @retval EFI_SUCCESS Successfully wrote the data into the special file. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval other Write data into file failed. + +**/ +EFI_STATUS +PxeBcTftpWriteFile ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN BOOLEAN Overwrite, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize + ); + + +/** + This function is a wrapper to get the data (file) from a directory using TFTP. + + @param[in] Private Pointer to PxeBc private data. + @param[in] Config Pointer to config data. + @param[in] Filename Pointer to boot file name. + @param[in] BlockSize Pointer to required block size. + @param[in] BufferPtr Pointer to buffer. + @param[in, out] BufferSize Pointer to buffer size. + @param[in] DontUseBuffer Indicates whether with a receive buffer. + + @retval EFI_SUCCES Successfully obtained the data from the file included in directory. + @retval EFI_DEVICE_ERROR The network device encountered an error during this operation. + @retval Others Operation failed. + +**/ +EFI_STATUS +PxeBcTftpReadDirectory ( + IN PXEBC_PRIVATE_DATA *Private, + IN VOID *Config, + IN UINT8 *Filename, + IN UINTN *BlockSize, + IN UINT8 *BufferPtr, + IN OUT UINT64 *BufferSize, + IN BOOLEAN DontUseBuffer + ); +#endif diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c new file mode 100644 index 0000000000..36b0665a96 --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c @@ -0,0 +1,1513 @@ +/** @file + Support functions implementation for UefiPxeBc Driver. + + Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PxeBcImpl.h" + + +/** + Flush the previous configration using the new station Ip address. + + @param[in] Private The pointer to the PxeBc private data. + @param[in] StationIp The pointer to the station Ip address. + @param[in] SubnetMask The pointer to the subnet mask address for v4. + + @retval EFI_SUCCESS Successfully flushed the previous configuration. + @retval Others Failed to flush using the new station Ip. + +**/ +EFI_STATUS +PxeBcFlushStationIp ( + PXEBC_PRIVATE_DATA *Private, + EFI_IP_ADDRESS *StationIp, + EFI_IP_ADDRESS *SubnetMask OPTIONAL + ) +{ + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + + ASSERT (StationIp != NULL); + + Mode = Private->PxeBc.Mode; + Status = EFI_SUCCESS; + + if (Mode->UsingIpv6) { + + CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS)); + CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS)); + + // + // Reconfigure the Ip6 instance to capture background ICMP6 packets with new station Ip address. + // + Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token); + Private->Ip6->Configure (Private->Ip6, NULL); + + Status = Private->Ip6->Configure (Private->Ip6, &Private->Ip6CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token); + } else { + ASSERT (SubnetMask != NULL); + CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + + // + // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address. + // + Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken); + Private->Ip4->Configure (Private->Ip4, NULL); + + Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken); + } + +ON_EXIT: + return Status; +} + + +/** + Notify the callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +PxeBcCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + + +/** + Do arp resolution from arp cache in PxeBcMode. + + @param Mode The pointer to EFI_PXE_BASE_CODE_MODE. + @param Ip4Addr The Ip4 address for resolution. + @param MacAddress The resoluted MAC address if the resolution is successful. + The value is undefined if the resolution fails. + + @retval TRUE Found an matched entry. + @retval FALSE Did not find a matched entry. + +**/ +BOOLEAN +PxeBcCheckArpCache ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_IPv4_ADDRESS *Ip4Addr, + OUT EFI_MAC_ADDRESS *MacAddress + ) +{ + UINT32 Index; + + ASSERT (!Mode->UsingIpv6); + + // + // Check whether the current Arp cache in mode data contains this information or not. + // + for (Index = 0; Index < Mode->ArpCacheEntries; Index++) { + if (EFI_IP4_EQUAL (&Mode->ArpCache[Index].IpAddr.v4, Ip4Addr)) { + CopyMem ( + MacAddress, + &Mode->ArpCache[Index].MacAddr, + sizeof (EFI_MAC_ADDRESS) + ); + return TRUE; + } + } + + return FALSE; +} + + +/** + Update the arp cache periodically. + + @param Event The pointer to EFI_PXE_BC_PROTOCOL. + @param Context Context of the timer event. + +**/ +VOID +EFIAPI +PxeBcArpCacheUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_ARP_FIND_DATA *ArpEntry; + UINT32 EntryLength; + UINT32 EntryCount; + UINT32 Index; + EFI_STATUS Status; + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = Private->PxeBc.Mode; + + ASSERT (!Mode->UsingIpv6); + + // + // Get the current Arp cache from Arp driver. + // + Status = Private->Arp->Find ( + Private->Arp, + TRUE, + NULL, + &EntryLength, + &EntryCount, + &ArpEntry, + TRUE + ); + if (EFI_ERROR (Status)) { + return; + } + + // + // Update the Arp cache in mode data. + // + Mode->ArpCacheEntries = MIN (EntryCount, EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES); + + for (Index = 0; Index < Mode->ArpCacheEntries; Index++) { + CopyMem ( + &Mode->ArpCache[Index].IpAddr, + ArpEntry + 1, + ArpEntry->SwAddressLength + ); + CopyMem ( + &Mode->ArpCache[Index].MacAddr, + (UINT8 *) (ArpEntry + 1) + ArpEntry->SwAddressLength, + ArpEntry->HwAddressLength + ); + ArpEntry = (EFI_ARP_FIND_DATA *) ((UINT8 *) ArpEntry + EntryLength); + } +} + + +/** + Notify function to handle the received ICMP message in DPC. + + @param Context The PXEBC private data. + +**/ +VOID +EFIAPI +PxeBcIcmpErrorDpcHandle ( + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_IP4_RECEIVE_DATA *RxData; + EFI_IP4_PROTOCOL *Ip4; + PXEBC_PRIVATE_DATA *Private; + EFI_PXE_BASE_CODE_MODE *Mode; + UINT8 Type; + UINTN Index; + UINT32 CopiedLen; + UINT8 *IcmpError; + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = &Private->Mode; + Status = Private->IcmpToken.Status; + RxData = Private->IcmpToken.Packet.RxData; + Ip4 = Private->Ip4; + + ASSERT (!Mode->UsingIpv6); + + if (Status == EFI_ABORTED) { + // + // It's triggered by user cancellation. + // + return; + } + + if (RxData == NULL) { + goto ON_EXIT; + } + + if (Status != EFI_ICMP_ERROR) { + // + // The return status should be recognized as EFI_ICMP_ERROR. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (EFI_IP4 (RxData->Header->SourceAddress) != 0 && + !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) { + // + // The source address of the received packet should be a valid unicast address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) { + // + // The destination address of the received packet should be equal to the host address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) { + // + // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer); + + if (Type != ICMP_DEST_UNREACHABLE && + Type != ICMP_SOURCE_QUENCH && + Type != ICMP_REDIRECT && + Type != ICMP_TIME_EXCEEDED && + Type != ICMP_PARAMETER_PROBLEM) { + // + // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + // + // Copy the right ICMP error message into mode data. + // + CopiedLen = 0; + IcmpError = (UINT8 *) &Mode->IcmpError; + + for (Index = 0; Index < RxData->FragmentCount; Index++) { + CopiedLen += RxData->FragmentTable[Index].FragmentLength; + if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) { + CopyMem ( + IcmpError, + RxData->FragmentTable[Index].FragmentBuffer, + RxData->FragmentTable[Index].FragmentLength + ); + } else { + CopyMem ( + IcmpError, + RxData->FragmentTable[Index].FragmentBuffer, + CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR) + ); + } + IcmpError += CopiedLen; + } + +ON_EXIT: + Private->IcmpToken.Status = EFI_NOT_READY; + Ip4->Receive (Ip4, &Private->IcmpToken); +} + + +/** + Callback function to update the latest ICMP6 error message. + + @param Event The event signalled. + @param Context The context passed in using the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmpErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + QueueDpc (TPL_CALLBACK, PxeBcIcmpErrorDpcHandle, Context); +} + + +/** + Notify function to handle the received ICMP6 message in DPC. + + @param Context The PXEBC private data. + +**/ +VOID +EFIAPI +PxeBcIcmp6ErrorDpcHandle ( + IN VOID *Context + ) +{ + PXEBC_PRIVATE_DATA *Private; + EFI_IP6_RECEIVE_DATA *RxData; + EFI_IP6_PROTOCOL *Ip6; + EFI_PXE_BASE_CODE_MODE *Mode; + EFI_STATUS Status; + UINTN Index; + UINT8 Type; + UINT32 CopiedLen; + UINT8 *Icmp6Error; + + Private = (PXEBC_PRIVATE_DATA *) Context; + Mode = &Private->Mode; + Status = Private->Icmp6Token.Status; + RxData = Private->Icmp6Token.Packet.RxData; + Ip6 = Private->Ip6; + + ASSERT (Mode->UsingIpv6); + + if (Status == EFI_ABORTED) { + // + // It's triggered by user cancellation. + // + return; + } + + if (RxData == NULL) { + goto ON_EXIT; + } + + if (Status != EFI_ICMP_ERROR) { + // + // The return status should be recognized as EFI_ICMP_ERROR. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) { + // + // The source address of the received packet should be a valid unicast address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) && + !EFI_IP6_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v6)) { + // + // The destination address of the received packet should be equal to the host address. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + if (RxData->Header->NextHeader != IP6_ICMP) { + // + // The nextheader in the header of the receveid packet should be IP6_ICMP. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer); + + if (Type != ICMP_V6_DEST_UNREACHABLE && + Type != ICMP_V6_PACKET_TOO_BIG && + Type != ICMP_V6_PACKET_TOO_BIG && + Type != ICMP_V6_PARAMETER_PROBLEM) { + // + // The type of the receveid packet should be an ICMP6 error message. + // + gBS->SignalEvent (RxData->RecycleSignal); + goto ON_EXIT; + } + + // + // Copy the right ICMP6 error message into mode data. + // + CopiedLen = 0; + Icmp6Error = (UINT8 *) &Mode->IcmpError; + + for (Index = 0; Index < RxData->FragmentCount; Index++) { + CopiedLen += RxData->FragmentTable[Index].FragmentLength; + if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) { + CopyMem ( + Icmp6Error, + RxData->FragmentTable[Index].FragmentBuffer, + RxData->FragmentTable[Index].FragmentLength + ); + } else { + CopyMem ( + Icmp6Error, + RxData->FragmentTable[Index].FragmentBuffer, + CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR) + ); + } + Icmp6Error += CopiedLen; + } + +ON_EXIT: + Private->Icmp6Token.Status = EFI_NOT_READY; + Ip6->Receive (Ip6, &Private->Icmp6Token); +} + + +/** + Callback function to update the latest ICMP6 error message. + + @param Event The event signalled. + @param Context The context passed in using the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmp6ErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + QueueDpc (TPL_CALLBACK, PxeBcIcmp6ErrorDpcHandle, Context); +} + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL. + @param[in] StationIp The pointer to the station address. + @param[in] SubnetMask The pointer to the subnet mask. + @param[in] Gateway The pointer to the gateway address. + @param[in, out] SrcPort The pointer to the source port. + @param[in] DoNotFragment If TRUE, fragment is not enabled. + Otherwise, fragment is enabled. + + @retval EFI_SUCCESS Successfully configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_IPv4_ADDRESS *StationIp, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *Gateway, + IN OUT UINT16 *SrcPort, + IN BOOLEAN DoNotFragment + ) +{ + EFI_UDP4_CONFIG_DATA Udp4CfgData; + EFI_STATUS Status; + + ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData)); + + Udp4CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + Udp4CfgData.TypeOfService = DEFAULT_ToS; + Udp4CfgData.TimeToLive = DEFAULT_TTL; + Udp4CfgData.AllowDuplicatePort = TRUE; + Udp4CfgData.DoNotFragment = DoNotFragment; + + CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp)); + CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask)); + + Udp4CfgData.StationPort = *SrcPort; + + // + // Reset the UDPv4 instance. + // + Udp4->Configure (Udp4, NULL); + + Status = Udp4->Configure (Udp4, &Udp4CfgData); + if (!EFI_ERROR (Status) && !EFI_IP4_EQUAL (Gateway, &mZeroIp4Addr)) { + // + // The basic configuration is OK, need to add the default route entry + // + Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway); + if (EFI_ERROR (Status)) { + Udp4->Configure (Udp4, NULL); + } + } + + if (!EFI_ERROR (Status) && *SrcPort == 0) { + Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL); + *SrcPort = Udp4CfgData.StationPort; + } + + return Status; +} + + +/** + This function is to configure a UDPv6 instance for UdpWrite. + + @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL. + @param[in] StationIp The pointer to the station address. + @param[in, out] SrcPort The pointer to the source port. + + @retval EFI_SUCCESS Successfully configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_IPv6_ADDRESS *StationIp, + IN OUT UINT16 *SrcPort + ) +{ + EFI_UDP6_CONFIG_DATA CfgData; + EFI_STATUS Status; + + ZeroMem (&CfgData, sizeof (EFI_UDP6_CONFIG_DATA)); + + CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME; + CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME; + CfgData.HopLimit = PXEBC_DEFAULT_HOPLIMIT; + CfgData.AllowDuplicatePort = TRUE; + CfgData.StationPort = *SrcPort; + + CopyMem (&CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS)); + + // + // Reset the UDPv6 instance. + // + Udp6->Configure (Udp6, NULL); + + Status = Udp6->Configure (Udp6, &CfgData); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!EFI_ERROR (Status) && *SrcPort == 0) { + Udp6->GetModeData (Udp6, &CfgData, NULL, NULL, NULL); + *SrcPort = CfgData.StationPort; + } + + return Status; +} + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL. + @param[in] Session The pointer to the UDP4 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] Gateway The pointer to the gateway address. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully send out data using Udp4Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN EFI_IPv4_ADDRESS *Gateway OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + EFI_UDP4_COMPLETION_TOKEN Token; + EFI_UDP4_TRANSMIT_DATA *TxData; + UINT32 TxLength; + UINT32 FragCount; + UINT32 DataLength; + BOOLEAN IsDone; + EFI_STATUS Status; + + // + // Arrange one fragment buffer for data, and another fragment buffer for header if has. + // + FragCount = (HeaderSize != NULL) ? 2 : 1; + TxLength = sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA); + TxData = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (TxLength); + if (TxData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxData->FragmentCount = FragCount; + TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize; + TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr; + DataLength = (UINT32) *BufferSize; + + if (HeaderSize != NULL) { + TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize; + TxData->FragmentTable[0].FragmentBuffer = HeaderPtr; + DataLength += (UINT32) *HeaderSize; + } + + if (Gateway != NULL) { + TxData->GatewayAddress = Gateway; + } + + TxData->UdpSessionData = Session; + TxData->DataLength = DataLength; + Token.Packet.TxData = TxData; + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Udp4->Transmit (Udp4, &Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!IsDone && + Token.Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + Udp4->Poll (Udp4); + } + + Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status; + +ON_EXIT: + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + FreePool (TxData); + + return Status; +} + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL. + @param[in] Session The pointer to the UDP6 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully sent out data using Udp6Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ) +{ + EFI_UDP6_COMPLETION_TOKEN Token; + EFI_UDP6_TRANSMIT_DATA *TxData; + UINT32 TxLength; + UINT32 FragCount; + UINT32 DataLength; + BOOLEAN IsDone; + EFI_STATUS Status; + + // + // Arrange one fragment buffer for data, and another fragment buffer for header if has. + // + FragCount = (HeaderSize != NULL) ? 2 : 1; + TxLength = sizeof (EFI_UDP6_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA); + TxData = (EFI_UDP6_TRANSMIT_DATA *) AllocateZeroPool (TxLength); + if (TxData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TxData->FragmentCount = FragCount; + TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize; + TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr; + DataLength = (UINT32) *BufferSize; + + if (HeaderSize != NULL) { + TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize; + TxData->FragmentTable[0].FragmentBuffer = HeaderPtr; + DataLength += (UINT32) *HeaderSize; + } + + TxData->UdpSessionData = Session; + TxData->DataLength = DataLength; + Token.Packet.TxData = TxData; + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PxeBcCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Udp6->Transmit (Udp6, &Token); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!IsDone && + Token.Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + Udp6->Poll (Udp6); + } + + Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status; + +ON_EXIT: + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + FreePool (TxData); + + return Status; +} + + +/** + Check the received packet using the Ip filter. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the Ip filter successfully. + @retval FALSE Failed to pass the Ip filter. + +**/ +BOOLEAN +PxeBcCheckByIpFilter ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN UINT16 OpFlags + ) +{ + EFI_IP_ADDRESS DestinationIp; + UINTN Index; + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) == 0) { + return TRUE; + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) { + return TRUE; + } + + // + // Convert the destination address in session data to host order. + // + if (Mode->UsingIpv6) { + CopyMem ( + &DestinationIp, + &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + NTOHLLL (&DestinationIp.v6); + } else { + ZeroMem (&DestinationIp, sizeof (EFI_IP_ADDRESS)); + CopyMem ( + &DestinationIp, + &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + EFI_NTOHL (DestinationIp); + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0 && + (IP4_IS_MULTICAST (DestinationIp.Addr[0]) || + IP6_IS_MULTICAST (&DestinationIp))) { + return TRUE; + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0 && + IP4_IS_LOCAL_BROADCAST (DestinationIp.Addr[0])) { + ASSERT (!Mode->UsingIpv6); + return TRUE; + } + + if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 && + (EFI_IP4_EQUAL (&Mode->StationIp.v4, &DestinationIp) || + EFI_IP6_EQUAL (&Mode->StationIp.v6, &DestinationIp))) { + // + // Matched if the dest address is equal to the station address. + // + return TRUE; + } + + for (Index = 0; Index < Mode->IpFilter.IpCnt; Index++) { + ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT); + if (EFI_IP4_EQUAL (&Mode->IpFilter.IpList[Index].v4, &DestinationIp) || + EFI_IP6_EQUAL (&Mode->IpFilter.IpList[Index].v6, &DestinationIp)) { + // + // Matched if the dest address is equal to any of address in the filter list. + // + return TRUE; + } + } + + return FALSE; +} + + +/** + Filter the received packet using the destination Ip. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] DestIp The pointer to the destination Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *DestIp, + IN UINT16 OpFlags + ) +{ + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) { + // + // Copy the destination address from the received packet if accept any. + // + if (DestIp != NULL) { + if (Mode->UsingIpv6) { + CopyMem ( + DestIp, + &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + ZeroMem (DestIp, sizeof (EFI_IP_ADDRESS)); + CopyMem ( + DestIp, + &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + } + return TRUE; + } else if (DestIp != NULL && + (EFI_IP4_EQUAL (DestIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) || + EFI_IP6_EQUAL (DestIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress))) { + // + // The destination address in the received packet is matched if present. + // + return TRUE; + } else if (EFI_IP4_EQUAL (&Mode->StationIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) || + EFI_IP6_EQUAL (&Mode->StationIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress)) { + // + // The destination address in the received packet is equal to the host address. + // + return TRUE; + } + + return FALSE; +} + + +/** + Check the received packet using the destination port. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] DestPort The pointer to the destination port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *DestPort, + IN UINT16 OpFlags + ) +{ + UINT16 Port; + + if (Mode->UsingIpv6) { + Port = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort; + } else { + Port = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort; + } + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) { + // + // Return the destination port in the received packet if accept any. + // + if (DestPort != NULL) { + *DestPort = Port; + } + return TRUE; + } else if (DestPort != NULL && *DestPort == Port) { + // + // The destination port in the received packet is matched if present. + // + return TRUE; + } + + return FALSE; +} + + +/** + Filter the received packet using the source Ip. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] SrcIp The pointer to the source Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *SrcIp, + IN UINT16 OpFlags + ) +{ + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) { + // + // Copy the source address from the received packet if accept any. + // + if (SrcIp != NULL) { + if (Mode->UsingIpv6) { + CopyMem ( + SrcIp, + &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + ZeroMem (SrcIp, sizeof (EFI_IP_ADDRESS)); + CopyMem ( + SrcIp, + &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + } + + } + return TRUE; + } else if (SrcIp != NULL && + (EFI_IP4_EQUAL (SrcIp, &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress) || + EFI_IP6_EQUAL (SrcIp, &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress))) { + // + // The source address in the received packet is matched if present. + // + return TRUE; + } + + return FALSE; +} + + +/** + Filter the received packet using the source port. + + @param[in] Mode The pointer to the mode data of PxeBc. + @param[in] Session The pointer to the current UDPv4 session. + @param[in, out] SrcPort The pointer to the source port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Passed the IPv4 filter successfully. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *SrcPort, + IN UINT16 OpFlags + ) +{ + UINT16 Port; + + if (Mode->UsingIpv6) { + Port = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort; + } else { + Port = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort; + } + + if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) { + // + // Return the source port in the received packet if accept any. + // + if (SrcPort != NULL) { + *SrcPort = Port; + } + return TRUE; + } else if (SrcPort != NULL && *SrcPort == Port) { + // + // The source port in the received packet is matched if present. + // + return TRUE; + } + + return FALSE; +} + + +/** + This function is to receive packet using Udp4Read. + + @param[in] Udp4 The pointer to EFI_UDP4_PROTOCOL. + @param[in] Token The pointer to EFI_UDP4_COMPLETION_TOKEN. + @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone The pointer to the IsDone flag. + @param[out] IsMatched The pointer to the IsMatched flag. + @param[in, out] DestIp The pointer to the destination address. + @param[in, out] DestPort The pointer to the destination port. + @param[in, out] SrcIp The pointer to the source address. + @param[in, out] SrcPort The pointer to the source port. + + @retval EFI_SUCCESS Successfully read the data using Udp4. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Read ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ) +{ + EFI_UDP4_RECEIVE_DATA *RxData; + EFI_UDP4_SESSION_DATA *Session; + EFI_STATUS Status; + + Token->Status = EFI_NOT_READY; + *IsDone = FALSE; + + Status = Udp4->Receive (Udp4, Token); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!(*IsDone) && + Token->Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + // + // Poll the token utill reply/ICMPv6 error message received or timeout. + // + Udp4->Poll (Udp4); + if (Token->Status == EFI_ICMP_ERROR || + Token->Status == EFI_NETWORK_UNREACHABLE || + Token->Status == EFI_HOST_UNREACHABLE || + Token->Status == EFI_PROTOCOL_UNREACHABLE || + Token->Status == EFI_PORT_UNREACHABLE) { + break; + } + } + + Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status; + + if (!EFI_ERROR (Status)) { + // + // check whether this packet matches the filters + // + RxData = Token->Packet.RxData; + Session = &RxData->UdpSession; + + *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags); + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags); + } + + if (!(*IsMatched)) { + // + // Recycle the receiving buffer if not matched. + // + gBS->SignalEvent (RxData->RecycleSignal); + } + } + + return Status; +} + + +/** + This function is to receive packets using Udp6Read. + + @param[in] Udp6 The pointer to EFI_UDP6_PROTOCOL. + @param[in] Token The pointer to EFI_UDP6_COMPLETION_TOKEN. + @param[in] Mode The pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone The pointer to the IsDone flag. + @param[out] IsMatched The pointer to the IsMatched flag. + @param[in, out] DestIp The pointer to the destination address. + @param[in, out] DestPort The pointer to the destination port. + @param[in, out] SrcIp The pointer to the source address. + @param[in, out] SrcPort The pointer to the source port. + + @retval EFI_SUCCESS Successfully read data using Udp6. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Read ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ) +{ + EFI_UDP6_RECEIVE_DATA *RxData; + EFI_UDP6_SESSION_DATA *Session; + EFI_STATUS Status; + + Token->Status = EFI_NOT_READY; + *IsDone = FALSE; + + Status = Udp6->Receive (Udp6, Token); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Poll the UDPv6 read instance if no packet received and no timeout triggered. + // + while (!(*IsDone) && + Token->Status == EFI_NOT_READY && + EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + // + // Poll the token utill reply/ICMPv6 error message received or timeout. + // + Udp6->Poll (Udp6); + if (Token->Status == EFI_ICMP_ERROR || + Token->Status == EFI_NETWORK_UNREACHABLE || + Token->Status == EFI_HOST_UNREACHABLE || + Token->Status == EFI_PROTOCOL_UNREACHABLE || + Token->Status == EFI_PORT_UNREACHABLE) { + break; + } + } + + Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status; + + if (!EFI_ERROR (Status)) { + // + // check whether this packet matches the filters + // + RxData = Token->Packet.RxData; + Session = &RxData->UdpSession; + + *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags); + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags); + } + + if (*IsMatched) { + *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags); + } + + if (!(*IsMatched)) { + // + // Recycle the receiving buffer if not matched. + // + gBS->SignalEvent (RxData->RecycleSignal); + } + } + + return Status; +} + + +/** + This function is to display the IPv4 address. + + @param[in] Ip The pointer to the IPv4 address. + +**/ +VOID +PxeBcShowIp4Addr ( + IN EFI_IPv4_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 4; Index++) { + AsciiPrint ("%d", Ip->Addr[Index]); + if (Index < 3) { + AsciiPrint ("."); + } + } +} + + +/** + This function is to display the IPv6 address. + + @param[in] Ip The pointer to the IPv6 address. + +**/ +VOID +PxeBcShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ) +{ + UINTN Index; + + for (Index = 0; Index < 16; Index++) { + + if (Ip->Addr[Index] != 0) { + AsciiPrint ("%x", Ip->Addr[Index]); + } + Index++; + if (Index > 15) { + return; + } + if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) { + AsciiPrint ("0"); + } + AsciiPrint ("%x", Ip->Addr[Index]); + if (Index < 15) { + AsciiPrint (":"); + } + } +} + + +/** + This function is to convert UINTN to ASCII string with the required formatting. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + @param[in] Length The length of the required format. + +**/ +VOID +PxeBcUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ) +{ + UINTN Remainder; + + while (Length > 0) { + Length--; + Remainder = Number % 10; + Number /= 10; + Buffer[Length] = (UINT8) ('0' + Remainder); + } +} + + +/** + This function is to convert a UINTN to a ASCII string, and return the + actual length of the buffer. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer The pointer to the buffer for ASCII string. + @param[in] BufferSize The maxsize of the buffer. + + @return Length The actual length of the ASCII string. + +**/ +UINTN +PxeBcUintnToAscDec ( + IN UINTN Number, + IN UINT8 *Buffer, + IN UINTN BufferSize + ) +{ + UINTN Index; + UINTN Length; + CHAR8 TempStr[64]; + + Index = 63; + TempStr[Index] = 0; + + do { + Index--; + TempStr[Index] = (CHAR8) ('0' + (Number % 10)); + Number = (UINTN) (Number / 10); + } while (Number != 0); + + AsciiStrCpyS ((CHAR8 *) Buffer, BufferSize, &TempStr[Index]); + + Length = AsciiStrLen ((CHAR8 *) Buffer); + + return Length; +} + + +/** + This function is to convert unicode hex number to a UINT8. + + @param[out] Digit The converted UINT8 for output. + @param[in] Char The unicode hex number to be converted. + + @retval EFI_SUCCESS Successfully converted the unicode hex. + @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex. + +**/ +EFI_STATUS +PxeBcUniHexToUint8 ( + OUT UINT8 *Digit, + IN CHAR16 Char + ) +{ + if ((Char >= L'0') && (Char <= L'9')) { + *Digit = (UINT8) (Char - L'0'); + return EFI_SUCCESS; + } + + if ((Char >= L'A') && (Char <= L'F')) { + *Digit = (UINT8) (Char - L'A' + 0x0A); + return EFI_SUCCESS; + } + + if ((Char >= L'a') && (Char <= L'f')) { + *Digit = (UINT8) (Char - L'a' + 0x0A); + return EFI_SUCCESS; + } + + return EFI_INVALID_PARAMETER; +} + +/** + Calculate the elapsed time. + + @param[in] Private The pointer to PXE private data + +**/ +VOID +CalcElapsedTime ( + IN PXEBC_PRIVATE_DATA *Private + ) +{ + EFI_TIME Time; + UINT64 CurrentStamp; + UINT64 ElapsedTimeValue; + + // + // Generate a time stamp of the centiseconds from 1900/1/1, assume 30day/month. + // + ZeroMem (&Time, sizeof (EFI_TIME)); + gRT->GetTime (&Time, NULL); + CurrentStamp = (UINT64) + ( + ((((((Time.Year - 1900) * 360 + + (Time.Month - 1)) * 30 + + (Time.Day - 1)) * 24 + Time.Hour) * 60 + + Time.Minute) * 60 + Time.Second) * 100 + + DivU64x32(Time.Nanosecond, 10000000) + ); + + // + // Sentinel value of 0 means that this is the first DHCP packet that we are + // sending and that we need to initialize the value. First DHCP Solicit + // gets 0 elapsed-time. Otherwise, calculate based on StartTime. + // + if (Private->ElapsedTime == 0) { + Private->ElapsedTime = CurrentStamp; + } else { + ElapsedTimeValue = CurrentStamp - Private->ElapsedTime; + + // + // If elapsed time cannot fit in two bytes, set it to 0xffff. + // + if (ElapsedTimeValue > 0xffff) { + ElapsedTimeValue = 0xffff; + } + // + // Save the elapsed time + // + Private->ElapsedTime = ElapsedTimeValue; + } +} + diff --git a/Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h new file mode 100644 index 0000000000..0a43aeb79b --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h @@ -0,0 +1,515 @@ +/** @file + Support functions declaration for UefiPxeBc Driver. + + Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_PXEBC_SUPPORT_H__ +#define __EFI_PXEBC_SUPPORT_H__ + + +#define ICMP_DEST_UNREACHABLE 3 +#define ICMP_SOURCE_QUENCH 4 +#define ICMP_REDIRECT 5 +#define ICMP_ECHO_REQUEST 8 +#define ICMP_TIME_EXCEEDED 11 +#define ICMP_PARAMETER_PROBLEM 12 + + + +/** + Flush the previous configration using the new station Ip address. + + @param[in] Private Pointer to PxeBc private data. + @param[in] StationIp Pointer to the station Ip address. + @param[in] SubnetMask Pointer to the subnet mask address for v4. + + @retval EFI_SUCCESS Successfully flushed the previous config. + @retval Others Failed to flush using the new station Ip. + +**/ +EFI_STATUS +PxeBcFlushStationIp ( + PXEBC_PRIVATE_DATA *Private, + EFI_IP_ADDRESS *StationIp, + EFI_IP_ADDRESS *SubnetMask OPTIONAL + ); + + +/** + Notify callback function when an event is triggered. + + @param[in] Event The triggered event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +PxeBcCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + Perform arp resolution from the arp cache in PxeBcMode. + + @param Mode Pointer to EFI_PXE_BASE_CODE_MODE. + @param Ip4Addr The Ip4 address for resolution. + @param MacAddress The resoluted MAC address if the resolution is successful. + The value is undefined if resolution fails. + + @retval TRUE Found a matched entry. + @retval FALSE Did not find a matched entry. + +**/ +BOOLEAN +PxeBcCheckArpCache ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_IPv4_ADDRESS *Ip4Addr, + OUT EFI_MAC_ADDRESS *MacAddress + ); + + +/** + Update arp cache periodically. + + @param Event Pointer to EFI_PXE_BC_PROTOCOL. + @param Context Context of the timer event. + +**/ +VOID +EFIAPI +PxeBcArpCacheUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + xxx + + @param Event The event signaled. + @param Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmpErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + xxx + + @param Event The event signaled. + @param Context The context passed in by the event notifier. + +**/ +VOID +EFIAPI +PxeBcIcmp6ErrorUpdate ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + @param[in] StationIp Pointer to the station address. + @param[in] SubnetMask Pointer to the subnet mask. + @param[in] Gateway Pointer to the gateway address. + @param[in, out] SrcPort Pointer to the source port. + @param[in] DoNotFragment The flag of DoNotFragment bit in the IPv4 + packet. + + @retval EFI_SUCCESS Successfully configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_IPv4_ADDRESS *StationIp, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *Gateway, + IN OUT UINT16 *SrcPort, + IN BOOLEAN DoNotFragment + ); + + +/** + This function is to configure a UDPv6 instance for UdpWrite. + + @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL. + @param[in] StationIp Pointer to the station address. + @param[in, out] SrcPort Pointer to the source port. + + @retval EFI_SUCCESS Successfuly configured this instance. + @retval Others Failed to configure this instance. + +**/ +EFI_STATUS +PxeBcConfigUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_IPv6_ADDRESS *StationIp, + IN OUT UINT16 *SrcPort + ); + +/** + This function is to configure a UDPv4 instance for UdpWrite. + + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + @param[in] Session Pointer to the UDP4 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] Gateway Pointer to the gateway address. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully sent out data with Udp4Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Write ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN EFI_IPv4_ADDRESS *Gateway OPTIONAL, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ); + + +/** + This function is to configure a UDPv6 instance for UdpWrite. + + @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL. + @param[in] Session Pointer to the UDP6 session data. + @param[in] TimeoutEvent The event for timeout. + @param[in] HeaderSize An optional field which may be set to the length of a header + at HeaderPtr to be prefixed to the data at BufferPtr. + @param[in] HeaderPtr If HeaderSize is not NULL, a pointer to a header to be + prefixed to the data at BufferPtr. + @param[in] BufferSize A pointer to the size of the data at BufferPtr. + @param[in] BufferPtr A pointer to the data to be written. + + @retval EFI_SUCCESS Successfully to send out data with Udp6Write. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Write ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_SESSION_DATA *Session, + IN EFI_EVENT TimeoutEvent, + IN UINTN *HeaderSize OPTIONAL, + IN VOID *HeaderPtr OPTIONAL, + IN UINTN *BufferSize, + IN VOID *BufferPtr + ); + + +/** + Check the received packet with the Ip filter. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the Ip filter. + @retval FALSE Failed to pass the Ip filter. + +**/ +BOOLEAN +PxeBcCheckByIpFilter ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN UINT16 OpFlags + ); + + +/** + Filter the received packet with the destination Ip. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] DestIp Pointer to the dest Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *DestIp, + IN UINT16 OpFlags + ); + + +/** + Check the received packet with the destination port. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] DestPort Pointer to the destination port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcCheckByDestPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *DestPort, + IN UINT16 OpFlags + ); + + +/** + Filter the received packet with the source Ip. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] SrcIp Pointer to the source Ip address. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcIp ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT EFI_IP_ADDRESS *SrcIp, + IN UINT16 OpFlags + ); + + +/** + Filter the received packet with the source port. + + @param[in] Mode Pointer to mode data of PxeBc. + @param[in] Session Pointer to the current UDPv4 session. + @param[in, out] SrcPort Pointer to the source port. + @param[in] OpFlags Operation flag for UdpRead/UdpWrite. + + @retval TRUE Succesfully passed the IPv4 filter. + @retval FALSE Failed to pass the IPv4 filter. + +**/ +BOOLEAN +PxeBcFilterBySrcPort ( + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN VOID *Session, + IN OUT UINT16 *SrcPort, + IN UINT16 OpFlags + ); + + +/** + This function is to receive packet with Udp4Read. + + @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. + @param[in] Token Pointer to EFI_UDP4_COMPLETION_TOKEN. + @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone Pointer to IsDone flag. + @param[out] IsMatched Pointer to IsMatched flag. + @param[in, out] DestIp Pointer to destination address. + @param[in, out] DestPort Pointer to destination port. + @param[in, out] SrcIp Pointer to source address. + @param[in, out] SrcPort Pointer to source port. + + @retval EFI_SUCCESS Successfully read data with Udp4. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp4Read ( + IN EFI_UDP4_PROTOCOL *Udp4, + IN EFI_UDP4_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ); + + +/** + This function is to receive packet with Udp6Read. + + @param[in] Udp6 Pointer to EFI_UDP6_PROTOCOL. + @param[in] Token Pointer to EFI_UDP6_COMPLETION_TOKEN. + @param[in] Mode Pointer to EFI_PXE_BASE_CODE_MODE. + @param[in] TimeoutEvent The event for timeout. + @param[in] OpFlags The UDP operation flags. + @param[in] IsDone Pointer to IsDone flag. + @param[out] IsMatched Pointer to IsMatched flag. + @param[in, out] DestIp Pointer to destination address. + @param[in, out] DestPort Pointer to destination port. + @param[in, out] SrcIp Pointer to source address. + @param[in, out] SrcPort Pointer to source port. + + @retval EFI_SUCCESS Successfully read data with Udp6. + @retval Others Failed to send out data. + +**/ +EFI_STATUS +PxeBcUdp6Read ( + IN EFI_UDP6_PROTOCOL *Udp6, + IN EFI_UDP6_COMPLETION_TOKEN *Token, + IN EFI_PXE_BASE_CODE_MODE *Mode, + IN EFI_EVENT TimeoutEvent, + IN UINT16 OpFlags, + IN BOOLEAN *IsDone, + OUT BOOLEAN *IsMatched, + IN OUT EFI_IP_ADDRESS *DestIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort OPTIONAL, + IN OUT EFI_IP_ADDRESS *SrcIp OPTIONAL, + IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort OPTIONAL + ); + + +/** + This function is to display the IPv4 address. + + @param[in] Ip Pointer to the IPv4 address. + +**/ +VOID +PxeBcShowIp4Addr ( + IN EFI_IPv4_ADDRESS *Ip + ); + + +/** + This function is to display the IPv6 address. + + @param[in] Ip Pointer to the IPv6 address. + +**/ +VOID +PxeBcShowIp6Addr ( + IN EFI_IPv6_ADDRESS *Ip + ); + + +/** + This function is to convert UINTN to ASCII string with required format. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer Pointer to the buffer for ASCII string. + @param[in] Length Length of the required format. + +**/ +VOID +PxeBcUintnToAscDecWithFormat ( + IN UINTN Number, + IN UINT8 *Buffer, + IN INTN Length + ); + + +/** + This function is to convert a UINTN to a ASCII string, and return the + actual length of the buffer. + + @param[in] Number Numeric value to be converted. + @param[in] Buffer Pointer to the buffer for ASCII string. + @param[in] BufferSize The maxsize of the buffer. + + @return Length The actual length of the ASCII string. + +**/ +UINTN +PxeBcUintnToAscDec ( + IN UINTN Number, + IN UINT8 *Buffer, + IN UINTN BufferSize + ); + +/** + This function is to convert unicode hex number to a UINT8. + + @param[out] Digit The converted UINT8 for output. + @param[in] Char The unicode hex number to be converted. + + @retval EFI_SUCCESS Successfully converted the unicode hex. + @retval EFI_INVALID_PARAMETER Failed to convert the unicode hex. + +**/ +EFI_STATUS +PxeBcUniHexToUint8 ( + OUT UINT8 *Digit, + IN CHAR16 Char + ); + +/** + Calculate the elapsed time. + + @param[in] Private The pointer to PXE private data + +**/ +VOID +CalcElapsedTime ( + IN PXEBC_PRIVATE_DATA *Private + ); + +/** + Get the Nic handle using any child handle in the IPv4 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv4. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp4Children ( + IN EFI_HANDLE ControllerHandle + ); + +/** + Get the Nic handle using any child handle in the IPv6 stack. + + @param[in] ControllerHandle Pointer to child handle over IPv6. + + @return NicHandle The pointer to the Nic handle. + +**/ +EFI_HANDLE +PxeBcGetNicByIp6Children ( + IN EFI_HANDLE ControllerHandle + ); +#endif diff --git a/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf new file mode 100644 index 0000000000..c3ca218ba3 --- /dev/null +++ b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf @@ -0,0 +1,109 @@ +## @file +# Access PXE-compatible devices for network access and network booting. +# +# This driver provides PXE Base Code Protocol which is used to accessing +# PXE-compatible device for network access or booting. It could work together +# with an IPv4 stack, an IPv6 stack or both. +# +# +# Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UefiPxeBcDxe + FILE_GUID = B95E9FDA-26DE-48d2-8807-1F9107AC5E3A + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = PxeBcDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = UefiPxeBcDxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + ComponentName.c + PxeBcDriver.c + PxeBcDriver.h + PxeBcImpl.c + PxeBcImpl.h + PxeBcBoot.c + PxeBcBoot.h + PxeBcDhcp6.c + PxeBcDhcp6.h + PxeBcDhcp4.c + PxeBcDhcp4.h + PxeBcMtftp.c + PxeBcMtftp.h + PxeBcSupport.c + PxeBcSupport.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + BaseMemoryLib + MemoryAllocationLib + DebugLib + NetLib + DpcLib + DevicePathLib + PcdLib + +[Protocols] + ## TO_START + ## SOMETIMES_CONSUMES + gEfiDevicePathProtocolGuid + gEfiNetworkInterfaceIdentifierProtocolGuid_31 ## SOMETIMES_CONSUMES + gEfiArpServiceBindingProtocolGuid ## TO_START + gEfiArpProtocolGuid ## TO_START + gEfiIp4ServiceBindingProtocolGuid ## TO_START + gEfiIp4ProtocolGuid ## TO_START + gEfiIp4Config2ProtocolGuid ## TO_START + gEfiIp6ServiceBindingProtocolGuid ## TO_START + gEfiIp6ProtocolGuid ## TO_START + gEfiIp6ConfigProtocolGuid ## TO_START + gEfiUdp4ServiceBindingProtocolGuid ## TO_START + gEfiUdp4ProtocolGuid ## TO_START + gEfiMtftp4ServiceBindingProtocolGuid ## TO_START + gEfiMtftp4ProtocolGuid ## TO_START + gEfiDhcp4ServiceBindingProtocolGuid ## TO_START + gEfiDhcp4ProtocolGuid ## TO_START + gEfiUdp6ServiceBindingProtocolGuid ## TO_START + gEfiUdp6ProtocolGuid ## TO_START + gEfiMtftp6ServiceBindingProtocolGuid ## TO_START + gEfiMtftp6ProtocolGuid ## TO_START + gEfiDhcp6ServiceBindingProtocolGuid ## TO_START + gEfiDhcp6ProtocolGuid ## TO_START + gEfiPxeBaseCodeCallbackProtocolGuid ## SOMETIMES_PRODUCES + gEfiPxeBaseCodeProtocolGuid ## BY_START + gEfiLoadFileProtocolGuid ## BY_START + gEfiAdapterInformationProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + gEfiAdapterInfoUndiIpv6SupportGuid ## SOMETIMES_CONSUMES ## GUID + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdTftpBlockSize ## SOMETIMES_CONSUMES +[UserExtensions.TianoCore."ExtraFiles"] + UefiPxeBcDxeExtra.uni diff --git a/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni new file mode 100644 index 0000000000..37ca0cb036 Binary files /dev/null and b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni differ diff --git a/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni new file mode 100644 index 0000000000..2877631fc4 Binary files /dev/null and b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni differ -- cgit v1.2.3