summaryrefslogtreecommitdiff
path: root/Core
diff options
context:
space:
mode:
authorGuo Mang <mang.guo@intel.com>2016-12-22 16:59:01 +0800
committerGuo Mang <mang.guo@intel.com>2016-12-26 19:14:47 +0800
commit064db996bdfbea1077881b95c43ee7bc74cf3351 (patch)
treed37a8f0e7a5befac74b60dd68592699c3f91d6a9 /Core
parentb04f772efbdc02feff97b235cf7e083db92b36bc (diff)
downloadedk2-platforms-064db996bdfbea1077881b95c43ee7bc74cf3351.tar.xz
NetWorkPkg: Move to new location
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang <mang.guo@intel.com>
Diffstat (limited to 'Core')
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6.c1776
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6.h79
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6.inf59
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6.unibin0 -> 1884 bytes
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.unibin0 -> 1328 bytes
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.unibin0 -> 15680 bytes
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Delete.c110
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Delete.h42
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Dump.c579
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Dump.h34
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/ForEach.c115
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/ForEach.h54
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Helper.c420
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Helper.h143
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Indexer.c255
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Indexer.h58
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.c810
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.h150
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf68
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.unibin0 -> 1972 bytes
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.unibin0 -> 1338 bytes
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.unibin0 -> 24240 bytes
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Match.c163
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/Match.h41
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c2081
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.h159
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ia32/Tsc.c28
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ipf/Itc.c28
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6.c1178
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6.h87
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6.inf70
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6.unibin0 -> 1788 bytes
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6Extra.unibin0 -> 1312 bytes
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6Strings.unibin0 -> 7484 bytes
-rw-r--r--Core/NetworkPkg/Application/Ping6/X64/Tsc.c28
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfig.c668
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfig.inf53
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfig.unibin0 -> 1804 bytes
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfigExtra.unibin0 -> 1328 bytes
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfigStrings.unibin0 -> 8624 bytes
-rw-r--r--Core/NetworkPkg/Contributions.txt218
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/ComponentName.c441
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c819
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h156
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf82
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.unibin0 -> 2016 bytes
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.unibin0 -> 1318 bytes
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c1216
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h605
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c3170
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.h227
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c1330
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.h360
-rw-r--r--Core/NetworkPkg/DnsDxe/ComponentName.c443
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDhcp.c920
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDhcp.h145
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDriver.c1554
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDriver.h606
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDxe.inf78
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDxe.unibin0 -> 1960 bytes
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDxeExtra.unibin0 -> 1306 bytes
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsImpl.c2077
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsImpl.h1161
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsProtocol.c1591
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootClient.c1077
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootClient.h139
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.c183
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.h99
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c829
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h292
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c984
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h198
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxe.c1168
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxe.h450
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxe.inf77
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxe.unibin0 -> 2040 bytes
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.unibin0 -> 1328 bytes
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c435
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootImpl.h50
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootSupport.c987
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h320
-rw-r--r--Core/NetworkPkg/HttpDxe/ComponentName.c138
-rw-r--r--Core/NetworkPkg/HttpDxe/ComponentName.h98
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDns.c415
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDns.h58
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDriver.c1065
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDriver.h397
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDxe.inf69
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDxe.unibin0 -> 1854 bytes
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDxeExtra.unibin0 -> 1310 bytes
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpImpl.c1329
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpImpl.h240
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpProto.c2098
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpProto.h590
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.c126
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.h212
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf51
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.unibin0 -> 1700 bytes
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.unibin0 -> 1348 bytes
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesImpl.c279
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesProtocol.c393
-rw-r--r--Core/NetworkPkg/IScsiDxe/ComponentName.c344
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c67
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiCHAP.c477
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiCHAP.h108
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiConfig.c2661
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiConfig.h157
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h185
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiConfigStrings.unibin0 -> 12526 bytes
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr361
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDhcp.c498
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDhcp.h62
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDhcp6.c525
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDhcp6.h80
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDriver.c1647
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDriver.h809
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDxe.inf128
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDxe.unibin0 -> 1876 bytes
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.unibin0 -> 1316 bytes
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c425
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiIbft.c546
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiIbft.h39
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiImpl.h198
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiInitiatorName.c136
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiMisc.c1547
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiMisc.h406
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiProto.c3133
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiProto.h1036
-rw-r--r--Core/NetworkPkg/Include/Guid/IScsiConfigHii.h26
-rw-r--r--Core/NetworkPkg/Include/Guid/Ip6ConfigHii.h25
-rw-r--r--Core/NetworkPkg/Ip6Dxe/ComponentName.c448
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Common.c673
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Common.h318
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Config.vfr178
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c2388
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h295
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c2095
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h68
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Driver.c1000
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Driver.h192
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Dxe.inf116
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Dxe.unibin0 -> 2912 bytes
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.unibin0 -> 1310 bytes
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.unibin0 -> 10244 bytes
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c684
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Icmp.h108
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6If.c798
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6If.h267
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Impl.c1847
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Impl.h752
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Input.c1831
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Input.h235
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Mld.c908
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Mld.h198
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Nd.c3155
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Nd.h749
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6NvData.h69
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Option.c758
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Option.h191
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Output.c1091
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Output.h141
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Route.c635
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Route.h299
-rw-r--r--Core/NetworkPkg/IpSecDxe/ComponentName.c351
-rw-r--r--Core/NetworkPkg/IpSecDxe/IetfConstants.c388
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ike.h266
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkeCommon.c254
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkeCommon.h189
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkePacket.c265
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkePacket.h82
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkeService.c794
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkeService.h262
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/ChildSa.c199
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Exchange.c809
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Ikev2.h258
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Info.c402
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c3369
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h438
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c2262
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c2787
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h1134
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.c3138
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.h955
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecCryptIo.c1021
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecCryptIo.h827
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecDebug.c334
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecDebug.h107
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecDriver.c665
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecDxe.inf110
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecDxe.unibin0 -> 2676 bytes
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.unibin0 -> 1318 bytes
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecImpl.c2184
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecImpl.h390
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecMain.c242
-rw-r--r--Core/NetworkPkg/License.txt25
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/ComponentName.c430
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c755
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h152
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf76
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.unibin0 -> 1956 bytes
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.unibin0 -> 1318 bytes
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c647
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h475
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c416
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h148
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c921
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c1213
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h359
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c604
-rw-r--r--Core/NetworkPkg/NetworkPkg.dec82
-rw-r--r--Core/NetworkPkg/NetworkPkg.dsc117
-rw-r--r--Core/NetworkPkg/NetworkPkg.unibin0 -> 7964 bytes
-rw-r--r--Core/NetworkPkg/NetworkPkgExtra.unibin0 -> 1342 bytes
-rw-r--r--Core/NetworkPkg/TcpDxe/ComponentName.c528
-rw-r--r--Core/NetworkPkg/TcpDxe/SockImpl.c1230
-rw-r--r--Core/NetworkPkg/TcpDxe/SockImpl.h103
-rw-r--r--Core/NetworkPkg/TcpDxe/SockInterface.c999
-rw-r--r--Core/NetworkPkg/TcpDxe/Socket.h924
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpDispatcher.c913
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpDriver.c1006
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpDriver.h296
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpDxe.inf93
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpDxe.unibin0 -> 2412 bytes
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpDxeExtra.unibin0 -> 1310 bytes
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpFunc.h699
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpInput.c1608
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpIo.c192
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpMain.c1075
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpMain.h766
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpMisc.c1023
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpOption.c374
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpOption.h145
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpOutput.c1219
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpProto.h344
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpTimer.c593
-rw-r--r--Core/NetworkPkg/Udp6Dxe/ComponentName.c429
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6Driver.c623
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6Driver.h182
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6Dxe.inf69
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6Dxe.unibin0 -> 2024 bytes
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.unibin0 -> 1314 bytes
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6Impl.c1965
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6Impl.h654
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6Main.c853
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/ComponentName.c358
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c1259
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h100
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c1672
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h409
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c2096
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h303
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c1825
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h181
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c2411
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h225
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c1113
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h137
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c1513
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h515
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf109
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.unibin0 -> 2416 bytes
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.unibin0 -> 1332 bytes
262 files changed, 143527 insertions, 0 deletions
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.<BR>
+
+ 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 <Library/ShellLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/HiiLib.h>
+#include <Library/NetLib.h>
+
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+
+#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.<BR>
+
+ 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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.uni
Binary files differ
diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni
new file mode 100644
index 0000000000..e39bcbea2b
--- /dev/null
+++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni
Binary files differ
diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni
new file mode 100644
index 0000000000..e22879e2e4
--- /dev/null
+++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni
Binary files 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/HiiLib.h>
+
+#include <Protocol/IpSec.h>
+
+#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.<BR>
+
+ 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 <Library/BaseMemoryLib.h>
+#include <Library/UefiLib.h>
+#include <Library/ShellLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/NetLib.h>
+
+#include <Protocol/IpSecConfig.h>
+
+#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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.uni
Binary files differ
diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni
new file mode 100644
index 0000000000..bdc200e25e
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni
Binary files differ
diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni
new file mode 100644
index 0000000000..4ebccd48b3
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni
Binary files 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Library/BaseLib.h>
+
+/**
+ 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.<BR>
+
+ 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 <Library/BaseLib.h>
+
+/**
+ 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.<BR>
+
+ 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 <Library/ShellLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/HiiLib.h>
+#include <Library/NetLib.h>
+
+#include <Protocol/Cpu.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+
+#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.<BR>
+
+ 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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/Application/Ping6/Ping6.uni
Binary files differ
diff --git a/Core/NetworkPkg/Application/Ping6/Ping6Extra.uni b/Core/NetworkPkg/Application/Ping6/Ping6Extra.uni
new file mode 100644
index 0000000000..8963ea71b1
--- /dev/null
+++ b/Core/NetworkPkg/Application/Ping6/Ping6Extra.uni
Binary files differ
diff --git a/Core/NetworkPkg/Application/Ping6/Ping6Strings.uni b/Core/NetworkPkg/Application/Ping6/Ping6Strings.uni
new file mode 100644
index 0000000000..ad9f739f82
--- /dev/null
+++ b/Core/NetworkPkg/Application/Ping6/Ping6Strings.uni
Binary files 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.<BR>
+
+ 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 <Library/BaseLib.h>
+
+/**
+ 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.<BR>
+
+ 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 <Uefi.h>
+
+#include <Protocol/VlanConfig.h>
+
+#include <Library/UefiApplicationEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/ShellLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/HiiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/NetLib.h>
+
+#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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/Application/VConfig/VConfig.uni
Binary files differ
diff --git a/Core/NetworkPkg/Application/VConfig/VConfigExtra.uni b/Core/NetworkPkg/Application/VConfig/VConfigExtra.uni
new file mode 100644
index 0000000000..4faf8ffae1
--- /dev/null
+++ b/Core/NetworkPkg/Application/VConfig/VConfigExtra.uni
Binary files differ
diff --git a/Core/NetworkPkg/Application/VConfig/VConfigStrings.uni b/Core/NetworkPkg/Application/VConfig/VConfigStrings.uni
new file mode 100644
index 0000000000..5cab8ba543
--- /dev/null
+++ b/Core/NetworkPkg/Application/VConfig/VConfigStrings.uni
Binary files 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 <contributor@example.com>
+Subject: [PATCH] CodeModule: Brief-single-line-summary
+
+Full-commit-message
+
+Contributed-under: TianoCore Contribution Agreement 1.0
+Signed-off-by: Contributor Name <contributor@example.com>
+---
+
+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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Protocol/ServiceBinding.h>
+
+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.<BR>
+# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni
new file mode 100644
index 0000000000..78f057400d
--- /dev/null
+++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni
Binary files 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.<BR>
+
+ 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.<BR>
+
+ 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 <Uefi.h>
+
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Udp6.h>
+#include <Protocol/Ip6Config.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+
+#include <Library/UdpIoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Library/PrintLib.h>
+
+
+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.<BR>
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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 <Protocol/DriverBinding.h>
+#include <Protocol/ServiceBinding.h>
+
+///
+/// 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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsDxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni b/Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni
new file mode 100644
index 0000000000..70d583d7d2
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni
Binary files 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.<BR>
+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.<BR>
+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 <Uefi.h>
+
+//
+// Libraries classes
+//
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/NetLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DpcLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UdpIoLib.h>
+
+//
+// UEFI Driver Model Protocols
+//
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/ComponentName.h>
+
+#include <Protocol/Udp4.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Dns4.h>
+
+#include <Protocol/Udp6.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Dns6.h>
+
+#include <Protocol/Ip4Config2.h>
+
+#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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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 {
+ //
+ // <IP address, IP expressed URI> or
+ // <IP address, IP expressed URI, Name-server (will be ignored)>
+ //
+ HttpOfferTypeDhcpIpUri,
+ //
+ // <IP address, Domain-name expressed URI, Name-server>
+ //
+ HttpOfferTypeDhcpNameUriDns,
+ //
+ // <IP address, Name-server>
+ //
+ HttpOfferTypeDhcpDns,
+ //
+ // <IP address>
+ //
+ HttpOfferTypeDhcpOnly,
+ //
+ // <Domain-name expressed URI> or
+ // <Domain-name expressed URI, Name-server (will be ignored)>
+ //
+ HttpOfferTypeProxyNameUri,
+ //
+ // <IP expressed URI> or
+ // <IP expressed URI, Name-server (will be ignored)>
+ //
+ HttpOfferTypeProxyIpUri,
+ //
+ // <IP address, Domain-name expressed URI>
+ //
+ 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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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 <Uefi.h>
+
+//
+// Libraries
+//
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DebugLib.h>
+#include <Library/NetLib.h>
+#include <Library/HttpLib.h>
+
+//
+// UEFI Driver Model Protocols
+//
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/ComponentName.h>
+
+//
+// Consumed Protocols
+//
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Dns6.h>
+#include <Protocol/Http.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/Ip6Config.h>
+//
+// Produced Protocols
+//
+#include <Protocol/LoadFile.h>
+
+//
+// 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.<BR>
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni b/Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni
new file mode 100644
index 0000000000..2d45c4cda5
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni
Binary files 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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+
+ 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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+
+ 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.<BR>
+
+ 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 <Uefi.h>
+
+//
+// Libraries
+//
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/NetLib.h>
+#include <Library/HttpLib.h>
+#include <Library/DpcLib.h>
+
+//
+// UEFI Driver Model Protocols
+//
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/ComponentName.h>
+
+//
+// Consumed Protocols
+//
+#include <Protocol/HttpUtilities.h>
+#include <Protocol/Tcp4.h>
+#include <Protocol/Tcp6.h>
+#include <Protocol/Dns4.h>
+#include <Protocol/Dns6.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/Ip6Config.h>
+
+
+//
+// Produced Protocols
+//
+#include <Protocol/Http.h>
+
+//
+// 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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpDxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni b/Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni
new file mode 100644
index 0000000000..1a06619eee
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni
Binary files 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.<BR>
+ (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+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.<BR>
+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.<BR>
+
+ 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.<BR>
+
+ 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 <Uefi.h>
+
+//
+// Libraries
+//
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+
+//
+// Consumed Protocols
+//
+#include <Protocol/HttpUtilities.h>
+#include <Protocol/Http.h>
+
+//
+// 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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni
new file mode 100644
index 0000000000..841ccda167
--- /dev/null
+++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni
Binary files 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.<BR>
+
+ 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.<BR>
+ 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.<BR>
+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.<BR>
+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.<BR>
+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=<A> CHAP_I=<I> CHAP_C=<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=<N> CHAP_R=<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=<A1,A2...> 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=<N> CHAP_R=<R> or
+ // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
+ // required too.
+ //
+ // CHAP_N=<N>
+ //
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig->CHAPName);
+ //
+ // CHAP_R=<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=<I>
+ //
+ IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);
+ AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
+ //
+ // CHAP_C=<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.<BR>
+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.<BR>
+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 <ConfigAltResp> 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=<StringToken>". That <StringToken> (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
+ <ConfigRequest> 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
+ <MultiConfigRequest> 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
+ <ConfigAltResp> 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 <ConfigHdr> 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 <ConfigResp> 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 <ConfigHdr> 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 <BlockConfig>
+ 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
+ <ConfigString> 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 <ConfigHdr>.
+ // 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.<BR>
+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.<BR>
+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 <Guid/IScsiConfigHii.h>
+
+#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
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni
Binary files 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.<BR>
+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.<BR>
+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:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
+ //
+ 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.<BR>
+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.<BR>
+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:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
+ //
+ 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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni b/Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni
new file mode 100644
index 0000000000..fe6988ffdd
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni
Binary files 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.<BR>
+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 <Target, Lun> 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.<BR>
+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.<BR>
+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 <IndustryStandard/Acpi.h>
+#include <IndustryStandard/IScsiBootFirmwareTable.h>
+#include <Protocol/AcpiTable.h>
+#include <Protocol/PciIo.h>
+
+#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.<BR>
+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 <Uefi.h>
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/HiiConfigAccess.h>
+
+#include <Protocol/Ip6.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Tcp4.h>
+#include <Protocol/Tcp6.h>
+
+#include <Protocol/AuthenticationInfo.h>
+#include <Protocol/IScsiInitiatorName.h>
+#include <Protocol/ScsiPassThruExt.h>
+
+#include <Library/HiiLib.h>
+#include <Library/UefiHiiServicesLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DpcLib.h>
+#include <Library/NetLib.h>
+#include <Library/TcpIoLib.h>
+#include <Library/BaseCryptLib.h>
+
+#include <Guid/MdeModuleHii.h>
+#include <Guid/EventGroup.h>
+#include <Guid/Acpi.h>
+
+#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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <ConfigAltResp> 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=<StringToken>". That <StringToken> (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
+ <ConfigRequest> 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
+ <MultiConfigRequest> 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
+ <ConfigAltResp> 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 <ConfigHdr> 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 <ConfigHdr> 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 <ConfigResp> 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 <BlockConfig>
+ 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
+ <ConfigString> 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 <ConfigHdr>.
+ // 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.<BR>
+
+ 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.<BR>
+ (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6Dxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni b/Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni
new file mode 100644
index 0000000000..4944567a75
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni
Binary files differ
diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni b/Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
new file mode 100644
index 0000000000..4558dacc61
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
Binary files 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+ (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
+
+ 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 <Uefi.h>
+
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/IpSec.h>
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/HiiConfigRouting.h>
+#include <Protocol/HiiConfigAccess.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DpcLib.h>
+#include <Library/HiiLib.h>
+#include <Library/UefiHiiServicesLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PrintLib.h>
+
+#include <Guid/MdeModuleHii.h>
+
+#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.<BR>
+ (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Guid/Ip6ConfigHii.h>
+
+#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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Library/UdpIoLib.h>
+#include <Library/BaseCryptLib.h>
+#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.<BR>
+
+ 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.<BR>
+
+ 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 <Protocol/Udp4.h>
+#include <Protocol/Udp6.h>
+#include <Protocol/Ip4Config2.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UdpIoLib.h>
+#include <Library/BaseCryptLib.h>
+
+#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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+ Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+ Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
+
+ 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 !
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // ! !
+ // ~ <Traffic Selectors> ~
+ // ! !
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+
+ 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.<BR>
+
+ 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.<BR>
+ Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+ Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+
+ 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 <Library/PcdLib.h>
+
+#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.<BR>
+
+ 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.<BR>
+
+ 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 <Protocol/IpSec.h>
+#include <Protocol/IpSecConfig.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PrintLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+
+#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.<BR>
+
+ 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.<BR>
+
+ 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 <Protocol/IpSecConfig.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseCryptLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Library/BaseCryptLib.h>
+
+#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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/IpSecDxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni b/Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni
new file mode 100644
index 0000000000..864b7f8f97
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni
Binary files 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.<BR>
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+
+ 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 <Uefi.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Protocol/IpSec.h>
+#include <Protocol/IpSecConfig.h>
+#include <Protocol/Dpc.h>
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+
+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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Protocol/ServiceBinding.h>
+
+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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni
new file mode 100644
index 0000000000..81c5b9b814
--- /dev/null
+++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni
Binary files 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.<BR>
+
+ 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. <BR>
+
+ 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 <Uefi.h>
+
+#include <Protocol/Udp6.h>
+#include <Protocol/Mtftp6.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Library/PrintLib.h>
+
+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.<BR>
+
+ 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.<BR>
+
+ 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 <Uefi.h>
+
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/NetLib.h>
+#include <Library/UdpIoLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+# Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+#
+# 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.<BR><BR>
+ # TRUE - Certificate Authentication feature is enabled.<BR>
+ # FALSE - Does not support Certificate Authentication.<BR>
+ # @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.<BR>
+# Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/NetworkPkg.uni
Binary files differ
diff --git a/Core/NetworkPkg/NetworkPkgExtra.uni b/Core/NetworkPkg/NetworkPkgExtra.uni
new file mode 100644
index 0000000000..51793d3513
--- /dev/null
+++ b/Core/NetworkPkg/NetworkPkgExtra.uni
Binary files 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Uefi.h>
+
+#include <Protocol/Tcp4.h>
+#include <Protocol/Tcp6.h>
+
+#include <Library/NetLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DpcLib.h>
+
+#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.<BR>
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ 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 <SrcIp, SrcPort, DstIp, DstPort> 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpDxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni b/Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni
new file mode 100644
index 0000000000..bddb35688d
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni
Binary files 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.<BR>
+
+ 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 <Ip, Port> equals to <IN Addr, IN Port>.
+
+ @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 <Addr Port> 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+
+ 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 <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+#include <Library/IpIoLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PrintLib.h>
+
+#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.<BR>
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+
+ 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 <Ip, Port> equals to <IN Addr, IN Port>.
+
+ @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 <Addr Port> 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Protocol/DriverBinding.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DevicePath.h>
+
+/**
+ 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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/Udp6Dxe/Udp6Dxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni b/Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni
new file mode 100644
index 0000000000..e11d89a74e
--- /dev/null
+++ b/Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni
Binary files 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.<BR>
+
+ 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 <Address, Port> 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 <Address, Port> 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 <Address, Port> 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 <Address, Port> 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 <Address, Port> 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.<BR>
+
+ 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 <Uefi.h>
+
+#include <Protocol/Ip6.h>
+#include <Protocol/Udp6.h>
+
+#include <Library/IpIoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DpcLib.h>
+#include <Library/PrintLib.h>
+
+#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 <Address, Port> 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <F8> or <Ctrl> + <M> 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+ Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Uefi.h>
+
+#include <Guid/SmBios.h>
+#include <IndustryStandard/SmBios.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/Arp.h>
+#include <Protocol/Ip4.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+#include <Protocol/Udp4.h>
+#include <Protocol/Udp6.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Mtftp4.h>
+#include <Protocol/Mtftp6.h>
+#include <Protocol/PxeBaseCode.h>
+#include <Protocol/LoadFile.h>
+#include <Protocol/PxeBaseCodeCallBack.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/AdapterInformation.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Library/DpcLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+
+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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+#
+# 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
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni
Binary files differ
diff --git a/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni
new file mode 100644
index 0000000000..2877631fc4
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni
Binary files differ