summaryrefslogtreecommitdiff
path: root/Core/NetworkPkg
diff options
context:
space:
mode:
authorGuo Mang <mang.guo@intel.com>2017-04-27 11:16:34 +0800
committerGuo Mang <mang.guo@intel.com>2017-04-27 11:16:34 +0800
commit098f8621634f1cbdd1253c9957eed09a505223f5 (patch)
tree13783836f52f77e37b32fc982cd82a1ee5888676 /Core/NetworkPkg
parent9f72a84180605527643891f5c27b8f9f31c43006 (diff)
downloadedk2-platforms-098f8621634f1cbdd1253c9957eed09a505223f5.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/NetworkPkg')
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6.c1793
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6.h79
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6.inf67
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6.uni23
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni20
-rw-r--r--Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni92
-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.c812
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.h149
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf76
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.uni23
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni20
-rw-r--r--Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni133
-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.c2076
-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.c1200
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6.h87
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6.inf78
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6.uni22
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6Extra.uni20
-rw-r--r--Core/NetworkPkg/Application/Ping6/Ping6Strings.uni53
-rw-r--r--Core/NetworkPkg/Application/Ping6/X64/Tsc.c28
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfig.c695
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfig.inf61
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfig.uni22
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfigExtra.uni20
-rw-r--r--Core/NetworkPkg/Application/VConfig/VConfigStrings.uni63
-rw-r--r--Core/NetworkPkg/Contributions.txt218
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/ComponentName.c448
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.c819
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Driver.h156
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf84
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.uni23
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni20
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c1216
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h484
-rw-r--r--Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c3174
-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.c457
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDhcp.c764
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDhcp.h145
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDriver.c1552
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDriver.h606
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDxe.inf77
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDxe.uni23
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni20
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsImpl.c2081
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsImpl.h1212
-rw-r--r--Core/NetworkPkg/DnsDxe/DnsProtocol.c1706
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootClient.c1149
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootClient.h141
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.c183
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootComponentName.h99
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootConfig.c703
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootConfig.h79
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h50
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni27
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr55
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c893
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h256
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c1016
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h175
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxe.c1196
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxe.h508
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxe.inf102
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxe.uni24
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni20
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c522
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootImpl.h50
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootSupport.c1288
-rw-r--r--Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h414
-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.c1067
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDriver.h404
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDxe.inf82
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDxe.uni23
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni20
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpImpl.c1648
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpImpl.h237
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpProto.c2128
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpProto.h606
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpsSupport.c1720
-rw-r--r--Core/NetworkPkg/HttpDxe/HttpsSupport.h261
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.c126
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.h122
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf53
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.uni23
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni20
-rw-r--r--Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesProtocol.c387
-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.c3906
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiConfig.h227
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h238
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni101
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr403
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDhcp.c511
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDhcp.h55
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDhcp6.c538
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDhcp6.h71
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDns.c435
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDns.h59
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDriver.c1888
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDriver.h819
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDxe.inf145
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDxe.uni23
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni20
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c425
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiIbft.c551
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiIbft.h39
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiImpl.h207
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiInitiatorName.c136
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiMisc.c2426
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiMisc.h471
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiProto.c3219
-rw-r--r--Core/NetworkPkg/IScsiDxe/IScsiProto.h1039
-rw-r--r--Core/NetworkPkg/Include/Guid/HttpBootConfigHii.h25
-rw-r--r--Core/NetworkPkg/Include/Guid/IScsiConfigHii.h26
-rw-r--r--Core/NetworkPkg/Include/Guid/Ip6ConfigHii.h25
-rw-r--r--Core/NetworkPkg/Include/Guid/TlsAuthConfigHii.h26
-rw-r--r--Core/NetworkPkg/Include/Guid/TlsAuthentication.h30
-rw-r--r--Core/NetworkPkg/Ip6Dxe/ComponentName.c474
-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.c2382
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h313
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c2095
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h68
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Driver.c1026
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Driver.h192
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Dxe.inf116
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Dxe.uni26
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni20
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni61
-rw-r--r--Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c685
-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.h754
-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.c330
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkeCommon.h195
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkePacket.c265
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkePacket.h82
-rw-r--r--Core/NetworkPkg/IpSecDxe/IkeService.c819
-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.c409
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c3353
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h443
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c2261
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c2802
-rw-r--r--Core/NetworkPkg/IpSecDxe/Ikev2/Utility.h1134
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.c3162
-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.uni25
-rw-r--r--Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni20
-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.uni23
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni20
-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.c1236
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h359
-rw-r--r--Core/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c604
-rw-r--r--Core/NetworkPkg/NetworkPkg.dec115
-rw-r--r--Core/NetworkPkg/NetworkPkg.dsc133
-rw-r--r--Core/NetworkPkg/NetworkPkg.uni82
-rw-r--r--Core/NetworkPkg/NetworkPkgExtra.uni19
-rw-r--r--Core/NetworkPkg/TcpDxe/ComponentName.c528
-rw-r--r--Core/NetworkPkg/TcpDxe/SockImpl.c1239
-rw-r--r--Core/NetworkPkg/TcpDxe/SockImpl.h121
-rw-r--r--Core/NetworkPkg/TcpDxe/SockInterface.c1177
-rw-r--r--Core/NetworkPkg/TcpDxe/Socket.h942
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpDispatcher.c896
-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.uni24
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni20
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpFunc.h699
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpInput.c1619
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpIo.c192
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpMain.c1109
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpMain.h783
-rw-r--r--Core/NetworkPkg/TcpDxe/TcpMisc.c1047
-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/TlsAuthConfigDxe/TlsAuthConfigDxe.c135
-rw-r--r--Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.inf74
-rw-r--r--Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.uni21
-rw-r--r--Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxeExtra.uni19
-rw-r--r--Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxeStrings.uni39
-rw-r--r--Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigImpl.c1689
-rw-r--r--Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigImpl.h282
-rw-r--r--Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigNvData.h50
-rw-r--r--Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigVfr.vfr153
-rw-r--r--Core/NetworkPkg/TlsDxe/TlsConfigProtocol.c153
-rw-r--r--Core/NetworkPkg/TlsDxe/TlsDriver.c497
-rw-r--r--Core/NetworkPkg/TlsDxe/TlsDriver.h238
-rw-r--r--Core/NetworkPkg/TlsDxe/TlsDxe.inf66
-rw-r--r--Core/NetworkPkg/TlsDxe/TlsDxe.uni25
-rw-r--r--Core/NetworkPkg/TlsDxe/TlsDxeExtra.uni19
-rw-r--r--Core/NetworkPkg/TlsDxe/TlsImpl.c327
-rw-r--r--Core/NetworkPkg/TlsDxe/TlsImpl.h316
-rw-r--r--Core/NetworkPkg/TlsDxe/TlsProtocol.c633
-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.uni23
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni20
-rw-r--r--Core/NetworkPkg/Udp6Dxe/Udp6Impl.c1976
-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.c1262
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h100
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c1754
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h371
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c2377
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h285
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c1849
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h181
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c2432
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h229
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c1113
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h137
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c1518
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h519
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf111
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni24
-rw-r--r--Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni20
291 files changed, 157445 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..48c3be3552
--- /dev/null
+++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.c
@@ -0,0 +1,1793 @@
+/** @file
+ The implementation for Shell application IfConfig6.
+
+ 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 <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/UefiHiiServicesLib.h>
+#include <Library/HiiLib.h>
+#include <Library/NetLib.h>
+
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+
+#include "IfConfig6.h"
+
+//
+// String token ID of ifconfig6 command help message text.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStringIfconfig6HelpTokenId = STRING_TOKEN (STR_IFCONFIG6_HELP);
+
+EFI_HII_HANDLE mHiiHandle;
+
+SHELL_PARAM_ITEM mIfConfig6CheckList[] = {
+ {
+ L"-b",
+ TypeFlag
+ },
+ {
+ L"-s",
+ TypeMaxValue
+ },
+ {
+ L"-l",
+ TypeValue
+ },
+ {
+ L"-r",
+ TypeValue
+ },
+ {
+ 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;
+ EFI_HII_PACKAGE_LIST_HEADER *PackageList;
+ LIST_ENTRY *ParamPackage;
+ CONST CHAR16 *ValueStr;
+ ARG_LIST *ArgList;
+ CHAR16 *ProblemParam;
+ CHAR16 *Str;
+
+ Private = NULL;
+
+ //
+ // Retrieve HII package list from ImageHandle
+ //
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gEfiHiiPackageListProtocolGuid,
+ (VOID **) &PackageList,
+ ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Publish HII package list to HII Database.
+ //
+ Status = gHiiDatabase->NewPackageList (
+ gHiiDatabase,
+ PackageList,
+ NULL,
+ &mHiiHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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"-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"-s")) && (ShellCommandLineGetFlag (ParamPackage, L"-l")))) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_IFCONFIG6_CONFLICT_OPTIONS), 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..f74897108d
--- /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 - 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 _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..519b7c3279
--- /dev/null
+++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.inf
@@ -0,0 +1,67 @@
+## @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 - 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.
+#
+##
+
+[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
+
+#
+#
+# This flag specifies whether HII resource section is generated into PE image.
+#
+ UEFI_HII_RESOURCE_SECTION = TRUE
+
+#
+# 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
+ UefiHiiServicesLib
+ BaseMemoryLib
+ ShellLib
+ MemoryAllocationLib
+ DebugLib
+ HiiLib
+ NetLib
+
+[Protocols]
+ gEfiIp6ServiceBindingProtocolGuid ## CONSUMES
+ gEfiIp6ConfigProtocolGuid ## CONSUMES
+ gEfiHiiPackageListProtocolGuid ## 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..e0ea589771
--- /dev/null
+++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6.uni
@@ -0,0 +1,23 @@
+// /** @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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Shell application IfConfig6"
+
+#string STR_MODULE_DESCRIPTION #language en-US "It is shell application which is used to set and get configurations for the EFI IPv6 network stack."
+
diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni
new file mode 100644
index 0000000000..7d3f27a073
--- /dev/null
+++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Extra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// IfConfig6 Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"IfConfig6 App"
+
+
diff --git a/Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni
new file mode 100644
index 0000000000..0c10bbdf78
--- /dev/null
+++ b/Core/NetworkPkg/Application/IfConfig6/IfConfig6Strings.uni
@@ -0,0 +1,92 @@
+/** @file
+ String definitions for the Shell application IfConfig6.
+
+ 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<BR>
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#langdef en-US "English"
+
+#string STR_IFCONFIG6_ERR_IP6CFG_GETDATA #language en-US "Ip6Config->GetData return %hr\n"
+#string STR_IFCONFIG6_INFO_BREAK #language en-US "-----------------------------------------------------------------"
+#string STR_IFCONFIG6_INFO_COLON #language en-US ":"
+#string STR_IFCONFIG6_INFO_JOINT #language en-US " >> "
+#string STR_IFCONFIG6_INFO_NEWLINE #language en-US "\n"
+#string STR_IFCONFIG6_INFO_IF_NAME #language en-US "\n%Hname : %s%N\n"
+#string STR_IFCONFIG6_INFO_POLICY_AUTO #language en-US "%Hpolicy : automatic%N\n"
+#string STR_IFCONFIG6_INFO_POLICY_MAN #language en-US "%Hpolicy : manual%N\n"
+#string STR_IFCONFIG6_INFO_DAD_TRANSMITS #language en-US "%Hdad xmits : %d%N\n"
+#string STR_IFCONFIG6_INFO_INTERFACE_ID_HEAD #language en-US "%Hinterface id : %N"
+#string STR_IFCONFIG6_INFO_MAC_ADDR_HEAD #language en-US "%Hmac addr : %N"
+#string STR_IFCONFIG6_INFO_MAC_ADDR_BODY #language en-US "%02x"
+#string STR_IFCONFIG6_INFO_IP_ADDR_HEAD #language en-US "\n%Hhost addr : %N\n"
+#string STR_IFCONFIG6_INFO_DNS_ADDR_HEAD #language en-US "\n%Hdns server : %N\n"
+#string STR_IFCONFIG6_INFO_IP_ADDR_BODY #language en-US "%02x"
+#string STR_IFCONFIG6_INFO_IP_ADDR_BODY4BIT #language en-US "%x"
+#string STR_IFCONFIG6_INFO_ROUTE_HEAD #language en-US "\n%Hroute table : %N\n"
+#string STR_IFCONFIG6_INFO_PREFIX_LEN #language en-US "/%d"
+
+#string STR_IFCONFIG6_LINE_HELP #language en-US "Displays or modifies the IPv6 configuration"
+#string STR_IFCONFIG6_ERR_LACK_INTERFACE #language en-US "Lack interface name.\n"
+ "Usage: IfConfig6 -s {ifname} {config options ...}\n"
+ "Example: IfConfig6 -s eth0 auto\n"
+#string STR_IFCONFIG6_LACK_OPTION #language en-US "Flags lack. Please type 'IfConfig6 -?' for help info.\n"
+#string STR_IFCONFIG6_CONFLICT_OPTIONS #language en-US "Flags conflict. Please type 'IfConfig6 -?' for help info.\n"
+#string STR_IFCONFIG6_ERR_LACK_COMMAND #language en-US "Lack interface config option.\n"
+ "Usage: IfConfig6 -s {ifname} {config options ...}\n"
+ "Example: IfConfig6 -s eth0 auto\n"
+#string STR_IFCONFIG6_ERR_INVALID_INTERFACE #language en-US "Invalid interface name.\n"
+ "Hint: Use {IfConfig6 -l} to check existing interface names.\n"
+#string STR_IFCONFIG6_ERR_INVALID_COMMAND #language en-US "Invalid command. Bad command %H%s%N is skipped.\n"
+ "Hint: Incorrect option or arguments. Please type 'IfConfig6 -?' for help info.\n"
+#string STR_IFCONFIG6_ERR_LACK_ARGUMENTS #language en-US "Lack arguments. Bad command %H%s%N is skipped.\n"
+ "Hint: Please type 'IfConfig6 -?' for help info.\n"
+#string STR_IFCONFIG6_ERR_LACK_OPTION #language en-US "Lack options.\n"
+ "Hint: Please type 'IfConfig6 -?' for help info.\n"
+#string STR_IFCONFIG6_ERR_MAN_HOST #language en-US "Manual address configuration failed. Please retry.\n"
+#string STR_IFCONFIG6_ERR_DUPLICATE_COMMAND #language en-US "Duplicate commands. Bad command %H%s%N is skipped.\n"
+ "Hint: Please type 'IfConfig6 -?' for help info.\n"
+#string STR_IFCONFIG6_ERR_CONFLICT_COMMAND #language en-US "Conflict commands. Bad command %H%s%N is skipped.\n"
+ "Hint: Please type 'IfConfig6 -?' for help info.\n"
+#string STR_IFCONFIG6_ERR_UNKNOWN_COMMAND #language en-US "Unknown commands. Bad command %H%s%N is skipped.\n"
+ "Hint: Please type 'IfConfig6 -?' for help info.\n"
+#string STR_IFCONFIG6_ERR_ADDRESS_FAILED #language en-US "It failed to set .\n"
+#string STR_IFCONFIG6_INVALID_IP #language en-US "%IfConfig6: Invalid IP6 address, %s\n"
+
+#string STR_IFCONFIG6_HELP #language en-US ""
+".TH IfConfig6 0 "Displays or modifies IPv6 configuration for network interface."\r\n"
+".SH NAME\r\n"
+"Displays or modifies IPv6 configuration for network interface.\r\n"
+".SH SYNOPSIS\r\n"
+" \r\n"
+"IfConfig6 [-b] [-r {ifname}] [-l {ifname}] [-s {ifname} {command ...}] [-?]\r\n"
+".SH OPTIONS\r\n"
+" \r\n"
+" -b (break) enable page break.\r\n"
+" -r (renew) renew configuration of interface and set automatic policy.\r\n"
+" -l (list) list the configuration of interface.\r\n"
+" -s (set) set configuration of interface as follows.\r\n"
+" |man/auto manual or automatic policy\r\n"
+" |id {mac} alternative interface id.\r\n"
+" |dad {num} dad transmits count.\r\n"
+" |host{ip} static host ip address, must under manual policy.\r\n"
+" |gw {ip} gateway ip address, must under manual policy.\r\n"
+" |dns {ip} dns server ip address, must under manual policy.\r\n"
+".SH EXAMPLES\r\n"
+" \r\n"
+"Examples:\r\n"
+" IfConfig6 -l\r\n"
+" IfConfig6 -b -l\r\n"
+" IfConfig6 -r eth0\r\n"
+" IfConfig6 -s eth0 auto dad 10\r\n"
+" IfConfig6 -s eth0 man id ff:dd:aa:88:66:cc\r\n"
+" IfConfig6 -s eth1 man host 2002::1/64 2002::2/64 gw 2002::3\r\n"
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..274f582b2b
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.c
@@ -0,0 +1,812 @@
+/** @file
+ The main process for 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 <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"
+
+//
+// String token ID of IpSecConfig command help message text.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStringIpSecHelpTokenId = STRING_TOKEN (STR_IPSEC_CONFIG_HELP);
+
+//
+// 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 },
+
+ //
+ // 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;
+ EFI_HII_PACKAGE_LIST_HEADER *PackageList;
+
+ //
+ // Retrieve HII package list from ImageHandle
+ //
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gEfiHiiPackageListProtocolGuid,
+ (VOID **) &PackageList,
+ ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Publish HII package list to HII Database.
+ //
+ Status = gHiiDatabase->NewPackageList (
+ gHiiDatabase,
+ PackageList,
+ NULL,
+ &mHiiHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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;
+ }
+ }
+
+ 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..95bb696113
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.h
@@ -0,0 +1,149 @@
+/** @file
+ The internal structure and function declaration 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 _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/UefiHiiServicesLib.h>
+#include <Library/NetLib.h>
+
+#include <Protocol/IpSecConfig.h>
+
+#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..02371e535d
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.inf
@@ -0,0 +1,76 @@
+## @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
+
+#
+#
+# This flag specifies whether HII resource section is generated into PE image.
+#
+ UEFI_HII_RESOURCE_SECTION = TRUE
+
+[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
+ UefiHiiServicesLib
+ BaseMemoryLib
+ ShellLib
+ MemoryAllocationLib
+ DebugLib
+ HiiLib
+ NetLib
+ UefiLib
+
+[Protocols]
+ gEfiIpSec2ProtocolGuid ##CONSUMES
+ gEfiIpSecConfigProtocolGuid ##CONSUMES
+ gEfiHiiPackageListProtocolGuid ##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..3dab958699
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfig.uni
@@ -0,0 +1,23 @@
+// /** @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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Shell application IpSecConfig"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This application is used to set and retrieve security and policy related information for the EFI IPsec protocol driver."
+
diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni
new file mode 100644
index 0000000000..53008576c0
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// IpSecConfig Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"IpSec Config App"
+
+
diff --git a/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni
new file mode 100644
index 0000000000..bd7f546327
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/IpSecConfigStrings.uni
@@ -0,0 +1,133 @@
+/** @file
+ String definitions for the Shell IpSecConfig application.
+
+ 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.
+
+**/
+
+#langdef en-US "English"
+
+#string STR_IPSEC_CONFIG_UNKNOWN_OPERATION #language en-US "%s: Operation not specified.\n"
+
+#string STR_IPSEC_CONFIG_INCORRECT_DB #language en-US "%s: Incorrect Database - %s.\n"
+
+#string STR_IPSEC_CONFIG_PROTOCOL_INEXISTENT #language en-US "%s: IPSEC_CONFIG protocol inexistent.\n"
+
+#string STR_IPSEC_CONFIG_MISSING_DB #language en-US "%s: Missing Database.\n"
+
+#string STR_IPSEC_CONFIG_FILE_OPEN_FAILED #language en-US "%s: Open file failed - %s.\n"
+
+#string STR_IPSEC_CONFIG_INCORRECT_PARAMETER_VALUE #language en-US "%s: Incorrect value of %s - %s.\n"
+
+#string STR_IPSEC_CONFIG_ACCEPT_PARAMETERS #language en-US " Values could be:"
+
+#string STR_IPSEC_CONFIG_MISSING_PARAMETER #language en-US "%s: Missing parameter - %s.\n"
+
+#string STR_IPSEC_CONFIG_MISSING_ONE_OF_PARAMETERS #language en-US "%s: Missing one of the parameters - %s.\n"
+
+#string STR_IPSEC_CONFIG_UNWANTED_PARAMETER #language en-US "%s: Unwanted parameter - %s.\n"
+
+#string STR_IPSEC_CONFIG_INSERT_FAILED #language en-US "%s: Policy entry insertion failed!\n"
+
+#string STR_IPSEC_CONFIG_DELETE_FAILED #language en-US "%s: Policy entry deletion failed!\n"
+
+#string STR_IPSEC_CONFIG_EDIT_FAILED #language en-US "%s: Policy entry edit failed!\n"
+
+#string STR_IPSEC_CONFIG_ALREADY_EXISTS #language en-US "%s: Policy entry already exists!\n"
+
+#string STR_IPSEC_CONFIG_INDEX_NOT_FOUND #language en-US "%s: Specified index not found!\n"
+
+#string STR_IPSEC_CONFIG_INDEX_NOT_SPECIFIED #language en-US "%s: Index should be Specified!\n"
+
+#string STR_IPSEC_CONFIG_INSERT_UNSUPPORT #language en-US "%s: Policy entry insertion not supported!\n"
+
+#string STR_IPSEC_MISTAKEN_OPTIONS #language en-US "Mistaken Input. Please refer to %H"IpSecConfig -?"%N for more help information.\n"
+
+#string STR_IPSEC_REDUNDANCY_MANY #language en-US "%s has one redundancy option: %H%s%N\n"
+
+#string STR_IPSEC_CONFIG_ALREADY_ENABLE #language en-US "IPsec has been already enabled!\n"
+
+#string STR_IPSEC_CONFIG_ENABLE_SUCCESS #language en-US "Enable IPsec ! \n"
+
+#string STR_IPSEC_CONFIG_DISABLE_SUCCESS #language en-US "Disable IPsec ! \n"
+
+#string STR_IPSEC_CONFIG_ALREADY_DISABLE #language en-US "IPsec has been already disabled !\n"
+
+#string STR_IPSEC_CONFIG_STATUS_ENABLE #language en-US "IPsec Status : Enabled ! \n"
+
+#string STR_IPSEC_CONFIG_STATUS_DISABLE #language en-US "IPsec Status : Disabled ! \n"
+
+#string STR_IPSEC_CONFIG_ENABLE_FAILED #language en-US "Error: Enable IPsec failed !\n"
+
+#string STR_IPSEC_CONFIG_DISABLE_FAILED #language en-US "Error: Disable IPsec failed !\n"
+
+#string STR_IPSEC_CONFIG_HELP #language en-US ""
+".TH IpSecConfig 0 "Displays or modifies the current IPsec configuration."\r\n"
+".SH NAME\r\n"
+"Displays or modifies the current IPsec configuration.\r\n"
+".SH SYNOPSIS\r\n"
+" \r\n"
+"%HIpSecConfig [-p {SPD|SAD|PAD}] [command] [options[parameters]]\r\n"
+".SH OPTIONS\r\n"
+" \r\n"
+"%H-p (SPD|SAD|PAD)%N required.point to certain policy database.\r\n"
+" \r\n"
+"%Hcommand%N:\r\n"
+" -a [options[parameters]] Add new policy entry.\r\n"
+" -i entryid [options[parameters]] Insert new policy entry before the one\r\n"
+" matched by the entryid.\r\n"
+" It's only supported on SPD policy database.\r\n"
+" -d entryid Delete the policy entry matched by the \r\n"
+" entryid.\r\n"
+" -e entryid [options[parameters]] Edit the policy entry matched by the\r\n"
+" entryid.\r\n"
+" -f Flush the entire policy database.\r\n"
+" -l List all entries for specified database.\r\n"
+" -enable Enable IPsec.\r\n"
+" -disable Disable IPsec.\r\n"
+" -status Show IPsec current status.\r\n"
+" \r\n"
+"%H[options[parameters]]%N for %HSPD%N:\r\n"
+" --local localaddress optional local address\r\n"
+" --remote remoteaddress required remote address\r\n"
+" --proto (TCP|UDP|ICMP|...) required IP protocol\r\n"
+" --local-port port optional local port for tcp/udp protocol\r\n"
+" --remote-port port optional remote port for tcp/udp protocol\r\n"
+" --name name optional SPD name\r\n"
+" --action (Bypass|Discard|Protect) required \r\n"
+" required IPsec action\r\n"
+" --mode (Transport|Tunnel) optional IPsec mode, transport by default\r\n"
+" --ipsec-proto (AH|ESP) optional IPsec protocol, ESP by default\r\n"
+" --auth-algo (NONE|SHA1HMAC) optional authentication algorithm\r\n"
+" --encrypt-algo(NONE|DESCBC|3DESCBC)optional encryption algorithm\r\n"
+" --tunnel-local tunnellocaladdr optional tunnel local address(only for tunnel mode)\r\n"
+" --tunnel-remote tunnelremoteaddr optional tunnel remote address(only for tunnel mode)\r\n"
+" \r\n"
+"%H[options[parameters]]%N for %HSAD%N:\r\n"
+" --spi spi required SPI value\r\n"
+" --ipsec-proto (AH|ESP) required IPsec protocol\r\n"
+" --local localaddress optional local address\r\n"
+" --remote remoteaddress required destination address\r\n"
+" --auth-algo (NONE|SHA1HMAC) required for AH. authentication algorithm\n"
+" --auth-key key required for AH. key for authentication\r\n"
+" --encrypt-algo (NONE|DESCBC|3DESCBC) required for ESP. encryption algorithm\r\n"
+" --encrypt-key key required for ESP. key for encryption\r\n"
+" --mode (Transport|Tunnel) optional IPsec mode, transport by default\r\n"
+" --tunnel-dest tunneldestaddr optional tunnel destination address(only for tunnel mode)\r\n"
+" --tunnel-source tunnelsourceaddr optional tunnel source address(only for tunnel mode)\r\n"
+" \r\n"
+"%H[options[parameters]]%N for %HPAD%N:\r\n"
+" --peer-address address required peer address\r\n"
+" --auth-proto (IKEv1|IKEv2) optional IKE protocol, IKEv1 by\r\n"
+" default\r\n"
+" --auth-method (PreSharedSecret|Certificates) required authentication method\r\n"
+" --auth-data authdata required data for authentication\r\n"
+" \r\n"
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..06eb30c091
--- /dev/null
+++ b/Core/NetworkPkg/Application/IpsecConfig/PolicyEntryOperation.c
@@ -0,0 +1,2076 @@
+/** @file
+ The implementation of policy entry operation 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"
+#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
+ //
+ OldData->SaIdCount = 0;
+
+ 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 the Selector already existed, this Entry will be updated by set data.
+ //
+ Status = mIpSecConfig->SetData (
+ mIpSecConfig,
+ Context->DataType,
+ Context->Selector, /// New created selector.
+ Data, /// Old date which has been modified, need to be set data.
+ Selector
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ if (CreateNew) {
+ //
+ // Edit the entry to a new one. So, we need delete the old entry.
+ //
+ Status = mIpSecConfig->SetData (
+ mIpSecConfig,
+ Context->DataType,
+ Selector, /// Old selector.
+ NULL, /// NULL means to delete this Entry specified by Selector.
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+ }
+
+ 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..66daac27be
--- /dev/null
+++ b/Core/NetworkPkg/Application/Ping6/Ping6.c
@@ -0,0 +1,1200 @@
+/** @file
+ The implementation for Ping6 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 <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/UefiHiiServicesLib.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"
+
+//
+// String token ID of Ping6 command help message text.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStringPing6HelpToken = STRING_TOKEN (STR_PING6_HELP);
+
+SHELL_PARAM_ITEM Ping6ParamList[] = {
+ {
+ L"-l",
+ TypeValue
+ },
+ {
+ L"-n",
+ TypeValue
+ },
+ {
+ L"-s",
+ TypeValue
+ },
+ {
+ 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;
+ EFI_HII_PACKAGE_LIST_HEADER *PackageList;
+
+ //
+ // Retrieve HII package list from ImageHandle
+ //
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gEfiHiiPackageListProtocolGuid,
+ (VOID **) &PackageList,
+ ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Publish HII package list to HII Database.
+ //
+ Status = gHiiDatabase->NewPackageList (
+ gHiiDatabase,
+ PackageList,
+ NULL,
+ &mHiiHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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;
+ }
+
+ SendNumber = 10;
+ BufferSize = 16;
+
+ //
+ // Parse the parameter 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 parameter 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 parameter 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 parameter 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..6f590af8c0
--- /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 - 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 _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..68b5f2d32f
--- /dev/null
+++ b/Core/NetworkPkg/Application/Ping6/Ping6.inf
@@ -0,0 +1,78 @@
+## @file
+# Shell application Ping6.
+#
+# It is an shell application which is used to Ping the target host with IPv6 stack.
+#
+# 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.
+#
+##
+
+[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
+
+#
+#
+# This flag specifies whether HII resource section is generated into PE image.
+#
+ UEFI_HII_RESOURCE_SECTION = TRUE
+
+#
+# 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
+ UefiHiiServicesLib
+ BaseMemoryLib
+ ShellLib
+ MemoryAllocationLib
+ DebugLib
+ HiiLib
+ NetLib
+
+[Protocols]
+ gEfiCpuArchProtocolGuid ## CONSUMES
+ gEfiIp6ProtocolGuid ## CONSUMES
+ gEfiIp6ServiceBindingProtocolGuid ## CONSUMES
+ gEfiIp6ConfigProtocolGuid ## CONSUMES
+ gEfiHiiPackageListProtocolGuid ## 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..afd14b796a
--- /dev/null
+++ b/Core/NetworkPkg/Application/Ping6/Ping6.uni
@@ -0,0 +1,22 @@
+// /** @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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Shell application Ping6"
+
+#string STR_MODULE_DESCRIPTION #language en-US "It is an shell application which is used to Ping the target host with IPv6 stack."
+
diff --git a/Core/NetworkPkg/Application/Ping6/Ping6Extra.uni b/Core/NetworkPkg/Application/Ping6/Ping6Extra.uni
new file mode 100644
index 0000000000..097ea5578a
--- /dev/null
+++ b/Core/NetworkPkg/Application/Ping6/Ping6Extra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Ping6 Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Ping6 App"
+
+
diff --git a/Core/NetworkPkg/Application/Ping6/Ping6Strings.uni b/Core/NetworkPkg/Application/Ping6/Ping6Strings.uni
new file mode 100644
index 0000000000..e4ab19fe63
--- /dev/null
+++ b/Core/NetworkPkg/Application/Ping6/Ping6Strings.uni
@@ -0,0 +1,53 @@
+/** @file
+ String definitions for the Shell Ping6 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.
+
+**/
+
+#langdef en-US "English"
+
+#string STR_PING6_INVALID_IP #language en-US "%Ping6: Invalid IP6 address, %s\n"
+#string STR_PING6_INVALID_INPUT #language en-US "%Ping6: Invalid input, please type 'Ping6 -?'for help\n"
+#string STR_PING6_INVALID_SEND_NUMBER #language en-US "%Ping6: Invalid send number, %s\n"
+#string STR_PING6_INVALID_BUFFER_SIZE #language en-US "%Ping6: Invalid buffer size, %s\n"
+#string STR_PING6_INVALID_SOURCE #language en-US "%Ping6: Require source interface option\n"
+#string STR_PING6_IP6_CONFIG #language en-US "%Ping6: Ip6->Config %r\n"
+#string STR_PING6_IP6_GETMODE #language en-US "%Ping6: Ip6->GetModeData %r\n"
+#string STR_PING6_IP6CFG_GETDATA #language en-US "%Ping6: Ip6Config->GetData %r\n"
+#string STR_PING6_SEND_REQUEST #language en-US "Echo request sequence %d fails.\n"
+#string STR_PING6_SOURCE_NOT_FOUND #language en-US "Source %s not found.\n"
+#string STR_PING6_NOSOURCE_INDOMAIN #language en-US "No sources in %s's multicast domain.\n"
+#string STR_PING6_START #language en-US "Ping %s %d data bytes\n\n"
+#string STR_PING6_TIMEOUT #language en-US "Echo request sequence %d timeout.\n"
+#string STR_PING6_REPLY_INFO #language en-US "%d bytes from %s : icmp_seq=%d ttl=%d time%c%dms\n"
+#string STR_PING6_STAT #language en-US "\n%d packets transmitted, %d received, %d%% packet loss, time %dms\n"
+#string STR_PING6_RTT #language en-US "\nRtt(round trip time) min=%dms max=%dms avg=%dms\n"
+#string STR_PING6_LINE_HELP #language en-US "Ping a target machine with UEFI IPv6 network stack"
+
+#string STR_PING6_HELP #language en-US ""
+".TH Ping6 0 "Ping a target machine with UEFI IPv6 network stack."\r\n"
+".SH NAME\r\n"
+"Ping a target machine with UEFI IPv6 network stack.\r\n"
+".SH SYNOPSIS\r\n"
+" \r\n"
+"Ping6 [-l size] [-n count] [-s SourceIp] TargetIp\r\n"
+".SH OPTIONS\r\n"
+" \r\n"
+" -l size Send buffer size, in bytes(default=16, min=16, max=32768).\r\n"
+" -n count Send request count, (default=10, min=1, max=10000).\r\n"
+" -s SourceIp Source IPv6 address.\r\n"
+" TargetIp Target IPv6 address.\r\n"
+".SH EXAMPLES\r\n"
+" \r\n"
+"Examples:\r\n"
+" Ping6 -s 2002::1 2002::2 -l 1000 -n 5\r\n"
+" Ping6 2002::2 -l 1000\r\n"
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..d00a041f49
--- /dev/null
+++ b/Core/NetworkPkg/Application/VConfig/VConfig.c
@@ -0,0 +1,695 @@
+/** @file
+ Shell application for VLAN configuration.
+
+ 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 <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/UefiHiiServicesLib.h>
+#include <Library/NetLib.h>
+
+//
+// String token ID of VConfig command help message text.
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_STRING_ID mStringVConfigHelpTokenId = STRING_TOKEN (STR_VCONFIG_HELP);
+
+#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;
+ EFI_HII_PACKAGE_LIST_HEADER *PackageList;
+ EFI_STATUS Status;
+
+ mImageHandle = ImageHandle;
+
+ //
+ // Retrieve HII package list from ImageHandle
+ //
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gEfiHiiPackageListProtocolGuid,
+ (VOID **) &PackageList,
+ ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Publish HII package list to HII Database.
+ //
+ Status = gHiiDatabase->NewPackageList (
+ gHiiDatabase,
+ PackageList,
+ NULL,
+ &mHiiHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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"-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..771f585a71
--- /dev/null
+++ b/Core/NetworkPkg/Application/VConfig/VConfig.inf
@@ -0,0 +1,61 @@
+## @file
+# Shell application VLAN configuration.
+#
+# It is shell application which is used to get and set VLAN configuration.
+#
+# 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.
+#
+##
+
+[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
+
+#
+#
+# This flag specifies whether HII resource section is generated into PE image.
+#
+ UEFI_HII_RESOURCE_SECTION = TRUE
+
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF
+#
+
+[Sources]
+ VConfigStrings.uni
+ VConfig.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ UefiApplicationEntryPoint
+ UefiBootServicesTableLib
+ UefiHiiServicesLib
+ UefiLib
+ ShellLib
+ NetLib
+ MemoryAllocationLib
+ HiiLib
+
+[Protocols]
+ gEfiVlanConfigProtocolGuid ## CONSUMES
+ gEfiHiiPackageListProtocolGuid ## 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..3647cace51
--- /dev/null
+++ b/Core/NetworkPkg/Application/VConfig/VConfig.uni
@@ -0,0 +1,22 @@
+// /** @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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Shell application VLAN configuration"
+
+#string STR_MODULE_DESCRIPTION #language en-US "It is shell application which is used to get and set VLAN configuration."
+
diff --git a/Core/NetworkPkg/Application/VConfig/VConfigExtra.uni b/Core/NetworkPkg/Application/VConfig/VConfigExtra.uni
new file mode 100644
index 0000000000..c5fed8bc2f
--- /dev/null
+++ b/Core/NetworkPkg/Application/VConfig/VConfigExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// VConfig Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Vlan Config App"
+
+
diff --git a/Core/NetworkPkg/Application/VConfig/VConfigStrings.uni b/Core/NetworkPkg/Application/VConfig/VConfigStrings.uni
new file mode 100644
index 0000000000..1bb66ba27e
--- /dev/null
+++ b/Core/NetworkPkg/Application/VConfig/VConfigStrings.uni
@@ -0,0 +1,63 @@
+/** @file
+ String definitions for VLAN configuration Shell 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.
+
+**/
+
+#langdef en-US "English"
+
+#string STR_VCONFIG_LOCATE_FAIL #language en-US "Failed to locate EFI_VLAN_CONFIG_PROTOCOL - %r.\n"
+#string STR_VCONFIG_MAC_FAIL #language en-US "Failed to get MAC address - %r.\n"
+#string STR_VCONFIG_FIND_FAIL #language en-US "Failed to find VLAN configuration - %r.\n"
+#string STR_VCONFIG_SET_FAIL #language en-US "Failed to set VLAN configuration - %r.\n"
+#string STR_VCONFIG_REMOVE_FAIL #language en-US "Failed to remove VLAN - %r.\n"
+#string STR_VCONFIG_NO_IF #language en-US "Network interface not specified.\n"
+#string STR_VCONFIG_NO_VID #language en-US "VLAN ID not specified.\n"
+#string STR_VCONFIG_INVALID_IF #language en-US "Invalid network interface - %s.\n"
+#string STR_VCONFIG_INVALID_VID #language en-US "Invalid VLAN ID - %s.\n"
+#string STR_VCONFIG_INVALID_PRIORITY #language en-US "Invalid VLAN Priority - %s.\n"
+#string STR_VCONFIG_NOT_FOUND #language en-US "Cannot find the VLAN device specified.\n"
+#string STR_VCONFIG_VLAN_DISPLAY #language en-US " VLAN ID: %4d Priority: %d\n"
+#string STR_VCONFIG_NO_VLAN #language en-US " VLAN is not configured.\n"
+#string STR_VCONFIG_ETH_MAC #language en-US "eth%d MAC:%s\n"
+#string STR_VCONFIG_SET_SUCCESS #language en-US "VLAN device added.\n"
+#string STR_VCONFIG_REMOVE_SUCCESS #language en-US "VLAN device removed.\n"
+#string STR_VCONFIG_NO_ARG #language en-US "Invalid argument, try "-?" for help.\n"
+
+#string STR_VCONFIG_HELP #language en-US ""
+".TH VConfig 0 "Display or modify VLAN configuration for network interface."\r\n"
+".SH NAME\r\n"
+"Display or modify VLAN configuration for network interface.\r\n"
+".SH SYNOPSIS\r\n"
+" \r\n"
+"VCONFIG [-?] [-l [IfName]] [-a IfName VlanId [Priority]] [-d IfName.VlanId]\r\n"
+".SH OPTIONS\r\n"
+" \r\n"
+" -l Display VLAN configuration for all or specified interface.\r\n"
+" -a Add a VLAN device for the network interface.\r\n"
+" -d Delete a VLAN device.\r\n"
+" IfName Name of network interface, e.g. eth0, eth1.\r\n"
+" VlanId Unique VLAN identifier (0~4094).\r\n"
+" Priority 802.1Q priority level (0~7), default 0.\r\n"
+".SH EXAMPLES\r\n"
+" \r\n"
+"Examples:\r\n"
+" * To display VLAN configuration:\r\n"
+" fs0:\> vconfig -l\r\n"
+" fs0:\> vconfig -l eth0\r\n"
+"\r\n"
+" * To add VLAN device:\r\n"
+" fs0:\> vconfig -a eth0 1000\r\n"
+" fs0:\> vconfig -a eth0 2000 7\r\n"
+"\r\n"
+" * To delete VLAN device:\r\n"
+" fs0:\> vconfig -d eth0.1000\r\n"
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..ba612156a0
--- /dev/null
+++ b/Core/NetworkPkg/Dhcp6Dxe/ComponentName.c
@@ -0,0 +1,448 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for Dhcp6 driver.
+
+ 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 "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];
+ }
+
+ if (Dhcp6ModeData.Ia != NULL) {
+ FreePool (Dhcp6ModeData.Ia);
+ }
+ if (Dhcp6ModeData.ClientId != NULL) {
+ FreePool (Dhcp6ModeData.ClientId);
+ }
+
+ 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..47969c3804
--- /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 - 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 __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 available 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..40a6ee7889
--- /dev/null
+++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf
@@ -0,0 +1,84 @@
+## @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 - 2017, 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
+
+[Guids]
+ gZeroGuid ## SOMETIMES_CONSUMES ## GUID
+
+[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..34ecd6c6ef
--- /dev/null
+++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.uni
@@ -0,0 +1,23 @@
+// /** @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.
+//
+// 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Client-side DHCPv6 services"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This driver produces EFI DHCPv6 Protocol which is used to get IPv6 addresses and other configuration parameters from DHCPv6 servers."
+
diff --git a/Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni
new file mode 100644
index 0000000000..ca9fa0c22e
--- /dev/null
+++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Dhcp6Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"DHCP6 DXE"
+
+
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..06780b678a
--- /dev/null
+++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Impl.h
@@ -0,0 +1,484 @@
+/** @file
+ Dhcp6 internal data structure and definition declaration.
+
+ Copyright (c) 2009 - 2017, 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 <IndustryStandard/Dhcp.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>
+#include <Guid/ZeroGuid.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')
+
+#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;
+
+//
+// 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..5cdeac6d5c
--- /dev/null
+++ b/Core/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c
@@ -0,0 +1,3174 @@
+/** @file
+ Dhcp6 internal functions implementation.
+
+ (C) Copyright 2014 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 "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 ;
+ }
+
+ if (Udp6Wrap->TotalSize < sizeof (EFI_DHCP6_HEADER)) {
+ goto ON_CONTINUE;
+ }
+
+ //
+ // 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..a65ed6d589
--- /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 - 2017, 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)) && !CompareGuid (&Uuid, &gZeroGuid)) {
+ //
+ //
+ // 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..d976bc6849
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/ComponentName.c
@@ -0,0 +1,457 @@
+/** @file
+Implementation of EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL protocol.
+
+Copyright (c) 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 "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 (ModeData.DnsCacheList != NULL) {
+ FreePool (ModeData.DnsCacheList);
+ }
+ if (ModeData.DnsServerList != NULL) {
+ FreePool (ModeData.DnsServerList);
+ }
+
+ 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 (ModeData.DnsCacheList != NULL) {
+ FreePool (ModeData.DnsCacheList);
+ }
+ if (ModeData.DnsServerList != NULL) {
+ FreePool (ModeData.DnsServerList);
+ }
+
+ 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..c4add702ea
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsDhcp.c
@@ -0,0 +1,764 @@
+/** @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"
+
+/**
+ 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;
+ }
+
+ //
+ // 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..5dc9afe448
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsDriver.c
@@ -0,0 +1,1552 @@
+/** @file
+The driver binding and service binding protocol for DnsDxe driver.
+
+Copyright (c) 2015 - 2017, 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.
+ //
+ 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 available 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 available 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..6632bb2198
--- /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 - 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 _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 available 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 available 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..01d44a6f1d
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsDxe.inf
@@ -0,0 +1,77 @@
+## @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
+ MODULE_UNI_FILE = DnsDxe.uni
+
+[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
+ gEfiManagedNetworkServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiManagedNetworkProtocolGuid ## SOMETIMES_CONSUMES
+
+ gEfiDns6ServiceBindingProtocolGuid ## BY_START
+ gEfiDns6ProtocolGuid ## BY_START
+ gEfiUdp6ServiceBindingProtocolGuid ## TO_START
+ gEfiUdp6ProtocolGuid ## TO_START
+ gEfiDhcp6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDhcp6ProtocolGuid ## SOMETIMES_CONSUMES
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ DnsDxeExtra.uni
+
diff --git a/Core/NetworkPkg/DnsDxe/DnsDxe.uni b/Core/NetworkPkg/DnsDxe/DnsDxe.uni
new file mode 100644
index 0000000000..fc1116aaa5
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsDxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// UEFI DNS DXE Driver.
+//
+// This driver provides UEFI 2.5 DNS protocols. It could work with an IPv4 and IPv6 stack.
+//
+//
+// 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI DNS service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This driver provides EFI DNS4 Protocol, EFI DNS6 Protocol, EFI DNS4 Service Binding Protocol and EFI DNS6 Service Binding Protocol. It could work with an IPv4 and IPv6 stack."
+
diff --git a/Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni b/Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni
new file mode 100644
index 0000000000..ab262d609c
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// DnsDxe Localized Strings and Content
+//
+// 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UEFI DNS DXE"
+
+
diff --git a/Core/NetworkPkg/DnsDxe/DnsImpl.c b/Core/NetworkPkg/DnsDxe/DnsImpl.c
new file mode 100644
index 0000000000..ea3d27da52
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsImpl.c
@@ -0,0 +1,2081 @@
+/** @file
+DnsDxe support functions implementation.
+
+Copyright (c) 2016 - 2017, 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))) {
+ if (Ip6Mode.AddressList != NULL) {
+ FreePool (Ip6Mode.AddressList);
+ }
+
+ if (Ip6Mode.GroupTable != NULL) {
+ FreePool (Ip6Mode.GroupTable);
+ }
+
+ if (Ip6Mode.RouteTable != NULL) {
+ FreePool (Ip6Mode.RouteTable);
+ }
+
+ if (Ip6Mode.NeighborCache != NULL) {
+ FreePool (Ip6Mode.NeighborCache);
+ }
+
+ if (Ip6Mode.PrefixTable != NULL) {
+ FreePool (Ip6Mode.PrefixTable);
+ }
+
+ if (Ip6Mode.IcmpTypeList != NULL) {
+ FreePool (Ip6Mode.IcmpTypeList);
+ }
+
+ if (!Ip6Mode.IsStarted || Ip6Mode.IsConfigured) {
+ Udp->Configure (Udp, NULL);
+ if (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS) {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ 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;
+}
+
+/**
+ 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 Class Class 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,
+ IN UINT16 Class,
+ 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);
+
+ if (NTOHS (DnsHeader->Identification) == Identification &&
+ NTOHS (QuerySection->Type) == Type &&
+ NTOHS (QuerySection->Class) == Class) {
+ 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;
+ UINT32 CNameTtl;
+
+ 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;
+ CNameTtl = 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,
+ QuerySection->Class,
+ &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,
+ QuerySection->Class,
+ &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) {
+ //
+ // The domain name referenced in the query does not exist.
+ //
+ if (DnsHeader->Flags.Bits.RCode == DNS_FLAGS_RCODE_NAME_ERROR) {
+ Status = EFI_NOT_FOUND;
+ } else {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ goto ON_COMPLETE;
+ }
+
+ //
+ // 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_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ Dns6TokenEntry->Token->RspData.GLookupData->RRList = AllocatePool (DnsHeader->AnswersNum * sizeof (DNS_RESOURCE_RECORD));
+ if (Dns6TokenEntry->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_AAAA) {
+ Dns6TokenEntry->Token->RspData.H2AData = AllocatePool (sizeof (DNS6_HOST_TO_ADDR_DATA));
+ if (Dns6TokenEntry->Token->RspData.H2AData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ Dns6TokenEntry->Token->RspData.H2AData->IpList = AllocatePool (DnsHeader->AnswersNum * sizeof (EFI_IPv6_ADDRESS));
+ if (Dns6TokenEntry->Token->RspData.H2AData->IpList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ } else {
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ //
+ // Processing AnswerSection.
+ //
+ while (AnswerSectionNum < DnsHeader->AnswersNum) {
+ //
+ // Answer name should be PTR, else EFI_UNSUPPORTED returned.
+ //
+ if ((*(UINT8 *) AnswerName & 0xC0) != 0xC0) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // 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_OUT_OF_RESOURCES;
+ 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_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ CopyMem (Dns4RR[RRCount].RData, AnswerData, Dns4RR[RRCount].DataLength);
+
+ RRCount ++;
+ Status = EFI_SUCCESS;
+ } 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_OUT_OF_RESOURCES;
+ 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_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ CopyMem (Dns6RR[RRCount].RData, AnswerData, Dns6RR[RRCount].DataLength);
+
+ RRCount ++;
+ Status = EFI_SUCCESS;
+ } 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);
+
+ if (AnswerSection->DataLength != 4) {
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+
+ HostAddr4 = Dns4TokenEntry->Token->RspData.H2AData->IpList;
+ AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection);
+ CopyMem (&HostAddr4[IpCount], AnswerData, sizeof (EFI_IPv4_ADDRESS));
+
+ //
+ // Allocate new CacheEntry pool to update DNS cache dynamically.
+ //
+ Dns4CacheEntry = AllocateZeroPool (sizeof (EFI_DNS4_CACHE_ENTRY));
+ if (Dns4CacheEntry == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ Dns4CacheEntry->HostName = AllocateZeroPool (2 * (StrLen(Dns4TokenEntry->QueryHostName) + 1));
+ if (Dns4CacheEntry->HostName == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ 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_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ CopyMem (Dns4CacheEntry->IpAddress, AnswerData, sizeof (EFI_IPv4_ADDRESS));
+
+ if (CNameTtl != 0 && AnswerSection->Ttl != 0) {
+ Dns4CacheEntry->Timeout = MIN (CNameTtl, AnswerSection->Ttl);
+ } else {
+ Dns4CacheEntry->Timeout = MAX (CNameTtl, AnswerSection->Ttl);
+ }
+
+ UpdateDns4Cache (&mDriverData->Dns4CacheList, FALSE, TRUE, *Dns4CacheEntry);
+
+ //
+ // Free allocated CacheEntry pool.
+ //
+ FreePool (Dns4CacheEntry->HostName);
+ Dns4CacheEntry->HostName = NULL;
+
+ FreePool (Dns4CacheEntry->IpAddress);
+ Dns4CacheEntry->IpAddress = NULL;
+
+ FreePool (Dns4CacheEntry);
+ Dns4CacheEntry = NULL;
+
+ IpCount ++;
+ Status = EFI_SUCCESS;
+ break;
+ case DNS_TYPE_AAAA:
+ //
+ // This is address entry, get Data.
+ //
+ ASSERT (Dns6TokenEntry != NULL);
+
+ if (AnswerSection->DataLength != 16) {
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+
+ HostAddr6 = Dns6TokenEntry->Token->RspData.H2AData->IpList;
+ AnswerData = (UINT8 *) AnswerSection + sizeof (*AnswerSection);
+ CopyMem (&HostAddr6[IpCount], AnswerData, sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // Allocate new CacheEntry pool to update DNS cache dynamically.
+ //
+ Dns6CacheEntry = AllocateZeroPool (sizeof (EFI_DNS6_CACHE_ENTRY));
+ if (Dns6CacheEntry == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ Dns6CacheEntry->HostName = AllocateZeroPool (2 * (StrLen(Dns6TokenEntry->QueryHostName) + 1));
+ if (Dns6CacheEntry->HostName == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ 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_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ CopyMem (Dns6CacheEntry->IpAddress, AnswerData, sizeof (EFI_IPv6_ADDRESS));
+
+ if (CNameTtl != 0 && AnswerSection->Ttl != 0) {
+ Dns6CacheEntry->Timeout = MIN (CNameTtl, AnswerSection->Ttl);
+ } else {
+ Dns6CacheEntry->Timeout = MAX (CNameTtl, AnswerSection->Ttl);
+ }
+
+ UpdateDns6Cache (&mDriverData->Dns6CacheList, FALSE, TRUE, *Dns6CacheEntry);
+
+ //
+ // Free allocated CacheEntry pool.
+ //
+ FreePool (Dns6CacheEntry->HostName);
+ Dns6CacheEntry->HostName = NULL;
+
+ FreePool (Dns6CacheEntry->IpAddress);
+ Dns6CacheEntry->IpAddress = NULL;
+
+ FreePool (Dns6CacheEntry);
+ Dns6CacheEntry = NULL;
+
+ IpCount ++;
+ Status = EFI_SUCCESS;
+ break;
+ case DNS_TYPE_CNAME:
+ //
+ // According RFC 1034 - 3.6.2, if the query name is an alias, the name server will include the CNAME
+ // record in the response and restart the query at the domain name specified in the data field of the
+ // CNAME record. So, just record the TTL value of the CNAME, then skip to parse the next record.
+ //
+ CNameTtl = AnswerSection->Ttl;
+ 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;
+ }
+ }
+ }
+
+ON_COMPLETE:
+ //
+ // Parsing is complete, free the sending packet and signal Event here.
+ //
+ if (Item != NULL && Item->Value != NULL) {
+ NetbufFree ((NET_BUF *) (Item->Value));
+ }
+
+ if (Instance->Service->IpVersion == IP_VERSION_4) {
+ ASSERT (Dns4TokenEntry != NULL);
+ Dns4RemoveTokenEntry (&Instance->Dns4TxTokens, Dns4TokenEntry);
+ Dns4TokenEntry->Token->Status = Status;
+ if (Dns4TokenEntry->Token->Event != NULL) {
+ gBS->SignalEvent (Dns4TokenEntry->Token->Event);
+ DispatchDpc ();
+ }
+ } else {
+ ASSERT (Dns6TokenEntry != NULL);
+ Dns6RemoveTokenEntry (&Instance->Dns6TxTokens, Dns6TokenEntry);
+ Dns6TokenEntry->Token->Status = Status;
+ if (Dns6TokenEntry->Token->Event != NULL) {
+ gBS->SignalEvent (Dns6TokenEntry->Token->Event);
+ DispatchDpc ();
+ }
+ }
+
+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);
+
+ if (Packet->TotalSize <= sizeof (DNS_HEADER)) {
+ goto ON_EXIT;
+ }
+
+ 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;
+
+ //
+ // Messages carried by UDP are restricted to 512 bytes (not counting the IP
+ // or UDP headers).
+ //
+ Frag.Bulk = AllocatePool (DNS_MAX_MESSAGE_SIZE * 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..5fa7f244c2
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsImpl.h
@@ -0,0 +1,1212 @@
+/** @file
+DnsDxe support functions implementation.
+
+Copyright (c) 2015 - 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 __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_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 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
+ );
+
+/**
+ 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 Class Class 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,
+ IN UINT16 Class,
+ 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
+ );
+
+
+/**
+ Retrieve mode data of this DNS instance.
+
+ 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 Point to the mode data.
+
+ @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_INVALID_PARAMETER This is NULL or DnsModeData is NULL.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+EFI_STATUS
+EFIAPI
+Dns4GetModeData (
+ IN EFI_DNS4_PROTOCOL *This,
+ OUT EFI_DNS4_MODE_DATA *DnsModeData
+ );
+
+/**
+ Configure this DNS instance.
+
+ This function is used to configure DNS mode data for this DNS instance.
+
+ @param[in] This Pointer to EFI_DNS4_PROTOCOL instance.
+ @param[in] DnsConfigData Point to the Configuration data.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED The designated protocol is not supported.
+ @retval EFI_INVALID_PARAMTER Thisis NULL.
+ The StationIp address provided in DnsConfigData is not a
+ valid unicast.
+ DnsServerList is NULL while DnsServerListCount
+ is not ZERO.
+ DnsServerListCount is ZERO while DnsServerList
+ is not NULL
+ @retval EFI_OUT_OF_RESOURCES The DNS instance data or required space could not be
+ allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The
+ EFI DNSv4 Protocol instance is not configured.
+ @retval EFI_ALREADY_STARTED Second call to Configure() with DnsConfigData. To
+ reconfigure the instance the caller must call Configure()
+ with NULL first to return driver to unconfigured state.
+**/
+EFI_STATUS
+EFIAPI
+Dns4Configure (
+ IN EFI_DNS4_PROTOCOL *This,
+ IN EFI_DNS4_CONFIG_DATA *DnsConfigData
+ );
+
+/**
+ Host name to host address translation.
+
+ The HostNameToIp () 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 Host name.
+ @param[in] Token Point to the completion token to translate host name
+ to host address.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ Token is NULL.
+ Token.Event is NULL.
+ HostName is NULL. HostName string is unsupported format.
+ @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
+ );
+
+/**
+ IPv4 address to host name translation also known as Reverse DNS lookup.
+
+ The IpToHostName() 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. Support of this function is optional.
+
+ @param[in] This Pointer to EFI_DNS4_PROTOCOL instance.
+ @param[in] IpAddress Ip Address.
+ @param[in] Token Point to the completion token to translate host
+ address to host name.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED This function is not supported.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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_ALREADY_STARTED This Token is being used in another DNS session.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+EFI_STATUS
+EFIAPI
+Dns4IpToHostName (
+ IN EFI_DNS4_PROTOCOL *This,
+ IN EFI_IPv4_ADDRESS IpAddress,
+ IN EFI_DNS4_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Retrieve arbitrary information from the DNS server.
+
+ This GeneralLookup() 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. The 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 completion token to retrieve arbitrary
+ information.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED This function is not supported. Or the requested
+ QType is not supported
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+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 to update the DNS Cache.
+
+ The UpdateDnsCache() 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 Cahce. If TRUE, this function will delete
+ matching DNS Cache entry.
+ @param[in] Override If TRUE, the maching 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 existed.
+ @param[in] DnsCacheEntry Pointer to DNS Cache entry.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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
+ );
+
+/**
+ 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 Pointer to EFI_DNS4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI DNS 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
+Dns4Poll (
+ IN EFI_DNS4_PROTOCOL *This
+ );
+
+/**
+ Abort an asynchronous DNS operation, including translation between IP and Host, and
+ general look up behavior.
+
+ The Cancel() function is used to abort a pending resolution request. 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 that the
+ asynchronous operation has completed, this function will not signal the token and
+ EFI_NOT_FOUND is returned.
+
+ @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_NOT_STARTED This EFI DNS4 Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @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
+ );
+
+
+/**
+ Retrieve mode data of this DNS instance.
+
+ 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 data.
+
+ @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_INVALID_PARAMETER This is NULL or DnsModeData is NULL.
+ @retval EFI_OUT_OF_RESOURCE Failed to allocate needed resources.
+**/
+EFI_STATUS
+EFIAPI
+Dns6GetModeData (
+ IN EFI_DNS6_PROTOCOL *This,
+ OUT EFI_DNS6_MODE_DATA *DnsModeData
+ );
+
+/**
+ Configure this DNS instance.
+
+ The Configure() 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_INVALID_PARAMTER This is NULL.
+ The StationIp address provided in DnsConfigData is not zero and not a valid unicast.
+ DnsServerList is NULL while DnsServerList Count is not ZERO.
+ DnsServerList Count is ZERO while DnsServerList is not NULL.
+ @retval EFI_OUT_OF_RESOURCES The DNS instance data or required space could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The
+ EFI DNSv6 Protocol instance is not configured.
+ @retval EFI_UNSUPPORTED The designated protocol is not supported.
+ @retval EFI_ALREADY_STARTED Second call to Configure() with DnsConfigData. To
+ reconfigure the instance the caller must call Configure() with
+ NULL first to return driver to unconfigured state.
+**/
+EFI_STATUS
+EFIAPI
+Dns6Configure (
+ IN EFI_DNS6_PROTOCOL *This,
+ IN EFI_DNS6_CONFIG_DATA *DnsConfigData
+ );
+
+/**
+ Host name to host address translation.
+
+ The HostNameToIp () 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 Host name.
+ @param[in] Token Point to the completion token to translate host name
+ to host address.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ Token is NULL.
+ Token.Event is NULL.
+ HostName is NULL or buffer contained unsupported characters.
+ @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_NOT_STARTED This instance has not been started.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+EFI_STATUS
+EFIAPI
+Dns6HostNameToIp (
+ IN EFI_DNS6_PROTOCOL *This,
+ IN CHAR16 *HostName,
+ IN EFI_DNS6_COMPLETION_TOKEN *Token
+ );
+
+/**
+ Host address to host name translation.
+
+ The IpToHostName () 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. Implementation can choose
+ to support this function or not.
+
+ @param[in] This Pointer to EFI_DNS6_PROTOCOL instance.
+ @param[in] IpAddress Ip Address.
+ @param[in] Token Point to the completion token to translate host
+ address to host name.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED This function is not supported.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+EFI_STATUS
+EFIAPI
+Dns6IpToHostName (
+ IN EFI_DNS6_PROTOCOL *This,
+ IN EFI_IPv6_ADDRESS IpAddress,
+ IN EFI_DNS6_COMPLETION_TOKEN *Token
+ );
+
+/**
+ This function provides capability to retrieve arbitrary information from the DNS
+ server.
+
+ This GeneralLookup() 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. The function is optional. Implementation can choose to support
+ it or not.
+
+ @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 completion token to retrieve arbitrary
+ information.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED This function is not supported. Or the requested
+ QType is not supported
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+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 to update the DNS Cache.
+
+ The UpdateDnsCache() 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 Cahce. If TRUE, this function will delete
+ matching DNS Cache entry.
+ @param[in] Override If TRUE, the maching 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 existed.
+ @param[in] DnsCacheEntry Pointer to DNS Cache entry.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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.
+ @retval EFI_OUT_OF_RESOURCE Failed to allocate needed resources.
+**/
+EFI_STATUS
+EFIAPI
+Dns6UpdateDnsCache (
+ IN EFI_DNS6_PROTOCOL *This,
+ IN BOOLEAN DeleteFlag,
+ IN BOOLEAN Override,
+ IN EFI_DNS6_CACHE_ENTRY DnsCacheEntry
+ );
+
+/**
+ 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 Pointer to EFI_DNS6_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NO_MAPPING There is no source address is available for use.
+ @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
+ );
+
+/**
+ Abort an asynchronous DNS operation, including translation between IP and Host, and
+ general look up behavior.
+
+ The Cancel() function is used to abort a pending resolution request. 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 that the
+ asynchronous operation has completed, this function will not signal the token and
+ EFI_NOT_FOUND is returned.
+
+ @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_NOT_STARTED This EFI DNS6 Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NO_MAPPING There's no source address is available for use.
+ @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..bd189aebbe
--- /dev/null
+++ b/Core/NetworkPkg/DnsDxe/DnsProtocol.c
@@ -0,0 +1,1706 @@
+/** @file
+Implementation of EFI_DNS4_PROTOCOL and EFI_DNS6_PROTOCOL interfaces.
+
+Copyright (c) 2015 - 2017, 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
+};
+
+/**
+ Retrieve mode data of this DNS instance.
+
+ 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 Point to the mode data.
+
+ @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_INVALID_PARAMETER This is NULL or DnsModeData is NULL.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+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) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ 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)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // 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);
+ if (ServerList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ Dns4CleanConfigure (&DnsModeData->DnsConfigData);
+ goto ON_EXIT;
+ }
+
+ 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);
+ if (CacheList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ Dns4CleanConfigure (&DnsModeData->DnsConfigData);
+ FreePool (ServerList);
+ goto ON_EXIT;
+ }
+
+ 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;
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Configure this DNS instance.
+
+ This function is used to configure DNS mode data for this DNS instance.
+
+ @param[in] This Pointer to EFI_DNS4_PROTOCOL instance.
+ @param[in] DnsConfigData Point to the Configuration data.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED The designated protocol is not supported.
+ @retval EFI_INVALID_PARAMTER Thisis NULL.
+ The StationIp address provided in DnsConfigData is not a
+ valid unicast.
+ DnsServerList is NULL while DnsServerListCount
+ is not ZERO.
+ DnsServerListCount is ZERO while DnsServerList
+ is not NULL
+ @retval EFI_OUT_OF_RESOURCES The DNS instance data or required space could not be
+ allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The
+ EFI DNSv4 Protocol instance is not configured.
+ @retval EFI_ALREADY_STARTED Second call to Configure() with DnsConfigData. To
+ reconfigure the instance the caller must call Configure()
+ with NULL first to return driver to unconfigured state.
+**/
+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) || (Netmask != 0 && !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);
+ Instance->Dns4CfgData.DnsServerList = NULL;
+ }
+ 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);
+ Instance->Dns4CfgData.DnsServerList = NULL;
+ }
+ goto ON_EXIT;
+ }
+
+ Instance->State = DNS_STATE_CONFIGED;
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Host name to host address translation.
+
+ The HostNameToIp () 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 Host name.
+ @param[in] Token Point to the completion token to translate host name
+ to host address.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ Token is NULL.
+ Token.Event is NULL.
+ HostName is NULL. HostName string is unsupported format.
+ @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 = NetLibCreateDnsQName (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)) {
+ Dns4RemoveTokenEntry (&Instance->Dns4TxTokens, TokenEntry);
+
+ if (TokenEntry != NULL) {
+ FreePool (TokenEntry);
+ }
+
+ NetbufFree (Packet);
+ }
+
+ON_EXIT:
+ if (QueryName != NULL) {
+ FreePool (QueryName);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ IPv4 address to host name translation also known as Reverse DNS lookup.
+
+ The IpToHostName() 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. Support of this function is optional.
+
+ @param[in] This Pointer to EFI_DNS4_PROTOCOL instance.
+ @param[in] IpAddress Ip Address.
+ @param[in] Token Point to the completion token to translate host
+ address to host name.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED This function is not supported.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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_ALREADY_STARTED This Token is being used in another DNS session.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+EFI_STATUS
+EFIAPI
+Dns4IpToHostName (
+ IN EFI_DNS4_PROTOCOL *This,
+ IN EFI_IPv4_ADDRESS IpAddress,
+ IN EFI_DNS4_COMPLETION_TOKEN *Token
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Retrieve arbitrary information from the DNS server.
+
+ This GeneralLookup() 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. The 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 completion token to retrieve arbitrary
+ information.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED This function is not supported. Or the requested
+ QType is not supported
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+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)) {
+ Dns4RemoveTokenEntry (&Instance->Dns4TxTokens, TokenEntry);
+
+ if (TokenEntry != NULL) {
+ FreePool (TokenEntry);
+ }
+
+ NetbufFree (Packet);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ This function is to update the DNS Cache.
+
+ The UpdateDnsCache() 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 Cahce. If TRUE, this function will delete
+ matching DNS Cache entry.
+ @param[in] Override If TRUE, the maching 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 existed.
+ @param[in] DnsCacheEntry Pointer to DNS Cache entry.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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;
+}
+
+/**
+ 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 Pointer to EFI_DNS4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI DNS 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
+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);
+}
+
+/**
+ Abort an asynchronous DNS operation, including translation between IP and Host, and
+ general look up behavior.
+
+ The Cancel() function is used to abort a pending resolution request. 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 that the
+ asynchronous operation has completed, this function will not signal the token and
+ EFI_NOT_FOUND is returned.
+
+ @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_NOT_STARTED This EFI DNS4 Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @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;
+}
+
+/**
+ Retrieve mode data of this DNS instance.
+
+ 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 data.
+
+ @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_INVALID_PARAMETER This is NULL or DnsModeData is NULL.
+ @retval EFI_OUT_OF_RESOURCE Failed to allocate needed resources.
+**/
+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) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ 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)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // 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);
+ if (ServerList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ Dns6CleanConfigure (&DnsModeData->DnsConfigData);
+ goto ON_EXIT;
+ }
+
+ 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);
+ if (CacheList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ Dns6CleanConfigure (&DnsModeData->DnsConfigData);
+ FreePool (ServerList);
+ goto ON_EXIT;
+ }
+
+ 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;
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Configure this DNS instance.
+
+ The Configure() 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_INVALID_PARAMTER This is NULL.
+ The StationIp address provided in DnsConfigData is not zero and not a valid unicast.
+ DnsServerList is NULL while DnsServerList Count is not ZERO.
+ DnsServerList Count is ZERO while DnsServerList is not NULL.
+ @retval EFI_OUT_OF_RESOURCES The DNS instance data or required space could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The
+ EFI DNSv6 Protocol instance is not configured.
+ @retval EFI_UNSUPPORTED The designated protocol is not supported.
+ @retval EFI_ALREADY_STARTED Second call to Configure() with DnsConfigData. To
+ reconfigure the instance the caller must call Configure() with
+ NULL first to return driver to unconfigured state.
+**/
+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
+ //
+ gBS->RestoreTPL (OldTpl);
+ Status = Dns6ConfigUdp (Instance, Instance->UdpIo);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (EFI_ERROR (Status)) {
+ if (Instance->Dns6CfgData.DnsServerList != NULL) {
+ FreePool (Instance->Dns6CfgData.DnsServerList);
+ Instance->Dns6CfgData.DnsServerList = NULL;
+ }
+ 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);
+ Instance->Dns6CfgData.DnsServerList = NULL;
+ }
+ goto ON_EXIT;
+ }
+
+ Instance->State = DNS_STATE_CONFIGED;
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Host name to host address translation.
+
+ The HostNameToIp () 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 Host name.
+ @param[in] Token Point to the completion token to translate host name
+ to host address.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ Token is NULL.
+ Token.Event is NULL.
+ HostName is NULL or buffer contained unsupported characters.
+ @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_NOT_STARTED This instance has not been started.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+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 = NetLibCreateDnsQName (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)) {
+ Dns6RemoveTokenEntry (&Instance->Dns6TxTokens, TokenEntry);
+
+ if (TokenEntry != NULL) {
+ FreePool (TokenEntry);
+ }
+
+ NetbufFree (Packet);
+ }
+
+ON_EXIT:
+ if (QueryName != NULL) {
+ FreePool (QueryName);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Host address to host name translation.
+
+ The IpToHostName () 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. Implementation can choose
+ to support this function or not.
+
+ @param[in] This Pointer to EFI_DNS6_PROTOCOL instance.
+ @param[in] IpAddress Ip Address.
+ @param[in] Token Point to the completion token to translate host
+ address to host name.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED This function is not supported.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+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 provides capability to retrieve arbitrary information from the DNS
+ server.
+
+ This GeneralLookup() 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. The function is optional. Implementation can choose to support
+ it or not.
+
+ @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 completion token to retrieve arbitrary
+ information.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_UNSUPPORTED This function is not supported. Or the requested
+ QType is not supported
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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_OUT_OF_RESOURCES Failed to allocate needed resources.
+**/
+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)) {
+ Dns6RemoveTokenEntry (&Instance->Dns6TxTokens, TokenEntry);
+
+ if (TokenEntry != NULL) {
+ FreePool (TokenEntry);
+ }
+
+ NetbufFree (Packet);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ This function is to update the DNS Cache.
+
+ The UpdateDnsCache() 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 Cahce. If TRUE, this function will delete
+ matching DNS Cache entry.
+ @param[in] Override If TRUE, the maching 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 existed.
+ @param[in] DnsCacheEntry Pointer to DNS Cache entry.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 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.
+ @retval EFI_OUT_OF_RESOURCE Failed to allocate needed resources.
+**/
+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;
+}
+
+/**
+ 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 Pointer to EFI_DNS6_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI DNS Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NO_MAPPING There is no source address is available for use.
+ @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);
+}
+
+/**
+ Abort an asynchronous DNS operation, including translation between IP and Host, and
+ general look up behavior.
+
+ The Cancel() function is used to abort a pending resolution request. 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 that the
+ asynchronous operation has completed, this function will not signal the token and
+ EFI_NOT_FOUND is returned.
+
+ @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_NOT_STARTED This EFI DNS6 Protocol instance has not been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NO_MAPPING There's no source address is available for use.
+ @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..99db3d5505
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootClient.c
@@ -0,0 +1,1149 @@
+/** @file
+ Implementation of the boot file download function.
+
+Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 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 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 (Private->FilePathUri == NULL) {
+ //
+ // In Corporate environment, we need a HttpOffer.
+ //
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
+ HttpOffer = SelectOffer;
+ } else {
+ ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
+ HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;
+ }
+ Private->BootFileUriParser = HttpOffer->UriParser;
+ Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;
+ } else {
+ //
+ // In Home environment the BootFileUri comes from the FilePath.
+ //
+ Private->BootFileUriParser = Private->FilePathUriParser;
+ Private->BootFileUri = Private->FilePathUri;
+ }
+
+ //
+ // Check the URI scheme.
+ //
+ Status = HttpBootCheckUriScheme (Private->BootFileUri);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status));
+ return Status;
+ }
+
+ //
+ // Configure the default DNS server if server assigned.
+ //
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {
+ 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 (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &Private->Port
+ );
+ if (EFI_ERROR (Status) || Private->Port == 0) {
+ Private->Port = 80;
+ }
+
+ //
+ // 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 (Private->FilePathUri == NULL) {
+ //
+ // In Corporate environment, we need a HttpOffer.
+ //
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
+ HttpOffer = SelectOffer;
+ } else {
+ ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
+ HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6;
+ }
+ Private->BootFileUriParser = HttpOffer->UriParser;
+ Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data;
+ } else {
+ //
+ // In Home environment the BootFileUri comes from the FilePath.
+ //
+ Private->BootFileUriParser = Private->FilePathUriParser;
+ Private->BootFileUri = Private->FilePathUri;
+ }
+
+ //
+ // Check the URI scheme.
+ //
+ Status = HttpBootCheckUriScheme (Private->BootFileUri);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status));
+ return Status;
+ }
+
+ //
+ // Set the Local station address to IP layer.
+ //
+ Status = HttpBootSetIp6Address (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Register the IPv6 gateway address to the network device.
+ //
+ Status = HttpBootSetIp6Gateway (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Configure the default DNS server if server assigned.
+ //
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {
+ 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 (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &IpAddr
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // The Http server address is expressed by Name Ip, so perform DNS resolution
+ //
+ Status = HttpUrlGetHostName (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &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));
+
+ //
+ // Extract the port from URL, and use default HTTP port 80 if not provided.
+ //
+ Status = HttpUrlGetPort (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &Private->Port
+ );
+ if (EFI_ERROR (Status) || Private->Port == 0) {
+ Private->Port = 80;
+ }
+
+ //
+ // 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;
+ EFI_HANDLE ImageHandle;
+
+ 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);
+ ImageHandle = Private->Ip4Nic->ImageHandle;
+ } else {
+ ConfigData.Config6.HttpVersion = HttpVersion11;
+ ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
+ IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);
+ ImageHandle = Private->Ip6Nic->ImageHandle;
+ }
+
+ Status = HttpIoCreateIo (
+ ImageHandle,
+ 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.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @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,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ )
+{
+ 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 || ImageType == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ 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 in cache, record image type.
+ //
+ *ImageType = Cache->ImageType;
+
+ //
+ // 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;
+ 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.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @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,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_STATUS_CODE StatusCode;
+ 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 || ImageType == 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, ImageType);
+ 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;
+ }
+ Cache->ImageType = ImageTypeMax;
+ 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_HEADER_HOST,
+ HostName
+ );
+ FreePool (HostName);
+ if (EFI_ERROR (Status)) {
+ goto ERROR_3;
+ }
+
+ //
+ // Add HTTP header field 2: Accept
+ //
+ Status = HttpBootSetHeader (
+ HttpIoHeader,
+ HTTP_HEADER_ACCEPT,
+ "*/*"
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_3;
+ }
+
+ //
+ // Add HTTP header field 3: User-Agent
+ //
+ Status = HttpBootSetHeader (
+ HttpIoHeader,
+ HTTP_HEADER_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) || EFI_ERROR (ResponseData->Status)) {
+ if (EFI_ERROR (ResponseData->Status)) {
+ StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;
+ HttpBootPrintErrorMessage (StatusCode);
+ Status = ResponseData->Status;
+ }
+ goto ERROR_5;
+ }
+
+ //
+ // Check the image type according to server's response.
+ //
+ Status = HttpBootCheckImageType (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ ResponseData->HeaderCount,
+ ResponseData->Headers,
+ ImageType
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_5;
+ }
+
+ //
+ // 3.2 Cache the response header.
+ //
+ if (Cache != NULL) {
+ Cache->ResponseData = ResponseData;
+ Cache->ImageType = *ImageType;
+ }
+
+ //
+ // 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) || EFI_ERROR (ResponseBody.Status)) {
+ if (EFI_ERROR (ResponseBody.Status)) {
+ Status = ResponseBody.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) || EFI_ERROR (ResponseBody.Status)) {
+ if (EFI_ERROR (ResponseBody.Status)) {
+ Status = ResponseBody.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;
+ } else {
+ Status = EFI_SUCCESS;
+ }
+ *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 Status;
+
+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..2c32341460
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootClient.h
@@ -0,0 +1,141 @@
+/** @file
+ Declaration of the boot file download function.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 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 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_RESPONSE_TIMEOUT 5000 // 5 seconds in uints of millisecond.
+#define HTTP_BOOT_BLOCK_SIZE 1500
+
+
+
+#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.
+ HTTP_BOOT_IMAGE_TYPE ImageType;
+ 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.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @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,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ );
+
+/**
+ 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/HttpBootConfig.c b/Core/NetworkPkg/HttpBootDxe/HttpBootConfig.c
new file mode 100644
index 0000000000..f32bf18e9d
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootConfig.c
@@ -0,0 +1,703 @@
+/** @file
+ Helper functions for configuring or getting the parameters relating to HTTP Boot.
+
+Copyright (c) 2016 - 2017, 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 "HttpBootDxe.h"
+#include <Library/UefiBootManagerLib.h>
+
+CHAR16 mHttpBootConfigStorageName[] = L"HTTP_BOOT_CONFIG_IFR_NVDATA";
+
+/**
+ Add new boot option for HTTP boot.
+
+ @param[in] Private Pointer to the driver private data.
+ @param[in] UsingIpv6 Set to TRUE if creating boot option for IPv6.
+ @param[in] Description The description text of the boot option.
+ @param[in] Uri The URI string of the boot file.
+
+ @retval EFI_SUCCESS The boot option is created successfully.
+ @retval Others Failed to create new boot option.
+
+**/
+EFI_STATUS
+HttpBootAddBootOption (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN BOOLEAN UsingIpv6,
+ IN CHAR16 *Description,
+ IN CHAR16 *Uri
+ )
+{
+ EFI_DEV_PATH *Node;
+ EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
+ UINTN Length;
+ CHAR8 AsciiUri[URI_STR_MAX_SIZE];
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
+
+ NewDevicePath = NULL;
+ Node = NULL;
+ TmpDevicePath = NULL;
+
+ if (StrLen (Description) == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert the scheme to all lower case.
+ //
+ for (Index = 0; Index < StrLen (Uri); Index++) {
+ if (Uri[Index] == L':') {
+ break;
+ }
+ if (Uri[Index] >= L'A' && Uri[Index] <= L'Z') {
+ Uri[Index] -= (CHAR16)(L'A' - L'a');
+ }
+ }
+
+ //
+ // Only accept empty URI, or http and https URI.
+ //
+ if ((StrLen (Uri) != 0) && (StrnCmp (Uri, L"http://", 7) != 0) && (StrnCmp (Uri, L"https://", 8) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Create a new device path by appending the IP node and URI node to
+ // the driver's parent device path
+ //
+ if (!UsingIpv6) {
+ Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Ipv4.Header.SubType = MSG_IPv4_DP;
+ SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
+ } else {
+ Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Ipv6.Header.SubType = MSG_IPv6_DP;
+ SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));
+ }
+ 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 input boot file URI.
+ //
+ UnicodeStrToAsciiStrS (Uri, AsciiUri, sizeof (AsciiUri));
+ Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (AsciiUri);
+ Node = AllocatePool (Length);
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ FreePool (TmpDevicePath);
+ goto ON_EXIT;
+ }
+ Node->DevPath.Type = MESSAGING_DEVICE_PATH;
+ Node->DevPath.SubType = MSG_URI_DP;
+ SetDevicePathNodeLength (Node, Length);
+ CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), AsciiUri, AsciiStrSize (AsciiUri));
+ NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (Node);
+ FreePool (TmpDevicePath);
+ if (NewDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Add a new load option.
+ //
+ Status = EfiBootManagerInitializeLoadOption (
+ &NewOption,
+ LoadOptionNumberUnassigned,
+ LoadOptionTypeBoot,
+ LOAD_OPTION_ACTIVE,
+ Description,
+ NewDevicePath,
+ NULL,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN) -1);
+ EfiBootManagerFreeLoadOption (&NewOption);
+
+ON_EXIT:
+
+ if (NewDevicePath != NULL) {
+ FreePool (NewDevicePath);
+ }
+
+ 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. 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
+HttpBootFormExtractConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Request,
+ OUT EFI_STRING *Progress,
+ OUT EFI_STRING *Results
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+ EFI_STRING ConfigRequestHdr;
+ EFI_STRING ConfigRequest;
+ BOOLEAN AllocatedRequest;
+ UINTN Size;
+
+ if (Progress == NULL || Results == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Progress = Request;
+ if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ ConfigRequestHdr = NULL;
+ ConfigRequest = NULL;
+ AllocatedRequest = FALSE;
+ Size = 0;
+
+ CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
+ //
+ // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
+ //
+ BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA);
+ ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize);
+ StrCpyS (CallbackInfo->HttpBootNvData.Description, DESCRIPTION_STR_MAX_SIZE / sizeof (CHAR16), HTTP_BOOT_DEFAULT_DESCRIPTION_STR);
+
+ 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 (&gHttpBootConfigGuid, mHttpBootConfigStorageName, CallbackInfo->ChildHandle);
+ Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
+ ConfigRequest = AllocateZeroPool (Size);
+ if (ConfigRequest == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ AllocatedRequest = TRUE;
+ UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
+ FreePool (ConfigRequestHdr);
+ }
+
+ Status = gHiiConfigRouting->BlockToConfig (
+ gHiiConfigRouting,
+ ConfigRequest,
+ (UINT8 *) &CallbackInfo->HttpBootNvData,
+ BufferSize,
+ Results,
+ Progress
+ );
+
+ //
+ // 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
+HttpBootFormRouteConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Configuration,
+ OUT EFI_STRING *Progress
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+ HTTP_BOOT_PRIVATE_DATA *Private;
+
+ if (Progress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ *Progress = Configuration;
+
+ if (Configuration == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check routing data in <ConfigHdr>.
+ // Note: there is no name for Name/Value storage, only GUID will be checked
+ //
+ if (!HiiIsConfigHdrMatch (Configuration, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
+ Private = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_INFO (CallbackInfo);
+
+ BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA);
+ ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize);
+
+ Status = gHiiConfigRouting->ConfigToBlock (
+ gHiiConfigRouting,
+ Configuration,
+ (UINT8 *) &CallbackInfo->HttpBootNvData,
+ &BufferSize,
+ Progress
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create a new boot option according to the configuration data.
+ //
+ HttpBootAddBootOption (
+ Private,
+ (CallbackInfo->HttpBootNvData.IpVersion == HTTP_BOOT_IP_VERSION_6) ? TRUE : FALSE,
+ CallbackInfo->HttpBootNvData.Description,
+ CallbackInfo->HttpBootNvData.Uri
+ );
+
+ 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
+HttpBootFormCallback (
+ 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
+ )
+{
+ EFI_INPUT_KEY Key;
+ CHAR16 *Uri;
+ UINTN UriLen;
+ CHAR8 *AsciiUri;
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+ EFI_STATUS Status;
+
+ Uri = NULL;
+ UriLen = 0;
+ AsciiUri = NULL;
+ Status = EFI_SUCCESS;
+
+ if (This == NULL || Value == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
+
+ if (Action != EFI_BROWSER_ACTION_CHANGING) {
+ return EFI_UNSUPPORTED;
+ }
+
+ switch (QuestionId) {
+ case KEY_INITIATOR_URI:
+ //
+ // Get user input URI string
+ //
+ Uri = HiiGetString (CallbackInfo->RegisteredHandle, Value->string, NULL);
+
+ //
+ // The URI should be either an empty string (for corporate environment) ,or http(s) for home environment.
+ // Pop up a message box for the unsupported URI.
+ //
+ if (StrLen (Uri) != 0) {
+ UriLen = StrLen (Uri) + 1;
+ AsciiUri = AllocateZeroPool (UriLen);
+ if (AsciiUri == NULL) {
+ FreePool (Uri);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UnicodeStrToAsciiStrS (Uri, AsciiUri, UriLen);
+
+ Status = HttpBootCheckUriScheme (AsciiUri);
+
+ if (Status == EFI_INVALID_PARAMETER) {
+
+ DEBUG ((EFI_D_ERROR, "HttpBootFormCallback: %r.\n", Status));
+
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"ERROR: Unsupported URI!",
+ L"Only supports HTTP and HTTPS",
+ NULL
+ );
+ } else if (Status == EFI_ACCESS_DENIED) {
+
+ DEBUG ((EFI_D_ERROR, "HttpBootFormCallback: %r.\n", Status));
+
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"ERROR: Unsupported URI!",
+ L"HTTP is disabled",
+ NULL
+ );
+ }
+ }
+
+ if (Uri != NULL) {
+ FreePool (Uri);
+ }
+
+ if (AsciiUri != NULL) {
+ FreePool (AsciiUri);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return Status;
+}
+
+/**
+ Initialize the configuration form.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+HttpBootConfigFormInit (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+ VENDOR_DEVICE_PATH VendorDeviceNode;
+ CHAR16 *MacString;
+ CHAR16 *OldMenuString;
+ CHAR16 MenuString[128];
+
+ CallbackInfo = &Private->CallbackInfo;
+
+ if (CallbackInfo->Initilized) {
+ return EFI_SUCCESS;
+ }
+
+ CallbackInfo->Signature = HTTP_BOOT_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 (
+ Private->ParentDevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode
+ );
+ if (CallbackInfo->HiiVendorDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ CallbackInfo->ConfigAccess.ExtractConfig = HttpBootFormExtractConfig;
+ CallbackInfo->ConfigAccess.RouteConfig = HttpBootFormRouteConfig;
+ CallbackInfo->ConfigAccess.Callback = HttpBootFormCallback;
+
+ //
+ // Install Device Path Protocol and Config Access protocol to driver handle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &CallbackInfo->ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ CallbackInfo->HiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &CallbackInfo->ConfigAccess,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ //
+ // Publish our HII data.
+ //
+ CallbackInfo->RegisteredHandle = HiiAddPackages (
+ &gHttpBootConfigGuid,
+ CallbackInfo->ChildHandle,
+ HttpBootDxeStrings,
+ HttpBootConfigVfrBin,
+ NULL
+ );
+ if (CallbackInfo->RegisteredHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ //
+ // Append MAC string in the menu help string
+ //
+ Status = NetLibGetMacString (Private->Controller, NULL, &MacString);
+ if (!EFI_ERROR (Status)) {
+ OldMenuString = HiiGetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP),
+ NULL
+ );
+ UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);
+ HiiSetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP),
+ MenuString,
+ NULL
+ );
+
+ FreePool (MacString);
+ FreePool (OldMenuString);
+
+ CallbackInfo->Initilized = TRUE;
+ return EFI_SUCCESS;
+ }
+
+Error:
+
+ HttpBootConfigFormUnload (Private);
+ return Status;
+}
+
+/**
+ Unload the configuration form, this includes: delete all the configuration
+ entries, uninstall the form callback protocol, and free the resources used.
+ The form will only be unload completely when both IP4 and IP6 stack are stopped.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is unloaded.
+ @retval Others Failed to unload the form.
+
+**/
+EFI_STATUS
+HttpBootConfigFormUnload (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+
+ if (Private->Ip4Nic != NULL || Private->Ip6Nic != NULL) {
+ //
+ // Only unload the configuration form when both IP4 and IP6 stack are stopped.
+ //
+ return EFI_SUCCESS;
+ }
+
+ CallbackInfo = &Private->CallbackInfo;
+ if (CallbackInfo->ChildHandle != NULL) {
+ //
+ // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ CallbackInfo->ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ CallbackInfo->HiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &CallbackInfo->ConfigAccess,
+ NULL
+ );
+ CallbackInfo->ChildHandle = NULL;
+ }
+
+ if (CallbackInfo->HiiVendorDevicePath != NULL) {
+ FreePool (CallbackInfo->HiiVendorDevicePath);
+ CallbackInfo->HiiVendorDevicePath = NULL;
+ }
+
+ if (CallbackInfo->RegisteredHandle != NULL) {
+ //
+ // Remove HII package list
+ //
+ HiiRemovePackages (CallbackInfo->RegisteredHandle);
+ CallbackInfo->RegisteredHandle = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootConfig.h b/Core/NetworkPkg/HttpBootDxe/HttpBootConfig.h
new file mode 100644
index 0000000000..e610fe8cd9
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootConfig.h
@@ -0,0 +1,79 @@
+/** @file
+ The header file of functions for configuring or getting the parameters
+ relating to HTTP Boot.
+
+Copyright (c) 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 _HTTP_BOOT_CONFIG_H_
+#define _HTTP_BOOT_CONFIG_H_
+
+
+#include "HttpBootConfigNVDataStruc.h"
+
+typedef struct _HTTP_BOOT_FORM_CALLBACK_INFO HTTP_BOOT_FORM_CALLBACK_INFO;
+
+extern UINT8 HttpBootDxeStrings[];
+extern UINT8 HttpBootConfigVfrBin[];
+
+#pragma pack()
+
+#define HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('H', 'B', 'f', 'c')
+
+#define HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(Callback) \
+ CR ( \
+ Callback, \
+ HTTP_BOOT_FORM_CALLBACK_INFO, \
+ ConfigAccess, \
+ HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE \
+ )
+
+struct _HTTP_BOOT_FORM_CALLBACK_INFO {
+ UINT32 Signature;
+ BOOLEAN Initilized;
+ EFI_HANDLE ChildHandle;
+ EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath;
+ EFI_HII_HANDLE RegisteredHandle;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess;
+ HTTP_BOOT_CONFIG_IFR_NVDATA HttpBootNvData;
+};
+
+/**
+ Initialize the configuration form.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+HttpBootConfigFormInit (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ Unload the configuration form, this includes: delete all the configuration
+ entries, uninstall the form callback protocol, and free the resources used.
+ The form will only be unload completely when both IP4 and IP6 stack are stopped.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is unloaded.
+ @retval Others Failed to unload the form.
+
+**/
+EFI_STATUS
+HttpBootConfigFormUnload (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+#endif
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h b/Core/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h
new file mode 100644
index 0000000000..682306ef1f
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h
@@ -0,0 +1,50 @@
+/** @file
+ Define NVData structures used by the HTTP Boot configuration component.
+
+Copyright (c) 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 _HTTP_BOOT_NVDATA_STRUC_H_
+#define _HTTP_BOOT_NVDATA_STRUC_H_
+
+#include <Guid/HttpBootConfigHii.h>
+
+#define HTTP_BOOT_IP_VERSION_4 0
+#define HTTP_BOOT_IP_VERSION_6 1
+
+//
+// Macros used for an IPv4 or an IPv6 address.
+//
+#define URI_STR_MIN_SIZE 0
+#define URI_STR_MAX_SIZE 255
+
+#define DESCRIPTION_STR_MIN_SIZE 6
+#define DESCRIPTION_STR_MAX_SIZE 75
+
+#define CONFIGURATION_VARSTORE_ID 0x1234
+
+#define FORMID_MAIN_FORM 1
+
+#define KEY_INITIATOR_URI 0x101
+
+#define HTTP_BOOT_DEFAULT_DESCRIPTION_STR L"UEFI HTTP"
+
+#pragma pack(1)
+typedef struct _HTTP_BOOT_CONFIG_IFR_NVDATA {
+ UINT8 IpVersion;
+ UINT8 Padding;
+ CHAR16 Description[DESCRIPTION_STR_MAX_SIZE];
+ CHAR16 Uri[URI_STR_MAX_SIZE];
+} HTTP_BOOT_CONFIG_IFR_NVDATA;
+#pragma pack()
+
+
+#endif
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni b/Core/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni
new file mode 100644
index 0000000000..6cddafef83
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni
@@ -0,0 +1,27 @@
+/** @file
+ String definitions for HTTP Boot configuration.
+
+ Copyright (c) 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.
+
+**/
+
+#langdef en-US "English"
+
+#string STR_HTTP_BOOT_CONFIG_FORM_TITLE #language en-US "HTTP Boot Configuration"
+#string STR_HTTP_BOOT_CONFIG_FORM_HELP #language en-US "Configure HTTP Boot parameters."
+#string STR_HTTP_BOOT_IP_VERSION_PROMPT #language en-US "Internet Protocol"
+#string STR_HTTP_BOOT_IP_VERSION_HELP #language en-US "Select the version of Internet Protocol."
+#string STR_HTTP_BOOT_IP_VERSION_4 #language en-US "IP4"
+#string STR_HTTP_BOOT_IP_VERSION_6 #language en-US "IP6"
+#string STR_BOOT_URI_PROMPT #language en-US "Boot URI"
+#string STR_BOOT_URI_HELP #language en-US "A new Boot Option will be created according to this Boot URI."
+#string STR_BOOT_DESCRIPTION_PROMPT #language en-US "Input the description"
+#string STR_NULL_STRING #language en-US ""
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr b/Core/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr
new file mode 100644
index 0000000000..7e8ddae33d
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr
@@ -0,0 +1,55 @@
+/** @file
+ VFR file used by the HTTP Boot configuration component.
+
+ Copyright (c) 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 "HttpBootConfigNVDataStruc.h"
+
+
+formset
+ guid = HTTP_BOOT_CONFIG_GUID,
+ title = STRING_TOKEN(STR_HTTP_BOOT_CONFIG_FORM_TITLE),
+ help = STRING_TOKEN(STR_HTTP_BOOT_CONFIG_FORM_HELP),
+
+ varstore HTTP_BOOT_CONFIG_IFR_NVDATA,
+ name = HTTP_BOOT_CONFIG_IFR_NVDATA,
+ guid = HTTP_BOOT_CONFIG_GUID;
+
+ form formid = FORMID_MAIN_FORM,
+ title = STRING_TOKEN(STR_HTTP_BOOT_CONFIG_FORM_TITLE);
+
+ string varid = HTTP_BOOT_CONFIG_IFR_NVDATA.Description,
+ prompt = STRING_TOKEN(STR_BOOT_DESCRIPTION_PROMPT),
+ help = STRING_TOKEN(STR_NULL_STRING),
+ minsize = DESCRIPTION_STR_MIN_SIZE,
+ maxsize = DESCRIPTION_STR_MAX_SIZE,
+ endstring;
+
+ oneof varid = HTTP_BOOT_CONFIG_IFR_NVDATA.IpVersion,
+ prompt = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_PROMPT),
+ help = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_HELP),
+ option text = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_4), value = HTTP_BOOT_IP_VERSION_4, flags = DEFAULT;
+ option text = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_6), value = HTTP_BOOT_IP_VERSION_6, flags = 0;
+ endoneof;
+
+ string varid = HTTP_BOOT_CONFIG_IFR_NVDATA.Uri,
+ prompt = STRING_TOKEN(STR_BOOT_URI_PROMPT),
+ help = STRING_TOKEN(STR_BOOT_URI_HELP),
+ flags = INTERACTIVE,
+ key = KEY_INITIATOR_URI,
+ minsize = URI_STR_MIN_SIZE,
+ maxsize = URI_STR_MAX_SIZE,
+ endstring;
+ endform;
+
+endformset;
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c
new file mode 100644
index 0000000000..fcea916225
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c
@@ -0,0 +1,893 @@
+/** @file
+ Functions implementation related with DHCPv4 for HTTP boot driver.
+
+Copyright (c) 2015 - 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 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] = {
+ DHCP4_TAG_BOOTFILE_LEN,
+ DHCP4_TAG_OVERLOAD,
+ DHCP4_TAG_MSG_TYPE,
+ DHCP4_TAG_SERVER_ID,
+ DHCP4_TAG_VENDOR_CLASS_ID,
+ DHCP4_TAG_BOOTFILE,
+ 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 = DHCP4_TAG_PARA_LIST;
+ OptList[Index]->Length = 27;
+ OptEnt.Para = (HTTP_BOOT_DHCP4_OPTION_PARA *) OptList[Index]->Data;
+ OptEnt.Para->ParaList[0] = DHCP4_TAG_NETMASK;
+ OptEnt.Para->ParaList[1] = DHCP4_TAG_TIME_OFFSET;
+ OptEnt.Para->ParaList[2] = DHCP4_TAG_ROUTER;
+ OptEnt.Para->ParaList[3] = DHCP4_TAG_TIME_SERVER;
+ OptEnt.Para->ParaList[4] = DHCP4_TAG_NAME_SERVER;
+ OptEnt.Para->ParaList[5] = DHCP4_TAG_DNS_SERVER;
+ OptEnt.Para->ParaList[6] = DHCP4_TAG_HOSTNAME;
+ OptEnt.Para->ParaList[7] = DHCP4_TAG_BOOTFILE_LEN;
+ OptEnt.Para->ParaList[8] = DHCP4_TAG_DOMAINNAME;
+ OptEnt.Para->ParaList[9] = DHCP4_TAG_ROOTPATH;
+ OptEnt.Para->ParaList[10] = DHCP4_TAG_EXTEND_PATH;
+ OptEnt.Para->ParaList[11] = DHCP4_TAG_EMTU;
+ OptEnt.Para->ParaList[12] = DHCP4_TAG_TTL;
+ OptEnt.Para->ParaList[13] = DHCP4_TAG_BROADCAST;
+ OptEnt.Para->ParaList[14] = DHCP4_TAG_NIS_DOMAIN;
+ OptEnt.Para->ParaList[15] = DHCP4_TAG_NIS_SERVER;
+ OptEnt.Para->ParaList[16] = DHCP4_TAG_NTP_SERVER;
+ OptEnt.Para->ParaList[17] = DHCP4_TAG_VENDOR;
+ OptEnt.Para->ParaList[18] = DHCP4_TAG_REQUEST_IP;
+ OptEnt.Para->ParaList[19] = DHCP4_TAG_LEASE;
+ OptEnt.Para->ParaList[20] = DHCP4_TAG_SERVER_ID;
+ OptEnt.Para->ParaList[21] = DHCP4_TAG_T1;
+ OptEnt.Para->ParaList[22] = DHCP4_TAG_T2;
+ OptEnt.Para->ParaList[23] = DHCP4_TAG_VENDOR_CLASS_ID;
+ OptEnt.Para->ParaList[25] = DHCP4_TAG_BOOTFILE;
+ OptEnt.Para->ParaList[26] = DHCP4_TAG_UUID;
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append UUID/Guid-based client identifier option
+ //
+ OptList[Index]->OpCode = 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 = 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 = 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 = DHCP4_TAG_VENDOR_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 != DHCP4_TAG_EOP) {
+
+ if (Option->OpCode == OptTag) {
+ //
+ // Found the required option.
+ //
+ return Option;
+ }
+
+ //
+ // Skip the current option to the next.
+ //
+ if (Option->OpCode == 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.
+
+ @retval EFI_SUCCESS Packet is copied.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+HttpBootCacheDhcp4Packet (
+ IN EFI_DHCP4_PACKET *Dst,
+ IN EFI_DHCP4_PACKET *Src
+ )
+{
+ if (Dst->Size < Src->Length) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
+ Dst->Length = Src->Length;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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;
+ BOOLEAN FileFieldOverloaded;
+
+ IsDnsOffer = FALSE;
+ IpExpressedUri = FALSE;
+ IsProxyOffer = FALSE;
+ IsHttpOffer = FALSE;
+ FileFieldOverloaded = 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) {
+ FileFieldOverloaded = TRUE;
+ 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 (!FileFieldOverloaded && 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) {
+ if (IsProxyOffer) {
+ OfferType = HttpOfferTypeProxyIpUri;
+ } else {
+ OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : 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.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval Others Operation failed.
+**/
+EFI_STATUS
+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;
+ EFI_STATUS Status;
+
+ 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.
+ //
+ Status = HttpBootCacheDhcp4Packet (Offer, RcvdOffer);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Validate the DHCPv4 packet, and parse the options and offer type.
+ //
+ if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // 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++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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;
+
+ if (Private->FilePathUri != NULL) {
+ //
+ // We are in home environment, the URI is already specified.
+ // Just need to choose a DHCP offer.
+ // The offer with DNS server address takes priority here.
+ //
+ if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpIpUriDns] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1;
+ }
+
+ } else {
+ //
+ // We are in corporate environment.
+ //
+ // Priority1: HttpOfferTypeDhcpIpUri or HttpOfferTypeDhcpIpUriDns
+ // 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[HttpOfferTypeDhcpIpUriDns] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][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),
+ 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 (Packet->Length > HTTP_BOOT_DHCP4_PACKET_MAX_SIZE) {
+ //
+ // Ignore the incoming packets which exceed the maximum length.
+ //
+ break;
+ }
+ if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {
+ //
+ // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record
+ // the OfferIndex and OfferCount.
+ // If error happens, just ignore this packet and continue to wait more offer.
+ //
+ 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..0b2cafbf50
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h
@@ -0,0 +1,256 @@
+/** @file
+ Functions declaration related with DHCPv4 for HTTP boot driver.
+
+Copyright (c) 2015 - 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 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
+
+#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 HTTP_CLIENT_ARCH_IA32
+#elif defined (MDE_CPU_X64)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_X64
+#elif defined (MDE_CPU_ARM)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_ARM
+#elif defined (MDE_CPU_AARCH64)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_AARCH64
+#elif defined (MDE_CPU_EBC)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_EBC
+#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>
+ //
+ HttpOfferTypeDhcpIpUri,
+ //
+ // <IP address, IP expressed URI, Name-server>
+ //
+ HttpOfferTypeDhcpIpUriDns,
+ //
+ // <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;
+
+#define HTTP_CACHED_DHCP4_PACKET_MAX_SIZE (OFFSET_OF (EFI_DHCP4_PACKET, Dhcp4) + HTTP_BOOT_DHCP4_PACKET_MAX_SIZE)
+
+typedef union {
+ EFI_DHCP4_PACKET Offer;
+ EFI_DHCP4_PACKET Ack;
+ UINT8 Buffer[HTTP_CACHED_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..f2b81957b7
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c
@@ -0,0 +1,1016 @@
+/** @file
+ Functions implementation related with DHCPv6 for HTTP boot driver.
+
+Copyright (c) 2015 - 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 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 (DHCP6_OPT_ORO);
+ OptList[Index]->OpLen = HTONS (8);
+ OptEnt.Oro = (HTTP_BOOT_DHCP6_OPTION_ORO *) OptList[Index]->Data;
+ OptEnt.Oro->OpCode[0] = HTONS(DHCP6_OPT_BOOT_FILE_URL);
+ OptEnt.Oro->OpCode[1] = HTONS(DHCP6_OPT_BOOT_FILE_PARAM);
+ OptEnt.Oro->OpCode[2] = HTONS(DHCP6_OPT_DNS_SERVERS);
+ OptEnt.Oro->OpCode[3] = HTONS(DHCP6_OPT_VENDOR_CLASS);
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client network device interface option
+ //
+ OptList[Index]->OpCode = HTONS (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 (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 (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) == DHCP6_OPT_IA_NA) {
+ Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option;
+ } else if (NTOHS (Option->OpCode) == 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) == DHCP6_OPT_BOOT_FILE_PARAM) {
+ Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
+ } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) {
+ Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option;
+ } else if (NTOHS (Option->OpCode) == 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),
+ 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) >= 16 &&
+ CompareMem ((Option->Data + 6), 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) {
+ if (IsProxyOffer) {
+ OfferType = HttpOfferTypeProxyIpUri;
+ } else {
+ OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : 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.
+
+ @retval EFI_SUCCESS Packet is copied.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+HttpBootCacheDhcp6Packet (
+ IN EFI_DHCP6_PACKET *Dst,
+ IN EFI_DHCP6_PACKET *Src
+ )
+{
+ if (Dst->Size < Src->Length) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
+ Dst->Length = Src->Length;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval Others Operation failed.
+
+**/
+EFI_STATUS
+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;
+ EFI_STATUS Status;
+
+ Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
+ Offer = &Cache6->Packet.Offer;
+
+ //
+ // Cache the content of DHCPv6 packet firstly.
+ //
+ Status = HttpBootCacheDhcp6Packet(Offer, RcvdOffer);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Validate the DHCPv6 packet, and parse the options and offer type.
+ //
+ if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // 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++;
+
+ 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.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources.
+
+**/
+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;
+
+ ASSERT (Packet != NULL);
+
+ Private = (HTTP_BOOT_PRIVATE_DATA *) Context;
+ Status = EFI_SUCCESS;
+ switch (Dhcp6Event) {
+
+ case Dhcp6RcvdAdvertise:
+ Status = EFI_NOT_READY;
+ if (Packet->Length > HTTP_BOOT_DHCP6_PACKET_MAX_SIZE) {
+ //
+ // Ignore the incoming packets which exceed the maximum length.
+ //
+ break;
+ }
+ if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {
+ //
+ // Cache the dhcp offers to OfferBuffer[] for select later, and record
+ // the OfferIndex and OfferCount.
+ // If error happens, just ignore this packet and continue to wait more offer.
+ //
+ 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);
+ if (*NewPacket == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ 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));
+ Dhcp6->Configure (Dhcp6, &Config);
+ if (Mode.ClientId != NULL) {
+ FreePool (Mode.ClientId);
+ }
+ if (Mode.Ia != NULL) {
+ FreePool (Mode.Ia);
+ }
+ }
+
+ return Status;
+
+}
+
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h
new file mode 100644
index 0000000000..9f2989831e
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h
@@ -0,0 +1,175 @@
+/** @file
+ Functions declaration related with DHCPv6 for HTTP boot driver.
+
+Copyright (c) 2015 - 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 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_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[11];
+ 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;
+
+#define HTTP_CACHED_DHCP6_PACKET_MAX_SIZE (OFFSET_OF (EFI_DHCP6_PACKET, Dhcp6) + HTTP_BOOT_DHCP6_PACKET_MAX_SIZE)
+
+typedef union {
+ EFI_DHCP6_PACKET Offer;
+ EFI_DHCP6_PACKET Ack;
+ UINT8 Buffer[HTTP_CACHED_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..642e0fe31e
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.c
@@ -0,0 +1,1196 @@
+/** @file
+ Driver Binding functions implementation for UEFI HTTP boot.
+
+Copyright (c) 2015 - 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 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;
+ 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;
+ }
+
+ //
+ // Initialize the HII configuration form.
+ //
+ Status = HttpBootConfigFormInit (Private);
+ 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->ImageHandle = This->DriverBindingHandle;
+ 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);
+ HttpBootConfigFormUnload (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);
+
+ //
+ // Unload the config form.
+ //
+ HttpBootConfigFormUnload (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;
+ 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;
+ }
+
+ //
+ // Initialize the HII configuration form.
+ //
+ Status = HttpBootConfigFormInit (Private);
+ 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->ImageHandle = This->DriverBindingHandle;
+ 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);
+ HttpBootConfigFormUnload (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);
+
+ //
+ // Unload the config form.
+ //
+ HttpBootConfigFormUnload (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..a1e6792514
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.h
@@ -0,0 +1,508 @@
+/** @file
+ UEFI HTTP boot driver's private data structure and interfaces declaration.
+
+Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 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 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>
+
+#include <IndustryStandard/Http11.h>
+#include <IndustryStandard/Dhcp.h>
+
+//
+// Libraries
+//
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiHiiServicesLib.h>
+#include <Library/UefiRuntimeServicesTableLib.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>
+#include <Library/HiiLib.h>
+#include <Library/PrintLib.h>
+#include <Library/DpcLib.h>
+
+//
+// UEFI Driver Model Protocols
+//
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/ComponentName.h>
+
+//
+// Consumed Protocols
+//
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/HiiConfigAccess.h>
+#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>
+#include <Protocol/RamDisk.h>
+//
+// Produced Protocols
+//
+#include <Protocol/LoadFile.h>
+
+//
+// Consumed Guids
+//
+#include <Guid/HttpBootConfigHii.h>
+
+//
+// Driver Version
+//
+#define HTTP_BOOT_DXE_VERSION 0xa
+
+//
+// Standard Media Types defined in
+// http://www.iana.org/assignments/media-types
+//
+#define HTTP_CONTENT_TYPE_APP_EFI "application/efi"
+#define HTTP_CONTENT_TYPE_APP_IMG "application/vnd.efi-img"
+#define HTTP_CONTENT_TYPE_APP_ISO "application/vnd.efi-iso"
+
+//
+// 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;
+
+typedef enum {
+ ImageTypeEfi,
+ ImageTypeVirtualCd,
+ ImageTypeVirtualDisk,
+ ImageTypeMax
+} HTTP_BOOT_IMAGE_TYPE;
+
+//
+// Include files with internal function prototypes
+//
+#include "HttpBootComponentName.h"
+#include "HttpBootDhcp4.h"
+#include "HttpBootDhcp6.h"
+#include "HttpBootImpl.h"
+#include "HttpBootSupport.h"
+#include "HttpBootClient.h"
+#include "HttpBootConfig.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_HANDLE ImageHandle;
+ EFI_LOAD_FILE_PROTOCOL LoadFile;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ HTTP_BOOT_PRIVATE_DATA *Private;
+};
+
+#define HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_INFO(Callback) \
+ CR ( \
+ Callback, \
+ HTTP_BOOT_PRIVATE_DATA, \
+ CallbackInfo, \
+ HTTP_BOOT_PRIVATE_DATA_SIGNATURE \
+ )
+
+struct _HTTP_BOOT_PRIVATE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+
+ 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;
+
+ //
+ // HII callback info block
+ //
+ HTTP_BOOT_FORM_CALLBACK_INFO CallbackInfo;
+
+ //
+ // Mode data
+ //
+ BOOLEAN UsingIpv6;
+ BOOLEAN Started;
+ EFI_IP_ADDRESS StationIp;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS GatewayIp;
+ EFI_IP_ADDRESS ServerIp;
+ UINT16 Port;
+
+ //
+ // The URI string attempt to download through HTTP, may point to
+ // the memory in cached DHCP offer, or to the memory in FilePathUri.
+ //
+ CHAR8 *BootFileUri;
+ VOID *BootFileUriParser;
+ UINTN BootFileSize;
+ BOOLEAN NoGateway;
+ HTTP_BOOT_IMAGE_TYPE ImageType;
+
+ //
+ // URI string extracted from the input FilePath parameter.
+ //
+ CHAR8 *FilePathUri;
+ VOID *FilePathUriParser;
+
+ //
+ // 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..ec983ba7ad
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.inf
@@ -0,0 +1,102 @@
+## @file
+# This modules produce the Load File Protocol for UEFI HTTP boot.
+#
+# Copyright (c) 2015 - 2017, 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
+ NetworkPkg/NetworkPkg.dec
+
+[Sources]
+ HttpBootConfigNVDataStruc.h
+ HttpBootDxe.h
+ HttpBootDxe.c
+ HttpBootConfig.h
+ HttpBootConfig.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
+ HttpBootConfigVfr.vfr
+ HttpBootConfigStrings.uni
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseLib
+ UefiLib
+ DevicePathLib
+ DebugLib
+ NetLib
+ HttpLib
+ HiiLib
+ PrintLib
+ DpcLib
+ UefiHiiServicesLib
+ UefiBootManagerLib
+
+[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
+ gEfiRamDiskProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiHiiConfigAccessProtocolGuid ## BY_START
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID # HiiIsConfigHdrMatch mHttpBootConfigStorageName
+ ## SOMETIMES_PRODUCES ## GUID # HiiConstructConfigHdr mHttpBootConfigStorageName
+ ## SOMETIMES_PRODUCES ## GUID # HiiGetBrowserData mHttpBootConfigStorageName
+ ## SOMETIMES_CONSUMES ## HII
+ gHttpBootConfigGuid
+ gEfiVirtualCdGuid ## SOMETIMES_CONSUMES ## GUID
+ gEfiVirtualDiskGuid ## SOMETIMES_CONSUMES ## GUID
+
+[Pcd]
+ gEfiNetworkPkgTokenSpaceGuid.PcdAllowHttpConnections ## 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..370f115347
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxe.uni
@@ -0,0 +1,24 @@
+// /** @file
+// UEFI HTTP boot service.
+//
+// This driver provides EFI Load File Protocol which is used to download
+// the boot image from HTTP server. It could work with an IPv4 or IPv6 stack.
+//
+//
+// 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI HTTP boot service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This driver provides EFI Load File Protocol which is used to download the boot image from HTTP server. It could work with an IPv4 or IPv6 stack."
+
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni b/Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni
new file mode 100644
index 0000000000..d6a0f0cef6
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// HttpBootDxe Localized Strings and Content
+//
+// 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UEFI HTTP BOOT DXE"
+
+
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c
new file mode 100644
index 0000000000..cf6de80a17
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootImpl.c
@@ -0,0 +1,522 @@
+/** @file
+ The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.
+
+Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 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 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.
+
+ If the driver has already been started but not satisfy the requirement (IP stack and
+ specified boot file path), this function will stop the driver and start it again.
+
+ @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.
+ @param[in] FilePath The device specific path of the file to load.
+
+ @retval EFI_SUCCESS HTTP boot was successfully enabled.
+ @retval EFI_INVALID_PARAMETER Private is NULL or FilePath is NULL.
+ @retval EFI_INVALID_PARAMETER The FilePath doesn't contain a valid URI device path node.
+ @retval EFI_ALREADY_STARTED The driver is already in started state.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources.
+
+**/
+EFI_STATUS
+HttpBootStart (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN BOOLEAN UsingIpv6,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ UINTN Index;
+ EFI_STATUS Status;
+ CHAR8 *Uri;
+
+
+ if (Private == NULL || FilePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check the URI in the input FilePath, in order to see whether it is
+ // required to boot from a new specified boot file.
+ //
+ Status = HttpBootParseFilePath (FilePath, &Uri);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether we need to stop and restart the HTTP boot driver.
+ //
+ if (Private->Started) {
+ //
+ // Restart is needed in 2 cases:
+ // 1. Http boot driver has already been started but not on the required IP stack.
+ // 2. The specified boot file URI in FilePath is different with the one we have
+ // recorded before.
+ //
+ if ((UsingIpv6 != Private->UsingIpv6) ||
+ ((Uri != NULL) && (AsciiStrCmp (Private->BootFileUri, Uri) != 0))) {
+ //
+ // Restart is required, first stop then continue this start function.
+ //
+ Status = HttpBootStop (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ //
+ // Restart is not required.
+ //
+ if (Uri != NULL) {
+ FreePool (Uri);
+ }
+ 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 {
+ if (Uri != NULL) {
+ FreePool (Uri);
+ }
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Record the specified URI and prepare the URI parser if needed.
+ //
+ Private->FilePathUri = Uri;
+ if (Private->FilePathUri != NULL) {
+ Status = HttpParseUrl (
+ Private->FilePathUri,
+ (UINT32) AsciiStrLen (Private->FilePathUri),
+ FALSE,
+ &Private->FilePathUriParser
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Private->FilePathUri);
+ return Status;
+ }
+ }
+
+ //
+ // 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_CACHED_DHCP4_PACKET_MAX_SIZE;
+ }
+ } else {
+ for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
+ Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = HTTP_CACHED_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.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @retval EFI_SUCCESS Boot file was loaded successfully.
+ @retval EFI_INVALID_PARAMETER Private is NULL, or ImageType is NULL, or BufferSize is NULL.
+ @retval EFI_INVALID_PARAMETER *BufferSize is not zero, and Buffer 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
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ )
+{
+ EFI_STATUS Status;
+
+ if (Private == NULL || ImageType == NULL || BufferSize == NULL ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*BufferSize != 0 && Buffer == 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,
+ &Private->ImageType
+ );
+ 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,
+ &Private->ImageType
+ );
+ if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
+ return Status;
+ }
+ }
+ }
+
+ if (*BufferSize < Private->BootFileSize) {
+ *BufferSize = Private->BootFileSize;
+ *ImageType = Private->ImageType;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Load the boot file into Buffer
+ //
+ return HttpBootGetBootFile (
+ Private,
+ FALSE,
+ BufferSize,
+ Buffer,
+ ImageType
+ );
+}
+
+/**
+ 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);
+ }
+ }
+ }
+
+ if (Private->FilePathUri!= NULL) {
+ FreePool (Private->FilePathUri);
+ HttpUrlFreeParser (Private->FilePathUriParser);
+ Private->FilePathUri = NULL;
+ Private->FilePathUriParser = NULL;
+ }
+
+ ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
+ Private->OfferNum = 0;
+ ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
+ ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
+
+ HttpBootFreeCacheList (Private);
+
+ 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;
+ HTTP_BOOT_IMAGE_TYPE ImageType;
+
+ if (This == NULL || BufferSize == NULL || FilePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Only support BootPolicy
+ //
+ if (!BootPolicy) {
+ return EFI_UNSUPPORTED;
+ }
+
+ VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This);
+ Private = VirtualNic->Private;
+
+ //
+ // 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.
+ //
+ UsingIpv6 = FALSE;
+ if (VirtualNic == Private->Ip6Nic) {
+ UsingIpv6 = TRUE;
+ }
+
+ //
+ // Initialize HTTP boot.
+ //
+ Status = HttpBootStart (Private, UsingIpv6, FilePath);
+ if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) {
+ return Status;
+ }
+
+ //
+ // Load the boot file.
+ //
+ ImageType = ImageTypeMax;
+ Status = HttpBootLoadFile (Private, BufferSize, Buffer, &ImageType);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_BUFFER_TOO_SMALL && (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk)) {
+ Status = EFI_WARN_FILE_SYSTEM;
+ } else if (Status != EFI_BUFFER_TOO_SMALL) {
+ HttpBootStop (Private);
+ }
+ return Status;
+ }
+
+ //
+ // Register the RAM Disk to the system if needed.
+ //
+ if (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk) {
+ Status = HttpBootRegisterRamDisk (Private, *BufferSize, Buffer, ImageType);
+ if (!EFI_ERROR (Status)) {
+ Status = EFI_WARN_FILE_SYSTEM;
+ }
+ }
+
+ //
+ // Stop the HTTP Boot service after the boot image is downloaded.
+ //
+ HttpBootStop (Private);
+ 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..56c4c15836
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.c
@@ -0,0 +1,1288 @@
+/** @file
+ Support functions implementation for UEFI HTTP boot driver.
+
+Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 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 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;
+
+ for (; Length > 0; Length--) {
+ Remainder = Number % 10;
+ Number /= 10;
+ Buffer[Length - 1] = (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->Ip6Nic->ImageHandle,
+ &gEfiDns6ServiceBindingProtocolGuid,
+ &Dns6Handle
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dns6Handle,
+ &gEfiDns6ProtocolGuid,
+ (VOID **) &Dns6,
+ Private->Ip6Nic->ImageHandle,
+ 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->Ip6Nic->ImageHandle,
+ Private->Controller
+ );
+ }
+
+ if (Dns6Handle != NULL) {
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ Private->Ip6Nic->ImageHandle,
+ &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);
+ }
+}
+
+/**
+ 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 = HttpFindHeader (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;
+}
+
+/**
+ Notify the callback function when an event is triggered.
+
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotifyDpc (
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, HttpIoNotifyDpc, 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
+ )
+{
+ 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,
+ HttpIoNotify,
+ &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,
+ HttpIoNotify,
+ &HttpIo->IsRxDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ HttpIo->RspToken.Event = Event;
+ HttpIo->RspToken.Message = &HttpIo->RspMessage;
+
+ //
+ // Create TimeoutEvent for response
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ HttpIo->TimeoutEvent = Event;
+
+ 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);
+ }
+
+ Event = HttpIo->TimeoutEvent;
+ 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;
+
+ if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Start the timer, and wait Timeout seconds to receive the header packet.
+ //
+ Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HTTP_BOOT_RESPONSE_TIMEOUT * TICKS_PER_MS);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // 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)) {
+ gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
+ return Status;
+ }
+
+ //
+ // Poll the network until receive finish.
+ //
+ while (!HttpIo->IsRxDone && ((HttpIo->TimeoutEvent == NULL) || EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent)))) {
+ Http->Poll (Http);
+ }
+
+ gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
+
+ if (!HttpIo->IsRxDone) {
+ //
+ // Timeout occurs, cancel the response token.
+ //
+ Http->Cancel (Http, &HttpIo->RspToken);
+
+ Status = EFI_TIMEOUT;
+
+ return Status;
+ } else {
+ HttpIo->IsRxDone = FALSE;
+ }
+
+ //
+ // Store the received data into the wrapper.
+ //
+ ResponseData->Status = HttpIo->RspToken.Status;
+ ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
+ ResponseData->Headers = HttpIo->RspToken.Message->Headers;
+ ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;
+
+ return Status;
+}
+
+/**
+ This function checks the HTTP(S) URI scheme.
+
+ @param[in] Uri The pointer to the URI string.
+
+ @retval EFI_SUCCESS The URI scheme is valid.
+ @retval EFI_INVALID_PARAMETER The URI scheme is not HTTP or HTTPS.
+ @retval EFI_ACCESS_DENIED HTTP is disabled and the URI is HTTP.
+
+**/
+EFI_STATUS
+HttpBootCheckUriScheme (
+ IN CHAR8 *Uri
+ )
+{
+ UINTN Index;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // Convert the scheme to all lower case.
+ //
+ for (Index = 0; Index < AsciiStrLen (Uri); Index++) {
+ if (Uri[Index] == ':') {
+ break;
+ }
+ if (Uri[Index] >= 'A' && Uri[Index] <= 'Z') {
+ Uri[Index] -= (CHAR8)('A' - 'a');
+ }
+ }
+
+ //
+ // Return EFI_INVALID_PARAMETER if the URI is not HTTP or HTTPS.
+ //
+ if ((AsciiStrnCmp (Uri, "http://", 7) != 0) && (AsciiStrnCmp (Uri, "https://", 8) != 0)) {
+ DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: Invalid Uri.\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // HTTP is disabled, return EFI_ACCESS_DENIED if the URI is HTTP.
+ //
+ if (!PcdGetBool (PcdAllowHttpConnections) && (AsciiStrnCmp (Uri, "http://", 7) == 0)) {
+ DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: HTTP is disabled.\n"));
+ return EFI_ACCESS_DENIED;
+ }
+
+ return Status;
+}
+
+/**
+ Get the URI address string from the input device path.
+
+ Caller need to free the buffer in the UriAddress pointer.
+
+ @param[in] FilePath Pointer to the device path which contains a URI device path node.
+ @param[out] UriAddress The URI address string extract from the device path.
+
+ @retval EFI_SUCCESS The URI string is returned.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+HttpBootParseFilePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT CHAR8 **UriAddress
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ URI_DEVICE_PATH *UriDevicePath;
+ CHAR8 *Uri;
+ UINTN UriStrLength;
+
+ if (FilePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *UriAddress = NULL;
+
+ //
+ // Extract the URI address from the FilePath
+ //
+ TempDevicePath = FilePath;
+ while (!IsDevicePathEnd (TempDevicePath)) {
+ if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (TempDevicePath) == MSG_URI_DP)) {
+ UriDevicePath = (URI_DEVICE_PATH*) TempDevicePath;
+ //
+ // UEFI Spec doesn't require the URI to be a NULL-terminated string
+ // So we allocate a new buffer and always append a '\0' to it.
+ //
+ UriStrLength = DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL);
+ if (UriStrLength == 0) {
+ //
+ // return a NULL UriAddress if it's a empty URI device path node.
+ //
+ break;
+ }
+ Uri = AllocatePool (UriStrLength + 1);
+ if (Uri == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (Uri, UriDevicePath->Uri, DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL));
+ Uri[DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)] = '\0';
+
+ *UriAddress = Uri;
+ }
+ TempDevicePath = NextDevicePathNode (TempDevicePath);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function returns the image type according to server replied HTTP message
+ and also the image's URI info.
+
+ @param[in] Uri The pointer to the image's URI string.
+ @param[in] UriParser URI Parse result returned by NetHttpParseUrl().
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @retval EFI_SUCCESS The image type is returned in ImageType.
+ @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL.
+ @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL.
+ @retval EFI_NOT_FOUND Failed to identify the image type.
+ @retval Others Unexpect error happened.
+
+**/
+EFI_STATUS
+HttpBootCheckImageType (
+ IN CHAR8 *Uri,
+ IN VOID *UriParser,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_HEADER *Header;
+ CHAR8 *FilePath;
+ CHAR8 *FilePost;
+
+ if (Uri == NULL || UriParser == NULL || ImageType == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (HeaderCount != 0 && Headers == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Determine the image type by the HTTP Content-Type header field first.
+ // "application/efi" -> EFI Image
+ // "application/vnd.efi-iso" -> CD/DVD Image
+ // "application/vnd.efi-img" -> Virtual Disk Image
+ //
+ Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_TYPE);
+ if (Header != NULL) {
+ if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) == 0) {
+ *ImageType = ImageTypeEfi;
+ return EFI_SUCCESS;
+ } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_ISO) == 0) {
+ *ImageType = ImageTypeVirtualCd;
+ return EFI_SUCCESS;
+ } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_IMG) == 0) {
+ *ImageType = ImageTypeVirtualDisk;
+ return EFI_SUCCESS;
+ }
+ }
+
+ //
+ // Determine the image type by file extension:
+ // *.efi -> EFI Image
+ // *.iso -> CD/DVD Image
+ // *.img -> Virtual Disk Image
+ //
+ Status = HttpUrlGetPath (
+ Uri,
+ UriParser,
+ &FilePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ FilePost = FilePath + AsciiStrLen (FilePath) - 4;
+ if (AsciiStrCmp (FilePost, ".efi") == 0) {
+ *ImageType = ImageTypeEfi;
+ } else if (AsciiStrCmp (FilePost, ".iso") == 0) {
+ *ImageType = ImageTypeVirtualCd;
+ } else if (AsciiStrCmp (FilePost, ".img") == 0) {
+ *ImageType = ImageTypeVirtualDisk;
+ } else {
+ *ImageType = ImageTypeMax;
+ }
+
+ FreePool (FilePath);
+
+ return (*ImageType < ImageTypeMax) ? EFI_SUCCESS : EFI_NOT_FOUND;
+}
+
+/**
+ This function register the RAM disk info to the system.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] BufferSize The size of Buffer in bytes.
+ @param[in] Buffer The base address of the RAM disk.
+ @param[in] ImageType The image type of the file in Buffer.
+
+ @retval EFI_SUCCESS The RAM disk has been registered.
+ @retval EFI_NOT_FOUND No RAM disk protocol instances were found.
+ @retval EFI_UNSUPPORTED The ImageType is not supported.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootRegisterRamDisk (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN UINTN BufferSize,
+ IN VOID *Buffer,
+ IN HTTP_BOOT_IMAGE_TYPE ImageType
+ )
+{
+ EFI_RAM_DISK_PROTOCOL *RamDisk;
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_GUID *RamDiskType;
+
+ ASSERT (Private != NULL);
+ ASSERT (Buffer != NULL);
+ ASSERT (BufferSize != 0);
+
+ Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID**) &RamDisk);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "HTTP Boot: Couldn't find the RAM Disk protocol - %r\n", Status));
+ return Status;
+ }
+
+ if (ImageType == ImageTypeVirtualCd) {
+ RamDiskType = &gEfiVirtualCdGuid;
+ } else if (ImageType == ImageTypeVirtualDisk) {
+ RamDiskType = &gEfiVirtualDiskGuid;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = RamDisk->Register (
+ (UINTN)Buffer,
+ (UINT64)BufferSize,
+ RamDiskType,
+ Private->UsingIpv6 ? Private->Ip6Nic->DevicePath : Private->Ip4Nic->DevicePath,
+ &DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "HTTP Boot: Failed to register RAM Disk - %r\n", Status));
+ }
+
+ return Status;
+}
+
diff --git a/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h b/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h
new file mode 100644
index 0000000000..65302d2be2
--- /dev/null
+++ b/Core/NetworkPkg/HttpBootDxe/HttpBootSupport.h
@@ -0,0 +1,414 @@
+/** @file
+ Support functions declaration for UEFI HTTP boot driver.
+
+Copyright (c) 2015 - 2017, 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
+ );
+
+/**
+ 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
+ );
+
+//
+// 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;
+
+ EFI_EVENT TimeoutEvent;
+} 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;
+ EFI_STATUS Status;
+} 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
+ );
+
+/**
+ This function checks the HTTP(S) URI scheme.
+
+ @param[in] Uri The pointer to the URI string.
+
+ @retval EFI_SUCCESS The URI scheme is valid.
+ @retval EFI_INVALID_PARAMETER The URI scheme is not HTTP or HTTPS.
+ @retval EFI_ACCESS_DENIED HTTP is disabled and the URI is HTTP.
+
+**/
+EFI_STATUS
+HttpBootCheckUriScheme (
+ IN CHAR8 *Uri
+ );
+
+/**
+ Get the URI address string from the input device path.
+
+ Caller need to free the buffer in the UriAddress pointer.
+
+ @param[in] FilePath Pointer to the device path which contains a URI device path node.
+ @param[out] UriAddress The URI address string extract from the device path.
+
+ @retval EFI_SUCCESS The URI string is returned.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+HttpBootParseFilePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT CHAR8 **UriAddress
+ );
+
+/**
+ This function returns the image type according to server replied HTTP message
+ and also the image's URI info.
+
+ @param[in] Uri The pointer to the image's URI string.
+ @param[in] UriParser URI Parse result returned by NetHttpParseUrl().
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @retval EFI_SUCCESS The image type is returned in ImageType.
+ @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL.
+ @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL.
+ @retval EFI_NOT_FOUND Failed to identify the image type.
+ @retval Others Unexpect error happened.
+
+**/
+EFI_STATUS
+HttpBootCheckImageType (
+ IN CHAR8 *Uri,
+ IN VOID *UriParser,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ );
+
+/**
+ This function register the RAM disk info to the system.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] BufferSize The size of Buffer in bytes.
+ @param[in] Buffer The base address of the RAM disk.
+ @param[in] ImageType The image type of the file in Buffer.
+
+ @retval EFI_SUCCESS The RAM disk has been registered.
+ @retval EFI_NOT_FOUND No RAM disk protocol instances were found.
+ @retval EFI_UNSUPPORTED The ImageType is not supported.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootRegisterRamDisk (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN UINTN BufferSize,
+ IN VOID *Buffer,
+ IN HTTP_BOOT_IMAGE_TYPE ImageType
+ );
+#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..59cd7b3251
--- /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) 2017, 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->Ip4DriverBindingHandle,
+ &gEfiDns4ServiceBindingProtocolGuid,
+ &Dns4Handle
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dns4Handle,
+ &gEfiDns4ProtocolGuid,
+ (VOID **) &Dns4,
+ Service->Ip4DriverBindingHandle,
+ 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->Ip4DriverBindingHandle,
+ Service->ControllerHandle
+ );
+ }
+
+ if (Dns4Handle != NULL) {
+ NetLibDestroyServiceChild (
+ Service->ControllerHandle,
+ Service->Ip4DriverBindingHandle,
+ &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->Ip6DriverBindingHandle,
+ &gEfiDns6ServiceBindingProtocolGuid,
+ &Dns6Handle
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dns6Handle,
+ &gEfiDns6ProtocolGuid,
+ (VOID **) &Dns6,
+ Service->Ip6DriverBindingHandle,
+ 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->Ip6DriverBindingHandle,
+ Service->ControllerHandle
+ );
+ }
+
+ if (Dns6Handle != NULL) {
+ NetLibDestroyServiceChild (
+ Service->ControllerHandle,
+ Service->Ip6DriverBindingHandle,
+ &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..5727526273
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpDriver.c
@@ -0,0 +1,1067 @@
+/** @file
+ The driver binding and service binding protocol for HttpDxe driver.
+
+ Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 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_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[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,
+ 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->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->Ip4DriverBindingHandle,
+ HttpService->ControllerHandle
+ );
+
+ NetLibDestroyServiceChild (
+ HttpService->ControllerHandle,
+ HttpService->Ip4DriverBindingHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ HttpService->Tcp4ChildHandle
+ );
+
+ HttpService->Tcp4ChildHandle = NULL;
+ }
+ } else {
+ if (HttpService->Tcp6ChildHandle != NULL) {
+ gBS->CloseProtocol (
+ HttpService->Tcp6ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ HttpService->Ip6DriverBindingHandle,
+ HttpService->ControllerHandle
+ );
+
+ NetLibDestroyServiceChild (
+ HttpService->ControllerHandle,
+ HttpService->Ip6DriverBindingHandle,
+ &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, &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) {
+ HttpService->Ip4DriverBindingHandle = This->DriverBindingHandle;
+
+ 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;
+ HttpService->Ip6DriverBindingHandle = This->DriverBindingHandle;
+
+ 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 available 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;
+ HttpInstance->Method = HttpMethodMax;
+
+ 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..93a412ae2f
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpDriver.h
@@ -0,0 +1,404 @@
+/** @file
+ The header files of the driver binding and service binding protocol for HttpDxe driver.
+
+ Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 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.
+
+**/
+
+#ifndef __EFI_HTTP_DRIVER_H__
+#define __EFI_HTTP_DRIVER_H__
+
+#include <Uefi.h>
+#include <IndustryStandard/Http11.h>
+
+//
+// Libraries
+//
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.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>
+#include <Protocol/Tls.h>
+#include <Protocol/TlsConfig.h>
+
+#include <Guid/ImageAuthentication.h>
+//
+// Produced Protocols
+//
+#include <Protocol/Http.h>
+
+#include <Guid/TlsAuthentication.h>
+
+#include <IndustryStandard/Tls1.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 "HttpsSupport.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 available 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
+ );
+
+#endif
diff --git a/Core/NetworkPkg/HttpDxe/HttpDxe.inf b/Core/NetworkPkg/HttpDxe/HttpDxe.inf
new file mode 100644
index 0000000000..df2efdc257
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpDxe.inf
@@ -0,0 +1,82 @@
+## @file
+# Implementation of EFI HTTP protocol interfaces.
+#
+# Copyright (c) 2015 - 2017, 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
+ NetworkPkg/NetworkPkg.dec
+
+[Sources]
+ ComponentName.h
+ ComponentName.c
+ HttpDns.h
+ HttpDns.c
+ HttpDriver.h
+ HttpDriver.c
+ HttpImpl.h
+ HttpImpl.c
+ HttpProto.h
+ HttpProto.c
+ HttpsSupport.h
+ HttpsSupport.c
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ 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
+ gEfiTlsServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiTlsProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiTlsConfigurationProtocolGuid ## SOMETIMES_CONSUMES
+
+[Guids]
+ gEfiTlsCaCertificateGuid ## CONSUMES ## GUID
+
+[Pcd]
+ gEfiNetworkPkgTokenSpaceGuid.PcdAllowHttpConnections ## 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..8aee8f72a6
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpDxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// UEFI HTTP DXE Driver.
+//
+// This driver provides UEFI 2.5 HTTP protocols. It could work with an IPv4 or IPv6 stack.
+//
+//
+// 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI HTTP service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This driver provides EFI HTTP Protocol and EFI HTTP Service Binding Protocol. It could work with an IPv4 or IPv6 stack."
+
diff --git a/Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni b/Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni
new file mode 100644
index 0000000000..2e96fc8aa5
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// HttpDxe Localized Strings and Content
+//
+// 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UEFI HTTP DXE"
+
+
diff --git a/Core/NetworkPkg/HttpDxe/HttpImpl.c b/Core/NetworkPkg/HttpDxe/HttpImpl.c
new file mode 100644
index 0000000000..1f7a4fa52a
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpImpl.c
@@ -0,0 +1,1648 @@
+/** @file
+ Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
+
+ Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2015-2016 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.
+ HttpInstance->LocalAddressIsIPv6 is FALSE and
+ HttpConfigData->IPv4Node is NULL.
+ HttpInstance->LocalAddressIsIPv6 is TRUE and
+ HttpConfigData->IPv6Node is NULL.
+ @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiHttpGetModeData (
+ IN EFI_HTTP_PROTOCOL *This,
+ OUT EFI_HTTP_CONFIG_DATA *HttpConfigData
+ )
+{
+ HTTP_PROTOCOL *HttpInstance;
+
+ //
+ // Check input parameters.
+ //
+ if ((This == NULL) || (HttpConfigData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
+ ASSERT (HttpInstance != NULL);
+
+ if ((HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv6Node == NULL) ||
+ (!HttpInstance->LocalAddressIsIPv6 && HttpConfigData->AccessPoint.IPv4Node == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ 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) {
+ CopyMem (
+ HttpConfigData->AccessPoint.IPv6Node,
+ &HttpInstance->Ipv6Node,
+ sizeof (HttpInstance->Ipv6Node)
+ );
+ } else {
+ CopyMem (
+ HttpConfigData->AccessPoint.IPv4Node,
+ &HttpInstance->IPv4Node,
+ sizeof (HttpInstance->IPv4Node)
+ );
+ }
+
+ 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.
+
+ 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 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;
+ BOOLEAN TlsConfigure;
+ CHAR8 *RequestMsg;
+ CHAR8 *Url;
+ UINTN UrlLen;
+ CHAR16 *HostNameStr;
+ HTTP_TOKEN_WRAP *Wrap;
+ CHAR8 *FileUrl;
+ UINTN RequestMsgSize;
+ EFI_HANDLE ImageHandle;
+
+ //
+ // Initializations
+ //
+ Url = NULL;
+ UrlParser = NULL;
+ RemotePort = 0;
+ HostName = NULL;
+ RequestMsg = NULL;
+ HostNameStr = NULL;
+ Wrap = NULL;
+ FileUrl = NULL;
+ TlsConfigure = FALSE;
+
+ if ((This == NULL) || (Token == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HttpMsg = Token->Message;
+ if (HttpMsg == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Request = HttpMsg->Data.Request;
+
+ //
+ // Only support GET, HEAD, PUT and POST method in current implementation.
+ //
+ if ((Request != NULL) && (Request->Method != HttpMethodGet) &&
+ (Request->Method != HttpMethodHead) && (Request->Method != HttpMethodPut) && (Request->Method != HttpMethodPost)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This);
+ ASSERT (HttpInstance != NULL);
+
+ //
+ // Capture the method into HttpInstance.
+ //
+ if (Request != NULL) {
+ HttpInstance->Method = Request->Method;
+ }
+
+ if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Request == NULL) {
+ //
+ // Request would be NULL only for PUT/POST operation (in the current implementation)
+ //
+ if ((HttpInstance->Method != HttpMethodPut) && (HttpInstance->Method != HttpMethodPost)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // For PUT/POST, we need to have the TCP already configured. Bail out if it is not!
+ //
+ if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // We need to have the Message Body for sending the HTTP message across in these cases.
+ //
+ if (HttpMsg->Body == NULL || HttpMsg->BodyLength == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Use existing TCP instance to transmit the packet.
+ //
+ Configure = FALSE;
+ ReConfigure = FALSE;
+ } else {
+ //
+ // Check whether the token already existed.
+ //
+ if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // 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);
+
+ //
+ // From the information in Url, the HTTP instance will
+ // be able to determine whether to use http or https.
+ //
+ HttpInstance->UseHttps = IsHttpsUrl (Url);
+
+ //
+ // HTTP is disabled, return directly if the URI is not HTTPS.
+ //
+ if (!PcdGetBool (PcdAllowHttpConnections) && !(HttpInstance->UseHttps)) {
+
+ DEBUG ((EFI_D_ERROR, "EfiHttpRequest: HTTP is disabled.\n"));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Check whether we need to create Tls child and open the TLS protocol.
+ //
+ if (HttpInstance->UseHttps && HttpInstance->TlsChildHandle == NULL) {
+ //
+ // Use TlsSb to create Tls child and open the TLS protocol.
+ //
+ if (HttpInstance->LocalAddressIsIPv6) {
+ ImageHandle = HttpInstance->Service->Ip6DriverBindingHandle;
+ } else {
+ ImageHandle = HttpInstance->Service->Ip4DriverBindingHandle;
+ }
+
+ HttpInstance->TlsChildHandle = TlsCreateChild (
+ ImageHandle,
+ &(HttpInstance->Tls),
+ &(HttpInstance->TlsConfiguration)
+ );
+ if (HttpInstance->TlsChildHandle == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ TlsConfigure = TRUE;
+ }
+
+ UrlParser = NULL;
+ Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
+
+ HostName = NULL;
+ Status = HttpUrlGetHostName (Url, UrlParser, &HostName);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
+
+ Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);
+ if (EFI_ERROR (Status)) {
+ if (HttpInstance->UseHttps) {
+ RemotePort = HTTPS_DEFAULT_PORT;
+ } else {
+ 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) &&
+ (!HttpInstance->UseHttps || (HttpInstance->UseHttps &&
+ !TlsConfigure &&
+ HttpInstance->TlsSessionState == EfiTlsSessionDataTransferring))) {
+ //
+ // Host Name and port number of the request URL are the same with previous call to Request().
+ // If Https protocol used, the corresponding SessionState is EfiTlsSessionDataTransferring.
+ // 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);
+ }
+
+ if (HttpInstance->UseHttps && !TlsConfigure) {
+ Status = TlsCloseSession (HttpInstance);
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
+
+ TlsCloseTxRxEvent (HttpInstance);
+ }
+
+ 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;
+ if (Request != NULL) {
+ Wrap->TcpWrap.Method = Request->Method;
+ }
+
+ Status = HttpInitSession (
+ HttpInstance,
+ Wrap,
+ Configure || ReConfigure,
+ TlsConfigure
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ if (!Configure && !ReConfigure && !TlsConfigure) {
+ //
+ // 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 (Url != NULL && *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;
+ }
+ }
+
+ Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);
+
+ if (EFI_ERROR (Status) || NULL == RequestMsg) {
+ goto Error3;
+ }
+
+ ASSERT (RequestMsg != NULL);
+
+ //
+ // Every request we insert a TxToken and a response call would remove the TxToken.
+ // In cases of PUT/POST, after an initial request-response pair, we would do a
+ // continuous request without a response call. So, in such cases, where Request
+ // structure is NULL, we would not insert a TxToken.
+ //
+ if (Request != NULL) {
+ Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap);
+ if (EFI_ERROR (Status)) {
+ goto Error4;
+ }
+ }
+
+ //
+ // Transmit the request message.
+ //
+ Status = HttpTransmitTcp (
+ HttpInstance,
+ Wrap,
+ (UINT8*) RequestMsg,
+ RequestMsgSize
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error5;
+ }
+
+ DispatchDpc ();
+
+ if (HostName != NULL) {
+ FreePool (HostName);
+ }
+
+ return EFI_SUCCESS;
+
+Error5:
+ //
+ // We would have inserted a TxToken only if Request structure is not NULL.
+ // Hence check before we do a remove in this error case.
+ //
+ if (Request != NULL) {
+ NetMapRemoveTail (&HttpInstance->TxTokens, NULL);
+ }
+
+Error4:
+ if (RequestMsg != NULL) {
+ FreePool (RequestMsg);
+ }
+
+Error3:
+ if (HttpInstance->UseHttps) {
+ TlsCloseSession (HttpInstance);
+ TlsCloseTxRxEvent (HttpInstance);
+ }
+
+Error2:
+ HttpCloseConnection (HttpInstance);
+
+ 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;
+
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) {
+ //
+ // Cancle the Token before close its Event.
+ //
+ HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &Wrap->TcpWrap.Rx4Token.CompletionToken);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
+ //
+ DispatchDpc ();
+ }
+ } else {
+ if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) {
+ //
+ // Cancle the Token before close its Event.
+ //
+ HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &Wrap->TcpWrap.Rx6Token.CompletionToken);
+
+ //
+ // Dispatch the DPC queued by the NotifyFunction of the canceled token's events.
+ //
+ DispatchDpc ();
+ }
+ }
+
+ //
+ // 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;
+ }
+ }
+
+ if (!HttpInstance->UseHttps) {
+ //
+ // Then check the tokens queued by EfiHttpResponse(), except for Https.
+ //
+ 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;
+ }
+ }
+ } else {
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);
+ } else {
+ HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);
+ }
+ }
+
+ 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_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;
+ NET_FRAGMENT Fragment;
+
+ 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;
+ ValueInItem = NULL;
+ Fragment.Len = 0;
+ Fragment.Bulk = NULL;
+
+ if (HttpMsg->Data.Response != NULL) {
+ //
+ // 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;
+
+
+ if (HttpInstance->TimeoutEvent == NULL) {
+ //
+ // Create TimeoutEvent for response
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &HttpInstance->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ }
+
+ //
+ // Start the timer, and wait Timeout seconds to receive the header packet.
+ //
+ Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ Status = HttpTcpReceiveHeader (HttpInstance, &SizeofHeaders, &BufferSize, HttpInstance->TimeoutEvent);
+
+ gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
+
+ 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) {
+ Status = EFI_NOT_READY;
+ 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) {
+ Status = EFI_NOT_READY;
+ goto Error;
+ }
+
+ //
+ // We could have response with just a HTTP message and no headers. For Example,
+ // "100 Continue". In such cases, we would not want to unnecessarily call a Parse
+ // method. A "\r\n" following Tmp string again would indicate an end. Compare and
+ // set SizeofHeaders to 0.
+ //
+ Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR);
+ if (CompareMem (Tmp, HTTP_CRLF_STR, AsciiStrLen (HTTP_CRLF_STR)) == 0) {
+ SizeofHeaders = 0;
+ } else {
+ SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders);
+ }
+
+ HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode);
+ HttpInstance->StatusCode = StatusCode;
+
+ Status = EFI_NOT_READY;
+ ValueInItem = NULL;
+
+ //
+ // In cases of PUT/POST, after an initial request-response pair, we would do a
+ // continuous request without a response call. So, we would not do an insert of
+ // TxToken. After we have sent the complete file, we will call a response to get
+ // a final response from server. In such a case, we would not have any TxTokens.
+ // Hence, check that case before doing a NetMapRemoveHead.
+ //
+ if (!NetMapIsEmpty (&HttpInstance->TxTokens)) {
+ 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;
+ }
+ }
+
+ if (SizeofHeaders != 0) {
+ HeaderTmp = AllocateZeroPool (SizeofHeaders);
+ if (HeaderTmp == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error2;
+ }
+
+ 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 Error2;
+ }
+
+ //
+ // 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 Error2;
+ }
+
+ FreePool (HttpHeaders);
+ HttpHeaders = NULL;
+
+
+ //
+ // Init message-body parser by header information.
+ //
+ Status = HttpInitMsgParser (
+ HttpInstance->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;
+ //
+ if (!HttpInstance->UseHttps) {
+ Status = HttpTcpReceiveBody (Wrap, HttpMsg);
+
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ } else {
+ if (HttpInstance->TimeoutEvent == NULL) {
+ //
+ // Create TimeoutEvent for response
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &HttpInstance->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+ }
+
+ //
+ // Start the timer, and wait Timeout seconds to receive the body packet.
+ //
+ Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_RESPONSE_TIMEOUT * TICKS_PER_SECOND);
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ Status = HttpsReceive (HttpInstance, &Fragment, HttpInstance->TimeoutEvent);
+
+ gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
+
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ //
+ // Check whether we receive a complete HTTP message.
+ //
+ Status = HttpParseMessageBody (
+ HttpInstance->MsgParser,
+ (UINTN) Fragment.Len,
+ (CHAR8 *) Fragment.Bulk
+ );
+ 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;
+ }
+
+ //
+ // We receive part of header of next HTTP msg.
+ //
+ if (HttpInstance->NextMsg != NULL) {
+ HttpMsg->BodyLength = MIN ((UINTN) HttpInstance->NextMsg - (UINTN) Fragment.Bulk, HttpMsg->BodyLength);
+ CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
+
+ HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
+ if (HttpInstance->CacheLen != 0) {
+ if (HttpInstance->CacheBody != NULL) {
+ FreePool (HttpInstance->CacheBody);
+ }
+
+ HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
+ if (HttpInstance->CacheBody == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error2;
+ }
+
+ CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
+ HttpInstance->CacheOffset = 0;
+
+ HttpInstance->NextMsg = HttpInstance->CacheBody + ((UINTN) HttpInstance->NextMsg - (UINTN) (Fragment.Bulk + HttpMsg->BodyLength));
+ }
+ } else {
+ HttpMsg->BodyLength = MIN (Fragment.Len, (UINT32) HttpMsg->BodyLength);
+ CopyMem (HttpMsg->Body, Fragment.Bulk, HttpMsg->BodyLength);
+ HttpInstance->CacheLen = Fragment.Len - HttpMsg->BodyLength;
+ if (HttpInstance->CacheLen != 0) {
+ if (HttpInstance->CacheBody != NULL) {
+ FreePool (HttpInstance->CacheBody);
+ }
+
+ HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen);
+ if (HttpInstance->CacheBody == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error2;
+ }
+
+ CopyMem (HttpInstance->CacheBody, Fragment.Bulk + HttpMsg->BodyLength, HttpInstance->CacheLen);
+ HttpInstance->CacheOffset = 0;
+ }
+ }
+
+ if (Fragment.Bulk != NULL) {
+ FreePool (Fragment.Bulk);
+ Fragment.Bulk = NULL;
+ }
+
+ goto Exit;
+ }
+
+ return Status;
+
+Exit:
+ Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
+ if (Item != NULL) {
+ NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
+ }
+
+ if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
+ Token->Status = EFI_HTTP_ERROR;
+ } else {
+ Token->Status = Status;
+ }
+
+ gBS->SignalEvent (Token->Event);
+ HttpCloseTcpRxEvent (Wrap);
+ FreePool (Wrap);
+ return Status;
+
+Error2:
+ if (ValueInItem != NULL) {
+ NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem);
+ }
+
+Error:
+ Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken);
+ if (Item != NULL) {
+ NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL);
+ }
+
+ if (!HttpInstance->UseHttps) {
+ HttpTcpTokenCleanup (Wrap);
+ } else {
+ FreePool (Wrap);
+ }
+
+ if (HttpHeaders != NULL) {
+ FreePool (HttpHeaders);
+ HttpHeaders = NULL;
+ }
+
+ if (Fragment.Bulk != NULL) {
+ FreePool (Fragment.Bulk);
+ Fragment.Bulk = NULL;
+ }
+
+ if (HttpMsg->Headers != NULL) {
+ FreePool (HttpMsg->Headers);
+ HttpMsg->Headers = NULL;
+ }
+
+ if (HttpInstance->CacheBody != NULL) {
+ FreePool (HttpInstance->CacheBody);
+ HttpInstance->CacheBody = NULL;
+ }
+
+ if (HttpInstance->StatusCode >= HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE) {
+ Token->Status = EFI_HTTP_ERROR;
+ } else {
+ 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 response is received 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;
+
+ //
+ // Notes: For Https, receive token wrapped in HTTP_TOKEN_WRAP is not used to
+ // receive the https response. A special TlsRxToken is used for receiving TLS
+ // related messages. It should be a blocking response.
+ //
+ if (!HttpInstance->UseHttps) {
+ 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..40b25048bc
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpImpl.h
@@ -0,0 +1,237 @@
+/** @file
+ The header files of implementation of EFI_HTTP_PROTOCOL protocol interfaces.
+
+ Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 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.
+
+**/
+
+#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_VERSION
+#define HTTP_VERSION_CRLF_STR " HTTP/1.1\r\n"
+#define HTTP_ERROR_OR_NOT_SUPPORT_STATUS_CODE 300
+
+
+/**
+ 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.
+ HttpInstance->LocalAddressIsIPv6 is FALSE and
+ HttpConfigData->IPv4Node is NULL.
+ HttpInstance->LocalAddressIsIPv6 is TRUE and
+ HttpConfigData->IPv6Node is NULL.
+ @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started.
+
+**/
+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.
+
+ 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 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_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 response is received 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..3d61ba2ae1
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpProto.c
@@ -0,0 +1,2128 @@
+/** @file
+ Miscellaneous routines for HttpDxe driver.
+
+Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 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);
+ Wrap->TcpWrap.Rx6Token.CompletionToken.Event = NULL;
+
+ if (EFI_ERROR (Wrap->TcpWrap.Rx6Token.CompletionToken.Status)) {
+ DEBUG ((EFI_D_ERROR, "HttpTcpReceiveNotifyDpc: %r!\n", Wrap->TcpWrap.Rx6Token.CompletionToken.Status));
+ Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status;
+ gBS->SignalEvent (Wrap->HttpToken->Event);
+
+ Item = NetMapFindKey (&HttpInstance->RxTokens, Wrap->HttpToken);
+ if (Item != NULL) {
+ NetMapRemoveItem (&HttpInstance->RxTokens, Item, NULL);
+ }
+
+ FreePool (Wrap);
+ Wrap = NULL;
+
+ return ;
+ }
+
+ } else {
+ gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event);
+ Wrap->TcpWrap.Rx4Token.CompletionToken.Event = NULL;
+
+ if (EFI_ERROR (Wrap->TcpWrap.Rx4Token.CompletionToken.Status)) {
+ DEBUG ((EFI_D_ERROR, "HttpTcpReceiveNotifyDpc: %r!\n", Wrap->TcpWrap.Rx4Token.CompletionToken.Status));
+ Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status;
+ gBS->SignalEvent (Wrap->HttpToken->Event);
+
+ Item = NetMapFindKey (&HttpInstance->RxTokens, Wrap->HttpToken);
+ if (Item != NULL) {
+ NetMapRemoveItem (&HttpInstance->RxTokens, Item, NULL);
+ }
+
+ FreePool (Wrap);
+ Wrap = NULL;
+
+ 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);
+ Wrap = NULL;
+}
+
+/**
+ 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->Ip4DriverBindingHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ &HttpInstance->Tcp4ChildHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ HttpInstance->Tcp4ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ (VOID **) &Interface,
+ HttpInstance->Service->Ip4DriverBindingHandle,
+ 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->Ip4DriverBindingHandle,
+ 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->Ip4DriverBindingHandle,
+ 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->Ip6DriverBindingHandle,
+ &gEfiTcp6ServiceBindingProtocolGuid,
+ &HttpInstance->Tcp6ChildHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ HttpInstance->Tcp6ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ (VOID **) &Interface,
+ HttpInstance->Service->Ip6DriverBindingHandle,
+ 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->Ip6DriverBindingHandle,
+ 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->Ip6DriverBindingHandle,
+ 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->Ip4DriverBindingHandle,
+ HttpInstance->Service->ControllerHandle
+ );
+
+ gBS->CloseProtocol (
+ HttpInstance->Tcp4ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ HttpInstance->Service->Ip4DriverBindingHandle,
+ HttpInstance->Handle
+ );
+
+ NetLibDestroyServiceChild (
+ HttpInstance->Service->ControllerHandle,
+ HttpInstance->Service->Ip4DriverBindingHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ HttpInstance->Tcp4ChildHandle
+ );
+ }
+
+ if (HttpInstance->Service->Tcp4ChildHandle != NULL) {
+ gBS->CloseProtocol (
+ HttpInstance->Service->Tcp4ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ HttpInstance->Service->Ip4DriverBindingHandle,
+ HttpInstance->Handle
+ );
+ }
+
+ if (HttpInstance->Tcp6ChildHandle != NULL) {
+ gBS->CloseProtocol (
+ HttpInstance->Tcp6ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ HttpInstance->Service->Ip6DriverBindingHandle,
+ HttpInstance->Service->ControllerHandle
+ );
+
+ gBS->CloseProtocol (
+ HttpInstance->Tcp6ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ HttpInstance->Service->Ip6DriverBindingHandle,
+ HttpInstance->Handle
+ );
+
+ NetLibDestroyServiceChild (
+ HttpInstance->Service->ControllerHandle,
+ HttpInstance->Service->Ip6DriverBindingHandle,
+ &gEfiTcp6ServiceBindingProtocolGuid,
+ HttpInstance->Tcp6ChildHandle
+ );
+ }
+
+ if (HttpInstance->Service->Tcp6ChildHandle != NULL) {
+ gBS->CloseProtocol (
+ HttpInstance->Service->Tcp6ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ HttpInstance->Service->Ip6DriverBindingHandle,
+ 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->TimeoutEvent != NULL) {
+ gBS->CloseEvent (HttpInstance->TimeoutEvent);
+ HttpInstance->TimeoutEvent = NULL;
+ }
+
+ 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->Ip4DriverBindingHandle,
+ HttpInstance->Service->ControllerHandle
+ );
+
+ gBS->CloseProtocol (
+ HttpInstance->Tcp4ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ HttpInstance->Service->Ip4DriverBindingHandle,
+ HttpInstance->Handle
+ );
+
+ NetLibDestroyServiceChild (
+ HttpInstance->Service->ControllerHandle,
+ HttpInstance->Service->Ip4DriverBindingHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ HttpInstance->Tcp4ChildHandle
+ );
+ }
+
+ if (HttpInstance->Service->Tcp4ChildHandle != NULL) {
+ gBS->CloseProtocol (
+ HttpInstance->Service->Tcp4ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ HttpInstance->Service->Ip4DriverBindingHandle,
+ HttpInstance->Handle
+ );
+ }
+
+ if (HttpInstance->Tcp6ChildHandle != NULL) {
+ gBS->CloseProtocol (
+ HttpInstance->Tcp6ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ HttpInstance->Service->Ip6DriverBindingHandle,
+ HttpInstance->Service->ControllerHandle
+ );
+
+ gBS->CloseProtocol (
+ HttpInstance->Tcp6ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ HttpInstance->Service->Ip6DriverBindingHandle,
+ HttpInstance->Handle
+ );
+
+ NetLibDestroyServiceChild (
+ HttpInstance->Service->ControllerHandle,
+ HttpInstance->Service->Ip6DriverBindingHandle,
+ &gEfiTcp6ServiceBindingProtocolGuid,
+ HttpInstance->Tcp6ChildHandle
+ );
+ }
+
+ if (HttpInstance->Service->Tcp6ChildHandle != NULL) {
+ gBS->CloseProtocol (
+ HttpInstance->Service->Tcp6ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ HttpInstance->Service->Ip6DriverBindingHandle,
+ HttpInstance->Handle
+ );
+ }
+
+ TlsCloseTxRxEvent (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
+ )
+{
+ 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. Then,
+ connect one TLS session if required.
+
+ @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);
+ }
+
+ Status = HttpCreateConnection (HttpInstance);
+ if (EFI_ERROR(Status)){
+ DEBUG ((EFI_D_ERROR, "Tcp4 Connection fail - %x\n", Status));
+ return Status;
+ }
+
+ //
+ // Tls session connection.
+ //
+ if (HttpInstance->UseHttps) {
+ if (HttpInstance->TimeoutEvent == NULL) {
+ //
+ // Create TimeoutEvent for TLS connection.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &HttpInstance->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ TlsCloseTxRxEvent (HttpInstance);
+ return Status;
+ }
+ }
+
+ //
+ // Start the timer, and wait Timeout seconds for connection.
+ //
+ Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_CONNECTION_TIMEOUT * TICKS_PER_SECOND);
+ if (EFI_ERROR (Status)) {
+ TlsCloseTxRxEvent (HttpInstance);
+ return Status;
+ }
+
+ Status = TlsConnectSession (HttpInstance, HttpInstance->TimeoutEvent);
+
+ gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
+
+ if (EFI_ERROR (Status)) {
+ TlsCloseTxRxEvent (HttpInstance);
+ return Status;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Check existing TCP connection, if in error state, recover TCP6 connection. Then,
+ connect one TLS session if required.
+
+ @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);
+ }
+
+ Status = HttpCreateConnection (HttpInstance);
+ if (EFI_ERROR(Status)){
+ DEBUG ((EFI_D_ERROR, "Tcp6 Connection fail - %x\n", Status));
+ return Status;
+ }
+
+ //
+ // Tls session connection.
+ //
+ if (HttpInstance->UseHttps) {
+ if (HttpInstance->TimeoutEvent == NULL) {
+ //
+ // Create TimeoutEvent for TLS connection.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &HttpInstance->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ TlsCloseTxRxEvent (HttpInstance);
+ return Status;
+ }
+ }
+
+ //
+ // Start the timer, and wait Timeout seconds for connection.
+ //
+ Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_CONNECTION_TIMEOUT * TICKS_PER_SECOND);
+ if (EFI_ERROR (Status)) {
+ TlsCloseTxRxEvent (HttpInstance);
+ return Status;
+ }
+
+ Status = TlsConnectSession (HttpInstance, HttpInstance->TimeoutEvent);
+
+ gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0);
+
+ if (EFI_ERROR (Status)) {
+ TlsCloseTxRxEvent (HttpInstance);
+ return Status;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Initialize Http session.
+
+ @param[in] HttpInstance The HTTP instance private data.
+ @param[in] Wrap The HTTP token's wrap data.
+ @param[in] Configure The Flag indicates whether need to initialize session.
+ @param[in] TlsConfigure The Flag indicates whether it's the new Tls session.
+
+ @retval EFI_SUCCESS The initialization of session is done.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+HttpInitSession (
+ IN HTTP_PROTOCOL *HttpInstance,
+ IN HTTP_TOKEN_WRAP *Wrap,
+ IN BOOLEAN Configure,
+ IN BOOLEAN TlsConfigure
+ )
+{
+ EFI_STATUS Status;
+ ASSERT (HttpInstance != NULL);
+
+ //
+ // Configure Tls session.
+ //
+ if (TlsConfigure) {
+ Status = TlsConfigureSession (HttpInstance);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ 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 or HTTPS 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;
+ UINT8 *Buffer;
+ UINTN BufferSize;
+ NET_FRAGMENT TempFragment;
+
+ Status = EFI_SUCCESS;
+ Buffer = NULL;
+ TempFragment.Len = 0;
+ TempFragment.Bulk = NULL;
+
+ //
+ // Need to encrypt data.
+ //
+ if (HttpInstance->UseHttps) {
+ //
+ // Build BufferOut data
+ //
+ BufferSize = sizeof (TLS_RECORD_HEADER) + TxStringLen;
+ Buffer = AllocateZeroPool (BufferSize);
+ if (Buffer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+ ((TLS_RECORD_HEADER *) Buffer)->ContentType = TlsContentTypeApplicationData;
+ ((TLS_RECORD_HEADER *) Buffer)->Version.Major = HttpInstance->TlsConfigData.Version.Major;
+ ((TLS_RECORD_HEADER *) Buffer)->Version.Minor = HttpInstance->TlsConfigData.Version.Minor;
+ ((TLS_RECORD_HEADER *) Buffer)->Length = (UINT16) (TxStringLen);
+ CopyMem (Buffer + sizeof (TLS_RECORD_HEADER), TxString, TxStringLen);
+
+ //
+ // Encrypt Packet.
+ //
+ Status = TlsProcessMessage (
+ HttpInstance,
+ Buffer,
+ BufferSize,
+ EfiTlsEncrypt,
+ &TempFragment
+ );
+
+ FreePool (Buffer);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ Tcp4 = HttpInstance->Tcp4;
+ Tx4Token = &Wrap->TcpWrap.Tx4Token;
+
+ if (HttpInstance->UseHttps) {
+ Tx4Token->Packet.TxData->DataLength = TempFragment.Len;
+ Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = TempFragment.Len;
+ Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TempFragment.Bulk;
+ } else {
+ 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;
+
+ if (HttpInstance->UseHttps) {
+ Tx6Token->Packet.TxData->DataLength = TempFragment.Len;
+ Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = TempFragment.Len;
+ Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TempFragment.Bulk;
+ } else {
+ 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;
+}
+
+/**
+ 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 or HTTPS 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 *RequestMsg;
+ CHAR8 *Url;
+ UINTN UrlSize;
+ UINTN RequestMsgSize;
+
+ RequestMsg = NULL;
+
+ 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.
+ //
+ Status = HttpGenRequestMessage (
+ ValueInItem->HttpToken->Message,
+ Url,
+ &RequestMsg,
+ &RequestMsgSize
+ );
+ FreePool (Url);
+
+ if (EFI_ERROR (Status) || NULL == RequestMsg){
+ return Status;
+ }
+
+ ASSERT (RequestMsg != NULL);
+
+ //
+ // Transmit the request message.
+ //
+ Status = HttpTransmitTcp (
+ ValueInItem->HttpInstance,
+ ValueInItem,
+ (UINT8*) RequestMsg,
+ RequestMsgSize
+ );
+ FreePool (RequestMsg);
+ 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.
+ @param[in] Timeout The time to wait for receiving the header packet.
+
+ @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,
+ IN EFI_EVENT Timeout
+ )
+{
+ 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;
+ NET_FRAGMENT Fragment;
+
+ ASSERT (HttpInstance != NULL);
+
+ EndofHeader = HttpInstance->EndofHeader;
+ HttpHeaders = HttpInstance->HttpHeaders;
+ Tcp4 = HttpInstance->Tcp4;
+ Tcp6 = HttpInstance->Tcp6;
+ Buffer = NULL;
+ Rx4Token = NULL;
+ Rx6Token = NULL;
+ Fragment.Len = 0;
+ Fragment.Bulk = NULL;
+
+ if (HttpInstance->LocalAddressIsIPv6) {
+ ASSERT (Tcp6 != NULL);
+ } else {
+ ASSERT (Tcp4 != NULL);
+ }
+
+ if (!HttpInstance->UseHttps) {
+ Status = HttpCreateTcpRxEventForHeader (HttpInstance);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ if (!HttpInstance->UseHttps) {
+ 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) {
+ if (!HttpInstance->UseHttps) {
+ 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 && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+ Tcp4->Poll (Tcp4);
+ }
+
+ if (!HttpInstance->IsRxDone) {
+ //
+ // Cancle the Token before close its Event.
+ //
+ Tcp4->Cancel (HttpInstance->Tcp4, &Rx4Token->CompletionToken);
+ gBS->CloseEvent (Rx4Token->CompletionToken.Event);
+ Rx4Token->CompletionToken.Status = EFI_TIMEOUT;
+ }
+
+ Status = Rx4Token->CompletionToken.Status;
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Fragment.Len = Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength;
+ Fragment.Bulk = (UINT8 *) Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer;
+ } else {
+ if (Fragment.Bulk != NULL) {
+ FreePool (Fragment.Bulk);
+ Fragment.Bulk = NULL;
+ }
+
+ Status = HttpsReceive (HttpInstance, &Fragment, Timeout);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));
+ return Status;
+ }
+ }
+
+ //
+ // Append the response string.
+ //
+ *BufferSize = *SizeofHeaders + Fragment.Len;
+ 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,
+ Fragment.Bulk,
+ Fragment.Len
+ );
+ *HttpHeaders = Buffer;
+ *SizeofHeaders = *BufferSize;
+
+ //
+ // Check whether we received end of HTTP headers.
+ //
+ *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR);
+ };
+
+ //
+ // Free the buffer.
+ //
+ if (Rx4Token != NULL && Rx4Token->Packet.RxData != NULL && Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
+ FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer);
+ Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;
+ Fragment.Bulk = NULL;
+ }
+
+ if (Fragment.Bulk != NULL) {
+ FreePool (Fragment.Bulk);
+ Fragment.Bulk = NULL;
+ }
+ } else {
+ if (!HttpInstance->UseHttps) {
+ 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) {
+ if (!HttpInstance->UseHttps) {
+ 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 && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+ Tcp6->Poll (Tcp6);
+ }
+
+ if (!HttpInstance->IsRxDone) {
+ //
+ // Cancle the Token before close its Event.
+ //
+ Tcp6->Cancel (HttpInstance->Tcp6, &Rx6Token->CompletionToken);
+ gBS->CloseEvent (Rx6Token->CompletionToken.Event);
+ Rx6Token->CompletionToken.Status = EFI_TIMEOUT;
+ }
+
+ Status = Rx6Token->CompletionToken.Status;
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Fragment.Len = Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength;
+ Fragment.Bulk = (UINT8 *) Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer;
+ } else {
+ if (Fragment.Bulk != NULL) {
+ FreePool (Fragment.Bulk);
+ Fragment.Bulk = NULL;
+ }
+
+ Status = HttpsReceive (HttpInstance, &Fragment, Timeout);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status));
+ return Status;
+ }
+ }
+
+ //
+ // Append the response string.
+ //
+ *BufferSize = *SizeofHeaders + Fragment.Len;
+ 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,
+ Fragment.Bulk,
+ Fragment.Len
+ );
+ *HttpHeaders = Buffer;
+ *SizeofHeaders = *BufferSize;
+
+ //
+ // Check whether we received end of HTTP headers.
+ //
+ *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR);
+ };
+
+ //
+ // Free the buffer.
+ //
+ if (Rx6Token != NULL && Rx6Token->Packet.RxData != NULL && Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
+ FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer);
+ Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;
+ Fragment.Bulk = NULL;
+ }
+
+ if (Fragment.Bulk != NULL) {
+ FreePool (Fragment.Bulk);
+ Fragment.Bulk = 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) {
+ Rx6Token = &Wrap->TcpWrap.Rx6Token;
+
+ if (Rx6Token->CompletionToken.Event != NULL) {
+ gBS->CloseEvent (Rx6Token->CompletionToken.Event);
+ Rx6Token->CompletionToken.Event = NULL;
+ }
+
+ FreePool (Wrap);
+
+ Rx6Token = &HttpInstance->Rx6Token;
+
+ if (Rx6Token->CompletionToken.Event != NULL) {
+ gBS->CloseEvent (Rx6Token->CompletionToken.Event);
+ Rx6Token->CompletionToken.Event = NULL;
+ }
+
+ if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
+ FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer);
+ Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;
+ }
+
+ } else {
+ Rx4Token = &Wrap->TcpWrap.Rx4Token;
+
+ if (Rx4Token->CompletionToken.Event != NULL) {
+ gBS->CloseEvent (Rx4Token->CompletionToken.Event);
+ Rx4Token->CompletionToken.Event = NULL;
+ }
+
+ FreePool (Wrap);
+
+ Rx4Token = &HttpInstance->Rx4Token;
+
+ if (Rx4Token->CompletionToken.Event != NULL) {
+ gBS->CloseEvent (Rx4Token->CompletionToken.Event);
+ Rx4Token->CompletionToken.Event = NULL;
+ }
+
+
+ if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) {
+ FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer);
+ Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL;
+ }
+ }
+
+}
diff --git a/Core/NetworkPkg/HttpDxe/HttpProto.h b/Core/NetworkPkg/HttpDxe/HttpProto.h
new file mode 100644
index 0000000000..95fb4843ce
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpProto.h
@@ -0,0 +1,606 @@
+/** @file
+ The header files of miscellaneous routines for HttpDxe driver.
+
+Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 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.
+
+**/
+
+#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_RESPONSE_TIMEOUT 5
+#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 Ip4DriverBindingHandle;
+ EFI_HANDLE Ip6DriverBindingHandle;
+ 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 {
+ EFI_TLS_VERSION Version;
+ EFI_TLS_CONNECTION_END ConnectionEnd;
+ EFI_TLS_VERIFY VerifyMethod;
+ EFI_TLS_SESSION_STATE SessionState;
+} TLS_CONFIG_DATA;
+
+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_HTTP_METHOD Method;
+
+ UINTN StatusCode;
+
+ EFI_EVENT TimeoutEvent;
+
+ 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;
+
+ //
+ // Https Support
+ //
+ BOOLEAN UseHttps;
+
+ EFI_HANDLE TlsChildHandle; /// Tls ChildHandle
+ TLS_CONFIG_DATA TlsConfigData;
+ EFI_TLS_PROTOCOL *Tls;
+ EFI_TLS_CONFIGURATION_PROTOCOL *TlsConfiguration;
+ EFI_TLS_SESSION_STATE TlsSessionState;
+
+ //
+ // TlsTxData used for transmitting TLS related messages.
+ //
+ EFI_TCP4_IO_TOKEN Tcp4TlsTxToken;
+ EFI_TCP4_TRANSMIT_DATA Tcp4TlsTxData;
+ EFI_TCP6_IO_TOKEN Tcp6TlsTxToken;
+ EFI_TCP6_TRANSMIT_DATA Tcp6TlsTxData;
+ BOOLEAN TlsIsTxDone;
+
+ //
+ // TlsRxData used for receiving TLS related messages.
+ //
+ EFI_TCP4_IO_TOKEN Tcp4TlsRxToken;
+ EFI_TCP4_RECEIVE_DATA Tcp4TlsRxData;
+ EFI_TCP6_IO_TOKEN Tcp6TlsRxToken;
+ EFI_TCP6_RECEIVE_DATA Tcp6TlsRxData;
+ BOOLEAN TlsIsRxDone;
+} 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, recover TCP4 connection. Then,
+ connect one TLS session if required.
+
+ @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. Then,
+ connect one TLS session if required.
+
+ @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 or HTTPS 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
+ );
+
+/**
+ 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 Http session.
+
+ @param[in] HttpInstance The HTTP instance private data.
+ @param[in] Wrap The HTTP token's wrap data.
+ @param[in] Configure The Flag indicates whether need to initialize session.
+ @param[in] TlsConfigure The Flag indicates whether it's the new Tls session.
+
+ @retval EFI_SUCCESS The initialization of session is done.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+HttpInitSession (
+ IN HTTP_PROTOCOL *HttpInstance,
+ IN HTTP_TOKEN_WRAP *Wrap,
+ IN BOOLEAN Configure,
+ IN BOOLEAN TlsConfigure
+ );
+
+/**
+ Transmit the HTTP or HTTPS 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.
+ @param[in] Timeout The time to wait for receiving the header packet.
+
+ @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,
+ IN EFI_EVENT Timeout
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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/HttpDxe/HttpsSupport.c b/Core/NetworkPkg/HttpDxe/HttpsSupport.c
new file mode 100644
index 0000000000..e4d9a37bee
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpsSupport.c
@@ -0,0 +1,1720 @@
+/** @file
+ Miscellaneous routines specific to Https for HttpDxe driver.
+
+Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 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"
+
+/**
+ Returns the first occurrence of a Null-terminated ASCII sub-string in a Null-terminated
+ ASCII string and ignore case during the search process.
+
+ This function scans the contents of the ASCII string specified by String
+ and returns the first occurrence of SearchString and ignore case during the search process.
+ If SearchString is not found in String, then NULL is returned. If the length of SearchString
+ is zero, then String is returned.
+
+ If String is NULL, then ASSERT().
+ If SearchString is NULL, then ASSERT().
+
+ @param[in] String A pointer to a Null-terminated ASCII string.
+ @param[in] SearchString A pointer to a Null-terminated ASCII string to search for.
+
+ @retval NULL If the SearchString does not appear in String.
+ @retval others If there is a match return the first occurrence of SearchingString.
+ If the length of SearchString is zero,return String.
+
+**/
+CHAR8 *
+AsciiStrCaseStr (
+ IN CONST CHAR8 *String,
+ IN CONST CHAR8 *SearchString
+ )
+{
+ CONST CHAR8 *FirstMatch;
+ CONST CHAR8 *SearchStringTmp;
+
+ CHAR8 Src;
+ CHAR8 Dst;
+
+ //
+ // ASSERT both strings are less long than PcdMaximumAsciiStringLength
+ //
+ ASSERT (AsciiStrSize (String) != 0);
+ ASSERT (AsciiStrSize (SearchString) != 0);
+
+ if (*SearchString == '\0') {
+ return (CHAR8 *) String;
+ }
+
+ while (*String != '\0') {
+ SearchStringTmp = SearchString;
+ FirstMatch = String;
+
+ while ((*SearchStringTmp != '\0')
+ && (*String != '\0')) {
+ Src = *String;
+ Dst = *SearchStringTmp;
+
+ if ((Src >= 'A') && (Src <= 'Z')) {
+ Src -= ('A' - 'a');
+ }
+
+ if ((Dst >= 'A') && (Dst <= 'Z')) {
+ Dst -= ('A' - 'a');
+ }
+
+ if (Src != Dst) {
+ break;
+ }
+
+ String++;
+ SearchStringTmp++;
+ }
+
+ if (*SearchStringTmp == '\0') {
+ return (CHAR8 *) FirstMatch;
+ }
+
+ String = FirstMatch + 1;
+ }
+
+ return NULL;
+}
+
+/**
+ The callback function to free the net buffer list.
+
+ @param[in] Arg The opaque parameter.
+
+**/
+VOID
+EFIAPI
+FreeNbufList (
+ IN VOID *Arg
+ )
+{
+ ASSERT (Arg != NULL);
+
+ NetbufFreeList ((LIST_ENTRY *) Arg);
+ FreePool (Arg);
+}
+
+/**
+ Check whether the Url is from Https.
+
+ @param[in] Url The pointer to a HTTP or HTTPS URL string.
+
+ @retval TRUE The Url is from HTTPS.
+ @retval FALSE The Url is from HTTP.
+
+**/
+BOOLEAN
+IsHttpsUrl (
+ IN CHAR8 *Url
+ )
+{
+ CHAR8 *Tmp;
+
+ Tmp = NULL;
+
+ Tmp = AsciiStrCaseStr (Url, HTTPS_FLAG);
+ if (Tmp != NULL && Tmp == Url) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Creates a Tls child handle, open EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[out] TlsProto Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[out] TlsConfiguration Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance.
+
+ @return The child handle with opened EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL.
+
+**/
+EFI_HANDLE
+EFIAPI
+TlsCreateChild (
+ IN EFI_HANDLE ImageHandle,
+ OUT EFI_TLS_PROTOCOL **TlsProto,
+ OUT EFI_TLS_CONFIGURATION_PROTOCOL **TlsConfiguration
+ )
+{
+ EFI_STATUS Status;
+ EFI_SERVICE_BINDING_PROTOCOL *TlsSb;
+ EFI_HANDLE TlsChildHandle;
+
+ TlsSb = NULL;
+ TlsChildHandle = 0;
+
+ //
+ // Locate TlsServiceBinding protocol.
+ //
+ gBS->LocateProtocol (
+ &gEfiTlsServiceBindingProtocolGuid,
+ NULL,
+ (VOID **) &TlsSb
+ );
+ if (TlsSb == NULL) {
+ return NULL;
+ }
+
+ Status = TlsSb->CreateChild (TlsSb, &TlsChildHandle);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Status = gBS->OpenProtocol (
+ TlsChildHandle,
+ &gEfiTlsProtocolGuid,
+ (VOID **) TlsProto,
+ ImageHandle,
+ TlsChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ TlsSb->DestroyChild (TlsSb, TlsChildHandle);
+ return NULL;
+ }
+
+ Status = gBS->OpenProtocol (
+ TlsChildHandle,
+ &gEfiTlsConfigurationProtocolGuid,
+ (VOID **) TlsConfiguration,
+ ImageHandle,
+ TlsChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ TlsSb->DestroyChild (TlsSb, TlsChildHandle);
+ return NULL;
+ }
+
+ return TlsChildHandle;
+}
+
+/**
+ Create event for the TLS receive and transmit tokens which are used to receive and
+ transmit TLS related messages.
+
+ @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure.
+
+ @retval EFI_SUCCESS The events are created successfully.
+ @retval others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCreateTxRxEvent (
+ IN OUT HTTP_PROTOCOL *HttpInstance
+ )
+{
+ EFI_STATUS Status;
+
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ //
+ // For Tcp4TlsTxToken.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpCommonNotify,
+ &HttpInstance->TlsIsTxDone,
+ &HttpInstance->Tcp4TlsTxToken.CompletionToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ HttpInstance->Tcp4TlsTxData.Push = TRUE;
+ HttpInstance->Tcp4TlsTxData.Urgent = FALSE;
+ HttpInstance->Tcp4TlsTxData.DataLength = 0;
+ HttpInstance->Tcp4TlsTxData.FragmentCount = 1;
+ HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp4TlsTxData.DataLength;
+ HttpInstance->Tcp4TlsTxData.FragmentTable[0].FragmentBuffer = NULL;
+ HttpInstance->Tcp4TlsTxToken.Packet.TxData = &HttpInstance->Tcp4TlsTxData;
+ HttpInstance->Tcp4TlsTxToken.CompletionToken.Status = EFI_NOT_READY;
+
+ //
+ // For Tcp4TlsRxToken.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpCommonNotify,
+ &HttpInstance->TlsIsRxDone,
+ &HttpInstance->Tcp4TlsRxToken.CompletionToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ HttpInstance->Tcp4TlsRxData.DataLength = 0;
+ HttpInstance->Tcp4TlsRxData.FragmentCount = 1;
+ HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp4TlsRxData.DataLength ;
+ HttpInstance->Tcp4TlsRxData.FragmentTable[0].FragmentBuffer = NULL;
+ HttpInstance->Tcp4TlsRxToken.Packet.RxData = &HttpInstance->Tcp4TlsRxData;
+ HttpInstance->Tcp4TlsRxToken.CompletionToken.Status = EFI_NOT_READY;
+ } else {
+ //
+ // For Tcp6TlsTxToken.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpCommonNotify,
+ &HttpInstance->TlsIsTxDone,
+ &HttpInstance->Tcp6TlsTxToken.CompletionToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ HttpInstance->Tcp6TlsTxData.Push = TRUE;
+ HttpInstance->Tcp6TlsTxData.Urgent = FALSE;
+ HttpInstance->Tcp6TlsTxData.DataLength = 0;
+ HttpInstance->Tcp6TlsTxData.FragmentCount = 1;
+ HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp6TlsTxData.DataLength;
+ HttpInstance->Tcp6TlsTxData.FragmentTable[0].FragmentBuffer = NULL;
+ HttpInstance->Tcp6TlsTxToken.Packet.TxData = &HttpInstance->Tcp6TlsTxData;
+ HttpInstance->Tcp6TlsTxToken.CompletionToken.Status = EFI_NOT_READY;
+
+ //
+ // For Tcp6TlsRxToken.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpCommonNotify,
+ &HttpInstance->TlsIsRxDone,
+ &HttpInstance->Tcp6TlsRxToken.CompletionToken.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ HttpInstance->Tcp6TlsRxData.DataLength = 0;
+ HttpInstance->Tcp6TlsRxData.FragmentCount = 1;
+ HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentLength = HttpInstance->Tcp6TlsRxData.DataLength ;
+ HttpInstance->Tcp6TlsRxData.FragmentTable[0].FragmentBuffer = NULL;
+ HttpInstance->Tcp6TlsRxToken.Packet.RxData = &HttpInstance->Tcp6TlsRxData;
+ HttpInstance->Tcp6TlsRxToken.CompletionToken.Status = EFI_NOT_READY;
+ }
+
+ return Status;
+
+ERROR:
+ //
+ // Error handling
+ //
+ TlsCloseTxRxEvent (HttpInstance);
+
+ return Status;
+}
+
+/**
+ Close events in the TlsTxToken and TlsRxToken.
+
+ @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
+
+**/
+VOID
+EFIAPI
+TlsCloseTxRxEvent (
+ IN HTTP_PROTOCOL *HttpInstance
+ )
+{
+ ASSERT (HttpInstance != NULL);
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ if (NULL != HttpInstance->Tcp4TlsTxToken.CompletionToken.Event) {
+ gBS->CloseEvent(HttpInstance->Tcp4TlsTxToken.CompletionToken.Event);
+ HttpInstance->Tcp4TlsTxToken.CompletionToken.Event = NULL;
+ }
+
+ if (NULL != HttpInstance->Tcp4TlsRxToken.CompletionToken.Event) {
+ gBS->CloseEvent (HttpInstance->Tcp4TlsRxToken.CompletionToken.Event);
+ HttpInstance->Tcp4TlsRxToken.CompletionToken.Event = NULL;
+ }
+ } else {
+ if (NULL != HttpInstance->Tcp6TlsTxToken.CompletionToken.Event) {
+ gBS->CloseEvent(HttpInstance->Tcp6TlsTxToken.CompletionToken.Event);
+ HttpInstance->Tcp6TlsTxToken.CompletionToken.Event = NULL;
+ }
+
+ if (NULL != HttpInstance->Tcp6TlsRxToken.CompletionToken.Event) {
+ gBS->CloseEvent (HttpInstance->Tcp6TlsRxToken.CompletionToken.Event);
+ HttpInstance->Tcp6TlsRxToken.CompletionToken.Event = NULL;
+ }
+ }
+}
+
+/**
+ Read the TlsCaCertificate variable and configure it.
+
+ @param[in, out] HttpInstance The HTTP instance private data.
+
+ @retval EFI_SUCCESS TlsCaCertificate is configured.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_NOT_FOUND Fail to get 'TlsCaCertificate' variable.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+TlsConfigCertificate (
+ IN OUT HTTP_PROTOCOL *HttpInstance
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *CACert;
+ UINTN CACertSize;
+ UINT32 Index;
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_DATA *Cert;
+ UINTN CertCount;
+ UINT32 ItemDataSize;
+
+ CACert = NULL;
+ CACertSize = 0;
+
+ //
+ // Try to read the TlsCaCertificate variable.
+ //
+ Status = gRT->GetVariable (
+ EFI_TLS_CA_CERTIFICATE_VARIABLE,
+ &gEfiTlsCaCertificateGuid,
+ NULL,
+ &CACertSize,
+ NULL
+ );
+
+ if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
+ return Status;
+ }
+
+ //
+ // Allocate buffer and read the config variable.
+ //
+ CACert = AllocatePool (CACertSize);
+ if (CACert == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gRT->GetVariable (
+ EFI_TLS_CA_CERTIFICATE_VARIABLE,
+ &gEfiTlsCaCertificateGuid,
+ NULL,
+ &CACertSize,
+ CACert
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // GetVariable still error or the variable is corrupted.
+ // Fall back to the default value.
+ //
+ FreePool (CACert);
+
+ return EFI_NOT_FOUND;
+ }
+
+ ASSERT (CACert != NULL);
+
+ //
+ // Enumerate all data and erasing the target item.
+ //
+ ItemDataSize = (UINT32) CACertSize;
+ CertList = (EFI_SIGNATURE_LIST *) CACert;
+ while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ for (Index = 0; Index < CertCount; Index++) {
+ //
+ // EfiTlsConfigDataTypeCACertificate
+ //
+ Status = HttpInstance->TlsConfiguration->SetData (
+ HttpInstance->TlsConfiguration,
+ EfiTlsConfigDataTypeCACertificate,
+ Cert->SignatureData,
+ CertList->SignatureSize - sizeof (Cert->SignatureOwner)
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (CACert);
+ return Status;
+ }
+
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
+ }
+
+ ItemDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
+
+ FreePool (CACert);
+ return Status;
+}
+
+/**
+ Configure TLS session data.
+
+ @param[in, out] HttpInstance The HTTP instance private data.
+
+ @retval EFI_SUCCESS TLS session data is configured.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsConfigureSession (
+ IN OUT HTTP_PROTOCOL *HttpInstance
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // TlsConfigData initialization
+ //
+ HttpInstance->TlsConfigData.ConnectionEnd = EfiTlsClient;
+ HttpInstance->TlsConfigData.VerifyMethod = EFI_TLS_VERIFY_PEER;
+ HttpInstance->TlsConfigData.SessionState = EfiTlsSessionNotStarted;
+
+ //
+ // EfiTlsConnectionEnd,
+ // EfiTlsVerifyMethod
+ // EfiTlsSessionState
+ //
+ Status = HttpInstance->Tls->SetSessionData (
+ HttpInstance->Tls,
+ EfiTlsConnectionEnd,
+ &(HttpInstance->TlsConfigData.ConnectionEnd),
+ sizeof (EFI_TLS_CONNECTION_END)
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->SetSessionData (
+ HttpInstance->Tls,
+ EfiTlsVerifyMethod,
+ &HttpInstance->TlsConfigData.VerifyMethod,
+ sizeof (EFI_TLS_VERIFY)
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->SetSessionData (
+ HttpInstance->Tls,
+ EfiTlsSessionState,
+ &(HttpInstance->TlsConfigData.SessionState),
+ sizeof (EFI_TLS_SESSION_STATE)
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Tls Config Certificate
+ //
+ Status = TlsConfigCertificate (HttpInstance);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "TLS Certificate Config Error!\n"));
+ return Status;
+ }
+
+ //
+ // TlsCreateTxRxEvent
+ //
+ Status = TlsCreateTxRxEvent (HttpInstance);
+ if (EFI_ERROR (Status)) {
+ goto ERROR;
+ }
+
+ return Status;
+
+ERROR:
+ TlsCloseTxRxEvent (HttpInstance);
+
+ return Status;
+}
+
+/**
+ Transmit the Packet by processing the associated HTTPS token.
+
+ @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[in] Packet The packet to transmit.
+
+ @retval EFI_SUCCESS The packet is transmitted.
+ @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCommonTransmit (
+ IN OUT HTTP_PROTOCOL *HttpInstance,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_STATUS Status;
+ VOID *Data;
+ UINTN Size;
+
+ if ((HttpInstance == NULL) || (Packet == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ Size = sizeof (EFI_TCP4_TRANSMIT_DATA) +
+ (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA);
+ } else {
+ Size = sizeof (EFI_TCP6_TRANSMIT_DATA) +
+ (Packet->BlockOpNum - 1) * sizeof (EFI_TCP6_FRAGMENT_DATA);
+ }
+
+ Data = AllocatePool (Size);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ ((EFI_TCP4_TRANSMIT_DATA *) Data)->Push = TRUE;
+ ((EFI_TCP4_TRANSMIT_DATA *) Data)->Urgent = FALSE;
+ ((EFI_TCP4_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize;
+
+ //
+ // Build the fragment table.
+ //
+ ((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum;
+
+ NetbufBuildExt (
+ Packet,
+ (NET_FRAGMENT *) &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentTable[0],
+ &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount
+ );
+
+ HttpInstance->Tcp4TlsTxToken.Packet.TxData = (EFI_TCP4_TRANSMIT_DATA *) Data;
+
+ Status = EFI_DEVICE_ERROR;
+
+ //
+ // Transmit the packet.
+ //
+ Status = HttpInstance->Tcp4->Transmit (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsTxToken);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ while (!HttpInstance->TlsIsTxDone) {
+ HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
+ }
+
+ HttpInstance->TlsIsTxDone = FALSE;
+ Status = HttpInstance->Tcp4TlsTxToken.CompletionToken.Status;
+ } else {
+ ((EFI_TCP6_TRANSMIT_DATA *) Data)->Push = TRUE;
+ ((EFI_TCP6_TRANSMIT_DATA *) Data)->Urgent = FALSE;
+ ((EFI_TCP6_TRANSMIT_DATA *) Data)->DataLength = Packet->TotalSize;
+
+ //
+ // Build the fragment table.
+ //
+ ((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum;
+
+ NetbufBuildExt (
+ Packet,
+ (NET_FRAGMENT *) &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentTable[0],
+ &((EFI_TCP6_TRANSMIT_DATA *) Data)->FragmentCount
+ );
+
+ HttpInstance->Tcp6TlsTxToken.Packet.TxData = (EFI_TCP6_TRANSMIT_DATA *) Data;
+
+ Status = EFI_DEVICE_ERROR;
+
+ //
+ // Transmit the packet.
+ //
+ Status = HttpInstance->Tcp6->Transmit (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsTxToken);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ while (!HttpInstance->TlsIsTxDone) {
+ HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);
+ }
+
+ HttpInstance->TlsIsTxDone = FALSE;
+ Status = HttpInstance->Tcp6TlsTxToken.CompletionToken.Status;
+ }
+
+ON_EXIT:
+ FreePool (Data);
+
+ return Status;
+}
+
+/**
+ Receive the Packet by processing the associated HTTPS token.
+
+ @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[in] Packet The packet to transmit.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS The Packet is received.
+ @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCommonReceive (
+ IN OUT HTTP_PROTOCOL *HttpInstance,
+ IN NET_BUF *Packet,
+ IN EFI_EVENT Timeout
+ )
+{
+ EFI_TCP4_RECEIVE_DATA *Tcp4RxData;
+ EFI_TCP6_RECEIVE_DATA *Tcp6RxData;
+ EFI_STATUS Status;
+ NET_FRAGMENT *Fragment;
+ UINT32 FragmentCount;
+ UINT32 CurrentFragment;
+
+ Tcp4RxData = NULL;
+ Tcp6RxData = NULL;
+
+ if ((HttpInstance == NULL) || (Packet == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FragmentCount = Packet->BlockOpNum;
+ Fragment = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT));
+ if (Fragment == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Build the fragment table.
+ //
+ NetbufBuildExt (Packet, Fragment, &FragmentCount);
+
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ Tcp4RxData = HttpInstance->Tcp4TlsRxToken.Packet.RxData;
+ if (Tcp4RxData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Tcp4RxData->FragmentCount = 1;
+ } else {
+ Tcp6RxData = HttpInstance->Tcp6TlsRxToken.Packet.RxData;
+ if (Tcp6RxData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Tcp6RxData->FragmentCount = 1;
+ }
+
+ CurrentFragment = 0;
+ Status = EFI_SUCCESS;
+
+ while (CurrentFragment < FragmentCount) {
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ Tcp4RxData->DataLength = Fragment[CurrentFragment].Len;
+ Tcp4RxData->FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len;
+ Tcp4RxData->FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk;
+ Status = HttpInstance->Tcp4->Receive (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken);
+ } else {
+ Tcp6RxData->DataLength = Fragment[CurrentFragment].Len;
+ Tcp6RxData->FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len;
+ Tcp6RxData->FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk;
+ Status = HttpInstance->Tcp6->Receive (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken);
+ }
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ while (!HttpInstance->TlsIsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+ //
+ // Poll until some data is received or an error occurs.
+ //
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ HttpInstance->Tcp4->Poll (HttpInstance->Tcp4);
+ } else {
+ HttpInstance->Tcp6->Poll (HttpInstance->Tcp6);
+ }
+ }
+
+ if (!HttpInstance->TlsIsRxDone) {
+ //
+ // Timeout occurs, cancel the receive request.
+ //
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ HttpInstance->Tcp4->Cancel (HttpInstance->Tcp4, &HttpInstance->Tcp4TlsRxToken.CompletionToken);
+ } else {
+ HttpInstance->Tcp6->Cancel (HttpInstance->Tcp6, &HttpInstance->Tcp6TlsRxToken.CompletionToken);
+ }
+
+ Status = EFI_TIMEOUT;
+ goto ON_EXIT;
+ } else {
+ HttpInstance->TlsIsRxDone = FALSE;
+ }
+
+ if (!HttpInstance->LocalAddressIsIPv6) {
+ Status = HttpInstance->Tcp4TlsRxToken.CompletionToken.Status;
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Fragment[CurrentFragment].Len -= Tcp4RxData->FragmentTable[0].FragmentLength;
+ if (Fragment[CurrentFragment].Len == 0) {
+ CurrentFragment++;
+ } else {
+ Fragment[CurrentFragment].Bulk += Tcp4RxData->FragmentTable[0].FragmentLength;
+ }
+ } else {
+ Status = HttpInstance->Tcp6TlsRxToken.CompletionToken.Status;
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Fragment[CurrentFragment].Len -= Tcp6RxData->FragmentTable[0].FragmentLength;
+ if (Fragment[CurrentFragment].Len == 0) {
+ CurrentFragment++;
+ } else {
+ Fragment[CurrentFragment].Bulk += Tcp6RxData->FragmentTable[0].FragmentLength;
+ }
+ }
+ }
+
+ON_EXIT:
+
+ if (Fragment != NULL) {
+ FreePool (Fragment);
+ }
+
+ return Status;
+}
+
+/**
+ Receive one TLS PDU. An TLS PDU contains an TLS record header and it's
+ corresponding record data. These two parts will be put into two blocks of buffers in the
+ net buffer.
+
+ @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[out] Pdu The received TLS PDU.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS An TLS PDU is received.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_PROTOCOL_ERROR An unexpected TLS packet was received.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsReceiveOnePdu (
+ IN OUT HTTP_PROTOCOL *HttpInstance,
+ OUT NET_BUF **Pdu,
+ IN EFI_EVENT Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ LIST_ENTRY *NbufList;
+
+ UINT32 Len;
+
+ NET_BUF *PduHdr;
+ UINT8 *Header;
+ TLS_RECORD_HEADER RecordHeader;
+
+ NET_BUF *DataSeg;
+
+ NbufList = NULL;
+ PduHdr = NULL;
+ Header = NULL;
+ DataSeg = NULL;
+
+ NbufList = AllocatePool (sizeof (LIST_ENTRY));
+ if (NbufList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (NbufList);
+
+ //
+ // Allocate buffer to receive one TLS header.
+ //
+ Len = sizeof (TLS_RECORD_HEADER);
+ PduHdr = NetbufAlloc (Len);
+ if (PduHdr == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL);
+ if (Header == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // First step, receive one TLS header.
+ //
+ Status = TlsCommonReceive (HttpInstance, PduHdr, Timeout);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ RecordHeader = *(TLS_RECORD_HEADER *) Header;
+ if ((RecordHeader.ContentType == TlsContentTypeHandshake ||
+ RecordHeader.ContentType == TlsContentTypeAlert ||
+ RecordHeader.ContentType == TlsContentTypeChangeCipherSpec ||
+ RecordHeader.ContentType == TlsContentTypeApplicationData) &&
+ (RecordHeader.Version.Major == 0x03) && /// Major versions are same.
+ (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR ||
+ RecordHeader.Version.Minor ==TLS11_PROTOCOL_VERSION_MINOR ||
+ RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR)
+ ) {
+ InsertTailList (NbufList, &PduHdr->List);
+ } else {
+ Status = EFI_PROTOCOL_ERROR;
+ goto ON_EXIT;
+ }
+
+ Len = SwapBytes16(RecordHeader.Length);
+ if (Len == 0) {
+ //
+ // No TLS payload.
+ //
+ goto FORM_PDU;
+ }
+
+ //
+ // Allocate buffer to receive one TLS payload.
+ //
+ DataSeg = NetbufAlloc (Len);
+ if (DataSeg == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL);
+
+ //
+ // Second step, receive one TLS payload.
+ //
+ Status = TlsCommonReceive (HttpInstance, DataSeg, Timeout);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ InsertTailList (NbufList, &DataSeg->List);
+
+FORM_PDU:
+ //
+ // Form the PDU from a list of PDU.
+ //
+ *Pdu = NetbufFromBufList (NbufList, 0, 0, FreeNbufList, 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.
+ //
+ FreeNbufList (NbufList);
+ }
+
+ return Status;
+}
+
+/**
+ Connect one TLS session by finishing the TLS handshake process.
+
+ @param[in] HttpInstance The HTTP instance private data.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS The TLS session is established.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_ABORTED TLS session state is incorrect.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsConnectSession (
+ IN HTTP_PROTOCOL *HttpInstance,
+ IN EFI_EVENT Timeout
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *BufferOut;
+ UINTN BufferOutSize;
+ NET_BUF *PacketOut;
+ UINT8 *DataOut;
+ NET_BUF *Pdu;
+ UINT8 *BufferIn;
+ UINTN BufferInSize;
+ UINT8 *GetSessionDataBuffer;
+ UINTN GetSessionDataBufferSize;
+
+ BufferOut = NULL;
+ PacketOut = NULL;
+ DataOut = NULL;
+ Pdu = NULL;
+ BufferIn = NULL;
+
+ //
+ // Initialize TLS state.
+ //
+ HttpInstance->TlsSessionState = EfiTlsSessionNotStarted;
+ Status = HttpInstance->Tls->SetSessionData (
+ HttpInstance->Tls,
+ EfiTlsSessionState,
+ &(HttpInstance->TlsSessionState),
+ sizeof (EFI_TLS_SESSION_STATE)
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create ClientHello
+ //
+ BufferOutSize = DEF_BUF_LEN;
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ NULL,
+ 0,
+ BufferOut,
+ &BufferOutSize
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ FreePool (BufferOut);
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ NULL,
+ 0,
+ BufferOut,
+ &BufferOutSize
+ );
+ }
+ if (EFI_ERROR (Status)) {
+ FreePool (BufferOut);
+ return Status;
+ }
+
+ //
+ // Transmit ClientHello
+ //
+ PacketOut = NetbufAlloc ((UINT32) BufferOutSize);
+ DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+ if (DataOut == NULL) {
+ FreePool (BufferOut);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (DataOut, BufferOut, BufferOutSize);
+ Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+ FreePool (BufferOut);
+ NetbufFree (PacketOut);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ while(HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring && \
+ ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
+ //
+ // Receive one TLS record.
+ //
+ Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ BufferInSize = Pdu->TotalSize;
+ BufferIn = AllocateZeroPool (BufferInSize);
+ if (BufferIn == NULL) {
+ NetbufFree (Pdu);
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ NetbufCopy (Pdu, 0, (UINT32)BufferInSize, BufferIn);
+
+ NetbufFree (Pdu);
+
+ //
+ // Handle Receive data.
+ //
+ BufferOutSize = DEF_BUF_LEN;
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ BufferIn,
+ BufferInSize,
+ BufferOut,
+ &BufferOutSize
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ FreePool (BufferOut);
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ FreePool (BufferIn);
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ BufferIn,
+ BufferInSize,
+ BufferOut,
+ &BufferOutSize
+ );
+ }
+
+ FreePool (BufferIn);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (BufferOut);
+ return Status;
+ }
+
+ if (BufferOutSize != 0) {
+ //
+ // Transmit the response packet.
+ //
+ PacketOut = NetbufAlloc ((UINT32) BufferOutSize);
+ DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+ if (DataOut == NULL) {
+ FreePool (BufferOut);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (DataOut, BufferOut, BufferOutSize);
+
+ Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+ NetbufFree (PacketOut);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (BufferOut);
+ return Status;
+ }
+ }
+
+ FreePool (BufferOut);
+
+ //
+ // Get the session state, then decide whether need to continue handle received packet.
+ //
+ GetSessionDataBufferSize = DEF_BUF_LEN;
+ GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize);
+ if (GetSessionDataBuffer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->GetSessionData (
+ HttpInstance->Tls,
+ EfiTlsSessionState,
+ GetSessionDataBuffer,
+ &GetSessionDataBufferSize
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ FreePool (GetSessionDataBuffer);
+ GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize);
+ if (GetSessionDataBuffer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->GetSessionData (
+ HttpInstance->Tls,
+ EfiTlsSessionState,
+ GetSessionDataBuffer,
+ &GetSessionDataBufferSize
+ );
+ }
+ if (EFI_ERROR (Status)) {
+ FreePool(GetSessionDataBuffer);
+ return Status;
+ }
+
+ ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE));
+ HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer;
+
+ FreePool (GetSessionDataBuffer);
+
+ if(HttpInstance->TlsSessionState == EfiTlsSessionError) {
+ return EFI_ABORTED;
+ }
+ }
+
+ if (HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring) {
+ Status = EFI_ABORTED;
+ }
+
+ return Status;
+}
+
+/**
+ Close the TLS session and send out the close notification message.
+
+ @param[in] HttpInstance The HTTP instance private data.
+
+ @retval EFI_SUCCESS The TLS session is closed.
+ @retval EFI_INVALID_PARAMETER HttpInstance is NULL.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCloseSession (
+ IN HTTP_PROTOCOL *HttpInstance
+ )
+{
+ EFI_STATUS Status;
+
+ UINT8 *BufferOut;
+ UINTN BufferOutSize;
+
+ NET_BUF *PacketOut;
+ UINT8 *DataOut;
+
+ Status = EFI_SUCCESS;
+ BufferOut = NULL;
+ PacketOut = NULL;
+ DataOut = NULL;
+
+ if (HttpInstance == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HttpInstance->TlsSessionState = EfiTlsSessionClosing;
+
+ Status = HttpInstance->Tls->SetSessionData (
+ HttpInstance->Tls,
+ EfiTlsSessionState,
+ &(HttpInstance->TlsSessionState),
+ sizeof (EFI_TLS_SESSION_STATE)
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ BufferOutSize = DEF_BUF_LEN;
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ NULL,
+ 0,
+ BufferOut,
+ &BufferOutSize
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ FreePool (BufferOut);
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ NULL,
+ 0,
+ BufferOut,
+ &BufferOutSize
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ FreePool (BufferOut);
+ return Status;
+ }
+
+ PacketOut = NetbufAlloc ((UINT32) BufferOutSize);
+ DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+ if (DataOut == NULL) {
+ FreePool (BufferOut);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (DataOut, BufferOut, BufferOutSize);
+
+ Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+ FreePool (BufferOut);
+ NetbufFree (PacketOut);
+
+ return Status;
+}
+
+/**
+ Process one message according to the CryptMode.
+
+ @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[in] Message Pointer to the message buffer needed to processed.
+ @param[in] MessageSize Pointer to the message buffer size.
+ @param[in] ProcessMode Process mode.
+ @param[in, out] Fragment Only one Fragment returned after the Message is
+ processed successfully.
+
+ @retval EFI_SUCCESS Message is processed successfully.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsProcessMessage (
+ IN HTTP_PROTOCOL *HttpInstance,
+ IN UINT8 *Message,
+ IN UINTN MessageSize,
+ IN EFI_TLS_CRYPT_MODE ProcessMode,
+ IN OUT NET_FRAGMENT *Fragment
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Buffer;
+ UINT32 BufferSize;
+ UINT32 BytesCopied;
+ EFI_TLS_FRAGMENT_DATA *FragmentTable;
+ UINT32 FragmentCount;
+ EFI_TLS_FRAGMENT_DATA *OriginalFragmentTable;
+ UINTN Index;
+
+ Status = EFI_SUCCESS;
+ Buffer = NULL;
+ BufferSize = 0;
+ BytesCopied = 0;
+ FragmentTable = NULL;
+ OriginalFragmentTable = NULL;
+
+ //
+ // Rebuild fragment table from BufferIn.
+ //
+ FragmentCount = 1;
+ FragmentTable = AllocateZeroPool (FragmentCount * sizeof (EFI_TLS_FRAGMENT_DATA));
+ if (FragmentTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ FragmentTable->FragmentLength = (UINT32) MessageSize;
+ FragmentTable->FragmentBuffer = Message;
+
+ //
+ // Record the original FragmentTable.
+ //
+ OriginalFragmentTable = FragmentTable;
+
+ //
+ // Process the Message.
+ //
+ Status = HttpInstance->Tls->ProcessPacket (
+ HttpInstance->Tls,
+ &FragmentTable,
+ &FragmentCount,
+ ProcessMode
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Calculate the size according to FragmentTable.
+ //
+ for (Index = 0; Index < FragmentCount; Index++) {
+ BufferSize += FragmentTable[Index].FragmentLength;
+ }
+
+ //
+ // Allocate buffer for processed data.
+ //
+ Buffer = AllocateZeroPool (BufferSize);
+ if (Buffer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Copy the new FragmentTable buffer into Buffer.
+ //
+ for (Index = 0; Index < FragmentCount; Index++) {
+ CopyMem (
+ (Buffer + BytesCopied),
+ FragmentTable[Index].FragmentBuffer,
+ FragmentTable[Index].FragmentLength
+ );
+ BytesCopied += FragmentTable[Index].FragmentLength;
+
+ //
+ // Free the FragmentBuffer since it has been copied.
+ //
+ FreePool (FragmentTable[Index].FragmentBuffer);
+ }
+
+ Fragment->Len = BufferSize;
+ Fragment->Bulk = Buffer;
+
+ON_EXIT:
+
+ if (OriginalFragmentTable != NULL) {
+ FreePool (OriginalFragmentTable);
+ OriginalFragmentTable = NULL;
+ }
+
+ //
+ // Caller has the responsibility to free the FragmentTable.
+ //
+ if (FragmentTable != NULL) {
+ FreePool (FragmentTable);
+ FragmentTable = NULL;
+ }
+
+ return Status;
+}
+
+/**
+ Receive one fragment decrypted from one TLS record.
+
+ @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[in, out] Fragment The received Fragment.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS One fragment is received.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_ABORTED Something wrong decryption the message.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpsReceive (
+ IN HTTP_PROTOCOL *HttpInstance,
+ IN OUT NET_FRAGMENT *Fragment,
+ IN EFI_EVENT Timeout
+ )
+{
+ EFI_STATUS Status;
+ NET_BUF *Pdu;
+ TLS_RECORD_HEADER RecordHeader;
+ UINT8 *BufferIn;
+ UINTN BufferInSize;
+ NET_FRAGMENT TempFragment;
+ UINT8 *BufferOut;
+ UINTN BufferOutSize;
+ NET_BUF *PacketOut;
+ UINT8 *DataOut;
+ UINT8 *GetSessionDataBuffer;
+ UINTN GetSessionDataBufferSize;
+
+ Status = EFI_SUCCESS;
+ Pdu = NULL;
+ BufferIn = NULL;
+ BufferInSize = 0;
+ BufferOut = NULL;
+ BufferOutSize = 0;
+ PacketOut = NULL;
+ DataOut = NULL;
+ GetSessionDataBuffer = NULL;
+ GetSessionDataBufferSize = 0;
+
+ //
+ // Receive only one TLS record
+ //
+ Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ BufferInSize = Pdu->TotalSize;
+ BufferIn = AllocateZeroPool (BufferInSize);
+ if (BufferIn == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ NetbufFree (Pdu);
+ return Status;
+ }
+
+ NetbufCopy (Pdu, 0, (UINT32) BufferInSize, BufferIn);
+
+ NetbufFree (Pdu);
+
+ //
+ // Handle Receive data.
+ //
+ RecordHeader = *(TLS_RECORD_HEADER *) BufferIn;
+
+ if ((RecordHeader.ContentType == TlsContentTypeApplicationData) &&
+ (RecordHeader.Version.Major == 0x03) &&
+ (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR ||
+ RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR ||
+ RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR)
+ ) {
+ //
+ // Decrypt Packet.
+ //
+ Status = TlsProcessMessage (
+ HttpInstance,
+ BufferIn,
+ BufferInSize,
+ EfiTlsDecrypt,
+ &TempFragment
+ );
+
+ FreePool (BufferIn);
+
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_ABORTED) {
+ //
+ // Something wrong decryption the message.
+ // BuildResponsePacket() will be called to generate Error Alert message and send it out.
+ //
+ BufferOutSize = DEF_BUF_LEN;
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ NULL,
+ 0,
+ BufferOut,
+ &BufferOutSize
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ FreePool (BufferOut);
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ NULL,
+ 0,
+ BufferOut,
+ &BufferOutSize
+ );
+ }
+ if (EFI_ERROR (Status)) {
+ FreePool(BufferOut);
+ return Status;
+ }
+
+ if (BufferOutSize != 0) {
+ PacketOut = NetbufAlloc ((UINT32)BufferOutSize);
+ DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+ if (DataOut == NULL) {
+ FreePool (BufferOut);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (DataOut, BufferOut, BufferOutSize);
+
+ Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+ NetbufFree (PacketOut);
+ }
+
+ FreePool(BufferOut);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_ABORTED;
+ }
+
+ return Status;
+ }
+
+ //
+ // Parsing buffer.
+ //
+ ASSERT (((TLS_RECORD_HEADER *) (TempFragment.Bulk))->ContentType == TlsContentTypeApplicationData);
+
+ BufferInSize = ((TLS_RECORD_HEADER *) (TempFragment.Bulk))->Length;
+ BufferIn = AllocateZeroPool (BufferInSize);
+ if (BufferIn == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ CopyMem (BufferIn, TempFragment.Bulk + sizeof (TLS_RECORD_HEADER), BufferInSize);
+
+ //
+ // Free the buffer in TempFragment.
+ //
+ FreePool (TempFragment.Bulk);
+
+ } else if ((RecordHeader.ContentType == TlsContentTypeAlert) &&
+ (RecordHeader.Version.Major == 0x03) &&
+ (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR ||
+ RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR ||
+ RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR)
+ ) {
+ BufferOutSize = DEF_BUF_LEN;
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ FreePool (BufferIn);
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ BufferIn,
+ BufferInSize,
+ BufferOut,
+ &BufferOutSize
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ FreePool (BufferOut);
+ BufferOut = AllocateZeroPool (BufferOutSize);
+ if (BufferOut == NULL) {
+ FreePool (BufferIn);
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->BuildResponsePacket (
+ HttpInstance->Tls,
+ BufferIn,
+ BufferInSize,
+ BufferOut,
+ &BufferOutSize
+ );
+ }
+
+ FreePool (BufferIn);
+
+ if (EFI_ERROR (Status)) {
+ FreePool (BufferOut);
+ return Status;
+ }
+
+ if (BufferOutSize != 0) {
+ PacketOut = NetbufAlloc ((UINT32) BufferOutSize);
+ DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL);
+ if (DataOut == NULL) {
+ FreePool (BufferOut);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (DataOut, BufferOut, BufferOutSize);
+
+ Status = TlsCommonTransmit (HttpInstance, PacketOut);
+
+ NetbufFree (PacketOut);
+ }
+
+ FreePool (BufferOut);
+
+ //
+ // Get the session state.
+ //
+ GetSessionDataBufferSize = DEF_BUF_LEN;
+ GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize);
+ if (GetSessionDataBuffer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->GetSessionData (
+ HttpInstance->Tls,
+ EfiTlsSessionState,
+ GetSessionDataBuffer,
+ &GetSessionDataBufferSize
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ FreePool (GetSessionDataBuffer);
+ GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize);
+ if (GetSessionDataBuffer == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ return Status;
+ }
+
+ Status = HttpInstance->Tls->GetSessionData (
+ HttpInstance->Tls,
+ EfiTlsSessionState,
+ GetSessionDataBuffer,
+ &GetSessionDataBufferSize
+ );
+ }
+ if (EFI_ERROR (Status)) {
+ FreePool (GetSessionDataBuffer);
+ return Status;
+ }
+
+ ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE));
+ HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer;
+
+ FreePool (GetSessionDataBuffer);
+
+ if(HttpInstance->TlsSessionState == EfiTlsSessionError) {
+ DEBUG ((EFI_D_ERROR, "TLS Session State Error!\n"));
+ return EFI_ABORTED;
+ }
+
+ BufferIn = NULL;
+ BufferInSize = 0;
+ }
+
+ Fragment->Bulk = BufferIn;
+ Fragment->Len = (UINT32) BufferInSize;
+
+ return Status;
+}
+
diff --git a/Core/NetworkPkg/HttpDxe/HttpsSupport.h b/Core/NetworkPkg/HttpDxe/HttpsSupport.h
new file mode 100644
index 0000000000..68a6073ceb
--- /dev/null
+++ b/Core/NetworkPkg/HttpDxe/HttpsSupport.h
@@ -0,0 +1,261 @@
+/** @file
+ The header files of miscellaneous routines specific to Https for HttpDxe driver.
+
+Copyright (c) 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 __EFI_HTTPS_SUPPORT_H__
+#define __EFI_HTTPS_SUPPORT_H__
+
+#define HTTPS_DEFAULT_PORT 443
+
+#define HTTPS_FLAG "https://"
+
+/**
+ Check whether the Url is from Https.
+
+ @param[in] Url The pointer to a HTTP or HTTPS URL string.
+
+ @retval TRUE The Url is from HTTPS.
+ @retval FALSE The Url is from HTTP.
+
+**/
+BOOLEAN
+IsHttpsUrl (
+ IN CHAR8 *Url
+ );
+
+/**
+ Creates a Tls child handle, open EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[out] TlsProto Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[out] TlsConfiguration Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance.
+
+ @return The child handle with opened EFI_TLS_PROTOCOL and EFI_TLS_CONFIGURATION_PROTOCOL.
+
+**/
+EFI_HANDLE
+EFIAPI
+TlsCreateChild (
+ IN EFI_HANDLE ImageHandle,
+ OUT EFI_TLS_PROTOCOL **TlsProto,
+ OUT EFI_TLS_CONFIGURATION_PROTOCOL **TlsConfiguration
+ );
+
+/**
+ Create event for the TLS receive and transmit tokens which are used to receive and
+ transmit TLS related messages.
+
+ @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure.
+
+ @retval EFI_SUCCESS The events are created successfully.
+ @retval others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCreateTxRxEvent (
+ IN OUT HTTP_PROTOCOL *HttpInstance
+ );
+
+/**
+ Close events in the TlsTxToken and TlsRxToken.
+
+ @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
+
+**/
+VOID
+EFIAPI
+TlsCloseTxRxEvent (
+ IN HTTP_PROTOCOL *HttpInstance
+ );
+
+/**
+ Read the TlsCaCertificate variable and configure it.
+
+ @param[in, out] HttpInstance The HTTP instance private data.
+
+ @retval EFI_SUCCESS TlsCaCertificate is configured.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_NOT_FOUND Fail to get "TlsCaCertificate" variable.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+TlsConfigCertificate (
+ IN OUT HTTP_PROTOCOL *HttpInstance
+ );
+
+/**
+ Configure TLS session data.
+
+ @param[in, out] HttpInstance The HTTP instance private data.
+
+ @retval EFI_SUCCESS TLS session data is configured.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsConfigureSession (
+ IN OUT HTTP_PROTOCOL *HttpInstance
+ );
+
+/**
+ Transmit the Packet by processing the associated HTTPS token.
+
+ @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[in] Packet The packet to transmit.
+
+ @retval EFI_SUCCESS The packet is transmitted.
+ @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCommonTransmit (
+ IN OUT HTTP_PROTOCOL *HttpInstance,
+ IN NET_BUF *Packet
+ );
+
+/**
+ Receive the Packet by processing the associated HTTPS token.
+
+ @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[in] Packet The packet to transmit.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS The Packet is received.
+ @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCommonReceive (
+ IN OUT HTTP_PROTOCOL *HttpInstance,
+ IN NET_BUF *Packet,
+ IN EFI_EVENT Timeout
+ );
+
+/**
+ Receive one TLS PDU. An TLS PDU contains an TLS record header and it's
+ corresponding record data. These two parts will be put into two blocks of buffers in the
+ net buffer.
+
+ @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[out] Pdu The received TLS PDU.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS An TLS PDU is received.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_PROTOCOL_ERROR An unexpected TLS packet was received.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsReceiveOnePdu (
+ IN OUT HTTP_PROTOCOL *HttpInstance,
+ OUT NET_BUF **Pdu,
+ IN EFI_EVENT Timeout
+ );
+
+/**
+ Connect one TLS session by finishing the TLS handshake process.
+
+ @param[in] HttpInstance The HTTP instance private data.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS The TLS session is established.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_ABORTED TLS session state is incorrect.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsConnectSession (
+ IN HTTP_PROTOCOL *HttpInstance,
+ IN EFI_EVENT Timeout
+ );
+
+/**
+ Close the TLS session and send out the close notification message.
+
+ @param[in] HttpInstance The HTTP instance private data.
+
+ @retval EFI_SUCCESS The TLS session is closed.
+ @retval EFI_INVALID_PARAMETER HttpInstance is NULL or Packet is NULL.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval Others Other error as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsCloseSession (
+ IN HTTP_PROTOCOL *HttpInstance
+ );
+
+/**
+ Process one message according to the CryptMode.
+
+ @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[in] Message Pointer to the message buffer needed to processed.
+ @param[in] MessageSize Pointer to the message buffer size.
+ @param[in] ProcessMode Process mode.
+ @param[in, out] Fragment Only one Fragment returned after the Message is
+ processed successfully.
+
+ @retval EFI_SUCCESS Message is processed successfully.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsProcessMessage (
+ IN HTTP_PROTOCOL *HttpInstance,
+ IN UINT8 *Message,
+ IN UINTN MessageSize,
+ IN EFI_TLS_CRYPT_MODE ProcessMode,
+ IN OUT NET_FRAGMENT *Fragment
+ );
+
+/**
+ Receive one fragment decrypted from one TLS record.
+
+ @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure.
+ @param[in, out] Fragment The received Fragment.
+ @param[in] Timeout The time to wait for connection done.
+
+ @retval EFI_SUCCESS One fragment is received.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_ABORTED Something wrong decryption the message.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpsReceive (
+ IN HTTP_PROTOCOL *HttpInstance,
+ IN OUT NET_FRAGMENT *Fragment,
+ IN EFI_EVENT Timeout
+ );
+
+#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..7f719558a2
--- /dev/null
+++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.h
@@ -0,0 +1,122 @@
+/** @file
+ The header files of Http Utilities functions for HttpUtilities driver.
+
+ Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 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.
+
+**/
+
+#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>
+#include <Library/HttpLib.h>
+
+//
+// Consumed Protocols
+//
+#include <Protocol/HttpUtilities.h>
+#include <Protocol/Http.h>
+
+//
+// Protocol instances
+//
+extern EFI_HTTP_UTILITIES_PROTOCOL mHttpUtilitiesProtocol;
+
+/**
+ 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..3f7551a302
--- /dev/null
+++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.inf
@@ -0,0 +1,53 @@
+## @file
+# Implementation of EFI Http Utilities Protocol interfaces.
+#
+# Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+# (C) Copyright 2016 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.
+#
+#
+##
+
+[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
+ MODULE_UNI_FILE = HttpUtilitiesDxe.uni
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[Sources]
+ HttpUtilitiesDxe.h
+ HttpUtilitiesDxe.c
+ HttpUtilitiesProtocol.c
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ BaseLib
+ UefiLib
+ DebugLib
+ HttpLib
+
+[Protocols]
+ gEfiHttpUtilitiesProtocolGuid ## PRODUCES
+
+[Depex]
+ TRUE
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ HttpUtilitiesDxeExtra.uni
diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.uni b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.uni
new file mode 100644
index 0000000000..0efdcd5429
--- /dev/null
+++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// This module produces Http Utilities Protocol.
+//
+// This module produces Http Utilities 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces Http Utilities Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module provides Http Utilities Protocol."
+
diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni
new file mode 100644
index 0000000000..a9194be736
--- /dev/null
+++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// HttpUtilitiesDxe Localized Strings and Content
+//
+// 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UEFI HTTP UTILITIES DXE"
+
+
diff --git a/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesProtocol.c b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesProtocol.c
new file mode 100644
index 0000000000..645fca4084
--- /dev/null
+++ b/Core/NetworkPkg/HttpUtilitiesDxe/HttpUtilitiesProtocol.c
@@ -0,0 +1,387 @@
+/** @file
+ Implementation of EFI_HTTP_PROTOCOL protocol interfaces.
+
+ Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 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 "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 (HttpIsValidHttpHeader( DeleteList, DeleteCount, SeedHeaderFields[Index].FieldName)) {
+ Status = HttpSetFieldNameAndValue (
+ &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 = HttpSetFieldNameAndValue (
+ &NewHeaderFields[Index],
+ TempHeaderFields[Index].FieldName,
+ TempHeaderFields[Index].FieldValue
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ NewFieldCount = TempFieldCount;
+
+ for (Index = 0; Index < AppendCount; Index++) {
+ HttpHeader = HttpFindHeader (NewFieldCount, NewHeaderFields, AppendList[Index]->FieldName);
+ if (HttpHeader != NULL) {
+ Status = HttpSetFieldNameAndValue (
+ HttpHeader,
+ AppendList[Index]->FieldName,
+ AppendList[Index]->FieldValue
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ } else {
+ Status = HttpSetFieldNameAndValue (
+ &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;
+
+ *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;
+
+ ASSERT (*NewMessageSize == (UINTN)NewMessagePtr - (UINTN)(*NewMessage));
+
+ //
+ // Free allocated buffer
+ //
+ON_EXIT:
+ if (SeedHeaderFields != NULL) {
+ HttpFreeHeaderFields(SeedHeaderFields, SeedFieldCount);
+ }
+
+ if (TempHeaderFields != NULL) {
+ HttpFreeHeaderFields(TempHeaderFields, TempFieldCount);
+ }
+
+ if (NewHeaderFields != NULL) {
+ HttpFreeHeaderFields(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 = HttpGetFieldNameAndValue (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 = HttpGetFieldNameAndValue (Token, &FieldName, &FieldValue);
+ Token = NextToken;
+ if (FieldName == NULL || FieldValue == NULL) {
+ break;
+ }
+
+ Status = HttpSetFieldNameAndValue (&(*HeaderFields)[Index], FieldName, FieldValue);
+ if (EFI_ERROR (Status)) {
+ *FieldCount = 0;
+ HttpFreeHeaderFields (*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..56a8685c2c
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiConfig.c
@@ -0,0 +1,3906 @@
+/** @file
+ Helper functions for configuring or getting the parameters relating to iSCSI.
+
+Copyright (c) 2004 - 2017, 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) {
+ if (IP4_IS_UNSPECIFIED (NTOHL (Ip->Addr[0])) || IP4_IS_LOCAL_BROADCAST (NTOHL (Ip->Addr[0]))) {
+ return FALSE;
+ }
+ return TRUE;
+ } 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 Offset value specified by the input String.
+
+ @param[in] Configuration A null-terminated Unicode string in
+ <ConfigString> format.
+ @param[in] String The string is "&OFFSET=".
+ @param[out] Value The Offset value.
+
+ @retval EFI_OUT_OF_RESOURCES Insufficient resources to store neccessary
+ structures.
+ @retval EFI_SUCCESS Value of <Number> is outputted in Number
+ successfully.
+
+**/
+EFI_STATUS
+IScsiGetValue (
+ IN CONST EFI_STRING Configuration,
+ IN CHAR16 *String,
+ OUT UINTN *Value
+ )
+{
+ CHAR16 *StringPtr;
+ CHAR16 *TmpPtr;
+ CHAR16 *Str;
+ CHAR16 TmpStr[2];
+ UINTN Length;
+ UINTN Len;
+ UINTN Index;
+ UINT8 *Buf;
+ UINT8 DigitUint8;
+ EFI_STATUS Status;
+
+ //
+ // Get Value.
+ //
+ Buf = NULL;
+ StringPtr = StrStr (Configuration, String);
+ ASSERT(StringPtr != NULL);
+ StringPtr += StrLen (String);
+ TmpPtr = StringPtr;
+
+ while (*StringPtr != L'\0' && *StringPtr != L'&') {
+ StringPtr ++;
+ }
+ Length = StringPtr - TmpPtr;
+ Len = Length + 1;
+
+ Str = AllocateZeroPool (Len * sizeof (CHAR16));
+ if (Str == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ CopyMem (Str, TmpPtr, Len * sizeof (CHAR16));
+ *(Str + Length) = L'\0';
+
+ Len = (Len + 1) / 2;
+ Buf = (UINT8 *) AllocateZeroPool (Len);
+ if (Buf == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ ZeroMem (TmpStr, sizeof (TmpStr));
+ for (Index = 0; Index < Length; Index ++) {
+ TmpStr[0] = Str[Length - Index - 1];
+ DigitUint8 = (UINT8) StrHexToUint64 (TmpStr);
+ if ((Index & 1) == 0) {
+ Buf [Index/2] = DigitUint8;
+ } else {
+ Buf [Index/2] = (UINT8) ((DigitUint8 << 4) + Buf [Index/2]);
+ }
+ }
+
+ *Value = 0;
+ CopyMem (
+ Value,
+ Buf,
+ (((Length + 1) / 2) < sizeof (UINTN)) ? ((Length + 1) / 2) : sizeof (UINTN)
+ );
+
+ FreePool (Buf);
+ Status = EFI_SUCCESS;
+
+Exit:
+ if (Str != NULL) {
+ FreePool (Str);
+ }
+
+ return Status;
+}
+
+/**
+ 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;
+}
+
+/**
+ Extract the Index of the attempt list.
+
+ @param[in] AttemptNameList The Name list of the Attempts.
+ @param[out] AttemptIndexList The Index list of the Attempts.
+ @param[in] IsAddAttempts If TRUE, Indicates add one or more attempts.
+ If FALSE, Indicates delete attempts or change attempt order.
+
+ @retval EFI_SUCCESS The Attempt list is valid.
+ @retval EFI_INVALID_PARAMETERS The Attempt List is invalid.
+
+**/
+EFI_STATUS
+IScsiGetAttemptIndexList (
+ IN CHAR16 *AttemptNameList,
+ OUT UINT8 *AttemptIndexList,
+ IN BOOLEAN IsAddAttempts
+)
+{
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ CHAR16 *AttemptStr;
+ UINT8 AttemptIndex;
+ UINTN Len;
+ UINTN Index;
+
+ Index = 0;
+
+ if ((AttemptNameList == NULL) || (*AttemptNameList == L'\0')) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ AttemptStr = AttemptNameList;
+ Len = StrLen (L"attempt:");
+
+ while (*AttemptStr != L'\0') {
+ AttemptStr = StrStr (AttemptStr, L"attempt:");
+ if (AttemptStr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ AttemptStr += Len;
+ AttemptIndex = (UINT8)(*AttemptStr - L'0');
+ AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (IsAddAttempts) {
+ if ((AttemptConfigData != NULL) || ((AttemptIndex) > PcdGet8 (PcdMaxIScsiAttemptNumber))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ if (AttemptConfigData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ AttemptIndexList[Index] = AttemptIndex;
+ Index ++;
+ AttemptStr += 2;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ 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;
+ BOOLEAN DnsMode;
+
+ //
+ // Normal session configuration parameters.
+ //
+ SessionConfigData = &Attempt->SessionConfigData;
+ IfrNvData->Enabled = SessionConfigData->Enabled;
+ IfrNvData->IpMode = SessionConfigData->IpMode;
+ DnsMode = SessionConfigData->DnsMode;
+
+ 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);
+ if (SessionConfigData->TargetIp.v4.Addr[0] != '\0') {
+ 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));
+ if (SessionConfigData->TargetIp.v6.Addr[0] != '\0') {
+ IP6_COPY_ADDRESS (&Ip.v6, &SessionConfigData->TargetIp);
+ IScsiIpToStr (&Ip, TRUE, IfrNvData->TargetIp);
+ }
+ }
+
+ AsciiStrToUnicodeStrS (
+ SessionConfigData->TargetName,
+ IfrNvData->TargetName,
+ sizeof (IfrNvData->TargetName) / sizeof (IfrNvData->TargetName[0])
+ );
+
+ if (DnsMode) {
+ AsciiStrToUnicodeStrS (
+ SessionConfigData->TargetUrl,
+ IfrNvData->TargetIp,
+ sizeof (IfrNvData->TargetIp) / sizeof (IfrNvData->TargetIp[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 iSCSI configuration data into the IFR data Which will be used
+ to extract the iSCSI Keyword configuration in <ConfigAltResp> format.
+
+ @param[in, out] IfrNvData The IFR nv data.
+
+**/
+VOID
+EFIAPI
+IScsiConvertAttemptConfigDataToIfrNvDataByKeyword (
+ IN OUT ISCSI_CONFIG_IFR_NVDATA *IfrNvData
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt;
+ ISCSI_SESSION_CONFIG_NVDATA *SessionConfigData;
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData;
+ CHAR16 AttemptNameList[ATTEMPT_NAME_LIST_SIZE];
+ ISCSI_NIC_INFO *NicInfo;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ EFI_IP_ADDRESS Ip;
+ UINTN Index;
+ UINTN StringLen;
+
+ NicInfo = NULL;
+ ZeroMem (AttemptNameList, sizeof (AttemptNameList));
+
+ if ((mPrivate != NULL) && (mPrivate->AttemptCount != 0)) {
+ NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
+ Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ //
+ // Normal session configuration parameters.
+ //
+ SessionConfigData = &Attempt->SessionConfigData;
+
+ ASSERT ((Attempt->AttemptConfigIndex > 0) && (Attempt->AttemptConfigIndex <= FixedPcdGet8 (PcdMaxIScsiAttemptNumber)));
+ Index = Attempt->AttemptConfigIndex - 1;
+
+ //
+ // Save the attempt to AttemptNameList as Attempt:1 Attempt:2
+ //
+ AsciiStrToUnicodeStrS (
+ Attempt->AttemptName,
+ AttemptNameList + StrLen (AttemptNameList),
+ ATTEMPT_NAME_LIST_SIZE - StrLen (AttemptNameList)
+ );
+
+ StringLen = StrLen (AttemptNameList);
+ ASSERT (StringLen > 2);
+ *(AttemptNameList + StringLen - 2) = L':';
+ *(AttemptNameList + StringLen) = L' ';
+
+ AsciiStrToUnicodeStrS (
+ Attempt->AttemptName,
+ IfrNvData->ISCSIAttemptName + ATTEMPT_NAME_SIZE * Index,
+ ATTEMPT_NAME_LIST_SIZE - ATTEMPT_NAME_SIZE * Index
+ );
+
+ IfrNvData->ISCSIBootEnableList[Index] = SessionConfigData->Enabled;
+ IfrNvData->ISCSIIpAddressTypeList[Index] = SessionConfigData->IpMode;
+
+ IfrNvData->ISCSIInitiatorInfoViaDHCP[Index] = SessionConfigData->InitiatorInfoFromDhcp;
+ IfrNvData->ISCSITargetInfoViaDHCP[Index] = SessionConfigData->TargetInfoFromDhcp;
+ IfrNvData->ISCSIConnectRetry[Index] = SessionConfigData->ConnectRetryCount;
+ IfrNvData->ISCSIConnectTimeout[Index] = SessionConfigData->ConnectTimeout;
+ IfrNvData->ISCSITargetTcpPort[Index] = SessionConfigData->TargetPort;
+
+ if (SessionConfigData->IpMode == IP_MODE_IP4) {
+ CopyMem (&Ip.v4, &SessionConfigData->LocalIp, sizeof (EFI_IPv4_ADDRESS));
+ IScsiIpToStr (&Ip, FALSE, IfrNvData->Keyword[Index].ISCSIInitiatorIpAddress);
+ CopyMem (&Ip.v4, &SessionConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ IScsiIpToStr (&Ip, FALSE, IfrNvData->Keyword[Index].ISCSIInitiatorNetmask);
+ CopyMem (&Ip.v4, &SessionConfigData->Gateway, sizeof (EFI_IPv4_ADDRESS));
+ IScsiIpToStr (&Ip, FALSE, IfrNvData->Keyword[Index].ISCSIInitiatorGateway);
+ if (SessionConfigData->TargetIp.v4.Addr[0] != '\0') {
+ CopyMem (&Ip.v4, &SessionConfigData->TargetIp, sizeof (EFI_IPv4_ADDRESS));
+ IScsiIpToStr (&Ip, FALSE, IfrNvData->Keyword[Index].ISCSITargetIpAddress);
+ }
+ } else if (SessionConfigData->IpMode == IP_MODE_IP6) {
+ ZeroMem (IfrNvData->Keyword[Index].ISCSITargetIpAddress, sizeof (IfrNvData->TargetIp));
+ if (SessionConfigData->TargetIp.v6.Addr[0] != '\0') {
+ IP6_COPY_ADDRESS (&Ip.v6, &SessionConfigData->TargetIp);
+ IScsiIpToStr (&Ip, TRUE, IfrNvData->Keyword[Index].ISCSITargetIpAddress);
+ }
+ }
+
+ AsciiStrToUnicodeStrS (
+ SessionConfigData->TargetName,
+ IfrNvData->Keyword[Index].ISCSITargetName,
+ ISCSI_NAME_MAX_SIZE
+ );
+
+ if (SessionConfigData->DnsMode) {
+ AsciiStrToUnicodeStrS (
+ SessionConfigData->TargetUrl,
+ IfrNvData->Keyword[Index].ISCSITargetIpAddress,
+ sizeof (IfrNvData->Keyword[Index].ISCSITargetIpAddress) / sizeof (IfrNvData->Keyword[Index].ISCSITargetIpAddress[0])
+ );
+ }
+
+ IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->Keyword[Index].ISCSILun);
+ IScsiConvertIsIdToString (IfrNvData->Keyword[Index].ISCSIIsId, SessionConfigData->IsId);
+
+ IfrNvData->ISCSIAuthenticationMethod[Index] = Attempt->AuthenticationType;
+
+ if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ AuthConfigData = &Attempt->AuthConfigData.CHAP;
+ IfrNvData->ISCSIChapType[Index] = AuthConfigData->CHAPType;
+ AsciiStrToUnicodeStrS (
+ AuthConfigData->CHAPName,
+ IfrNvData->Keyword[Index].ISCSIChapUsername,
+ ISCSI_CHAP_NAME_STORAGE
+ );
+
+ AsciiStrToUnicodeStrS (
+ AuthConfigData->CHAPSecret,
+ IfrNvData->Keyword[Index].ISCSIChapSecret,
+ ISCSI_CHAP_SECRET_STORAGE
+ );
+
+ AsciiStrToUnicodeStrS (
+ AuthConfigData->ReverseCHAPName,
+ IfrNvData->Keyword[Index].ISCSIReverseChapUsername,
+ ISCSI_CHAP_NAME_STORAGE
+ );
+
+ AsciiStrToUnicodeStrS (
+ AuthConfigData->ReverseCHAPSecret,
+ IfrNvData->Keyword[Index].ISCSIReverseChapSecret,
+ ISCSI_CHAP_SECRET_STORAGE
+ );
+ }
+ }
+ CopyMem(IfrNvData->ISCSIDisplayAttemptList, AttemptNameList, ATTEMPT_NAME_LIST_SIZE);
+ }
+
+ 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
+ );
+ CopyMem (
+ IfrNvData->ISCSIMacAddr + StrLen (IfrNvData->ISCSIMacAddr),
+ MacString,
+ StrLen (MacString) * sizeof (CHAR16)
+ );
+
+ *(IfrNvData->ISCSIMacAddr + StrLen (IfrNvData->ISCSIMacAddr)) = L'/';
+ }
+}
+
+/**
+ 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 (!Attempt->SessionConfigData.DnsMode) {
+ if (!IpIsUnicast (&Attempt->SessionConfigData.TargetIp, IfrNvData->IpMode)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Target IP is invalid!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ if (Attempt->SessionConfigData.TargetUrl[0] == '\0') {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"iSCSI target Url should not be NULL!",
+ 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_SIZE * sizeof (CHAR16));
+ if (AttemptName1 == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AttemptName2 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_SIZE * sizeof (CHAR16));
+ if (AttemptName2 == NULL) {
+ FreePool (AttemptName1);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AsciiStrToUnicodeStrS (Attempt->AttemptName, AttemptName1, ATTEMPT_NAME_SIZE);
+ AsciiStrToUnicodeStrS (SameNicAttempt->AttemptName, AttemptName2, ATTEMPT_NAME_SIZE);
+
+ 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.
+ //
+ 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
+ // 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.
+ //
+
+ //
+ // 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++;
+
+ 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 ();
+ }
+ Attempt->SessionConfigData.Enabled = IfrNvData->Enabled;
+
+ //
+ // Record the user configuration information in NVR.
+ //
+ UnicodeSPrint (mPrivate->PortString, (UINTN) ISCSI_NAME_IFR_MAX_SIZE, L"Attempt %d", Attempt->AttemptConfigIndex);
+
+ FreePool (MacString);
+
+ return gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ Attempt
+ );
+}
+
+/**
+ Convert the IFR data configured by keyword to iSCSI configuration data.
+
+ @param[in] IfrNvData Point to ISCSI_CONFIG_IFR_NVDATA.
+ @param[in] OffSet The offset of the variable to the configuration structure.
+
+ @retval EFI_INVALID_PARAMETER Any input or configured parameter is invalid.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+IScsiConvertlfrNvDataToAttemptConfigDataByKeyword (
+ IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData,
+ IN UINTN OffSet
+ )
+{
+ ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt;
+ UINT8 AttemptIndex;
+ UINT8 Index;
+ UINT8 ChapSecretLen;
+ UINT8 ReverseChapSecretLen;
+ CHAR16 *AttemptName1;
+ CHAR16 *AttemptName2;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *SameNicAttempt;
+ CHAR8 LunString[ISCSI_LUN_STR_MAX_LEN];
+ CHAR8 IScsiName[ISCSI_NAME_MAX_SIZE];
+ CHAR8 IpString[IP_STR_MAX_SIZE];
+ EFI_IP_ADDRESS HostIp;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS Gateway;
+ EFI_INPUT_KEY Key;
+ UINT64 Lun;
+ EFI_STATUS Status;
+
+ Attempt = NULL;
+ ZeroMem (IScsiName, sizeof (IScsiName));
+
+ if (OffSet < ATTEMPT_BOOTENABLE_VAR_OFFSET) {
+ return EFI_SUCCESS;
+
+ } else if ((OffSet >= ATTEMPT_BOOTENABLE_VAR_OFFSET) && (OffSet < ATTEMPT_ADDRESS_TYPE_VAR_OFFSET)) {
+ AttemptIndex = (UINT8) ((OffSet - ATTEMPT_BOOTENABLE_VAR_OFFSET) + 1);
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ IfrNvData->Enabled = IfrNvData->ISCSIBootEnableList[AttemptIndex - 1];
+ //
+ // Validate the configuration of attempt.
+ //
+ if (IfrNvData->Enabled != ISCSI_DISABLED) {
+ //
+ // 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_SIZE * sizeof (CHAR16));
+ if (AttemptName1 == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AttemptName2 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_SIZE * sizeof (CHAR16));
+ if (AttemptName2 == NULL) {
+ FreePool (AttemptName1);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AsciiStrToUnicodeStrS (Attempt->AttemptName, AttemptName1, ATTEMPT_NAME_SIZE);
+ AsciiStrToUnicodeStrS (SameNicAttempt->AttemptName, AttemptName2, ATTEMPT_NAME_SIZE);
+
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Warning! \"%s\" uses same NIC as Attempt \"%s\".",
+ AttemptName1,
+ AttemptName2
+ );
+
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ mPrivate->PortString,
+ NULL
+ );
+
+ FreePool (AttemptName1);
+ FreePool (AttemptName2);
+ }
+ }
+
+ 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++;
+ }
+ }
+ Attempt->SessionConfigData.Enabled = IfrNvData->Enabled;
+
+ } else if ((OffSet >= ATTEMPT_ADDRESS_TYPE_VAR_OFFSET) && (OffSet < ATTEMPT_CONNECT_RETRY_VAR_OFFSET)) {
+ AttemptIndex = (UINT8) ((OffSet - ATTEMPT_ADDRESS_TYPE_VAR_OFFSET) + 1);
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Attempt->SessionConfigData.IpMode = IfrNvData->ISCSIIpAddressTypeList[AttemptIndex - 1];
+ if (Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) {
+ Attempt->AutoConfigureMode = 0;
+ }
+
+ } else if ((OffSet >= ATTEMPT_CONNECT_RETRY_VAR_OFFSET) && (OffSet < ATTEMPT_CONNECT_TIMEOUT_VAR_OFFSET)) {
+ AttemptIndex = (UINT8) ((OffSet - ATTEMPT_CONNECT_RETRY_VAR_OFFSET) + 1);
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IfrNvData->ISCSIConnectRetry[AttemptIndex - 1] > CONNECT_MAX_RETRY) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"The minimum value is 0 and the maximum is 16. 0 means no retry.",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ Attempt->SessionConfigData.ConnectRetryCount = IfrNvData->ISCSIConnectRetry[AttemptIndex - 1];
+
+ } else if ((OffSet >= ATTEMPT_CONNECT_TIMEOUT_VAR_OFFSET) && (OffSet < ATTEMPT_INITIATOR_VIA_DHCP_VAR_OFFSET)) {
+ AttemptIndex = (UINT8) ((OffSet - ATTEMPT_CONNECT_TIMEOUT_VAR_OFFSET) / 2 + 1);
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((IfrNvData->ISCSIConnectTimeout[AttemptIndex - 1] < CONNECT_MIN_TIMEOUT) ||
+ (IfrNvData->ISCSIConnectTimeout[AttemptIndex - 1] > CONNECT_MAX_TIMEOUT)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"The minimum value is 100 milliseconds and the maximum is 20 seconds.",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Attempt->SessionConfigData.ConnectTimeout = IfrNvData->ISCSIConnectTimeout[AttemptIndex - 1];
+ if (Attempt->SessionConfigData.ConnectTimeout == 0) {
+ Attempt->SessionConfigData.ConnectTimeout = CONNECT_DEFAULT_TIMEOUT;
+ }
+
+ } else if ((OffSet >= ATTEMPT_INITIATOR_VIA_DHCP_VAR_OFFSET) && (OffSet < ATTEMPT_TARGET_VIA_DHCP_VAR_OFFSET)) {
+ AttemptIndex = (UINT8) ((OffSet - ATTEMPT_INITIATOR_VIA_DHCP_VAR_OFFSET) + 1);
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ Attempt->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->ISCSIInitiatorInfoViaDHCP[AttemptIndex - 1];
+
+ } else if ((OffSet >= ATTEMPT_TARGET_VIA_DHCP_VAR_OFFSET) && (OffSet < ATTEMPT_TARGET_TCP_PORT_VAR_OFFSET)) {
+ AttemptIndex = (UINT8) ((OffSet - ATTEMPT_TARGET_VIA_DHCP_VAR_OFFSET) + 1);
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) && (Attempt->SessionConfigData.InitiatorInfoFromDhcp)) {
+ Attempt->SessionConfigData.TargetInfoFromDhcp = IfrNvData->ISCSITargetInfoViaDHCP[AttemptIndex - 1];
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of IpMode or Enable DHCP!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_TARGET_TCP_PORT_VAR_OFFSET) && (OffSet < ATTEMPT_AUTHENTICATION_METHOD_VAR_OFFSET)) {
+ AttemptIndex = (UINT8) ((OffSet - ATTEMPT_TARGET_TCP_PORT_VAR_OFFSET) / 2 + 1);
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if ((Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) && (!Attempt->SessionConfigData.TargetInfoFromDhcp)) {
+ Attempt->SessionConfigData.TargetPort = IfrNvData->ISCSITargetTcpPort[AttemptIndex - 1];
+ if (Attempt->SessionConfigData.TargetPort == 0) {
+ Attempt->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of IpMode or Target Via DHCP!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_AUTHENTICATION_METHOD_VAR_OFFSET) && (OffSet < ATTEMPT_CHARTYPE_VAR_OFFSET)) {
+ AttemptIndex = (UINT8) ((OffSet - ATTEMPT_AUTHENTICATION_METHOD_VAR_OFFSET) + 1);
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Attempt->AuthenticationType = IfrNvData->ISCSIAuthenticationMethod[AttemptIndex - 1];
+
+ } else if ((OffSet >= ATTEMPT_CHARTYPE_VAR_OFFSET) && (OffSet < ATTEMPT_ISID_VAR_OFFSET)) {
+ AttemptIndex = (UINT8) ((OffSet - ATTEMPT_CHARTYPE_VAR_OFFSET) + 1);
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ Attempt->AuthConfigData.CHAP.CHAPType = IfrNvData->ISCSIChapType[AttemptIndex - 1];
+ }
+
+ } else if (OffSet >= ATTEMPT_ISID_VAR_OFFSET) {
+ Index = (UINT8) ((OffSet - ATTEMPT_ISID_VAR_OFFSET) / sizeof (KEYWORD_STR));
+ AttemptIndex = Index + 1;
+ Attempt = IScsiConfigGetAttemptByConfigIndex (AttemptIndex);
+ if (Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OffSet = OffSet - Index * sizeof (KEYWORD_STR);
+
+ if ((OffSet >= ATTEMPT_ISID_VAR_OFFSET) && (OffSet < ATTEMPT_INITIATOR_IP_ADDRESS_VAR_OFFSET)) {
+ IScsiParseIsIdFromString (IfrNvData->Keyword[Index].ISCSIIsId, Attempt->SessionConfigData.IsId);
+
+ } else if ((OffSet >= ATTEMPT_INITIATOR_IP_ADDRESS_VAR_OFFSET) && (OffSet < ATTEMPT_INITIATOR_NET_MASK_VAR_OFFSET)) {
+ if ((Attempt->SessionConfigData.IpMode == IP_MODE_IP4) && (!Attempt->SessionConfigData.InitiatorInfoFromDhcp)) {
+ //
+ // Config Local ip
+ //
+ Status = NetLibStrToIp4 (IfrNvData->Keyword[Index].ISCSIInitiatorIpAddress, &HostIp.v4);
+ if (EFI_ERROR (Status) || ((Attempt->SessionConfigData.SubnetMask.Addr[0] != 0) &&
+ !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), NTOHL(*(UINT32*)Attempt->SessionConfigData.SubnetMask.Addr)))) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid IP address!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ } else {
+ CopyMem (&Attempt->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4));
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of IpMode or Enable DHCP!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_INITIATOR_NET_MASK_VAR_OFFSET) && (OffSet < ATTEMPT_INITIATOR_GATE_WAY_VAR_OFFSET)) {
+ if ((Attempt->SessionConfigData.IpMode == IP_MODE_IP4) && (!Attempt->SessionConfigData.InitiatorInfoFromDhcp)) {
+ Status = NetLibStrToIp4 (IfrNvData->Keyword[Index].ISCSIInitiatorNetmask, &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
+ );
+ return EFI_INVALID_PARAMETER;
+ } else {
+ CopyMem (&Attempt->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4));
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of IpMode or Enable DHCP!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_INITIATOR_GATE_WAY_VAR_OFFSET) && (OffSet < ATTEMPT_TARGET_NAME_VAR_OFFSET)) {
+ if ((Attempt->SessionConfigData.IpMode == IP_MODE_IP4) && (!Attempt->SessionConfigData.InitiatorInfoFromDhcp)) {
+ Status = NetLibStrToIp4 (IfrNvData->Keyword[Index].ISCSIInitiatorGateway, &Gateway.v4);
+ if (EFI_ERROR (Status) ||
+ ((Gateway.Addr[0] != 0) && (Attempt->SessionConfigData.SubnetMask.Addr[0] != 0) &&
+ !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), NTOHL(*(UINT32*)Attempt->SessionConfigData.SubnetMask.Addr)))) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Gateway!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ } else {
+ CopyMem (&Attempt->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4));
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of IpMode or Enable DHCP!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_TARGET_NAME_VAR_OFFSET) && (OffSet < ATTEMPT_TARGET_IP_ADDRESS_VAR_OFFSET)) {
+ if ((Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) && (!Attempt->SessionConfigData.TargetInfoFromDhcp)) {
+ UnicodeStrToAsciiStrS (IfrNvData->Keyword[Index].ISCSITargetName, 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 (Attempt->SessionConfigData.TargetName, ISCSI_NAME_MAX_SIZE, IScsiName);
+ }
+ if (Attempt->SessionConfigData.Enabled != ISCSI_DISABLED) {
+ if (Attempt->SessionConfigData.TargetName[0] == L'\0') {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"iSCSI target name is NULL!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of IpMode or Target Via DHCP!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_TARGET_IP_ADDRESS_VAR_OFFSET) && (OffSet < ATTEMPT_LUN_VAR_OFFSET)) {
+ if ((Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) && (!Attempt->SessionConfigData.TargetInfoFromDhcp)) {
+ UnicodeStrToAsciiStrS (IfrNvData->Keyword[Index].ISCSITargetIpAddress, IpString, sizeof (IpString));
+ Status = IScsiAsciiStrToIp (IpString, Attempt->SessionConfigData.IpMode, &HostIp);
+ if (EFI_ERROR (Status) || !IpIsUnicast (&HostIp, Attempt->SessionConfigData.IpMode)) {
+ Attempt->SessionConfigData.DnsMode = TRUE;
+ ZeroMem (&Attempt->SessionConfigData.TargetIp, sizeof (Attempt->SessionConfigData.TargetIp));
+ UnicodeStrToAsciiStrS (IfrNvData->Keyword[Index].ISCSITargetIpAddress, Attempt->SessionConfigData.TargetUrl, ISCSI_NAME_MAX_SIZE);
+ } else {
+ Attempt->SessionConfigData.DnsMode = FALSE;
+ CopyMem (&Attempt->SessionConfigData.TargetIp, &HostIp, sizeof (HostIp));
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of IpMode or Target Via DHCP!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_LUN_VAR_OFFSET) && (OffSet < ATTEMPT_CHAR_USER_NAME_VAR_OFFSET)) {
+ if ((Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) && (Attempt->SessionConfigData.TargetInfoFromDhcp == 0)) {
+ //
+ // Config LUN.
+ //
+ UnicodeStrToAsciiStrS (IfrNvData->Keyword[Index].ISCSILun, LunString, ISCSI_LUN_STR_MAX_LEN);
+ Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid LUN string, Examples are: 4752-3A4F-6b7e-2F99, 6734-9-156f-127, 4186-9!",
+ NULL
+ );
+ } else {
+ CopyMem (&Attempt->SessionConfigData.BootLun, &Lun, sizeof (Lun));
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of IpMode or Target Via DHCP!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_CHAR_USER_NAME_VAR_OFFSET) && (OffSet < ATTEMPT_CHAR_SECRET_VAR_OFFSET)) {
+ if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ UnicodeStrToAsciiStrS (
+ IfrNvData->Keyword[Index].ISCSIChapUsername,
+ Attempt->AuthConfigData.CHAP.CHAPName,
+ ISCSI_CHAP_NAME_STORAGE
+ );
+
+ if (Attempt->SessionConfigData.Enabled != ISCSI_DISABLED) {
+ if (IfrNvData->Keyword[Index].ISCSIChapUsername[0] == L'\0') {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"CHAP Name is invalid!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of AuthenticationType!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_CHAR_SECRET_VAR_OFFSET) && (OffSet < ATTEMPT_CHAR_REVERSE_USER_NAME_VAR_OFFSET)) {
+ if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ ChapSecretLen = (UINT8)StrLen (IfrNvData->Keyword[Index].ISCSIChapSecret);
+ UnicodeStrToAsciiStrS (
+ IfrNvData->Keyword[Index].ISCSIChapSecret,
+ Attempt->AuthConfigData.CHAP.CHAPSecret,
+ ISCSI_CHAP_SECRET_STORAGE
+ );
+
+ if (Attempt->SessionConfigData.Enabled != ISCSI_DISABLED) {
+ if ((ChapSecretLen < ISCSI_CHAP_SECRET_MIN_LEN) || (ChapSecretLen > ISCSI_CHAP_SECRET_MAX_LEN)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"The Chap Secret minimum length is 12 bytes and the maximum length is 16 bytes.",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of AuthenticationType!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if ((OffSet >= ATTEMPT_CHAR_REVERSE_USER_NAME_VAR_OFFSET) && (OffSet < ATTEMPT_CHAR_REVERSE_SECRET_VAR_OFFSET)) {
+ if (Attempt->AuthConfigData.CHAP.CHAPType == ISCSI_CHAP_MUTUAL) {
+ UnicodeStrToAsciiStrS (
+ IfrNvData->Keyword[Index].ISCSIReverseChapUsername,
+ Attempt->AuthConfigData.CHAP.ReverseCHAPName,
+ ISCSI_CHAP_NAME_STORAGE
+ );
+ if (Attempt->SessionConfigData.Enabled != ISCSI_DISABLED) {
+ if (IfrNvData->Keyword[Index].ISCSIReverseChapUsername[0] == L'\0') {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Reverse CHAP Name is invalid!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of AuthenticationType or Chap Type!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else if (OffSet >= ATTEMPT_CHAR_REVERSE_SECRET_VAR_OFFSET) {
+ if (Attempt->AuthConfigData.CHAP.CHAPType == ISCSI_CHAP_MUTUAL) {
+ ReverseChapSecretLen = (UINT8)StrLen (IfrNvData->Keyword[Index].ISCSIReverseChapSecret);
+ UnicodeStrToAsciiStrS (
+ IfrNvData->Keyword[Index].ISCSIReverseChapSecret,
+ Attempt->AuthConfigData.CHAP.ReverseCHAPSecret,
+ ISCSI_CHAP_SECRET_STORAGE
+ );
+
+ if (Attempt->SessionConfigData.Enabled != ISCSI_DISABLED) {
+ if ((ReverseChapSecretLen < ISCSI_CHAP_SECRET_MIN_LEN) || (ReverseChapSecretLen > ISCSI_CHAP_SECRET_MAX_LEN)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"The Reverse CHAP Secret minimum length is 12 bytes and the maximum length is 16 bytes.",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ } else {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid Configuration, Check value of AuthenticationType or Chap Type!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+
+
+ //
+ // Record the user configuration information in NVR.
+ //
+ ASSERT (Attempt != NULL);
+ UnicodeSPrint (mPrivate->PortString, (UINTN) ISCSI_NAME_IFR_MAX_SIZE, L"Attempt %d", Attempt->AttemptConfigIndex);
+ 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;
+}
+
+/**
+ Update the MAIN form to display the configured attempts.
+
+**/
+VOID
+IScsiConfigUpdateAttempt (
+ VOID
+ )
+{
+ 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);
+ if (AttemptConfigData->Actived == ISCSI_ACTIVE_ENABLED) {
+ //
+ // Update Attempt Help Info.
+ //
+ UnicodeSPrint (mPrivate->PortString, (UINTN) ISCSI_NAME_IFR_MAX_SIZE, L"Attempt %d", (UINTN) AttemptConfigData->AttemptConfigIndex);
+ 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 "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;
+}
+
+/**
+ Add the attempts by keyword 'iSCSIAddAttempts', you can use this keyword with
+ value 'attempt:1 attempt:2' etc to add one or more attempts once. This is different
+ with IScsiConfigAddAttempt function which is used to add attempt by UI configuration.
+
+ @param[in] AttemptList The new attempt List will be added.
+
+ @retval EFI_SUCCESS The operation to add attempt list successfully.
+ @retval EFI_INVALID_PARAMETER Any parameter is invalid.
+ @retval EFI_NOT_FOUND Cannot find the corresponding variable.
+ @retval EFI_OUT_OF_RESOURCES Fail to finish the operation due to lack of
+ resources.
+
+**/
+EFI_STATUS
+IScsiConfigAddAttemptsByKeywords (
+ IN UINT8 *AttemptList
+ )
+{
+ UINT8 Index;
+ UINT8 Number;
+ UINTN TotalNumber;
+ UINT8 Nic;
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ UINT8 *AttemptConfigOrderTmp;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ ISCSI_NIC_INFO *NicInfo;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ CHAR16 IScsiMode[64];
+ CHAR16 IpMode[64];
+ EFI_STATUS Status;
+
+ Nic = mPrivate->CurrentNic;
+ NicInfo = IScsiGetNicInfoByIndex (Nic);
+ if (NicInfo == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // The MAC info will be recorded in Config Data.
+ //
+ IScsiMacAddrToStr (
+ &NicInfo->PermanentAddress,
+ NicInfo->HwAddressSize,
+ NicInfo->VlanId,
+ MacString
+ );
+
+ for (Index = 0; Index < PcdGet8 (PcdMaxIScsiAttemptNumber); Index++) {
+ if (AttemptList[Index] == 0) {
+ continue;
+ }
+
+ //
+ // Add the attempt.
+ //
+ Number = AttemptList[Index];
+
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ Number
+ );
+
+ GetVariable2 (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ (VOID**)&AttemptConfigData,
+ NULL
+ );
+ if (AttemptConfigData == NULL || AttemptConfigData->Actived == ISCSI_ACTIVE_ENABLED) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ AttemptConfigData->Actived = ISCSI_ACTIVE_ENABLED;
+ AttemptConfigData->NicIndex = NicInfo->NicIndex;
+ UnicodeStrToAsciiStrS (MacString, AttemptConfigData->MacString, ISCSI_MAX_MAC_STRING_LEN);
+
+ //
+ // 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);
+
+ //
+ // Configure the iSCSI Mode and IpMode to default.
+ // Add Attempt Help Info.
+ //
+ UnicodeSPrint (IScsiMode, 64, L"Disabled");
+ UnicodeSPrint (IpMode, 64, L"IP4");
+ 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
+ );
+ if (AttemptConfigData->AttemptTitleHelpToken == 0) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Get current Attempt order and number.
+ //
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"AttemptOrder",
+ &gIScsiConfigGuid,
+ &AttemptConfigOrderSize
+ );
+ TotalNumber = AttemptConfigOrderSize / sizeof (UINT8);
+ TotalNumber++;
+
+ //
+ // Append the new created attempt order to the end.
+ //
+ AttemptConfigOrderTmp = AllocateZeroPool (TotalNumber * sizeof (UINT8));
+ if (AttemptConfigOrderTmp == NULL) {
+ if (AttemptConfigOrder != NULL) {
+ FreePool (AttemptConfigOrder);
+ }
+ return EFI_OUT_OF_RESOURCES;
+ }
+ if (AttemptConfigOrder != NULL) {
+ CopyMem (AttemptConfigOrderTmp, AttemptConfigOrder, AttemptConfigOrderSize);
+ FreePool (AttemptConfigOrder);
+ }
+
+ AttemptConfigOrderTmp[TotalNumber - 1] = Number;
+ AttemptConfigOrder = AttemptConfigOrderTmp;
+ 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;
+ }
+
+ //
+ // Record the attempt in global link list.
+ //
+ InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
+ mPrivate->AttemptCount++;
+ UnicodeSPrint (mPrivate->PortString, (UINTN) ISCSI_NAME_IFR_MAX_SIZE, L"Attempt %d", AttemptConfigData->AttemptConfigIndex);
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptConfigData
+ );
+
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Callback function when user presses "Commit Changes and Exit" in Delete Attempts or Delete Attempts by Keyword.
+
+ @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;
+ UINT8 AttemptConfigIndex;
+ UINT32 Attribute;
+ UINTN Total;
+ UINTN NewTotal;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigData;
+
+ Index = 0;
+
+ 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;
+
+ 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--;
+ }
+
+ AttemptConfigIndex = AttemptConfigData->AttemptConfigIndex;
+ FreePool (AttemptConfigData);
+
+ //
+ // Create a 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;
+ //
+ // Configure the Attempt index and set variable.
+ //
+ AttemptConfigData->AttemptConfigIndex = AttemptConfigIndex;
+
+ //
+ // Set the attempt name to default.
+ //
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ (UINTN) AttemptConfigData->AttemptConfigIndex
+ );
+ UnicodeStrToAsciiStrS (mPrivate->PortString, AttemptConfigData->AttemptName, ATTEMPT_NAME_SIZE);
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptConfigData
+ );
+
+ //
+ // 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--;
+ if (mCallbackInfo->Current == AttemptConfigData) {
+ mCallbackInfo->Current = NULL;
+ }
+ 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 or Change Attempt Order by Keyword.
+
+ @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_UNSUPPORTED Can not create more attempts.
+ @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;
+ UINT8 CurrentAttemptConfigIndex;
+ ISCSI_NIC_INFO *NicInfo;
+ UINT8 NicIndex;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ UINTN Index;
+ EFI_INPUT_KEY Key;
+
+ AttemptConfigData = NULL;
+ //
+ // 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;
+ }
+
+ 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 an attempt following the initialized attempt order.
+ //
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"InitialAttemptOrder",
+ &gIScsiConfigGuid,
+ &AttemptConfigOrderSize
+ );
+
+ if (AttemptConfigOrder == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ (UINTN) AttemptConfigOrder[Index]
+ );
+ GetVariable2 (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ (VOID**)&AttemptConfigData,
+ NULL
+ );
+ if (AttemptConfigData == NULL || AttemptConfigData->Actived == ISCSI_ACTIVE_ENABLED) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (Index > PcdGet8 (PcdMaxIScsiAttemptNumber)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Can not create more attempts, Please configure the PcdMaxIScsiAttemptNumber if needed!",
+ NULL
+ );
+ return EFI_UNSUPPORTED;
+ }
+
+ if (AttemptConfigOrder != NULL) {
+ FreePool (AttemptConfigOrder);
+ }
+
+ //
+ // Record the MAC info in Config Data.
+ //
+ IScsiMacAddrToStr (
+ &NicInfo->PermanentAddress,
+ NicInfo->HwAddressSize,
+ NicInfo->VlanId,
+ MacString
+ );
+
+ ASSERT (AttemptConfigData != NULL);
+ UnicodeStrToAsciiStrS (MacString, AttemptConfigData->MacString, sizeof (AttemptConfigData->MacString));
+ AttemptConfigData->NicIndex = NicIndex;
+ AttemptConfigData->Actived = ISCSI_ACTIVE_ENABLED;
+
+ //
+ // 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_OUT_OF_RESOURCES;
+ }
+
+ } 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);
+ }
+
+ //
+ // Extract all AttemptConfigData to Keyword stroage of IfrNvData.
+ //
+ IScsiConvertAttemptConfigDataToIfrNvDataByKeyword (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);
+ if (ConfigRequest == NULL) {
+ FreePool (IfrNvData);
+ FreePool (InitiatorName);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ 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
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_CONFIG_IFR_NVDATA *IfrNvData;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ ISCSI_NIC_INFO *NicInfo;
+ EFI_INPUT_KEY Key;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ CHAR8 *InitiatorName;
+ UINT8 *AttemptList;
+ UINTN BufferSize;
+ UINTN OffSet;
+ UINTN Index;
+ UINTN Index2;
+
+ Index = 0;
+ Index2 = 0;
+ NicInfo = NULL;
+ AttemptList = NULL;
+ Status = EFI_SUCCESS;
+
+ 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;
+ }
+
+ IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));
+ if (IfrNvData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ BufferSize = ISCSI_NAME_MAX_SIZE;
+ InitiatorName = (CHAR8 *) AllocateZeroPool (BufferSize);
+ if (InitiatorName == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Convert <ConfigResp> to buffer data by helper function ConfigToBlock().
+ //
+ BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);
+ Status = gHiiConfigRouting->ConfigToBlock (
+ gHiiConfigRouting,
+ Configuration,
+ (UINT8 *) IfrNvData,
+ &BufferSize,
+ Progress
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (IfrNvData->InitiatorName[0] != L'\0') {
+ UnicodeStrToAsciiStrS (IfrNvData->InitiatorName, InitiatorName, ISCSI_NAME_MAX_SIZE);
+ BufferSize = AsciiStrSize (InitiatorName);
+
+ Status = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, InitiatorName);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid iSCSI Name!",
+ NULL
+ );
+ goto Exit;
+ }
+ } else {
+ Status = IScsiGetValue (Configuration, L"&OFFSET=", &OffSet);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (OffSet >= ATTEMPT_MAC_ADDR_VAR_OFFSET) {
+ Status = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Error: please configure iSCSI initiator name first!",
+ NULL
+ );
+ goto Exit;
+ }
+ } else {
+ goto Exit;
+ }
+
+ if (IfrNvData->ISCSIAddAttemptList[0] != L'\0') {
+ Status =IScsiGetAttemptIndexList (IfrNvData->ISCSIAddAttemptList, IfrNvData->AddAttemptList, TRUE);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Error: The add attempt list is invalid",
+ NULL
+ );
+ goto Exit;
+ }
+
+ Status = IScsiConfigAddAttemptsByKeywords (IfrNvData->AddAttemptList);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ } else if (IfrNvData->ISCSIDeleteAttemptList[0] != L'\0') {
+ AttemptList =(UINT8 *) AllocateZeroPool ((ISCSI_MAX_ATTEMPTS_NUM + 1) * sizeof (UINT8));
+ if (AttemptList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+ Status = IScsiGetAttemptIndexList (IfrNvData->ISCSIDeleteAttemptList, AttemptList, FALSE);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Error: The delete attempt list is invalid",
+ NULL
+ );
+ goto Exit;
+ }
+
+ //
+ // Mark the attempt which will be delete in the global list.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ while (AttemptList[Index] != 0) {
+ if (AttemptConfigData->AttemptConfigIndex == AttemptList[Index]) {
+ IfrNvData->DeleteAttemptList[Index2] = 1;
+ break;
+ }
+ Index ++;
+ }
+ Index2 ++;
+ Index = 0;
+ }
+
+ Status = IScsiConfigDeleteAttempts (IfrNvData);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ FreePool (AttemptList);
+
+ } else if (IfrNvData->ISCSIAttemptOrder[0] != L'\0') {
+ Status = IScsiGetAttemptIndexList (IfrNvData->ISCSIAttemptOrder, IfrNvData->DynamicOrderedList, FALSE);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Error: The new attempt order list is invalid",
+ NULL
+ );
+ goto Exit;
+ }
+
+ Status = IScsiConfigOrderAttempts (IfrNvData);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ } else if (IfrNvData->ISCSIMacAddr[0] != L'\0') {
+ 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
+ );
+ if (!StrCmp(MacString, IfrNvData->ISCSIMacAddr)) {
+ mPrivate->CurrentNic = NicInfo->NicIndex;
+ break;
+ }
+ }
+
+ if ((NicInfo == NULL) || (NicInfo->NicIndex == 0)) {
+ Status = EFI_NOT_FOUND;
+ goto Exit;
+ }
+
+ } else {
+ Status = IScsiConvertlfrNvDataToAttemptConfigDataByKeyword (IfrNvData, OffSet);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+ }
+
+ IScsiConfigUpdateAttempt ();
+
+Exit:
+ if (InitiatorName != NULL) {
+ FreePool (InitiatorName);
+ }
+
+ if (IfrNvData != NULL) {
+ FreePool (IfrNvData);
+ }
+
+ return Status;
+}
+
+/**
+
+ 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[ISCSI_NAME_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;
+ 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_SAVE_ATTEMPT_CONFIG:
+ Status = IScsiConvertIfrNvDataToAttemptConfigData (IfrNvData, Private->Current);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;
+ 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:
+ case IP_MODE_IP4:
+ ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));
+ Private->Current->AutoConfigureMode = 0;
+
+ break;
+ }
+
+ break;
+
+ case KEY_LOCAL_IP:
+ Status = NetLibStrToIp4 (IfrNvData->LocalIp, &HostIp.v4);
+ if (EFI_ERROR (Status) ||
+ ((Private->Current->SessionConfigData.SubnetMask.Addr[0] != 0) &&
+ !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), NTOHL(*(UINT32*)Private->Current->SessionConfigData.SubnetMask.Addr)))) {
+ 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) &&
+ (Private->Current->SessionConfigData.SubnetMask.Addr[0] != 0) &&
+ !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), NTOHL(*(UINT32*)Private->Current->SessionConfigData.SubnetMask.Addr)))) {
+ 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)) {
+ //
+ // The target is expressed in URL format or an invalid Ip address, just save.
+ //
+ Private->Current->SessionConfigData.DnsMode = TRUE;
+ ZeroMem (&Private->Current->SessionConfigData.TargetIp, sizeof (Private->Current->SessionConfigData.TargetIp));
+ UnicodeStrToAsciiStrS (IfrNvData->TargetIp, Private->Current->SessionConfigData.TargetUrl, ISCSI_NAME_MAX_SIZE);
+ } else {
+ Private->Current->SessionConfigData.DnsMode = FALSE;
+ 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);
+
+ 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..7793d52e2f
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiConfig.h
@@ -0,0 +1,227 @@
+/** @file
+ The header file of functions for configuring or getting the parameters
+ relating to iSCSI.
+
+Copyright (c) 2004 - 2017, 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)
+#define ATTEMPT_ADD_QUESTION_ID QUESTION_ID (AddAttemptList)
+#define ATTEMPT_ADD_VAR_OFFSET VAR_OFFSET (AddAttemptList)
+
+//
+// Define QuestionId and OffSet for Keywords.
+//
+#define ATTEMPT_MAC_ADDR_VAR_OFFSET VAR_OFFSET (ISCSIMacAddr)
+#define ATTEMPT_ATTEMPT_NAME_QUESTION_ID QUESTION_ID (ISCSIAttemptName)
+#define ATTEMPT_ATTEMPT_NAME_VAR_OFFSET VAR_OFFSET (ISCSIAttemptName)
+#define ATTEMPT_BOOTENABLE_QUESTION_ID QUESTION_ID (ISCSIBootEnableList)
+#define ATTEMPT_BOOTENABLE_VAR_OFFSET VAR_OFFSET (ISCSIBootEnableList)
+#define ATTEMPT_ADDRESS_TYPE_QUESTION_ID QUESTION_ID (ISCSIIpAddressTypeList)
+#define ATTEMPT_ADDRESS_TYPE_VAR_OFFSET VAR_OFFSET (ISCSIIpAddressTypeList)
+#define ATTEMPT_CONNECT_RETRY_QUESTION_ID QUESTION_ID (ISCSIConnectRetry)
+#define ATTEMPT_CONNECT_RETRY_VAR_OFFSET VAR_OFFSET (ISCSIConnectRetry)
+#define ATTEMPT_CONNECT_TIMEOUT_QUESTION_ID QUESTION_ID (ISCSIConnectTimeout)
+#define ATTEMPT_CONNECT_TIMEOUT_VAR_OFFSET VAR_OFFSET (ISCSIConnectTimeout)
+#define ATTEMPT_ISID_QUESTION_ID QUESTION_ID (Keyword->ISCSIIsId)
+#define ATTEMPT_ISID_VAR_OFFSET VAR_OFFSET (Keyword->ISCSIIsId)
+#define ATTEMPT_INITIATOR_VIA_DHCP_QUESTION_ID QUESTION_ID (ISCSIInitiatorInfoViaDHCP)
+#define ATTEMPT_INITIATOR_VIA_DHCP_VAR_OFFSET VAR_OFFSET (ISCSIInitiatorInfoViaDHCP)
+#define ATTEMPT_INITIATOR_IP_ADDRESS_QUESTION_ID QUESTION_ID (Keyword->ISCSIInitiatorIpAddress)
+#define ATTEMPT_INITIATOR_IP_ADDRESS_VAR_OFFSET VAR_OFFSET (Keyword->ISCSIInitiatorIpAddress)
+#define ATTEMPT_INITIATOR_NET_MASK_QUESTION_ID QUESTION_ID (Keyword->ISCSIInitiatorNetmask)
+#define ATTEMPT_INITIATOR_NET_MASK_VAR_OFFSET VAR_OFFSET (Keyword->ISCSIInitiatorNetmask)
+#define ATTEMPT_INITIATOR_GATE_WAY_QUESTION_ID QUESTION_ID (Keyword->ISCSIInitiatorGateway)
+#define ATTEMPT_INITIATOR_GATE_WAY_VAR_OFFSET VAR_OFFSET (Keyword->ISCSIInitiatorGateway)
+#define ATTEMPT_TARGET_VIA_DHCP_QUESTION_ID QUESTION_ID (ISCSITargetInfoViaDHCP)
+#define ATTEMPT_TARGET_VIA_DHCP_VAR_OFFSET VAR_OFFSET (ISCSITargetInfoViaDHCP)
+#define ATTEMPT_TARGET_NAME_QUESTION_ID QUESTION_ID (Keyword->ISCSITargetName)
+#define ATTEMPT_TARGET_NAME_VAR_OFFSET VAR_OFFSET (Keyword->ISCSITargetName)
+#define ATTEMPT_TARGET_IP_ADDRESS_QUESTION_ID QUESTION_ID (Keyword->ISCSITargetIpAddress)
+#define ATTEMPT_TARGET_IP_ADDRESS_VAR_OFFSET VAR_OFFSET (Keyword->ISCSITargetIpAddress)
+#define ATTEMPT_TARGET_TCP_PORT_QUESTION_ID QUESTION_ID (ISCSITargetTcpPort)
+#define ATTEMPT_TARGET_TCP_PORT_VAR_OFFSET VAR_OFFSET (ISCSITargetTcpPort)
+#define ATTEMPT_LUN_QUESTION_ID QUESTION_ID (Keyword->ISCSILun)
+#define ATTEMPT_LUN_VAR_OFFSET VAR_OFFSET (Keyword->ISCSILun)
+#define ATTEMPT_AUTHENTICATION_METHOD_QUESTION_ID QUESTION_ID (ISCSIAuthenticationMethod)
+#define ATTEMPT_AUTHENTICATION_METHOD_VAR_OFFSET VAR_OFFSET (ISCSIAuthenticationMethod)
+#define ATTEMPT_CHARTYPE_QUESTION_ID QUESTION_ID (ISCSIChapType)
+#define ATTEMPT_CHARTYPE_VAR_OFFSET VAR_OFFSET (ISCSIChapType)
+#define ATTEMPT_CHAR_USER_NAME_QUESTION_ID QUESTION_ID (Keyword->ISCSIChapUsername)
+#define ATTEMPT_CHAR_USER_NAME_VAR_OFFSET VAR_OFFSET (Keyword->ISCSIChapUsername)
+#define ATTEMPT_CHAR_SECRET_QUESTION_ID QUESTION_ID (Keyword->ISCSIChapSecret)
+#define ATTEMPT_CHAR_SECRET_VAR_OFFSET VAR_OFFSET (Keyword->ISCSIChapSecret)
+#define ATTEMPT_CHAR_REVERSE_USER_NAME_QUESTION_ID QUESTION_ID (Keyword->ISCSIReverseChapUsername)
+#define ATTEMPT_CHAR_REVERSE_USER_NAME_VAR_OFFSET VAR_OFFSET (Keyword->ISCSIReverseChapUsername)
+#define ATTEMPT_CHAR_REVERSE_SECRET_QUESTION_ID QUESTION_ID (Keyword->ISCSIReverseChapSecret)
+#define ATTEMPT_CHAR_REVERSE_SECRET_VAR_OFFSET VAR_OFFSET (Keyword->ISCSIReverseChapSecret)
+
+
+#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_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;
+ UINT8 Actived;
+};
+
+///
+/// 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;
+};
+
+/**
+ 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
+ );
+
+/**
+ 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..f89f320854
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h
@@ -0,0 +1,238 @@
+/** @file
+ Define NVData structures used by the iSCSI configuration component.
+
+Copyright (c) 2004 - 2017, 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_MAX_ATTEMPTS_NUM FixedPcdGet8 (PcdMaxIScsiAttemptNumber)
+
+#define ISCSI_NAME_IFR_MIN_SIZE 4
+#define ISCSI_NAME_IFR_MAX_SIZE 223
+#define ISCSI_NAME_MAX_SIZE 224
+
+#define ATTEMPT_NAME_LIST_SIZE 96
+#define ATTEMPT_NAME_SIZE 12
+
+#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_ACTIVE_DISABLED 0
+#define ISCSI_ACTIVE_ENABLED 1
+
+#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_LUN_STR_MAX_LEN 21
+
+#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 KEYWORD_ENTRY_LABEL 0x6000
+#define CONFIG_OPTION_OFFSET 0x9000
+
+#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
+
+//
+// sizeof (EFI_MAC_ADDRESS) * 3
+//
+#define ISCSI_MAX_MAC_STRING_LEN 96
+
+///
+/// Macro used for target Url.
+///
+#define ISCSI_TARGET_URI_MIN_SIZE 0
+#define ISCSI_TARGET_URI_MAX_SIZE 255
+
+#pragma pack(1)
+
+//
+// Used by keyword.
+//
+typedef struct {
+ CHAR16 ISCSIIsId[ISID_CONFIGURABLE_STORAGE];
+ CHAR16 ISCSIInitiatorIpAddress[IP4_STR_MAX_SIZE];
+ CHAR16 ISCSIInitiatorNetmask[IP4_STR_MAX_SIZE];
+ CHAR16 ISCSIInitiatorGateway[IP4_STR_MAX_SIZE];
+ CHAR16 ISCSITargetName[ISCSI_NAME_MAX_SIZE];
+ CHAR16 ISCSITargetIpAddress[IP_STR_MAX_SIZE];
+ CHAR16 ISCSILun[ISCSI_LUN_STR_MAX_LEN];
+ CHAR16 ISCSIChapUsername[ISCSI_CHAP_NAME_STORAGE];
+ CHAR16 ISCSIChapSecret[ISCSI_CHAP_SECRET_STORAGE];
+ CHAR16 ISCSIReverseChapUsername[ISCSI_CHAP_NAME_STORAGE];
+ CHAR16 ISCSIReverseChapSecret[ISCSI_CHAP_SECRET_STORAGE];
+} KEYWORD_STR;
+
+typedef struct _ISCSI_CONFIG_IFR_NVDATA {
+ CHAR16 InitiatorName[ISCSI_NAME_MAX_SIZE];
+ CHAR16 AttemptName[ATTEMPT_NAME_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[ISCSI_TARGET_URI_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];
+ UINT8 AddAttemptList[ISCSI_MAX_ATTEMPTS_NUM];
+ CHAR16 IsId[ISID_CONFIGURABLE_STORAGE];
+
+ //
+ // This will be used by keywords.
+ //
+ CHAR16 ISCSIMacAddr[ISCSI_MAX_MAC_STRING_LEN];
+ CHAR16 ISCSIAttemptOrder[ATTEMPT_NAME_LIST_SIZE];
+ CHAR16 ISCSIAddAttemptList[ATTEMPT_NAME_LIST_SIZE];
+ CHAR16 ISCSIDeleteAttemptList[ATTEMPT_NAME_LIST_SIZE];
+ CHAR16 ISCSIDisplayAttemptList[ATTEMPT_NAME_LIST_SIZE];
+ CHAR16 ISCSIAttemptName[ATTEMPT_NAME_LIST_SIZE];
+ UINT8 ISCSIBootEnableList[ISCSI_MAX_ATTEMPTS_NUM];
+ UINT8 ISCSIIpAddressTypeList[ISCSI_MAX_ATTEMPTS_NUM];
+ UINT8 ISCSIConnectRetry[ISCSI_MAX_ATTEMPTS_NUM];
+ UINT16 ISCSIConnectTimeout[ISCSI_MAX_ATTEMPTS_NUM];
+ UINT8 ISCSIInitiatorInfoViaDHCP[ISCSI_MAX_ATTEMPTS_NUM];
+ UINT8 ISCSITargetInfoViaDHCP[ISCSI_MAX_ATTEMPTS_NUM];
+ UINT16 ISCSITargetTcpPort[ISCSI_MAX_ATTEMPTS_NUM];
+ UINT8 ISCSIAuthenticationMethod[ISCSI_MAX_ATTEMPTS_NUM];
+ UINT8 ISCSIChapType[ISCSI_MAX_ATTEMPTS_NUM];
+ KEYWORD_STR Keyword[ISCSI_MAX_ATTEMPTS_NUM];
+} 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..79522583de
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni
@@ -0,0 +1,101 @@
+// *++
+//
+// Copyright (c) 2004 - 2017, 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.
+//
+// Module Name:
+//
+// IScsiConfigStrings.uni
+//
+// Abstract:
+//
+// String definitions for iSCSI configuration.
+//
+// Revision History:
+//
+// --*/
+
+/=#
+
+#langdef en-US "English"
+#langdef x-UEFI-ns "UefiNameSpace"
+
+#string STR_ISCSI_CONFIG_FORM_TITLE #language en-US "iSCSI Configuration"
+#string STR_ISCSI_CONFIG_FORM_HELP #language en-US "Configure the iSCSI parameters."
+#string STR_ISCSI_MAIN_FORM_TITLE #language en-US "iSCSI Configuration"
+#string STR_ISCSI_MAC_FORM_TITLE #language en-US "MAC Selection"
+#string STR_ISCSI_CONFIG_INIT_NAME #language en-US "iSCSI Initiator Name"
+ #language x-UEFI-ns "iSCSIInitiatorName"
+#string STR_ISCSI_CONFIG_INIT_NAME_HELP #language en-US "The worldwide unique name of iSCSI Initiator. Only IQN format is accepted."
+#string STR_ISCSI_ATTEMPT_NAME #language en-US "iSCSI Attempt Name"
+#string STR_ISCSI_ATTEMPT_NAME_HELP #language en-US "The human name defined for this attempt."
+#string STR_ISCSI_CONFIG_RETRY #language en-US "Connection Retry Count"
+#string STR_ISCSI_CONFIG_RETRY_HELP #language en-US "The minimum value is 0 and the maximum is 16. 0 means no retry."
+#string STR_ISCSI_CONFIG_TIMEOUT #language en-US "Connection Establishing Timeout"
+#string STR_ISCSI_CONFIG_TIMEOUT_HELP #language en-US "The timeout value in milliseconds. The minimum value is 100 milliseconds and the maximum is 20 seconds."
+#string STR_ADD_ATTEMPT_ENTRY #language en-US "Add an Attempt"
+#string STR_ISCSI_ATTEMPT_FORM_TITLE #language en-US "Attempt Configuration"
+#string STR_DEL_ATTEMPT_ENTRY #language en-US "Delete Attempts"
+#string STR_DEL_ATTEMPT_ENTRY_HELP #language en-US "Delete one or more attempts"
+#string STR_ORDER_ATTEMPT_ENTRY #language en-US "Change Attempt Order"
+#string STR_ISCSI_MODE_PROMPT #language en-US "iSCSI Mode"
+#string STR_ISCSI_MODE_HELP #language en-US "Disabled, Enabled, Enabled for MPIO"
+#string STR_ISCSI_MODE_DISABLED #language en-US "Disabled"
+#string STR_ISCSI_MODE_ENABLED #language en-US "Enabled"
+#string STR_ISCSI_MODE_ENABLED_FOR_MPIO #language en-US "Enabled for MPIO"
+#string STR_IP_MODE_PROMPT #language en-US "Internet Protocol"
+#string STR_IP_MODE_HELP #language en-US "Initiator IP address is system assigned in IP6 mode. In Autoconfigure mode, iSCSI driver will attempt to connect iSCSI target via IPv4 stack, if failed then attempt IPv6 stack."
+#string STR_IP_MODE_IP4 #language en-US "IP4"
+#string STR_IP_MODE_IP6 #language en-US "IP6"
+#string STR_IP_MODE_AUTOCONFIG #language en-US "Autoconfigure"
+#string STR_AUTHEN_TYPE_PROMPT #language en-US "Authentication Type"
+#string STR_AUTHEN_TYPE_HELP #language en-US "Authentication method: CHAP, Kerberos, or None"
+#string STR_AUTHEN_TYPE_CHAP #language en-US "CHAP"
+#string STR_AUTHEN_TYPE_KERBEROS #language en-US "Kerberos"
+#string STR_AUTHEN_TYPE_NONE #language en-US "None"
+#string STR_ISCSI_LOCAL_IP_ADDRESS #language en-US " Initiator IP Address"
+#string STR_ISCSI_LOCAL_MASK #language en-US " Initiator Subnet Mask"
+#string STR_ISCSI_LOCAL_GATEWAY #language en-US " Gateway"
+#string STR_ISCSI_IP_ADDRESS_HELP #language en-US "Enter IP address in dotted-decimal notation."
+#string STR_ISCSI_TARGET_NAME #language en-US " Target Name"
+#string STR_ISCSI_TARGET_NAME_HELP #language en-US "The worldwide unique name of the target. Only iqn. format is accepted."
+#string STR_ISCSI_TARGET_ADDRESS #language en-US " Target Address"
+#string STR_ISCSI_TARGET_ADDRESS_HELP #language en-US "Enter Target address in IPv4,IPv6 or URL format.You need to configure DNS server address in advance if input a URL string."
+#string STR_ISCSI_TARGET_PORT #language en-US " Target Port"
+#string STR_ISCSI_BOOT_LUN #language en-US " Boot LUN"
+#string STR_ISCSI_BOOT_LUN_HELP #language en-US "Hexadecimal representation of the LU number. Examples are: 4752-3A4F-6b7e-2F99, 6734-9-156f-127, 4186-9"
+#string STR_ISCSI_ENABLE_DHCP #language en-US "Enable DHCP"
+#string STR_ISCSI_ENABLE_DHCP_ON_TARGET #language en-US "Get target info via DHCP"
+#string STR_CHAP_TYPE_PROMPT #language en-US " CHAP Type"
+#string STR_CHAP_TYPE_HELP #language en-US "None, One way CHAP or mutual CHAP"
+#string STR_CHAP_TYPE_UNI #language en-US "One way"
+#string STR_CHAP_TYPE_MUTUAL #language en-US "Mutual"
+#string STR_ISCSI_CHAP_NAME #language en-US " CHAP Name"
+#string STR_ISCSI_CHAP_SECRET #language en-US " CHAP Secret"
+#string STR_ISCSI_CHAP_SECRET_HELP #language en-US "The minimum length is 12 bytes and the maximum length is 16 bytes."
+#string STR_ISCSI_REVERSE_CHAP_NAME #language en-US " Reverse CHAP Name"
+#string STR_ISCSI_REVERSE_CHAP_SECRET #language en-US " Reverse CHAP Secret"
+#string STR_RETURN_MAIN_FORM #language en-US "Back to Previous Page"
+#string STR_SAVE_CHANGES #language en-US "Save Changes"
+#string STR_SAVE_CHANGES_HELP #language en-US "Must reboot system manually for changes to take place."
+#string STR_NULL #language en-US ""
+#string STR_SAVE_AND_EXIT #language en-US "Commit Changes and Exit"
+#string STR_NO_SAVE_AND_EXIT #language en-US "Discard Changes and Exit"
+#string STR_ISCSI_CONFIG_ISID #language en-US "ISID"
+#string STR_ISCSI_CONFIG_ISID_HELP #language en-US "OUI-format ISID in 6 bytes, default value are derived from MAC address. Only last 3 bytes are configurable. Example: update 0ABBCCDDEEFF to 0ABBCCF07901 by input F07901."
+#string STR_ISCSI_MAC_PROMPT #language en-US "Configure the mac address for the attempt"
+ #language x-UEFI-ns "iSCSIMacAddr"
+#string STR_ISCSI_ADD_ATTEMPTS #language en-US "Add Attempts"
+ #language x-UEFI-ns "iSCSIAddAttempts"
+#string STR_ISCSI_DELETE_ATTEMPTS #language en-US "Delete Attempts"
+ #language x-UEFI-ns "iSCSIDeleteAttempts"
+#string STR_ISCSI_DISPLAY_ATTEMPTS #language en-US "Display Attempts"
+ #language x-UEFI-ns "iSCSIDisplayAttemptList"
+#string STR_ISCSI_ATTEMPT_ORDER #language en-US "New Attempt Order"
+ #language x-UEFI-ns "iSCSIAttemptOrder"
diff --git a/Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr b/Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr
new file mode 100644
index 0000000000..d401419a03
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr
@@ -0,0 +1,403 @@
+/** @file
+ VFR file used by the iSCSI configuration component.
+
+Copyright (c) 2004 - 2017, 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 = READ_ONLY,
+ key = KEY_ATTEMPT_NAME,
+ minsize = 0,
+ maxsize = ATTEMPT_NAME_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_ADDRESS),
+ help = STRING_TOKEN(STR_ISCSI_TARGET_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_TARGET_IP,
+ minsize = ISCSI_TARGET_URI_MIN_SIZE,
+ maxsize = ISCSI_TARGET_URI_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;
+
+ suppressif TRUE;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.ISCSIMacAddr,
+ prompt = STRING_TOKEN(STR_ISCSI_MAC_PROMPT),
+ help = STRING_TOKEN(STR_ISCSI_MAC_PROMPT),
+ minsize = 0,
+ maxsize = ISCSI_MAX_MAC_STRING_LEN,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.ISCSIAttemptOrder,
+ prompt = STRING_TOKEN(STR_ISCSI_ATTEMPT_ORDER),
+ help = STRING_TOKEN(STR_ISCSI_ATTEMPT_ORDER),
+ minsize = 0,
+ maxsize = ATTEMPT_NAME_LIST_SIZE,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.ISCSIAddAttemptList,
+ prompt = STRING_TOKEN(STR_ISCSI_ADD_ATTEMPTS),
+ help = STRING_TOKEN(STR_ISCSI_ADD_ATTEMPTS),
+ minsize = 0,
+ maxsize = ATTEMPT_NAME_LIST_SIZE,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.ISCSIDeleteAttemptList,
+ prompt = STRING_TOKEN(STR_ISCSI_DELETE_ATTEMPTS),
+ help = STRING_TOKEN(STR_ISCSI_DELETE_ATTEMPTS),
+ minsize = 0,
+ maxsize = ATTEMPT_NAME_LIST_SIZE,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.ISCSIDisplayAttemptList,
+ prompt = STRING_TOKEN(STR_ISCSI_DISPLAY_ATTEMPTS),
+ help = STRING_TOKEN(STR_ISCSI_DISPLAY_ATTEMPTS),
+ flags = READ_ONLY,
+ minsize = 0,
+ maxsize = ATTEMPT_NAME_LIST_SIZE,
+ endstring;
+
+ label KEYWORD_ENTRY_LABEL;
+ label LABEL_END;
+ 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..43ae50bbff
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDhcp.c
@@ -0,0 +1,511 @@
+/** @file
+ iSCSI DHCP4 related configuration routines.
+
+Copyright (c) 2004 - 2017, 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;
+ }
+
+ //
+ // Server name is expressed as domain name, just save it.
+ //
+ if ((!NET_IS_DIGIT (*(Field->Str))) && (*(Field->Str) != '[')) {
+ ConfigNvData->DnsMode = TRUE;
+ if (Field->Len > sizeof (ConfigNvData->TargetUrl)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len);
+ ConfigNvData->TargetUrl[Field->Len + 1] = '\0';
+ } else {
+ ZeroMem(ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl));
+ 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_ROOTPATH) {
+ 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_SERVER) {
+
+ 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_SERVER;
+ ParaList->Data[3] = DHCP4_TAG_ROOTPATH;
+
+ 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..7d55ec99ec
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDhcp.h
@@ -0,0 +1,55 @@
+/** @file
+ The head file of iSCSI DHCP4 related configuration routines.
+
+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.
+
+**/
+
+#ifndef _ISCSI_DHCP_H_
+#define _ISCSI_DHCP_H_
+
+#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..d3535d57cb
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDhcp6.c
@@ -0,0 +1,538 @@
+/** @file
+ iSCSI DHCP6 related configuration routines.
+
+Copyright (c) 2009 - 2017, 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;
+ }
+
+ //
+ // Server name is expressed as domain name, just save it.
+ //
+ if ((!NET_IS_DIGIT (*(Field->Str))) && (*(Field->Str) != '[')) {
+ ConfigNvData->DnsMode = TRUE;
+ if (Field->Len > sizeof (ConfigNvData->TargetUrl)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ CopyMem (&ConfigNvData->TargetUrl, Field->Str, Field->Len);
+ ConfigNvData->TargetUrl[Field->Len + 1] = '\0';
+ } else {
+ ZeroMem(&ConfigNvData->TargetUrl, sizeof (ConfigNvData->TargetUrl));
+ 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_PARAM) {
+ //
+ // 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_ORO);
+ 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_PARAM;
+
+ 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..1d59d10626
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDhcp6.h
@@ -0,0 +1,71 @@
+/** @file
+ The header file of iSCSI DHCP6 related configuration routines.
+
+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.
+
+**/
+
+#ifndef _ISCSI_DHCP6_H_
+#define _ISCSI_DHCP6_H_
+
+#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/IScsiDns.c b/Core/NetworkPkg/IScsiDxe/IScsiDns.c
new file mode 100644
index 0000000000..0ddfcbd526
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDns.c
@@ -0,0 +1,435 @@
+/** @file
+ Perform DNS resolution based on UEFI DNS protocols.
+
+Copyright (c) 2017, 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"
+
+/**
+ 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
+IScsiCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ Retrieve the host address using the EFI_DNS4_PROTOCOL.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in, out] NvData The Session config data structure.
+
+ @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
+IScsiDns4 (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_SESSION_CONFIG_NVDATA *NvData
+ )
+{
+ EFI_STATUS Status;
+ EFI_DNS4_PROTOCOL *Dns4;
+ EFI_DNS4_CONFIG_DATA Dns4CfgData;
+ EFI_DNS4_COMPLETION_TOKEN Token;
+ BOOLEAN IsDone;
+ EFI_HANDLE Dns4Handle;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+ EFI_IPv4_ADDRESS *DnsServerList;
+ UINTN DnsServerListCount;
+ UINTN DataSize;
+ CHAR16 *HostName;
+
+ DnsServerList = NULL;
+ DnsServerListCount = 0;
+ Dns4Handle = NULL;
+ Dns4 = NULL;
+ ZeroMem (&Token, sizeof (EFI_DNS4_COMPLETION_TOKEN));
+
+ //
+ // Get DNS server list from EFI IPv4 Configuration II protocol.
+ //
+ Status = gBS->HandleProtocol (Controller, &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);
+ }
+ }
+ }
+
+
+ //
+ // Create a DNS child instance and get the protocol.
+ //
+ Status = NetLibCreateServiceChild (
+ Controller,
+ Image,
+ &gEfiDns4ServiceBindingProtocolGuid,
+ &Dns4Handle
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dns4Handle,
+ &gEfiDns4ProtocolGuid,
+ (VOID **) &Dns4,
+ Image,
+ Controller,
+ 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.EnableDnsCache = TRUE;
+ IP4_COPY_ADDRESS (&Dns4CfgData.StationIp, &NvData->LocalIp);
+ IP4_COPY_ADDRESS (&Dns4CfgData.SubnetMask, &NvData->SubnetMask);
+ 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,
+ IScsiCommonNotify,
+ &IsDone,
+ &Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Start asynchronous name resolution.
+ //
+ Token.Status = EFI_NOT_READY;
+ IsDone = FALSE;
+
+ HostName = (CHAR16 *) AllocateZeroPool (ISCSI_NAME_MAX_SIZE);
+ if (HostName == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AsciiStrToUnicodeStrS (
+ NvData->TargetUrl,
+ HostName,
+ ISCSI_NAME_MAX_SIZE
+ );
+
+ 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 (&NvData->TargetIp.v4, 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,
+ Image,
+ Controller
+ );
+ }
+
+ if (Dns4Handle != NULL) {
+ NetLibDestroyServiceChild (
+ Controller,
+ Image,
+ &gEfiDns4ServiceBindingProtocolGuid,
+ Dns4Handle
+ );
+ }
+
+ return Status;
+}
+
+/**
+ Retrieve the host address using the EFI_DNS6_PROTOCOL.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in, out] NvData The Session config data structure.
+
+ @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
+IScsiDns6 (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_SESSION_CONFIG_NVDATA *NvData
+ )
+{
+ 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;
+ CHAR16 *HostName;
+
+ 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 (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 (
+ Controller,
+ Image,
+ &gEfiDns6ServiceBindingProtocolGuid,
+ &Dns6Handle
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dns6Handle,
+ &gEfiDns6ProtocolGuid,
+ (VOID **) &Dns6,
+ Image,
+ 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;
+ 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,
+ IScsiCommonNotify,
+ &IsDone,
+ &Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Start asynchronous name resolution.
+ //
+ HostName = (CHAR16 *) AllocateZeroPool (ISCSI_NAME_MAX_SIZE);
+ if (HostName == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AsciiStrToUnicodeStrS (
+ NvData->TargetUrl,
+ HostName,
+ ISCSI_NAME_MAX_SIZE
+ );
+ 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 (&NvData->TargetIp.v6, 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,
+ Image,
+ Controller
+ );
+ }
+
+ if (Dns6Handle != NULL) {
+ NetLibDestroyServiceChild (
+ Controller,
+ Image,
+ &gEfiDns6ServiceBindingProtocolGuid,
+ Dns6Handle
+ );
+ }
+
+ return Status;
+}
+
diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDns.h b/Core/NetworkPkg/IScsiDxe/IScsiDns.h
new file mode 100644
index 0000000000..0b7ff8680b
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDns.h
@@ -0,0 +1,59 @@
+/** @file
+ The header file of routines for IScsi driver to perform DNS
+ resolution based on UEFI DNS protocols.
+
+Copyright (c) 2017, 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_DNS_H_
+#define _ISCSI_DNS_H_
+
+/**
+ Retrieve the host address using the EFI_DNS4_PROTOCOL.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in, out] NvData The Session config data structure.
+
+ @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
+IScsiDns4 (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_SESSION_CONFIG_NVDATA *NvData
+ );
+
+/**
+ Retrieve the host address using the EFI_DNS6_PROTOCOL.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in, out] NvData The Session config data structure.
+
+ @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
+IScsiDns6 (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_SESSION_CONFIG_NVDATA *NvData
+ );
+
+#endif \ No newline at end of file
diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDriver.c b/Core/NetworkPkg/IScsiDxe/IScsiDriver.c
new file mode 100644
index 0000000000..2249919e18
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDriver.c
@@ -0,0 +1,1888 @@
+/** @file
+ The entry point of IScsi driver.
+
+Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2017 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 "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;
+}
+
+/**
+ Check whether an iSCSI HBA adapter already installs an AIP instance with
+ network boot policy matching the value specified in PcdIScsiAIPNetworkBootPolicy.
+ If yes, return EFI_SUCCESS.
+
+ @retval EFI_SUCCESS Found an AIP with matching network boot policy.
+ @retval EFI_NOT_FOUND AIP is unavailable or the network boot policy
+ not matched.
+**/
+EFI_STATUS
+IScsiCheckAip (
+ )
+{
+ UINTN AipHandleCount;
+ EFI_HANDLE *AipHandleBuffer;
+ UINTN AipIndex;
+ EFI_ADAPTER_INFORMATION_PROTOCOL *Aip;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;
+ EFI_GUID *InfoTypesBuffer;
+ UINTN InfoTypeBufferCount;
+ UINTN TypeIndex;
+ VOID *InfoBlock;
+ UINTN InfoBlockSize;
+ BOOLEAN Supported;
+ EFI_ADAPTER_INFO_NETWORK_BOOT *NetworkBoot;
+ EFI_STATUS Status;
+ UINT8 NetworkBootPolicy;
+
+ //
+ // Check any AIP instances exist in system.
+ //
+ AipHandleCount = 0;
+ AipHandleBuffer = NULL;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiAdapterInformationProtocolGuid,
+ NULL,
+ &AipHandleCount,
+ &AipHandleBuffer
+ );
+ if (EFI_ERROR (Status) || AipHandleCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ ASSERT (AipHandleBuffer != NULL);
+
+ InfoBlock = NULL;
+
+ for (AipIndex = 0; AipIndex < AipHandleCount; AipIndex++) {
+ Status = gBS->HandleProtocol (
+ AipHandleBuffer[AipIndex],
+ &gEfiAdapterInformationProtocolGuid,
+ (VOID *) &Aip
+ );
+ ASSERT_EFI_ERROR (Status);
+ ASSERT (Aip != NULL);
+
+ Status = gBS->HandleProtocol (
+ AipHandleBuffer[AipIndex],
+ &gEfiExtScsiPassThruProtocolGuid,
+ (VOID *) &ExtScsiPassThru
+ );
+ if (EFI_ERROR (Status) || ExtScsiPassThru == NULL) {
+ continue;
+ }
+
+ InfoTypesBuffer = NULL;
+ InfoTypeBufferCount = 0;
+ Status = Aip->GetSupportedTypes (Aip, &InfoTypesBuffer, &InfoTypeBufferCount);
+ if (EFI_ERROR (Status) || InfoTypesBuffer == NULL) {
+ continue;
+ }
+ //
+ // Check whether the AIP instance has Network boot information block.
+ //
+ Supported = FALSE;
+ for (TypeIndex = 0; TypeIndex < InfoTypeBufferCount; TypeIndex++) {
+ if (CompareGuid (&InfoTypesBuffer[TypeIndex], &gEfiAdapterInfoNetworkBootGuid)) {
+ Supported = TRUE;
+ break;
+ }
+ }
+
+ FreePool (InfoTypesBuffer);
+ if (!Supported) {
+ continue;
+ }
+
+ //
+ // We now have network boot information block.
+ //
+ InfoBlock = NULL;
+ InfoBlockSize = 0;
+ Status = Aip->GetInformation (Aip, &gEfiAdapterInfoNetworkBootGuid, &InfoBlock, &InfoBlockSize);
+ if (EFI_ERROR (Status) || InfoBlock == NULL) {
+ continue;
+ }
+
+ //
+ // Check whether the network boot policy matches.
+ //
+ NetworkBoot = (EFI_ADAPTER_INFO_NETWORK_BOOT *) InfoBlock;
+ NetworkBootPolicy = PcdGet8 (PcdIScsiAIPNetworkBootPolicy);
+
+ if (NetworkBootPolicy == STOP_UEFI_ISCSI_IF_HBA_INSTALL_AIP) {
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+ if (((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_SUPPORT_IP4) != 0 &&
+ !NetworkBoot->iScsiIpv4BootCapablity) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_SUPPORT_IP6) != 0 &&
+ !NetworkBoot->iScsiIpv6BootCapablity) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_SUPPORT_OFFLOAD) != 0 &&
+ !NetworkBoot->OffloadCapability) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_SUPPORT_MPIO) != 0 &&
+ !NetworkBoot->iScsiMpioCapability) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_CONFIGURED_IP4) != 0 &&
+ !NetworkBoot->iScsiIpv4Boot) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_CONFIGURED_IP6) != 0 &&
+ !NetworkBoot->iScsiIpv6Boot)) {
+ FreePool (InfoBlock);
+ continue;
+ }
+
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+Exit:
+ if (InfoBlock != NULL) {
+ FreePool (InfoBlock);
+ }
+ if (AipHandleBuffer != NULL) {
+ FreePool (AipHandleBuffer);
+ }
+ return Status;
+}
+
+/**
+ 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;
+ EFI_GUID *DnsServiceBindingGuid;
+
+ if (IpVersion == IP_VERSION_4) {
+ IScsiServiceBindingGuid = &gIScsiV4PrivateGuid;
+ TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ DhcpServiceBindingGuid = &gEfiDhcp4ServiceBindingProtocolGuid;
+ DnsServiceBindingGuid = &gEfiDns4ServiceBindingProtocolGuid;
+
+ } else {
+ IScsiServiceBindingGuid = &gIScsiV6PrivateGuid;
+ TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ DhcpServiceBindingGuid = &gEfiDhcp6ServiceBindingProtocolGuid;
+ DnsServiceBindingGuid = &gEfiDns6ServiceBindingProtocolGuid;
+ }
+
+ 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;
+ }
+ }
+
+ if (IScsiDnsIsConfigured (ControllerHandle)) {
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ DnsServiceBindingGuid,
+ 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_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR Failed to get TCP connection device path.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the Handle
+ because its interfaces are being used.
+
+**/
+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;
+ BOOLEAN NeedUpdate;
+ VOID *Interface;
+ EFI_GUID *ProtocolGuid;
+ UINT8 NetworkBootPolicy;
+ ISCSI_SESSION_CONFIG_NVDATA *NvData;
+
+ //
+ // 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;
+ }
+
+ NetworkBootPolicy = PcdGet8 (PcdIScsiAIPNetworkBootPolicy);
+ if (NetworkBootPolicy == ALWAYS_USE_ISCSI_HBA_AND_IGNORE_UEFI_ISCSI) {
+ return EFI_ABORTED;
+ }
+
+ if (NetworkBootPolicy != ALWAYS_USE_UEFI_ISCSI_AND_IGNORE_ISCSI_HBA) {
+ //
+ // Check existing iSCSI AIP.
+ //
+ Status = IScsiCheckAip ();
+ if (!EFI_ERROR (Status)) {
+ //
+ // Find iSCSI AIP with specified network boot policy. return EFI_ABORTED.
+ //
+ return EFI_ABORTED;
+ }
+ }
+
+ //
+ // 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;
+
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ (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);
+ }
+
+ //
+ // Restore the origial user setting which specifies the proxy/virtual iSCSI target to NV region.
+ //
+ NvData = &AttemptConfigData->SessionConfigData;
+ if (NvData->RedirectFlag) {
+ NvData->TargetPort = NvData->OriginalTargetPort;
+ CopyMem (&NvData->TargetIp, &NvData->OriginalTargetIp, sizeof (EFI_IP_ADDRESS));
+ NvData->RedirectFlag = FALSE;
+
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptConfigData
+ );
+ }
+
+ 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);
+ }
+
+ if (ExistPrivate->DevicePath != NULL) {
+ Status = gBS->UninstallProtocolInterface (
+ ExistPrivate->ExtScsiPassThruHandle,
+ &gEfiDevicePathProtocolGuid,
+ ExistPrivate->DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ FreePool (ExistPrivate->DevicePath);
+ }
+
+ gBS->CloseEvent (ExistPrivate->ExitBootServiceEvent);
+ FreePool (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.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the Handle
+ because its interfaces are being used.
+
+**/
+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);
+ }
+
+ Status = IScsiCleanDriverData (Private);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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;
+ }
+
+ //
+ // Create the Maximum Attempts.
+ //
+ Status = IScsiCreateAttempts (PcdGet8 (PcdMaxIScsiAttemptNumber));
+ if (EFI_ERROR (Status)) {
+ goto Error5;
+ }
+
+ //
+ // Create Keywords for all the Attempts.
+ //
+ Status = IScsiCreateKeywords (PcdGet8 (PcdMaxIScsiAttemptNumber));
+ if (EFI_ERROR (Status)) {
+ goto Error6;
+ }
+
+ //
+ // 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 Error6;
+ }
+ }
+
+ return EFI_SUCCESS;
+
+Error6:
+ IScsiCleanAttemptVariable ();
+
+Error5:
+ IScsiConfigFormUnload (gIScsiIp4DriverBinding.DriverBindingHandle);
+
+Error4:
+ if (mPrivate != NULL) {
+ FreePool (mPrivate);
+ mPrivate = NULL;
+ }
+
+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..6c6e11b0d2
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDriver.h
@@ -0,0 +1,819 @@
+/** @file
+ The header file of IScsiDriver.c.
+
+Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2017 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.
+
+**/
+
+#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
+#define ALWAYS_USE_UEFI_ISCSI_AND_IGNORE_ISCSI_HBA 0x00
+#define STOP_UEFI_ISCSI_IF_HBA_INSTALL_AIP 0x01
+#define STOP_UEFI_ISCSI_IF_AIP_SUPPORT_IP4 0x02
+#define STOP_UEFI_ISCSI_IF_AIP_SUPPORT_IP6 0x04
+#define STOP_UEFI_ISCSI_IF_AIP_SUPPORT_OFFLOAD 0x08
+#define STOP_UEFI_ISCSI_IF_AIP_SUPPORT_MPIO 0x10
+#define STOP_UEFI_ISCSI_IF_AIP_CONFIGURED_IP4 0x20
+#define STOP_UEFI_ISCSI_IF_AIP_CONFIGURED_IP6 0x40
+#define ALWAYS_USE_ISCSI_HBA_AND_IGNORE_UEFI_ISCSI 0xFF
+
+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;
+} 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..c9b724bd30
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDxe.inf
@@ -0,0 +1,145 @@
+## @file
+# Client-side iSCSI service.
+#
+# The iSCSI driver provides iSCSI service in the preboot environment and supports
+# booting over iSCSI.
+#
+# Copyright (c) 2004 - 2017, 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
+ IScsiDns.c
+ IScsiDns.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 ## SOMETIMES_CONSUMES
+ gEfiDhcp6ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDhcp4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDhcp6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDns4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDns4ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiIp6ConfigProtocolGuid ## SOMETIMES_CONSUMES
+ 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
+ ## CONSUMES
+ gEfiAdapterInformationProtocolGuid
+
+[Guids]
+ gEfiEventExitBootServicesGuid ## SOMETIMES_CONSUMES ## Event
+ gEfiIfrTianoGuid ## SOMETIMES_PRODUCES ## UNDEFINED
+ gEfiAcpiTableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ gEfiAcpi10TableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ gEfiAcpi20TableGuid ## SOMETIMES_CONSUMES ## SystemTable
+ gEfiAdapterInfoNetworkBootGuid ## SOMETIMES_CONSUMES ## UNDEFINED
+
+ ## SOMETIMES_PRODUCES ## Variable:L"AttemptOrder"
+ ## SOMETIMES_CONSUMES ## Variable:L"AttemptOrder"
+ ## SOMETIMES_PRODUCES ## Variable:L"InitialAttemptOrder"
+ ## SOMETIMES_CONSUMES ## Variable:L"InitialAttemptOrder"
+ ## SOMETIMES_CONSUMES ## UNDEFINED # HiiIsConfigHdrMatch mVendorStorageName
+ ## SOMETIMES_PRODUCES ## UNDEFINED # HiiConstructConfigHdr mVendorStorageName
+ ## SOMETIMES_PRODUCES ## UNDEFINED # HiiGetBrowserData mVendorStorageName
+ ## SOMETIMES_CONSUMES ## UNDEFINED # HiiSetBrowserData mVendorStorageName
+ ## SOMETIMES_CONSUMES ## HII
+ gIScsiConfigGuid
+
+[Pcd]
+ gEfiNetworkPkgTokenSpaceGuid.PcdIScsiAIPNetworkBootPolicy ## CONSUMES
+ gEfiNetworkPkgTokenSpaceGuid.PcdMaxIScsiAttemptNumber ## CONSUMES
+
+[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..8522a4991c
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDxe.uni
@@ -0,0 +1,23 @@
+// /** @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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Client-side iSCSI service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "The iSCSI driver provides iSCSI service in the preboot environment and supports booting over iSCSI."
+
diff --git a/Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni b/Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni
new file mode 100644
index 0000000000..4a0579875e
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// IScsiDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"iSCSI DXE"
+
+
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..27d098d578
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiIbft.c
@@ -0,0 +1,551 @@
+/** @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;
+ EFI_ACPI_DESCRIPTION_HEADER *Xsdt;
+ UINT8 *Heap;
+ UINT8 Checksum;
+
+ Rsdt = NULL;
+ Xsdt = NULL;
+
+ Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol);
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+
+ //
+ // Find ACPI table RSD_PTR from the system table.
+ //
+ Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, (VOID **) &Rsdp);
+ if (EFI_ERROR (Status)) {
+ Status = EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, (VOID **) &Rsdp);
+ }
+
+ if (EFI_ERROR (Status) || (Rsdp == NULL)) {
+ return ;
+ } else if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION && Rsdp->XsdtAddress != 0) {
+ Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->XsdtAddress;
+ } else if (Rsdp->RsdtAddress != 0) {
+ Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress;
+ }
+
+ if ((Xsdt == NULL) && (Rsdt == NULL)) {
+ return ;
+ }
+
+ 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.
+ //
+ if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) {
+ IScsiInitIbfTableHeader (Table, Xsdt->OemId, &Xsdt->OemTableId);
+ } else {
+ 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..741c49784a
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiImpl.h
@@ -0,0 +1,207 @@
+/** @file
+ The shared head file for iSCSI driver.
+
+Copyright (c) 2004 - 2017, 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 <IndustryStandard/Dhcp.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/Dns4.h>
+#include <Protocol/Dns6.h>
+#include <Protocol/Tcp4.h>
+#include <Protocol/Tcp6.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/Ip6Config.h>
+
+#include <Protocol/AuthenticationInfo.h>
+#include <Protocol/IScsiInitiatorName.h>
+#include <Protocol/ScsiPassThruExt.h>
+#include <Protocol/AdapterInformation.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 "IScsiDns.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..2c93590deb
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiMisc.c
@@ -0,0 +1,2426 @@
+/** @file
+ Miscellaneous routines for iSCSI driver.
+
+Copyright (c) 2004 - 2017, 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);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Create and initialize the Attempts.
+
+ @param[in] AttemptNum The number of Attempts will be created.
+
+ @retval EFI_SUCCESS The Attempts have been created successfully.
+ @retval Others Failed to create the Attempt.
+
+**/
+EFI_STATUS
+IScsiCreateAttempts (
+ IN UINTN AttemptNum
+)
+{
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigData;
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ UINT8 *AttemptOrderTmp;
+ UINTN TotalNumber;
+ UINT8 Index;
+ EFI_STATUS Status;
+
+ for (Index = 1; Index <= AttemptNum; Index ++) {
+ //
+ // Get the initialized attempt order. This is used to essure creating attempts by order.
+ //
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"InitialAttemptOrder",
+ &gIScsiConfigGuid,
+ &AttemptConfigOrderSize
+ );
+ TotalNumber = AttemptConfigOrderSize / sizeof (UINT8);
+ if (TotalNumber == AttemptNum) {
+ Status = EFI_SUCCESS;
+ break;
+ }
+ TotalNumber++;
+
+ //
+ // Append the new created attempt 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] = Index;
+ AttemptConfigOrder = AttemptOrderTmp;
+ AttemptConfigOrderSize = TotalNumber * sizeof (UINT8);
+
+ Status = gRT->SetVariable (
+ L"InitialAttemptOrder",
+ &gIScsiConfigGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ AttemptConfigOrderSize,
+ AttemptConfigOrder
+ );
+ FreePool (AttemptConfigOrder);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // 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;
+ //
+ // Configure the Attempt index and set variable.
+ //
+ AttemptConfigData->AttemptConfigIndex = Index;
+
+ //
+ // Set the attempt name according to the order.
+ //
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ (UINTN) AttemptConfigData->AttemptConfigIndex
+ );
+ UnicodeStrToAsciiStrS (mPrivate->PortString, AttemptConfigData->AttemptName, ATTEMPT_NAME_SIZE);
+
+ Status = gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptConfigData
+ );
+ FreePool (AttemptConfigData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Create the iSCSI configuration Keywords for each attempt. You can find the keywords
+ defined in the "x-UEFI-ns" namespace (http://www.uefi.org/confignamespace).
+
+ @param[in] KeywordNum The number Sets of Keywords will be created.
+
+ @retval EFI_SUCCESS The operation is completed.
+ @retval Others Failed to create the Keywords.
+
+**/
+EFI_STATUS
+IScsiCreateKeywords (
+ IN UINTN KeywordNum
+)
+{
+ VOID *StartOpCodeHandle;
+ EFI_IFR_GUID_LABEL *StartLabel;
+ VOID *EndOpCodeHandle;
+ EFI_IFR_GUID_LABEL *EndLabel;
+ UINTN Index;
+ EFI_STRING_ID StringToken;
+ CHAR16 StringId[64];
+ CHAR16 KeywordId[32];
+ EFI_STATUS Status;
+
+ Status = IScsiCreateOpCode (
+ KEYWORD_ENTRY_LABEL,
+ &StartOpCodeHandle,
+ &StartLabel,
+ &EndOpCodeHandle,
+ &EndLabel
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Index = 1; Index <= KeywordNum; Index ++) {
+ //
+ // Create iSCSIAttemptName Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_ATTEMPTT_NAME_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIAttemptName:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_ATTEMPT_NAME_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_ATTEMPT_NAME_VAR_OFFSET + ATTEMPT_NAME_SIZE * (Index - 1) * sizeof (CHAR16)),
+ StringToken,
+ StringToken,
+ EFI_IFR_FLAG_READ_ONLY,
+ 0,
+ 0,
+ ATTEMPT_NAME_SIZE,
+ NULL
+ );
+
+ //
+ // Create iSCSIBootEnable Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_MODE_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIBootEnable:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateNumericOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_BOOTENABLE_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_BOOTENABLE_VAR_OFFSET + (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ EFI_IFR_NUMERIC_SIZE_1,
+ 0,
+ 2,
+ 0,
+ NULL
+ );
+
+ //
+ // Create iSCSIIpAddressType Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_IP_MODE_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIIpAddressType:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateNumericOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_ADDRESS_TYPE_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_ADDRESS_TYPE_VAR_OFFSET + (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ EFI_IFR_NUMERIC_SIZE_1,
+ 0,
+ 2,
+ 0,
+ NULL
+ );
+
+ //
+ // Create iSCSIConnectRetry Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_CONNECT_RETRY_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIConnectRetry:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateNumericOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_CONNECT_RETRY_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_CONNECT_RETRY_VAR_OFFSET + (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ EFI_IFR_NUMERIC_SIZE_1,
+ 0,
+ 16,
+ 0,
+ NULL
+ );
+
+ //
+ // Create iSCSIConnectTimeout Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_CONNECT_TIMEOUT_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIConnectTimeout:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateNumericOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_CONNECT_TIMEOUT_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_CONNECT_TIMEOUT_VAR_OFFSET + 2 * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ EFI_IFR_NUMERIC_SIZE_2,
+ CONNECT_MIN_TIMEOUT,
+ CONNECT_MAX_TIMEOUT,
+ 0,
+ NULL
+ );
+
+ //
+ // Create ISID Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_ISID_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIISID:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_ISID_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_ISID_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ ISID_CONFIGURABLE_MIN_LEN,
+ ISID_CONFIGURABLE_STORAGE,
+ NULL
+ );
+
+ //
+ // Create iSCSIInitiatorInfoViaDHCP Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_INITIATOR_VIA_DHCP_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIInitiatorInfoViaDHCP:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateNumericOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_INITIATOR_VIA_DHCP_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_INITIATOR_VIA_DHCP_VAR_OFFSET + (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ NULL
+ );
+
+ //
+ // Create iSCSIInitiatorIpAddress Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_INITIATOR_IP_ADDRESS_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIInitiatorIpAddress:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_INITIATOR_IP_ADDRESS_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_INITIATOR_IP_ADDRESS_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ IP4_MIN_SIZE,
+ IP4_MAX_SIZE,
+ NULL
+ );
+
+ //
+ // Create iSCSIInitiatorNetmask Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_INITIATOR_NET_MASK_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIInitiatorNetmask:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_INITIATOR_NET_MASK_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_INITIATOR_NET_MASK_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ IP4_MIN_SIZE,
+ IP4_MAX_SIZE,
+ NULL
+ );
+
+ //
+ // Create iSCSIInitiatorGateway Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_INITIATOR_GATE_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIInitiatorGateway:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_INITIATOR_GATE_WAY_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_INITIATOR_GATE_WAY_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ IP4_MIN_SIZE,
+ IP4_MAX_SIZE,
+ NULL
+ );
+
+ //
+ // Create iSCSITargetInfoViaDHCP Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_TARGET_VIA_DHCP_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSITargetInfoViaDHCP:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateNumericOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_TARGET_VIA_DHCP_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_TARGET_VIA_DHCP_VAR_OFFSET + (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ NULL
+ );
+
+ //
+ // Create iSCSITargetTcpPort Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_TARGET_TCP_PORT_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSITargetTcpPort:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateNumericOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_TARGET_TCP_PORT_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_TARGET_TCP_PORT_VAR_OFFSET + 2 * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ EFI_IFR_NUMERIC_SIZE_2,
+ TARGET_PORT_MIN_NUM,
+ TARGET_PORT_MAX_NUM,
+ 0,
+ NULL
+ );
+
+ //
+ // Create iSCSITargetName Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_TARGET_NAME_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSITargetName:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_TARGET_NAME_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_TARGET_NAME_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ ISCSI_NAME_IFR_MIN_SIZE,
+ ISCSI_NAME_IFR_MAX_SIZE,
+ NULL
+ );
+
+ //
+ // Create iSCSITargetIpAddress Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_TARGET_IP_ADDRESS_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSITargetIpAddress:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_TARGET_IP_ADDRESS_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_TARGET_IP_ADDRESS_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ IP_MIN_SIZE,
+ IP_MAX_SIZE,
+ NULL
+ );
+
+ //
+ // Create iSCSILUN Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_LUN_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSILUN:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_LUN_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_LUN_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ LUN_MIN_SIZE,
+ LUN_MAX_SIZE,
+ NULL
+ );
+
+ //
+ // Create iSCSIAuthenticationMethod Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_AUTHENTICATION_METHOD_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIAuthenticationMethod:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateNumericOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_AUTHENTICATION_METHOD_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_AUTHENTICATION_METHOD_VAR_OFFSET + (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ NULL
+ );
+
+ //
+ // Create iSCSIChapType Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_CHARTYPE_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIChapType:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateNumericOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_CHARTYPE_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_CHARTYPE_VAR_OFFSET + (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ NULL
+ );
+
+ //
+ // Create iSCSIChapUsername Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_CHAR_USER_NAME_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIChapUsername:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_CHAR_USER_NAME_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_CHAR_USER_NAME_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ 0,
+ ISCSI_CHAP_NAME_MAX_LEN,
+ NULL
+ );
+
+ //
+ // Create iSCSIChapSecret Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_CHAR_SECRET_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIChapSecret:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_CHAR_SECRET_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_CHAR_SECRET_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ ISCSI_CHAP_SECRET_MIN_LEN,
+ ISCSI_CHAP_SECRET_MAX_LEN,
+ NULL
+ );
+
+ //
+ // Create iSCSIReverseChapUsername Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_CHAR_REVERSE_USER_NAME_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIReverseChapUsername:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_CHAR_REVERSE_USER_NAME_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_CHAR_REVERSE_USER_NAME_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ 0,
+ ISCSI_CHAP_NAME_MAX_LEN,
+ NULL
+ );
+
+ //
+ // Create iSCSIReverseChapSecret Keyword.
+ //
+ UnicodeSPrint (StringId, sizeof (StringId), L"STR_ISCSI_CHAR_REVERSE_SECRET_PROMPT%d", Index);
+ StringToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ StringId,
+ NULL
+ );
+ UnicodeSPrint (KeywordId, sizeof (KeywordId), L"iSCSIReverseChapSecret:%d", Index);
+ HiiSetString (mCallbackInfo->RegisteredHandle, StringToken, KeywordId, "x-UEFI-ns");
+ HiiCreateStringOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_CHAR_REVERSE_SECRET_QUESTION_ID + (Index - 1)),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_CHAR_REVERSE_SECRET_VAR_OFFSET + sizeof (KEYWORD_STR) * (Index - 1)),
+ StringToken,
+ StringToken,
+ 0,
+ 0,
+ ISCSI_CHAP_SECRET_MIN_LEN,
+ ISCSI_CHAP_SECRET_MAX_LEN,
+ NULL
+ );
+ }
+
+ Status = HiiUpdateForm (
+ mCallbackInfo->RegisteredHandle, // HII handle
+ &gIScsiConfigGuid, // Formset GUID
+ FORMID_ATTEMPT_FORM, // Form ID
+ StartOpCodeHandle, // Label for where to insert opcodes
+ EndOpCodeHandle // Replace data
+ );
+
+ HiiFreeOpCodeHandle (StartOpCodeHandle);
+ HiiFreeOpCodeHandle (EndOpCodeHandle);
+
+ return Status;
+}
+
+/**
+
+ Free the attempt configure data variable.
+
+**/
+VOID
+IScsiCleanAttemptVariable (
+ IN VOID
+)
+{
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ UINTN Index;
+
+ //
+ // Get the initialized attempt order.
+ //
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"InitialAttemptOrder",
+ &gIScsiConfigGuid,
+ &AttemptConfigOrderSize
+ );
+ if (AttemptConfigOrder == NULL || AttemptConfigOrderSize == 0) {
+ return;
+ }
+
+ for (Index = 1; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ Index
+ );
+
+ GetVariable2 (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ (VOID**)&AttemptConfigData,
+ NULL
+ );
+
+ if (AttemptConfigData != NULL) {
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ 0,
+ 0,
+ NULL
+ );
+ }
+ }
+ return;
+}
+
+/**
+ 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 according 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.
+
+ @retval EFI_SUCCESS The clean operation is successful.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiCleanDriverData (
+ IN ISCSI_DRIVER_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (Private->DevicePath != NULL) {
+ Status = gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiDevicePathProtocolGuid,
+ Private->DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ FreePool (Private->DevicePath);
+ }
+
+ if (Private->ExtScsiPassThruHandle != NULL) {
+ Status = gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru
+ );
+ if (!EFI_ERROR (Status)) {
+ mPrivate->OneSessionEstablished = FALSE;
+ }
+ }
+
+EXIT:
+
+ gBS->CloseEvent (Private->ExitBootServiceEvent);
+
+ mCallbackInfo->Current = NULL;
+
+ FreePool (Private);
+ return Status;
+}
+
+/**
+ 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 AttemptMacString[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"Attempt %d",
+ (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;
+ }
+
+ AsciiStrToUnicodeStrS (AttemptTmp->MacString, AttemptMacString, sizeof (AttemptMacString) / sizeof (AttemptMacString[0]));
+
+ if (AttemptTmp->Actived == ISCSI_ACTIVE_DISABLED || StrCmp (MacString, AttemptMacString)) {
+ 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;
+}
+
+/**
+ Check whether the Controller handle is configured to use DNS protocol.
+
+ @param[in] Controller The handle of the controller.
+
+ @retval TRUE The handle of the controller need the Dns protocol.
+ @retval FALSE The handle of the controller does not need the Dns protocol.
+
+**/
+BOOLEAN
+IScsiDnsIsConfigured (
+ IN EFI_HANDLE Controller
+ )
+{
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptTmp;
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ UINTN Index;
+ EFI_STATUS Status;
+ EFI_MAC_ADDRESS MacAddr;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+ CHAR16 AttemptMacString[ISCSI_MAX_MAC_STRING_LEN];
+ 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"Attempt %d",
+ (UINTN) AttemptConfigOrder[Index]
+ );
+
+ Status = GetVariable2 (
+ AttemptName,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ (VOID**)&AttemptTmp,
+ NULL
+ );
+ if(AttemptTmp == NULL || EFI_ERROR (Status)) {
+ continue;
+ }
+
+ ASSERT (AttemptConfigOrder[Index] == AttemptTmp->AttemptConfigIndex);
+
+ AsciiStrToUnicodeStrS (AttemptTmp->MacString, AttemptMacString, sizeof (AttemptMacString) / sizeof (AttemptMacString[0]));
+
+ if (AttemptTmp->SessionConfigData.Enabled == ISCSI_DISABLED || StrCmp (MacString, AttemptMacString)) {
+ FreePool (AttemptTmp);
+ continue;
+ }
+
+ if (AttemptTmp->SessionConfigData.DnsMode) {
+ FreePool (AttemptTmp);
+ FreePool (AttemptConfigOrder);
+ return TRUE;
+ } else {
+ FreePool (AttemptTmp);
+ continue;
+ }
+
+ }
+
+ 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.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+IScsiGetConfigData (
+ IN ISCSI_DRIVER_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ CHAR16 AttemptMacString[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.
+ //
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ (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.
+ //
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ (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) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ (UINTN) AttemptConfigOrder[Index]
+ );
+
+ GetVariable2 (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ (VOID**)&AttemptConfigData,
+ NULL
+ );
+ AsciiStrToUnicodeStrS (AttemptConfigData->MacString, AttemptMacString, sizeof (AttemptMacString) / sizeof (AttemptMacString[0]));
+
+ if (AttemptConfigData == NULL || AttemptConfigData->Actived == ISCSI_ACTIVE_DISABLED ||
+ StrCmp (MacString, AttemptMacString)) {
+ 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.
+ //
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ (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
+ );
+ if (AttemptConfigData->AttemptTitleHelpToken == 0) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // 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..caa2f94bb1
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiMisc.h
@@ -0,0 +1,471 @@
+/** @file
+ Miscellaneous definitions for iSCSI driver.
+
+Copyright (c) 2004 - 2017, 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];
+
+ BOOLEAN RedirectFlag;
+ UINT16 OriginalTargetPort; // The port of proxy/virtual target.
+ EFI_IP_ADDRESS OriginalTargetIp; // The address of proxy/virtual target.
+
+ BOOLEAN DnsMode; // Flag indicate whether the Target address is expressed as URL format.
+ CHAR8 TargetUrl[ISCSI_TARGET_URI_MAX_SIZE];
+
+} 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
+ );
+
+/**
+ Create and initialize the Attempts.
+
+ @param[in] AttemptNum The number of Attempts will be created.
+
+ @retval EFI_SUCCESS The Attempts have been created successfully.
+ @retval Others Failed to create the Attempt.
+
+**/
+EFI_STATUS
+IScsiCreateAttempts (
+ IN UINTN AttemptNum
+ );
+
+/**
+ Create the iSCSI configuration Keywords for each attempt.
+
+ @param[in] KeywordNum The number Sets of Keywords will be created.
+
+ @retval EFI_SUCCESS The operation is completed.
+ @retval Others Failed to create the Keywords.
+
+**/
+EFI_STATUS
+IScsiCreateKeywords (
+ IN UINTN KeywordNum
+ );
+
+/**
+
+ Free the attempt configure data variable.
+
+**/
+VOID
+IScsiCleanAttemptVariable (
+ IN VOID
+ );
+
+/**
+ 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 according 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.
+
+ @retval EFI_SUCCES The clean operation is successful.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+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
+ );
+
+/**
+ Check wheather the Controller handle is configured to use DNS protocol.
+
+ @param[in] Controller The handle of the controller.
+
+ @retval TRUE The handle of the controller need the DNS protocol.
+ @retval FALSE The handle of the controller does not need the DNS protocol.
+
+**/
+BOOLEAN
+IScsiDnsIsConfigured (
+ IN EFI_HANDLE Controller
+ );
+
+/**
+ 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..1602a26132
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiProto.c
@@ -0,0 +1,3219 @@
+/** @file
+ The implementation of iSCSI protocol based on RFC3720.
+
+Copyright (c) 2004 - 2017, 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,
+ MultU64x32 (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 (NvData->DnsMode) {
+ //
+ // perform dns process if target address expressed by domain name.
+ //
+ if (!Conn->Ipv6Flag) {
+ Status = IScsiDns4 (Private->Image, Private->Controller, NvData);
+ } else {
+ Status = IScsiDns6 (Private->Image, Private->Controller, NvData);
+ }
+
+ if (EFI_ERROR(Status)) {
+ DEBUG ((EFI_D_ERROR, "The configuration of Target address or DNS server address is invalid!\n"));
+ FreePool (Conn);
+ return NULL;
+ }
+ }
+
+ 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);
+ if (LoginReq == NULL) {
+ NetbufFree (Nbuf);
+ return 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 parameter 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;
+ ISCSI_SESSION_CONFIG_NVDATA *NvData;
+
+ KeyValueList = IScsiBuildKeyValueList (Data, Len);
+ if (KeyValueList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_NOT_FOUND;
+ NvData = &Session->ConfigData->SessionConfigData;
+
+ while (TRUE) {
+ TargetAddress = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ADDRESS);
+ if (TargetAddress == NULL) {
+ break;
+ }
+
+ //
+ // RFC 3720 defines format of the TargetAddress=domainname[:port][,portal-group-tag]
+ // The domainname can be specified as either a DNS host name, adotted-decimal IPv4 address,
+ // or a bracketed IPv6 address as specified in [RFC2732].
+ //
+ if (NET_IS_DIGIT (TargetAddress[0])) {
+ //
+ // The domainname of the target is presented in a dotted-decimal IPv4 address format.
+ //
+ IpStr = TargetAddress;
+
+ while ((*TargetAddress != '\0') && (*TargetAddress != ':') && (*TargetAddress != ',')) {
+ //
+ // NULL, ':', or ',' ends the IPv4 string.
+ //
+ TargetAddress++;
+ }
+ } else if (*TargetAddress == ISCSI_REDIRECT_ADDR_START_DELIMITER){
+ //
+ // The domainname of the target is presented in a bracketed IPv6 address format.
+ //
+ TargetAddress ++;
+ IpStr = TargetAddress;
+ while ((*TargetAddress != '\0') && (*TargetAddress != ISCSI_REDIRECT_ADDR_END_DELIMITER)) {
+ //
+ // ']' ends the IPv6 string.
+ //
+ TargetAddress++;
+ }
+
+ if (*TargetAddress != ISCSI_REDIRECT_ADDR_END_DELIMITER) {
+ continue;
+ }
+
+ *TargetAddress = '\0';
+ TargetAddress ++;
+
+ } else {
+ //
+ // The domainname of the target is presented in the format of a DNS host name.
+ //
+ IpStr = TargetAddress;
+
+ while ((*TargetAddress != '\0') && (*TargetAddress != ':') && (*TargetAddress != ',')) {
+ TargetAddress++;
+ }
+ NvData->DnsMode = TRUE;
+ }
+
+ //
+ // Save the origial user setting which specifies the proxy/virtual iSCSI target.
+ //
+ NvData->OriginalTargetPort = NvData->TargetPort;
+
+ 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 {
+ NvData->TargetPort = (UINT16) Number;
+ }
+ } else {
+ //
+ // The string only contains the Target address. Use the well-known port.
+ //
+ NvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
+ }
+
+ //
+ // Save the origial user setting which specifies the proxy/virtual iSCSI target.
+ //
+ CopyMem (&NvData->OriginalTargetIp, &NvData->TargetIp, sizeof (EFI_IP_ADDRESS));
+
+ //
+ // Update the target IP address.
+ //
+ if (NvData->IpMode < IP_MODE_AUTOCONFIG) {
+ IpMode = NvData->IpMode;
+ } else {
+ IpMode = Session->ConfigData->AutoConfigureMode;
+ }
+
+ if (NvData->DnsMode) {
+ //
+ // Target address is expressed as URL format, just save it and
+ // do DNS resolution when creating a TCP connection.
+ //
+ if (AsciiStrSize (IpStr) > sizeof (Session->ConfigData->SessionConfigData.TargetUrl)){
+ return EFI_INVALID_PARAMETER;
+ }
+ CopyMem (&Session->ConfigData->SessionConfigData.TargetUrl, IpStr, AsciiStrSize (IpStr));
+ } else {
+ Status = IScsiAsciiStrToIp (
+ IpStr,
+ IpMode,
+ &Session->ConfigData->SessionConfigData.TargetIp
+ );
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ } else {
+ NvData->RedirectFlag = TRUE;
+ 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);
+ if (Header == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ 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 associated 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);
+ if (DataOutHdr == NULL) {
+ IScsiFreeNbufList (NbufList);
+ return 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..367914d477
--- /dev/null
+++ b/Core/NetworkPkg/IScsiDxe/IScsiProto.h
@@ -0,0 +1,1039 @@
+/** @file
+ The header file of iSCSI Protocol that defines many specific data structures.
+
+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.
+
+**/
+
+#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_REDIRECT_ADDR_START_DELIMITER '['
+#define ISCSI_REDIRECT_ADDR_END_DELIMITER ']'
+
+#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/HttpBootConfigHii.h b/Core/NetworkPkg/Include/Guid/HttpBootConfigHii.h
new file mode 100644
index 0000000000..7e44436633
--- /dev/null
+++ b/Core/NetworkPkg/Include/Guid/HttpBootConfigHii.h
@@ -0,0 +1,25 @@
+/** @file
+ GUIDs used as HII FormSet and HII Package list GUID in HTTP boot driver.
+
+Copyright (c) 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 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 __HTTP_BOOT_HII_GUID_H__
+#define __HTTP_BOOT_HII_GUID_H__
+
+#define HTTP_BOOT_CONFIG_GUID \
+ { \
+ 0x4d20583a, 0x7765, 0x4e7a, { 0x8a, 0x67, 0xdc, 0xde, 0x74, 0xee, 0x3e, 0xc5 } \
+ }
+
+extern EFI_GUID gHttpBootConfigGuid;
+
+#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/Include/Guid/TlsAuthConfigHii.h b/Core/NetworkPkg/Include/Guid/TlsAuthConfigHii.h
new file mode 100644
index 0000000000..5e5637c4c6
--- /dev/null
+++ b/Core/NetworkPkg/Include/Guid/TlsAuthConfigHii.h
@@ -0,0 +1,26 @@
+/** @file
+ GUIDs used as HII FormSet and HII Package list GUID in TlsAuthConfigDxe driver.
+
+Copyright (c) 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 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 __TLS_AUTH_CONFIG_HII_GUID_H__
+#define __TLS_AUTH_CONFIG_HII_GUID_H__
+
+#define TLS_AUTH_CONFIG_GUID \
+ { \
+ 0xb0eae4f8, 0x9a04, 0x4c6d, { 0xa7, 0x48, 0x79, 0x3d, 0xaa, 0xf, 0x65, 0xdf } \
+ }
+
+extern EFI_GUID gTlsAuthConfigGuid;
+
+#endif
+
diff --git a/Core/NetworkPkg/Include/Guid/TlsAuthentication.h b/Core/NetworkPkg/Include/Guid/TlsAuthentication.h
new file mode 100644
index 0000000000..e8497be68b
--- /dev/null
+++ b/Core/NetworkPkg/Include/Guid/TlsAuthentication.h
@@ -0,0 +1,30 @@
+/** @file
+ This file defines TlsCaCertificate variable.
+
+Copyright (c) 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 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 __TLS_AUTHENTICATION_H__
+#define __TLS_AUTHENTICATION_H__
+
+// Private variable for CA Certificate configuration
+//
+#define EFI_TLS_CA_CERTIFICATE_GUID \
+ { \
+ 0xfd2340D0, 0x3dab, 0x4349, { 0xa6, 0xc7, 0x3b, 0x4f, 0x12, 0xb4, 0x8e, 0xae } \
+ }
+
+#define EFI_TLS_CA_CERTIFICATE_VARIABLE L"TlsCaCertificate"
+
+extern EFI_GUID gEfiTlsCaCertificateGuid;
+
+#endif
+
diff --git a/Core/NetworkPkg/Ip6Dxe/ComponentName.c b/Core/NetworkPkg/Ip6Dxe/ComponentName.c
new file mode 100644
index 0000000000..6a1f082edd
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/ComponentName.c
@@ -0,0 +1,474 @@
+/** @file
+ Implementation of EFI_COMPONENT_NAME_PROTOCOL and
+ EFI_COMPONENT_NAME2_PROTOCOL protocol.
+
+ 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 "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)) {
+ 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 (!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..7007301f81
--- /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 - 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 "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_MAX) {
+ 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_MAX);
+
+ 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..bde5982b69
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c
@@ -0,0 +1,2382 @@
+/** @file
+ The implementation of EFI IPv6 Configuration Protocol.
+
+ Copyright (c) 2009 - 2017, 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 parameters 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 (DHCP6_OPT_ORO);
+ Oro->OpLen = HTONS (2);
+ *((UINT16 *) &Oro->Data[0]) = HTONS (DHCP6_OPT_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 {
+ //
+ // 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);
+
+ if (NewPolicy == Ip6ConfigPolicyManual) {
+ //
+ // 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;
+ 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.
+ //
+ if (Tmp != NULL) {
+ 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
+ )
+{
+ UnicodeSPrint (
+ IfInfo->Name,
+ sizeof (IfInfo->Name),
+ L"eth%d",
+ IpSb->Ip6ConfigInstance.IfIndex
+ );
+
+ 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 == DHCP6_OPT_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 = Ip6ConfigPolicyManual;
+ 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..3a6e8ad4d1
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h
@@ -0,0 +1,313 @@
+/** @file
+ Definitions for EFI IPv6 Configuartion Protocol implementation.
+
+ 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 __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 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;
+};
+
+/**
+ 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
+ );
+
+/**
+ 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..8a8cc8916a
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6Driver.c
@@ -0,0 +1,1026 @@
+/** @file
+ The driver binding and service binding protocol for IP6 driver.
+
+ Copyright (c) 2009 - 2016, 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;
+
+ IpSb->State = IP6_SERVICE_DESTROY;
+
+ 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;
+ }
+
+ 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);
+ }
+
+ //
+ // 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;
+
+ 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;
+ }
+
+ 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;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+
+ IpSb = NULL;
+ Ip6Cfg = NULL;
+ DataItem = NULL;
+
+ //
+ // 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);
+
+ Ip6Cfg = &IpSb->Ip6ConfigInstance.Ip6Config;
+
+ //
+ // Install the Ip6ServiceBinding Protocol onto ControlerHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ &IpSb->ServiceBinding,
+ &gEfiIp6ConfigProtocolGuid,
+ Ip6Cfg,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Read the config data from NV variable again.
+ // The default data can be changed by other drivers.
+ //
+ Status = Ip6ConfigReadConfigData (IpSb->MacString, &IpSb->Ip6ConfigInstance);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // If there is any default manual address, set it.
+ //
+ DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress];
+ if (DataItem->Data.Ptr != NULL) {
+ Status = Ip6Cfg->SetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeManualAddress,
+ DataItem->DataSize,
+ DataItem->Data.Ptr
+ );
+ if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // If there is any default gateway address, set it.
+ //
+ DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeGateway];
+ if (DataItem->Data.Ptr != NULL) {
+ Status = Ip6Cfg->SetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeGateway,
+ DataItem->DataSize,
+ DataItem->Data.Ptr
+ );
+ if (EFI_ERROR(Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // 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;
+ 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 available 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..0874c4c93b
--- /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 - 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 __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 prototypes for the ServiceBinding Protocol
+//
+
+/**
+ 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 available 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..2d0fecc77e
--- /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 - 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.
+#
+##
+
+[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..11e97344f4
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6Dxe.uni
@@ -0,0 +1,26 @@
+// /** @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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Basic IPv6 packet I/O Service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "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."
+
diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni b/Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni
new file mode 100644
index 0000000000..c3c34e4ab7
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Ip6Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Ip6 DXE"
+
+
diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni b/Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
new file mode 100644
index 0000000000..3c9beec6a0
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
@@ -0,0 +1,61 @@
+/** @file
+ String definitions for IP6 configuration.
+
+ 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.
+
+**/
+
+#langdef en-US "English"
+
+#string STR_IP6_CONFIG_FORM_TITLE #language en-US "IPv6 Network Configuration"
+#string STR_IP6_CONFIG_FORM_HELP #language en-US "Configure IPv6 network parameters."
+#string STR_IP6_DEVICE_FORM_TITLE #language en-US "IPv6 Current Setting"
+#string STR_IP6_DEVICE_FORM_HELP #language en-US "Display IPv6 network parameters."
+#string STR_IP6_INTERFACE_NAME #language en-US "Interface Name :"
+#string STR_IP6_INTERFACE_NAME_HELP #language en-US "The name of the interface."
+#string STR_IP6_INTERFACE_NAME_CONTENT #language en-US ""
+#string STR_IP6_INTERFACE_TYPE #language en-US "Interface Type :"
+#string STR_IP6_INTERFACE_TYPE_HELP #language en-US "The interface type of the network interface, defined in RFC1700."
+#string STR_IP6_INTERFACE_TYPE_CONTENT #language en-US ""
+#string STR_IP6_MAC_ADDRESS #language en-US "MAC address :"
+#string STR_IP6_MAC_ADDRESS_HELP #language en-US "The hardware address of the network interface."
+#string STR_IP6_MAC_ADDRESS_CONTENT #language en-US ""
+#string STR_IP6_HOST_ADDRESS #language en-US "Host addresses :"
+#string STR_IP6_HOST_ADDRESS_HELP #language en-US "The address list which contain the local IPv6 addresses and the corresponding prefix length information."
+#string STR_IP6_ROUTE_TABLE #language en-US "Route Table :"
+#string STR_IP6_ROUTE_TABLE_HELP #language en-US "The route table of the IPv6 network stack runs on this interface."
+#string STR_IP6_GATEWAY_ADDRESS #language en-US "Gateway addresses :"
+#string STR_IP6_GATEWAY_ADDRESS_HELP #language en-US "Current gateway IPv6 addresses."
+#string STR_IP6_DNS_ADDRESS #language en-US "DNS addresses :"
+#string STR_IP6_DNS_ADDRESS_HELP #language en-US "Current DNS server list."
+#string STR_IP6_INTERFACE_ID #language en-US "Interface ID"
+#string STR_IP6_INTERFACE_ID_HELP #language en-US "The 64 bit alternative interface ID for the device. The string is colon separated. e.g. ff:dd:88:66:cc:1:2:3"
+#string STR_IP6_DAD_TRANSMIT_COUNT #language en-US "DAD Transmit Count"
+#string STR_IP6_DAD_TRANSMIT_COUNT_HELP #language en-US "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 is not performed."
+#string STR_POLICY_TYPE_PROMPT #language en-US "Policy"
+#string STR_POLICY_TYPE_HELP #language en-US "automatic or manual"
+#string STR_POLICY_TYPE_AUTO #language en-US "automatic"
+#string STR_POLICY_TYPE_MANUAL #language en-US "manual"
+#string STR_IP6_AD_CONFIG_FORM #language en-US "Advanced Configuration"
+#string STR_IP6_AD_CONFIG_FORM_HELP #language en-US "Configure the interface manually. IP address, gateway address, and DNS server address can be configured."
+#string STR_IP6_MANUAL_ADDRESS #language en-US "New IPv6 address"
+#string STR_IP6_MANUAL_ADDRESS_HELP #language en-US "Manual IP address can only be configured under manual policy. Separate the IP address with blank space to configure more than one address. e.g. 2002::1/64 2002::2/64"
+#string STR_IP6_NEW_GATEWAY_ADDRESS #language en-US "New Gateway addresses"
+#string STR_IP6_NEW_GATEWAY_ADDR_HELP #language en-US "Gateway IP address can only be configured under manual policy. e.g. 2002::3. Separate the IP address with blank space to configure more than one address."
+#string STR_IP6_NEW_DNS_ADDRESS #language en-US "New DNS addresses"
+#string STR_IP6_NEW_DNS_ADDRESS_HELP #language en-US "DNS addresses can only be configured under manual policy. e.g. 2002::4. Separate the IP address with blank space to configure more than one address."
+#string STR_SAVE_CHANGES #language en-US "Save Changes and Exit"
+#string STR_SAVE_CHANGES_HELP #language en-US "Save Changes for interface ID, DAD transmit count, policy, and data in advanced configuration."
+#string STR_SAVE_AND_EXIT #language en-US "Commit Changes and Exit"
+#string STR_NO_SAVE_AND_EXIT #language en-US "Discard Changes and Exit"
+#string STR_NULL #language en-US ""
+#string STR_GET_CURRENT_SETTING #language en-US "Enter Configuration Menu"
+#string STR_GET_CURRENT_SETTING_HELP #language en-US "Press ENTER to enter configuration menu for IPv6 configuration."
diff --git a/Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c b/Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c
new file mode 100644
index 0000000000..1828d51a7d
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6Icmp.c
@@ -0,0 +1,685 @@
+/** @file
+ The ICMPv6 handle routines to process the ICMPv6 control messages.
+
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 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 "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_MAX));
+
+ if (PrefixLength == 0) {
+ ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS));
+ return ;
+ }
+
+ if (PrefixLength >= IP6_PREFIX_MAX) {
+ return ;
+ }
+
+ Byte = (UINT8) (PrefixLength / 8);
+ Bit = (UINT8) (PrefixLength % 8);
+ Value = Prefix->Addr[Byte];
+
+ if (Byte > 0) {
+ 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..c937423428
--- /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 - 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 "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_MAX)) {
+ 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..9960a9a711
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6Impl.h
@@ -0,0 +1,754 @@
+/** @file
+ Implementation of EFI_IP6_PROTOCOL protocol interfaces and type definitions.
+
+ Copyright (c) 2009 - 2016, 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 <IndustryStandard/Dhcp.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..a3f49bb2da
--- /dev/null
+++ b/Core/NetworkPkg/Ip6Dxe/Ip6Nd.c
@@ -0,0 +1,3155 @@
+/** @file
+ Implementation of Neighbor Discovery support routines.
+
+ 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 "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_MAX) {
+ 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 (DHCP6_OPT_ORO);
+ Oro->OpLen = HTONS (2);
+ *((UINT16 *) &Oro->Data[0]) = HTONS (DHCP6_OPT_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..21d28b6942
--- /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 malformatted.
+
+ @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 malformatted.
+
+**/
+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 malformatted.
+
+**/
+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-formatted.
+
+ @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 formatted.
+ @retval FALSE The option is malformatted.
+
+**/
+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 octets 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 responsibility 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..242ec51c10
--- /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-formatted.
+
+ @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 formatted.
+ @retval FALSE The option is malformatted.
+
+**/
+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 responsibility 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 malformatted.
+
+**/
+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..8330e37935
--- /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 responsibility 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 enough 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 immediately.
+ //
+ 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 integer 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..3e47fa4cc6
--- /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 - 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 "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_MAX; 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_MAX; 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_MAX; 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_MAX; 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..c5fbfab6a9
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/IkeCommon.c
@@ -0,0 +1,330 @@
+/** @file
+ Common operation of the IKE
+
+ 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 "Ike.h"
+#include "IkeCommon.h"
+#include "IpSecConfigImpl.h"
+#include "IpSecDebug.h"
+
+/**
+ Check whether the new generated Spi has existed.
+
+ @param[in] IkeSaSession Pointer to the Child SA Session.
+ @param[in] SpiValue SPI Value.
+
+ @retval TRUE This SpiValue has existed in the Child SA Session
+ @retval FALSE This SpiValue doesn't exist in the Child SA Session.
+
+**/
+BOOLEAN
+IkeSpiValueExisted (
+ IN IKEV2_SA_SESSION *IkeSaSession,
+ IN UINT32 SpiValue
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ IKEV2_CHILD_SA_SESSION *SaSession;
+
+ Entry = NULL;
+ Next = NULL;
+ SaSession = NULL;
+
+ //
+ // Check whether the SPI value has existed in ChildSaEstablishSessionList.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IkeSaSession->ChildSaEstablishSessionList) {
+ SaSession= IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry);
+ if (SaSession->LocalPeerSpi == SpiValue) {
+ return TRUE;
+ }
+ }
+
+ //
+ // Check whether the SPI value has existed in ChildSaSessionList.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &IkeSaSession->ChildSaSessionList) {
+ SaSession= IKEV2_CHILD_SA_SESSION_BY_IKE_SA (Entry);
+ if (SaSession->LocalPeerSpi == SpiValue) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ 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.
+
+ @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to this Child SA
+ Session.
+ @param[in, out] SpiValue Pointer to the new generated SPI value.
+
+ @retval EFI_SUCCESS The operation performs successfully.
+ @retval Otherwise The operation is failed.
+
+**/
+EFI_STATUS
+IkeGenerateSpi (
+ IN IKEV2_SA_SESSION *IkeSaSession,
+ IN OUT UINT32 *SpiValue
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ while (TRUE) {
+ //
+ // Generate SPI randomly
+ //
+ Status = IpSecCryptoIoGenerateRandomBytes ((UINT8 *)SpiValue, sizeof (UINT32));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ //
+ // The set of SPI values in the range 1 through 255 are reserved by the
+ // Internet Assigned Numbers Authority (IANA) for future use; a reserved
+ // SPI value will not normally be assigned by IANA unless the use of the
+ // assigned SPI value is specified in an RFC.
+ //
+ if (*SpiValue < IKE_SPI_BASE) {
+ *SpiValue += IKE_SPI_BASE;
+ }
+
+ //
+ // Check whether the new generated SPI has existed.
+ //
+ if (!IkeSpiValueExisted (IkeSaSession, *SpiValue)) {
+ break;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ 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..eb7e913ee8
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/IkeCommon.h
@@ -0,0 +1,195 @@
+/** @file
+ Common operation of the IKE.
+
+ 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_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 0x100
+#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 new SPI.
+
+ @param[in] IkeSaSession Pointer to IKEV2_SA_SESSION related to this Child SA
+ Session.
+ @param[in, out] SpiValue Pointer to the new generated SPI value.
+
+ @retval EFI_SUCCESS The operation performs successfully.
+ @retval Otherwise The operation is failed.
+
+**/
+EFI_STATUS
+IkeGenerateSpi (
+ IN IKEV2_SA_SESSION *IkeSaSession,
+ IN OUT UINT32 *SpiValue
+ );
+
+/**
+ 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..4c0f6f5eb1
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/IkeService.c
@@ -0,0 +1,819 @@
+/** @file
+ Provide IPsec Key Exchange (IKE) service general interfaces.
+
+ 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 "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;
+ }
+
+ 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);
+ }
+
+ //
+ // 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..eaccad2086
--- /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_INIT;
+ }
+
+ } 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_INIT;
+ }
+ }
+
+ if (MessageId != NULL) {
+ 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..5609964fa4
--- /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..0d2b290817
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Info.c
@@ -0,0 +1,409 @@
+/** @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;
+ }
+
+ if (IkeSaSession->SessionCommon.IsInitiator) {
+ IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT ;
+ }
+ } 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 (ChildSaSession->SessionCommon.IsInitiator) {
+ IkePacket->Header->Flags = IKE_HEADER_FLAGS_INIT ;
+ }
+ }
+
+ if (InfoContext != NULL) {
+ 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..237743b1b1
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.c
@@ -0,0 +1,3353 @@
+/** @file
+ The implementation of Payloads Creation.
+
+ (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2010 - 2017, 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];
+ 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
+ //
+ 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;
+ }
+
+ //
+ // 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 (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
+ 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
+ )
+{
+ 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 == ((UINT32)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..7a85792ed7
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Payload.h
@@ -0,0 +1,443 @@
+/** @file
+ The Definitions related to IKEv2 payload.
+
+ 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_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 (1 octet) for IKEv2, defined in RFC 4306 section 3.1
+//
+// I(nitiator) (bit 3 of Flags, 0x08) - This bit MUST be set in messages sent by the
+// original initiator of the IKE_SA
+//
+// R(esponse) (bit 5 of Flags, 0x20) - This bit indicates that this message is a response to
+// a message containing the same message ID.
+//
+#define IKE_HEADER_FLAGS_INIT 0x08
+#define IKE_HEADER_FLAGS_RESPOND 0x20
+
+//
+// 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..f9421ed4e8
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Sa.c
@@ -0,0 +1,2261 @@
+/** @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 && !EFI_ERROR(Ikev2ParserNotifyCookiePayload (NotifyPayload, IkeSaSession))) {
+ return EFI_SUCCESS;
+ }
+ }
+
+ 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..2ca7f3c63c
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/Ikev2/Utility.c
@@ -0,0 +1,2802 @@
+/** @file
+ The Common operations used by IKE Exchange Process.
+
+ (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2010 - 2017, 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;
+
+ //
+ // Generate an new SPI.
+ //
+ Status = IkeGenerateSpi (IkeSaSession, &(ChildSaSession->LocalPeerSpi));
+ if (EFI_ERROR (Status)) {
+ FreePool (ChildSaSession);
+ return NULL;
+ }
+
+ 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.
+ If the authentication algorithm given by HashAlgId
+ cannot be found.
+ @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);
+ if (AuthKeyLength == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ 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..4a51bff96f
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.c
@@ -0,0 +1,3162 @@
+/** @file
+ The implementation of IPSEC_CONFIG_PROTOCOL.
+
+ Copyright (c) 2009 - 2017, 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 and RemoteAddress 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 and RemoteAddress 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 and RemoteAddress fields in the two Spdselectors.
+ // First, SpdSel1->LocalAddress to SpdSel2->RemoteAddress && Compare
+ // SpdSel1->RemoteAddress to SpdSel2->LocalAddress. 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.
+ - SadEntry requied to be set into new SpdEntry's Sas has
+ been found but it is invalid.
+ @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;
+ LIST_ENTRY *NextEntry2;
+ 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;
+
+ //
+ // Remove the related SAs from Sas(SadEntry->BySpd). If the SA entry is established by
+ // IKE, remove from mConfigData list(SadEntry->List) and then free it directly since its
+ // SpdEntry will be freed later.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry2, NextEntry2, SpdSas) {
+ SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry2);
+
+ if (SadEntry->Data->SpdEntry != NULL) {
+ RemoveEntryList (&SadEntry->BySpd);
+ SadEntry->Data->SpdEntry = NULL;
+ }
+
+ if (!(SadEntry->Data->ManualSet)) {
+ RemoveEntryList (&SadEntry->List);
+ FreePool (SadEntry);
+ }
+ }
+
+ //
+ // 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 + 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
+ )) {
+ //
+ // Check whether the found SadEntry is vaild.
+ //
+ if (IsSubSpdSelector (
+ (EFI_IPSEC_CONFIG_SELECTOR *) SadEntry->Data->SpdSelector,
+ (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector
+ )) {
+ if (SadEntry->Data->SpdEntry != NULL) {
+ RemoveEntryList (&SadEntry->BySpd);
+ }
+ InsertTailList (&SpdEntry->Data->Sas, &SadEntry->BySpd);
+ SadEntry->Data->SpdEntry = SpdEntry;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+ }
+
+ //
+ // 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 + 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 -
+ 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..23e68805ad
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/IpSecConfigImpl.h
@@ -0,0 +1,955 @@
+/** @file
+ Definitions related to IPSEC_CONFIG_PROTOCOL implementations.
+
+ Copyright (c) 2009 - 2017, 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) (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..31fa4fb447
--- /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 algorithm.
+
+ @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 algorithm.
+
+ @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 algorithm.
+
+ @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 algorithm.
+
+ @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 algorithm from the mIpsecEncryptAlgorithmList.
+
+ @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 algorithm from the mIpsecAuthAlgorithmList.
+
+ @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 algorithm 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 Algorithm 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 containing the Initialization
+ 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 algorithm 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 Algorithm 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 containing the Initialization
+ 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 algorithm ID. It computes all datas from InDataFragment and output
+ the result into the OutData buffer. If the OutDataSize is larger than the related
+ HMAC algorithm 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 algorithm ID. It computes all datas from InDataFragment and output
+ the result into the OutData buffer. If the OutDataSize is larger than the related
+ Hash algorithm 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 primelength, 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 Value 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 performs 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 performs 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 performs 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 random bytes to generate.
+
+ @retval EFI_SUCCESS The operation performs 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 octet 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 verified.
+ @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..0ef83eb4ca
--- /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 finalization.
+ 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.
+ Initializes the user-supplied key as the specified 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 initialized 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 initialized 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 Decryption.
+ //
+ CRYPTO_CIPHER_DECRYPT CipherDecrypt;
+} ENCRYPT_ALGORITHM;
+
+//
+// The struct used to store the information and operation of Authentication 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 information 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 algorithm.
+
+ @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 algorithm.
+
+ @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 algorithm.
+
+ @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 Authentication algorithm.
+
+ @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 Algorithm 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 algorithm 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 Algorithm 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 containing the Initialization
+ 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 algorithm 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 Algorithm 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 containing the Initialization
+ 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 algorithm ID. It computes all datas from InDataFragment and output
+ the result into the OutData buffer. If the OutDataSize is larger than the related
+ HMAC algorithm 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 algorithm ID. It computes all datas from InDataFragment and output
+ the result into the OutData buffer. If the OutDataSize is larger than the related
+ Hash algorithm 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 primelength, 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 Value 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 performs 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 performs 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 performs 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 random bytes to generate.
+
+ @retval EFI_SUCCESS The operation performs 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 octet 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 verified.
+ @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..9203174541
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/IpSecDxe.uni
@@ -0,0 +1,25 @@
+// /** @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 - 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Packet-level security for IP datagram"
+
+#string STR_MODULE_DESCRIPTION #language en-US "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."
+
diff --git a/Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni b/Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni
new file mode 100644
index 0000000000..35b0bb9042
--- /dev/null
+++ b/Core/NetworkPkg/IpSecDxe/IpSecDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// IpSecDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"IpSec DXE"
+
+
diff --git a/Core/NetworkPkg/IpSecDxe/IpSecImpl.c b/Core/NetworkPkg/IpSecDxe/IpSecImpl.c
new file mode 100644
index 0000000000..625f154ff4
--- /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 according 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..5853d2e328
--- /dev/null
+++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni
@@ -0,0 +1,23 @@
+// /** @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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Client-side Mtftp6 service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI MTFTPv6 Protocol which provides basic services for client-side unicast and/or multicast TFTP."
+
diff --git a/Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni
new file mode 100644
index 0000000000..2dab5e798e
--- /dev/null
+++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Mtftp6Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"MTFTP6 DXE"
+
+
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..e6b4127b78
--- /dev/null
+++ b/Core/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c
@@ -0,0 +1,1236 @@
+/** @file
+ Mtftp6 support functions implementation.
+
+ Copyright (c) 2009 - 2017, 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 (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.AddressList != NULL) {
+ FreePool (Ip6Mode.AddressList);
+ }
+
+ if (Ip6Mode.GroupTable != NULL) {
+ FreePool (Ip6Mode.GroupTable);
+ }
+
+ if (Ip6Mode.RouteTable != NULL) {
+ FreePool (Ip6Mode.RouteTable);
+ }
+
+ if (Ip6Mode.NeighborCache != NULL) {
+ FreePool (Ip6Mode.NeighborCache);
+ }
+
+ if (Ip6Mode.PrefixTable != NULL) {
+ FreePool (Ip6Mode.PrefixTable);
+ }
+
+ if (Ip6Mode.IcmpTypeList != NULL) {
+ FreePool (Ip6Mode.IcmpTypeList);
+ }
+
+ 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..28558d3c25
--- /dev/null
+++ b/Core/NetworkPkg/NetworkPkg.dec
@@ -0,0 +1,115 @@
+## @file
+# Network Package.
+#
+# This package provides network modules that conform to UEFI 2.4 specification.
+#
+# Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
+# (C) Copyright 2015-2017 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.
+#
+##
+
+[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}}
+
+ # Include/Guid/HttpBootConfigHii.h
+ gHttpBootConfigGuid = { 0x4d20583a, 0x7765, 0x4e7a, { 0x8a, 0x67, 0xdc, 0xde, 0x74, 0xee, 0x3e, 0xc5 }}
+
+ # Include/Guid/TlsAuthConfigHii.h
+ gTlsAuthConfigGuid = { 0xb0eae4f8, 0x9a04, 0x4c6d, { 0xa7, 0x48, 0x79, 0x3d, 0xaa, 0xf, 0x65, 0xdf }}
+
+ # Include/Guid/TlsAuthentication.h
+ gEfiTlsCaCertificateGuid = { 0xfd2340D0, 0x3dab, 0x4349, { 0xa6, 0xc7, 0x3b, 0x4f, 0x12, 0xb4, 0x8e, 0xae }}
+
+[PcdsFixedAtBuild]
+ ## The max attempt number will be created by iSCSI driver.
+ # @Prompt Max attempt number.
+ gEfiNetworkPkgTokenSpaceGuid.PcdMaxIScsiAttemptNumber|0x08|UINT8|0x0000000D
+
+[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
+
+ ## Indicates whether HTTP connections (i.e., unsecured) are permitted or not.
+ # TRUE - HTTP connections are allowed. Both the "https://" and "http://" URI schemes are permitted.
+ # FALSE - HTTP connections are denied. Only the "https://" URI scheme is permitted.
+ # @Prompt Indicates whether HTTP connections are permitted or not.
+ gEfiNetworkPkgTokenSpaceGuid.PcdAllowHttpConnections|FALSE|BOOLEAN|0x00000008
+
+[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
+
+ ## Network boot policy to stop UEFI iSCSI if applicable.
+ # 0x00 = Always use UEFI iSCSI and ignore iSCSI HBA.
+ # 0x01 = Stop UEFI iSCSI if iSCSI HBA adapter produces AIP protocol with Network Boot.
+ # 0x02 = Stop UEFI iSCSI if iSCSI HBA adapter supports booting from iSCSI IPv4 targets.
+ # 0x04 = Stop UEFI iSCSI if iSCSI HBA adapter supports booting from iSCSI IPv6 targets.
+ # 0x08 = Stop UEFI iSCSI if iSCSI HBA adapter supports an offload engine for iSCSI boot.
+ # 0x10 = Stop UEFI iSCSI if iSCSI HBA adapter supports multipath I/O for iSCSI boot.
+ # 0x20 = Stop UEFI iSCSI if iSCSI HBA adapter is currently configured to boot from iSCSI IPv4 targets.
+ # 0x40 = Stop UEFI iSCSI if iSCSI HBA adapter is currently configured to boot from iSCSI IPv6 targets.
+ # 0xFF = Always use iSCSI HBA and ignore UEFI iSCSI.
+ # @Prompt Type Value of network boot policy used in iSCSI.
+ gEfiNetworkPkgTokenSpaceGuid.PcdIScsiAIPNetworkBootPolicy|0x08|UINT8|0x10000007
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ NetworkPkgExtra.uni
diff --git a/Core/NetworkPkg/NetworkPkg.dsc b/Core/NetworkPkg/NetworkPkg.dsc
new file mode 100644
index 0000000000..b8e071b0a9
--- /dev/null
+++ b/Core/NetworkPkg/NetworkPkg.dsc
@@ -0,0 +1,133 @@
+## @file
+# UEFI 2.4 Network Module Package for All Architectures
+#
+# (C) Copyright 2014 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.
+#
+##
+
+[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|NOOPT
+ 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
+ UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
+ TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf
+ PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
+ PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+ DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf
+ DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.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
+ TlsLib|CryptoPkg/Library/TlsLib/TlsLib.inf
+ DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+ FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
+ FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
+ SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
+
+[LibraryClasses.common.UEFI_DRIVER]
+ HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
+ ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+ 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/TlsDxe/TlsDxe.inf
+ NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.inf
+
+ NetworkPkg/Application/Ping6/Ping6.inf
+
+[BuildOptions]
+ *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
diff --git a/Core/NetworkPkg/NetworkPkg.uni b/Core/NetworkPkg/NetworkPkg.uni
new file mode 100644
index 0000000000..cdaee76f7e
--- /dev/null
+++ b/Core/NetworkPkg/NetworkPkg.uni
@@ -0,0 +1,82 @@
+// /** @file
+// This package provides network modules that conform to UEFI 2.4 specification.
+//
+// This package provides network modules that conform to UEFI 2.4 specification.
+//
+// Copyright (c) 2009 - 2017, 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.
+//
+// **/
+
+
+#string STR_PACKAGE_ABSTRACT #language en-US "This package provides network modules that conform to UEFI 2.4 specification."
+
+#string STR_PACKAGE_DESCRIPTION #language en-US "This package provides network modules that conform to UEFI 2.4 specification."
+
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdMaxIScsiAttemptNumber_PROMPT #language en-US "Max attempt number."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdMaxIScsiAttemptNumber_HELP #language en-US "Max attempt number created by iSCSI."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCaFile_PROMPT #language en-US "CA file."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCaFile_HELP #language en-US "CA certificate used by IPsec."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCaFileSize_PROMPT #language en-US "CA file's size."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCaFileSize_HELP #language en-US "CA certificate file's size."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCertificate_PROMPT #language en-US "Pubic Key for remote peer."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCertificate_HELP #language en-US "X509 certificate as Public Key which is used by IPsec (DER format)"
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCertificateSize_PROMPT #language en-US "Pubic Key's size."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCertificateSize_HELP #language en-US "X509 certificate as Public Key's size."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCertificateKey_PROMPT #language en-US "Private Key."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCertificateKey_HELP #language en-US "Private Key used by IPsec (PEM format)."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCertificateKeySize_PROMPT #language en-US "Private Key's size."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecUefiCertificateKeySize_HELP #language en-US "Private Key's size."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdAllowHttpConnections_PROMPT #language en-US "Indicates whether HTTP connections are permitted or not."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdAllowHttpConnections_HELP #language en-US "Indicates whether HTTP connections are permitted or not.\n"
+ "TRUE - HTTP connections are allowed.\n"
+ "FALSE - HTTP connections are denied."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecCertificateEnabled_PROMPT #language en-US "Enable IPsec IKEv2 Certificate Authentication."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIpsecCertificateEnabled_HELP #language en-US "Indicates if the IPsec IKEv2 Certificate Authentication feature is enabled or not.<BR><BR>\n"
+ "TRUE - Certificate Authentication feature is enabled.<BR>\n"
+ "FALSE - Does not support Certificate Authentication.<BR>"
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdDhcp6UidType_PROMPT #language en-US "Type Value of Dhcp6 Unique Identifier (DUID)."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdDhcp6UidType_HELP #language en-US "IPv6 DHCP Unique Identifier (DUID) Type configuration (From RFCs 3315 and 6355).\n"
+ "01 = DUID Based on Link-layer Address Plus Time [DUID-LLT]\n"
+ "04 = UUID-Based DHCPv6 Unique Identifier (DUID-UUID)\n"
+ "02 = DUID Assigned by Vendor Based on Enterprise Number [DUID-EN] (not supported)\n"
+ "03 = DUID Based on Link-layer Address [DUID-LL] (not supported)"
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIScsiAIPNetworkBootPolicy_PROMPT #language en-US "Type Value of network boot policy used in iSCSI."
+
+#string STR_gEfiNetworkPkgTokenSpaceGuid_PcdIScsiAIPNetworkBootPolicy_HELP #language en-US "Network boot policy to stop UEFI iSCSI if applicable.\n"
+ "0x00 = Always use UEFI iSCSI and ignore AIP.\n"
+ "0x01 = Stop UEFI iSCSI if iSCSI HBA adapter produces AIP protocol with Network Boot.\n"
+ "0x02 = Stop UEFI iSCSI if iSCSI HBA adapter supports booting from iSCSI IPv4 targets.\n"
+ "0x04 = Stop UEFI iSCSI if iSCSI HBA adapter supports booting from iSCSI IPv6 targets.\n"
+ "0x08 = Stop UEFI iSCSI if iSCSI HBA adapter supports an offload engine for iSCSI boot.\n"
+ "0x10 = Stop UEFI iSCSI if iSCSI HBA adapter supports multipath I/O for iSCSI boot.\n"
+ "0x20 = Stop UEFI iSCSI if iSCSI HBA adapter is currently configured to boot from iSCSI IPv4 targets.\n"
+ "0x40 = Stop UEFI iSCSI if iSCSI HBA adapter is currently configured to boot from iSCSI IPv6 targets."
diff --git a/Core/NetworkPkg/NetworkPkgExtra.uni b/Core/NetworkPkg/NetworkPkgExtra.uni
new file mode 100644
index 0000000000..339cb86ffb
--- /dev/null
+++ b/Core/NetworkPkg/NetworkPkgExtra.uni
@@ -0,0 +1,19 @@
+// /** @file
+// Network Package Localized Strings and Content.
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_PACKAGE_NAME
+#language en-US
+"Network package"
+
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..c5fb176255
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/SockImpl.c
@@ -0,0 +1,1239 @@
+/** @file
+ Implementation of the Socket.
+
+ Copyright (c) 2009 - 2017, 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_NET,
+ "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;
+ }
+}
+
+/**
+ Cancel the tokens in the specific token list.
+
+ @param[in] Token Pointer to the Token. If NULL, all tokens
+ in SpecifiedTokenList will be canceled.
+ @param[in, out] SpecifiedTokenList Pointer to the token list to be checked.
+
+ @retval EFI_SUCCESS Cancel the tokens in the specific token listsuccessfully.
+ @retval EFI_NOT_FOUND The Token is not found in SpecifiedTokenList.
+
+**/
+EFI_STATUS
+SockCancelToken (
+ IN SOCK_COMPLETION_TOKEN *Token,
+ IN OUT LIST_ENTRY *SpecifiedTokenList
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ SOCK_TOKEN *SockToken;
+
+ Status = EFI_SUCCESS;
+ Entry = NULL;
+ SockToken = NULL;
+
+ if (IsListEmpty (SpecifiedTokenList) && Token != NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Iterate through the SpecifiedTokenList.
+ //
+ Entry = SpecifiedTokenList->ForwardLink;
+ while (Entry != SpecifiedTokenList) {
+ SockToken = NET_LIST_USER_STRUCT (Entry, SOCK_TOKEN, TokenList);
+
+ if (Token == NULL) {
+ SIGNAL_TOKEN (SockToken->Token, EFI_ABORTED);
+ RemoveEntryList (&SockToken->TokenList);
+ FreePool (SockToken);
+
+ Entry = SpecifiedTokenList->ForwardLink;
+ Status = EFI_SUCCESS;
+ } else {
+ if (Token == (VOID *) SockToken->Token) {
+ SIGNAL_TOKEN (Token, EFI_ABORTED);
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+
+ return EFI_SUCCESS;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ Entry = Entry->ForwardLink;
+ }
+ }
+
+ ASSERT (IsListEmpty (SpecifiedTokenList) || Token != NULL);
+
+ return Status;
+}
+
+/**
+ 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_NET,
+ "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
+ )
+{
+ ASSERT (SockStream == Sock->Type);
+
+ //
+ // 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;
+ }
+
+ 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..80692b161e
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/SockImpl.h
@@ -0,0 +1,121 @@
+/** @file
+ The function declaration that provided for Socket Interface.
+
+ Copyright (c) 2009 - 2017, 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"
+#include "TcpMain.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
+ );
+
+/**
+ Cancel the tokens in the specific token list.
+
+ @param[in] Token Pointer to the Token. If NULL, all tokens
+ in SpecifiedTokenList will be canceled.
+ @param[in, out] SpecifiedTokenList Pointer to the token list to be checked.
+
+ @retval EFI_SUCCESS Cancel the tokens in the specific token listsuccessfully.
+ @retval EFI_NOT_FOUND The Token is not found in SpecifiedTokenList.
+
+**/
+EFI_STATUS
+SockCancelToken (
+ IN SOCK_COMPLETION_TOKEN *Token,
+ IN OUT LIST_ENTRY *SpecifiedTokenList
+ );
+
+/**
+ 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..b4ba40afce
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/SockInterface.c
@@ -0,0 +1,1177 @@
+/** @file
+ Interface function of the Socket.
+
+ Copyright (c) 2009 - 2017, 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;
+ TCP_PROTO_DATA *ProtoData;
+ TCP_CB *Tcb;
+ EFI_GUID *IpProtocolGuid;
+ EFI_GUID *TcpProtocolGuid;
+ VOID *SockProtocol;
+
+ ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL));
+
+ if (Sock->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ Sock->InDestroy = TRUE;
+
+ if (Sock->IpVersion == IP_VERSION_4) {
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+ ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ 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;
+ }
+
+ //
+ // Close the IP protocol.
+ //
+ gBS->CloseProtocol (
+ Tcb->IpInfo->ChildHandle,
+ IpProtocolGuid,
+ ProtoData->TcpService->IpIo->Image,
+ Sock->SockHandle
+ );
+
+ if (Sock->DestroyCallback != NULL) {
+ Sock->DestroyCallback (Sock, Sock->Context);
+ }
+
+ //
+ // 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,
+ "SockDestroyChild: Open protocol installed on socket failed with %r\n",
+ Status)
+ );
+ }
+
+ //
+ // Uninstall the protocol installed on this sock
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ SockProtocol,
+ NULL
+ );
+
+ //
+ // 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;
+ VOID *SockProtocol;
+ EFI_GUID *TcpProtocolGuid;
+
+ //
+ // 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)
+ );
+ goto ERROR;
+ }
+ //
+ // 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)
+ );
+ goto ERROR;
+ }
+
+ return Sock;
+
+ERROR:
+
+ if (Sock->DestroyCallback != NULL) {
+ Sock->DestroyCallback (Sock, Sock->Context);
+ }
+
+ if (Sock->IpVersion == IP_VERSION_4) {
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+
+ gBS->OpenProtocol (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ &SockProtocol,
+ Sock->DriverBinding,
+ Sock->SockHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ //
+ // Uninstall the protocol installed on this sock
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ SockProtocol,
+ NULL
+ );
+ SockDestroy (Sock);
+ return NULL;
+}
+
+/**
+ 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_NET,
+ "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) {
+ SockProcessRcvToken (Sock, RcvToken);
+
+ 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;
+}
+
+/**
+ Abort the socket associated connection, listen, transmission or receive request.
+
+ @param[in, out] Sock Pointer to the socket to abort.
+ @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
+SockCancel (
+ IN OUT SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCancel: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ //
+ // 1. Check ConnectionToken.
+ //
+ if (Token == NULL || (SOCK_COMPLETION_TOKEN *) Token == Sock->ConnectionToken) {
+ if (Sock->ConnectionToken != NULL) {
+ SIGNAL_TOKEN (Sock->ConnectionToken, EFI_ABORTED);
+ Sock->ConnectionToken = NULL;
+ }
+
+ if (Token != NULL) {
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+ }
+
+ //
+ // 2. Check ListenTokenList.
+ //
+ Status = SockCancelToken (Token, &Sock->ListenTokenList);
+ if (Token != NULL && !EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // 3. Check RcvTokenList.
+ //
+ Status = SockCancelToken (Token, &Sock->RcvTokenList);
+ if (Token != NULL && !EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // 4. Check SndTokenList.
+ //
+ Status = SockCancelToken (Token, &Sock->SndTokenList);
+ if (Token != NULL && !EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // 5. Check ProcessingSndTokenList.
+ //
+ Status = SockCancelToken (Token, &Sock->ProcessingSndTokenList);
+
+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..371e9abd84
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/Socket.h
@@ -0,0 +1,942 @@
+/** @file
+ Common head file for TCP socket.
+
+ 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 _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
+ );
+
+/**
+ Abort the socket associated connection, listen, transmission or receive request.
+
+ @param[in, out] Sock Pointer to the socket to abort.
+ @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
+SockCancel (
+ IN OUT SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ 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..9a352b1531
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpDispatcher.c
@@ -0,0 +1,896 @@
+/** @file
+ The implementation of a dispatch routine for processing TCP requests.
+
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2017, 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;
+
+ ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ TcpFlushPcb (Tcb);
+
+ 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..6f815ef841
--- /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 - 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 "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 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
+ )
+{
+ 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..30b102f878
--- /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 - 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 _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 prototypes for the ServiceBinding Protocol
+//
+
+/**
+ 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..112f017d81
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpDxe.uni
@@ -0,0 +1,24 @@
+// /** @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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "TCPv4 I/O and TCPv6 I/O services"
+
+#string STR_MODULE_DESCRIPTION #language en-US "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."
+
diff --git a/Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni b/Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni
new file mode 100644
index 0000000000..097203cbdb
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// TcpDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"TCP DXE"
+
+
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..04c8a8269e
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpInput.c
@@ -0,0 +1,1619 @@
+/** @file
+ TCP input process routines.
+
+ 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 "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->RcvNxt, 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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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);
+
+ if (Nbuf->TotalSize < sizeof (TCP_HEAD)) {
+ DEBUG ((EFI_D_NET, "TcpInput: received a malformed packet\n"));
+ goto DISCARD;
+ }
+
+ Len = Nbuf->TotalSize - (Head->HeadLen << 2);
+
+ if ((Head->HeadLen < 5) || (Len < 0)) {
+
+ DEBUG ((EFI_D_NET, "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_NET, "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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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;
+
+ if (Nbuf->TotalSize < sizeof (TCP_HEAD)) {
+ goto CLEAN_EXIT;
+ }
+
+ 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..fc3713e533
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpMain.c
@@ -0,0 +1,1109 @@
+/** @file
+ Implementation of EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.
+
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2017, 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 (IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) {
+ 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 (!IP4_IS_VALID_NETMASK (NTOHL (SubnetMask)) ||
+ (SubnetMask != 0 && !NetIp4IsUnicast (NTOHL (Ip), 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 This The pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token The pointer to a token that has been issued by
+ EFI_TCP4_PROTOCOL.Connect(),
+ EFI_TCP4_PROTOCOL.Accept(),
+ EFI_TCP4_PROTOCOL.Transmit() or
+ EFI_TCP4_PROTOCOL.Receive(). If NULL, all pending
+ tokens issued by above four functions will be aborted. Type
+ EFI_TCP4_COMPLETION_TOKEN is defined in
+ EFI_TCP4_PROTOCOL.Connect().
+
+ @retval EFI_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @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 I/O request isn't found in the
+ transmission or receive queue. It has either
+ completed or wasn't issued by Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Cancel (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockCancel (Sock, Token);
+}
+
+/**
+ 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_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance hasn't been configured.
+ @retval EFI_NOT_FOUND The asynchronous I/O request isn't found in the transmission or
+ receive queue. It has either completed or wasn't issued by
+ Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Cancel (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockCancel (Sock, Token);
+}
+
+/**
+ 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..c10030174b
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpMain.h
@@ -0,0 +1,783 @@
+/** @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 - 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 _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 This The pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token The pointer to a token that has been issued by
+ EFI_TCP4_PROTOCOL.Connect(),
+ EFI_TCP4_PROTOCOL.Accept(),
+ EFI_TCP4_PROTOCOL.Transmit() or
+ EFI_TCP4_PROTOCOL.Receive(). If NULL, all pending
+ tokens issued by above four functions will be aborted. Type
+ EFI_TCP4_COMPLETION_TOKEN is defined in
+ EFI_TCP4_PROTOCOL.Connect().
+
+ @retval EFI_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @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 I/O request isn't found in the
+ transmission or receive queue. It has either
+ completed or wasn't issued by Transmit() and Receive().
+
+**/
+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_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance hasn't been configured.
+ @retval EFI_NOT_FOUND The asynchronous I/O request isn't found in the transmission or
+ receive queue. It has either completed or wasn't issued by
+ Transmit() and Receive().
+
+**/
+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..a8592c9689
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpMisc.c
@@ -0,0 +1,1047 @@
+/** @file
+ Misc support routines for TCP driver.
+
+ (C) Copyright 2014 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 "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);
+ if (!EFI_ERROR (Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL))) {
+ if (Ip6Mode.AddressList != NULL) {
+ FreePool (Ip6Mode.AddressList);
+ }
+
+ if (Ip6Mode.GroupTable != NULL) {
+ FreePool (Ip6Mode.GroupTable);
+ }
+
+ if (Ip6Mode.RouteTable != NULL) {
+ FreePool (Ip6Mode.RouteTable);
+ }
+
+ if (Ip6Mode.NeighborCache != NULL) {
+ FreePool (Ip6Mode.NeighborCache);
+ }
+
+ if (Ip6Mode.PrefixTable != NULL) {
+ FreePool (Ip6Mode.PrefixTable);
+ }
+
+ if (Ip6Mode.IcmpTypeList != NULL) {
+ FreePool (Ip6Mode.IcmpTypeList);
+ }
+ }
+
+ 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_NET,
+ "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_NET,
+ "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",
+ Tcb)
+ );
+
+ TcpSendAck (Tcb);
+ } else if (Tcb->DelayedAck == 0) {
+
+ DEBUG (
+ (EFI_D_NET,
+ "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..a46cb6099e
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/TcpOutput.c
@@ -0,0 +1,1219 @@
+/** @file
+ TCP output process routines.
+
+ 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 "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) < (ARRAY_SIZE (mTcpOutFlag)));
+ 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_NET,
+ "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_NET,
+ "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_NET,
+ "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/TlsAuthConfigDxe/TlsAuthConfigDxe.c b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.c
new file mode 100644
index 0000000000..351656ff0c
--- /dev/null
+++ b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.c
@@ -0,0 +1,135 @@
+/** @file
+ The DriverEntryPoint for TlsAuthConfigDxe driver.
+
+ Copyright (c) 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 "TlsAuthConfigImpl.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
+TlsAuthConfigDxeUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ TLS_AUTH_CONFIG_PRIVATE_DATA *PrivateData;
+
+ Status = gBS->HandleProtocol (
+ ImageHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &PrivateData
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (PrivateData->Signature == TLS_AUTH_CONFIG_PRIVATE_DATA_SIGNATURE);
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ &ImageHandle,
+ &gEfiCallerIdGuid,
+ PrivateData,
+ NULL
+ );
+
+ TlsAuthConfigFormUnload (PrivateData);
+
+ 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
+TlsAuthConfigDxeDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ TLS_AUTH_CONFIG_PRIVATE_DATA *PrivateData;
+
+ PrivateData = NULL;
+
+ //
+ // If already started, return.
+ //
+ Status = gBS->OpenProtocol (
+ ImageHandle,
+ &gEfiCallerIdGuid,
+ NULL,
+ ImageHandle,
+ ImageHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Initialize the private data structure.
+ //
+ PrivateData = AllocateZeroPool (sizeof (TLS_AUTH_CONFIG_PRIVATE_DATA));
+ if (PrivateData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the HII configuration form.
+ //
+ Status = TlsAuthConfigFormInit (PrivateData);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install private GUID.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ImageHandle,
+ &gEfiCallerIdGuid,
+ PrivateData,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ TlsAuthConfigFormUnload (PrivateData);
+ FreePool (PrivateData);
+
+ return Status;
+}
+
diff --git a/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.inf b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.inf
new file mode 100644
index 0000000000..2a893689bb
--- /dev/null
+++ b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.inf
@@ -0,0 +1,74 @@
+## @file
+# Provides the capability to configure Tls Authentication in a setup browser
+# By this module, user may change the content of TlsCaCertificate.
+#
+# Copyright (c) 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.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = TlsAuthConfigDxe
+ MODULE_UNI_FILE = TlsAuthConfigDxe.uni
+ FILE_GUID = 7ca1024f-eb17-11e5-9dba-28d2447c4829
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = TlsAuthConfigDxeDriverEntryPoint
+ UNLOAD_IMAGE = TlsAuthConfigDxeUnload
+
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ NetworkPkg/NetworkPkg.dec
+
+[Sources]
+ TlsAuthConfigImpl.c
+ TlsAuthConfigImpl.h
+ TlsAuthConfigNvData.h
+ TlsAuthConfigDxe.c
+ TlsAuthConfigDxeStrings.uni
+ TlsAuthConfigVfr.vfr
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ HiiLib
+ DevicePathLib
+ UefiHiiServicesLib
+ FileExplorerLib
+ PrintLib
+
+[Protocols]
+ gEfiDevicePathProtocolGuid ## PRODUCES
+ gEfiHiiConfigAccessProtocolGuid ## PRODUCES
+ gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES
+
+[Guids]
+ gTlsAuthConfigGuid ## PRODUCES ## GUID
+ gEfiCertX509Guid ## CONSUMES ## GUID # Indicate the cert type
+ gEfiIfrTianoGuid ## CONSUMES ## HII
+ gEfiTlsCaCertificateGuid ## PRODUCES ## GUID
+
+[Depex]
+ gEfiHiiConfigRoutingProtocolGuid AND
+ gEfiHiiDatabaseProtocolGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ TlsAuthConfigDxeExtra.uni
+
diff --git a/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.uni b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.uni
new file mode 100644
index 0000000000..dcd308fda0
--- /dev/null
+++ b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.uni
@@ -0,0 +1,21 @@
+// /** @file
+// Provides the capability to configure Tls Authentication in a setup browser
+//
+// By this module, user may change the content of TlsCaCertificate.
+//
+// Copyright (c) 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Provides the capability to configure Tls Authentication in a setup browser"
+
+#string STR_MODULE_DESCRIPTION #language en-US "By this module, user may change the content of TlsCaCertificate."
+
diff --git a/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxeExtra.uni b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxeExtra.uni
new file mode 100644
index 0000000000..d284537303
--- /dev/null
+++ b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxeExtra.uni
@@ -0,0 +1,19 @@
+// /** @file
+// TlsAuthConfigDxe Localized Strings and Content
+//
+// Copyright (c) 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"TLS Auth Config DXE"
+
+
diff --git a/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxeStrings.uni b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxeStrings.uni
new file mode 100644
index 0000000000..6ffa52df62
--- /dev/null
+++ b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxeStrings.uni
@@ -0,0 +1,39 @@
+/** @file
+ String definitions for Tls Authentication Configuration form.
+
+Copyright (c) 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.
+
+**/
+
+#langdef en-US "English"
+
+#string STR_TLS_AUTH_CONFIG_TITLE #language en-US "Tls Auth Configuration"
+#string STR_TLS_AUTH_CONFIG_HELP #language en-US "Press <Enter> to select Tls Auth Configuration."
+
+#string STR_TLS_AUTH_CONFIG_SERVER_CA #language en-US "Server CA Configuration"
+#string STR_TLS_AUTH_CONFIG_SERVER_CA_HELP #language en-US "Press <Enter> to configure Server CA."
+#string STR_TLS_AUTH_CONFIG_CLIENT_CERT #language en-US "Client Cert Configuration"
+#string STR_TLS_AUTH_CONFIG_CLIENT_CERT_HELP #language en-US "Client cert configuration is unsupported currently."
+
+#string STR_TLS_AUTH_CONFIG_ENROLL_CERT #language en-US "Enroll Cert"
+#string STR_TLS_AUTH_CONFIG_ENROLL_CERT_HELP #language en-US "Press <Enter> to enroll cert."
+#string STR_TLS_AUTH_CONFIG_DELETE_CERT #language en-US "Delete Cert"
+#string STR_TLS_AUTH_CONFIG_DELETE_CERT_HELP #language en-US "Press <Enter> to delete cert."
+
+#string STR_TLS_AUTH_CONFIG_ADD_CERT_FILE #language en-US "Enroll Cert Using File"
+
+#string STR_TLS_AUTH_CONFIG_CERT_GUID #language en-US "Cert GUID"
+#string STR_TLS_AUTH_CONFIG_CERT_GUID_HELP #language en-US "Input digit character in 11111111-2222-3333-4444-1234567890ab format."
+#string STR_TLS_AUTH_CONFIG_SAVE_AND_EXIT #language en-US "Commit Changes and Exit"
+#string STR_TLS_AUTH_CONFIG_NO_SAVE_AND_EXIT #language en-US "Discard Changes and Exit"
+
+#string STR_CERT_TYPE_PCKS_GUID #language en-US "GUID for CERT"
+
+#string STR_NULL #language en-US ""
diff --git a/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigImpl.c b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigImpl.c
new file mode 100644
index 0000000000..81f7e7d0f4
--- /dev/null
+++ b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigImpl.c
@@ -0,0 +1,1689 @@
+/** @file
+ The Miscellaneous Routines for TlsAuthConfigDxe driver.
+
+Copyright (c) 2016 - 2017, 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 "TlsAuthConfigImpl.h"
+
+VOID *mStartOpCodeHandle = NULL;
+VOID *mEndOpCodeHandle = NULL;
+EFI_IFR_GUID_LABEL *mStartLabel = NULL;
+EFI_IFR_GUID_LABEL *mEndLabel = NULL;
+
+
+CHAR16 mTlsAuthConfigStorageName[] = L"TLS_AUTH_CONFIG_IFR_NVDATA";
+
+TLS_AUTH_CONFIG_PRIVATE_DATA *mTlsAuthPrivateData = NULL;
+
+HII_VENDOR_DEVICE_PATH mTlsAuthConfigHiiVendorDevicePath = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+ }
+ },
+ TLS_AUTH_CONFIG_GUID
+ },
+ {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ (UINT8) (END_DEVICE_PATH_LENGTH),
+ (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
+ }
+ }
+};
+
+//
+// Possible DER-encoded certificate file suffixes, end with NULL pointer.
+//
+CHAR16* mDerPemEncodedSuffix[] = {
+ L".cer",
+ L".der",
+ L".crt",
+ L".pem",
+ NULL
+};
+
+/**
+ This code checks if the FileSuffix is one of the possible DER/PEM-encoded certificate suffix.
+
+ @param[in] FileSuffix The suffix of the input certificate file
+
+ @retval TRUE It's a DER/PEM-encoded certificate.
+ @retval FALSE It's NOT a DER/PEM-encoded certificate.
+
+**/
+BOOLEAN
+IsDerPemEncodeCertificate (
+ IN CONST CHAR16 *FileSuffix
+)
+{
+ UINTN Index;
+ for (Index = 0; mDerPemEncodedSuffix[Index] != NULL; Index++) {
+ if (StrCmp (FileSuffix, mDerPemEncodedSuffix[Index]) == 0) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ Worker function that prints an EFI_GUID into specified Buffer.
+
+ @param[in] Guid Pointer to GUID to print.
+ @param[in] Buffer Buffer to print Guid into.
+ @param[in] BufferSize Size of Buffer.
+
+ @retval Number of characters printed.
+
+**/
+UINTN
+GuidToString (
+ IN EFI_GUID *Guid,
+ IN CHAR16 *Buffer,
+ IN UINTN BufferSize
+ )
+{
+ return UnicodeSPrint (
+ Buffer,
+ BufferSize,
+ L"%g",
+ Guid
+ );
+}
+
+/**
+ List all cert in specified database by GUID in the page
+ for user to select and delete as needed.
+
+ @param[in] PrivateData Module's private data.
+ @param[in] VariableName The variable name of the vendor's signature database.
+ @param[in] VendorGuid A unique identifier for the vendor.
+ @param[in] LabelNumber Label number to insert opcodes.
+ @param[in] FormId Form ID of current page.
+ @param[in] QuestionIdBase Base question id of the signature list.
+
+ @retval EFI_SUCCESS Success to update the signature list page
+ @retval EFI_OUT_OF_RESOURCES Unable to allocate required resources.
+
+**/
+EFI_STATUS
+UpdateDeletePage (
+ IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT16 LabelNumber,
+ IN EFI_FORM_ID FormId,
+ IN EFI_QUESTION_ID QuestionIdBase
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINTN CertCount;
+ UINTN GuidIndex;
+ VOID *StartOpCodeHandle;
+ VOID *EndOpCodeHandle;
+ EFI_IFR_GUID_LABEL *StartLabel;
+ EFI_IFR_GUID_LABEL *EndLabel;
+ UINTN DataSize;
+ UINT8 *Data;
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_DATA *Cert;
+ UINT32 ItemDataSize;
+ CHAR16 *GuidStr;
+ EFI_STRING_ID GuidID;
+ EFI_STRING_ID Help;
+
+ Data = NULL;
+ CertList = NULL;
+ Cert = NULL;
+ GuidStr = NULL;
+ StartOpCodeHandle = NULL;
+ EndOpCodeHandle = NULL;
+
+ //
+ // Initialize the container for dynamic opcodes.
+ //
+ StartOpCodeHandle = HiiAllocateOpCodeHandle ();
+ if (StartOpCodeHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ EndOpCodeHandle = HiiAllocateOpCodeHandle ();
+ if (EndOpCodeHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Create Hii Extend Label OpCode.
+ //
+ StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ StartOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ StartLabel->Number = LabelNumber;
+
+ EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ EndOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ EndLabel->Number = LABEL_END;
+
+ //
+ // Read Variable.
+ //
+ DataSize = 0;
+ Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data);
+ if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
+ goto ON_EXIT;
+ }
+
+ Data = (UINT8 *) AllocateZeroPool (DataSize);
+ if (Data == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, Data);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ GuidStr = AllocateZeroPool (100);
+ if (GuidStr == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Enumerate all data.
+ //
+ ItemDataSize = (UINT32) DataSize;
+ CertList = (EFI_SIGNATURE_LIST *) Data;
+ GuidIndex = 0;
+
+ while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
+
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ Help = STRING_TOKEN (STR_CERT_TYPE_PCKS_GUID);
+ } else {
+ //
+ // The signature type is not supported in current implementation.
+ //
+ ItemDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ continue;
+ }
+
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ for (Index = 0; Index < CertCount; Index++) {
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList
+ + sizeof (EFI_SIGNATURE_LIST)
+ + CertList->SignatureHeaderSize
+ + Index * CertList->SignatureSize);
+ //
+ // Display GUID and help
+ //
+ GuidToString (&Cert->SignatureOwner, GuidStr, 100);
+ GuidID = HiiSetString (Private->RegisteredHandle, 0, GuidStr, NULL);
+ HiiCreateCheckBoxOpCode (
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (QuestionIdBase + GuidIndex++),
+ 0,
+ 0,
+ GuidID,
+ Help,
+ EFI_IFR_FLAG_CALLBACK,
+ 0,
+ NULL
+ );
+ }
+
+ ItemDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
+
+ON_EXIT:
+ HiiUpdateForm (
+ Private->RegisteredHandle,
+ &gTlsAuthConfigGuid,
+ FormId,
+ StartOpCodeHandle,
+ EndOpCodeHandle
+ );
+
+ if (StartOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (StartOpCodeHandle);
+ }
+
+ if (EndOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (EndOpCodeHandle);
+ }
+
+ if (Data != NULL) {
+ FreePool (Data);
+ }
+
+ if (GuidStr != NULL) {
+ FreePool (GuidStr);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Delete one entry from cert database.
+
+ @param[in] PrivateData Module's private data.
+ @param[in] VariableName The variable name of the database.
+ @param[in] VendorGuid A unique identifier for the vendor.
+ @param[in] LabelNumber Label number to insert opcodes.
+ @param[in] FormId Form ID of current page.
+ @param[in] QuestionIdBase Base question id of the cert list.
+ @param[in] DeleteIndex Cert index to delete.
+
+ @retval EFI_SUCCESS Delete siganture successfully.
+ @retval EFI_NOT_FOUND Can't find the signature item,
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+**/
+EFI_STATUS
+DeleteCert (
+ IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT16 LabelNumber,
+ IN EFI_FORM_ID FormId,
+ IN EFI_QUESTION_ID QuestionIdBase,
+ IN UINTN DeleteIndex
+ )
+{
+ EFI_STATUS Status;
+ UINTN DataSize;
+ UINT8 *Data;
+ UINT8 *OldData;
+ UINT32 Attr;
+ UINT32 Index;
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_LIST *NewCertList;
+ EFI_SIGNATURE_DATA *Cert;
+ UINTN CertCount;
+ UINT32 Offset;
+ BOOLEAN IsItemFound;
+ UINT32 ItemDataSize;
+ UINTN GuidIndex;
+
+ Data = NULL;
+ OldData = NULL;
+ CertList = NULL;
+ Cert = NULL;
+ Attr = 0;
+
+ //
+ // Get original signature list data.
+ //
+ DataSize = 0;
+ Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, NULL);
+ if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
+ goto ON_EXIT;
+ }
+
+ OldData = (UINT8 *) AllocateZeroPool (DataSize);
+ if (OldData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Status = gRT->GetVariable (VariableName, VendorGuid, &Attr, &DataSize, OldData);
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Allocate space for new variable.
+ //
+ Data = (UINT8*) AllocateZeroPool (DataSize);
+ if (Data == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Enumerate all data and erasing the target item.
+ //
+ IsItemFound = FALSE;
+ ItemDataSize = (UINT32) DataSize;
+ CertList = (EFI_SIGNATURE_LIST *) OldData;
+ Offset = 0;
+ GuidIndex = 0;
+ while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ //
+ // Copy EFI_SIGNATURE_LIST header then calculate the signature count in this list.
+ //
+ CopyMem (Data + Offset, CertList, (sizeof(EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize));
+ NewCertList = (EFI_SIGNATURE_LIST*) (Data + Offset);
+ Offset += (sizeof(EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ for (Index = 0; Index < CertCount; Index++) {
+ if (GuidIndex == DeleteIndex) {
+ //
+ // Find it! Skip it!
+ //
+ NewCertList->SignatureListSize -= CertList->SignatureSize;
+ IsItemFound = TRUE;
+ } else {
+ //
+ // This item doesn't match. Copy it to the Data buffer.
+ //
+ CopyMem (Data + Offset, (UINT8*)(Cert), CertList->SignatureSize);
+ Offset += CertList->SignatureSize;
+ }
+ GuidIndex++;
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
+ }
+ } else {
+ //
+ // This List doesn't match. Just copy it to the Data buffer.
+ //
+ CopyMem (Data + Offset, (UINT8*)(CertList), CertList->SignatureListSize);
+ Offset += CertList->SignatureListSize;
+ }
+
+ ItemDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
+
+ if (!IsItemFound) {
+ //
+ // Doesn't find the signature Item!
+ //
+ Status = EFI_NOT_FOUND;
+ goto ON_EXIT;
+ }
+
+ //
+ // Delete the EFI_SIGNATURE_LIST header if there is no signature in the list.
+ //
+ ItemDataSize = Offset;
+ CertList = (EFI_SIGNATURE_LIST *) Data;
+ Offset = 0;
+ ZeroMem (OldData, ItemDataSize);
+ while ((ItemDataSize > 0) && (ItemDataSize >= CertList->SignatureListSize)) {
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ DEBUG ((DEBUG_INFO, " CertCount = %x\n", CertCount));
+ if (CertCount != 0) {
+ CopyMem (OldData + Offset, (UINT8*)(CertList), CertList->SignatureListSize);
+ Offset += CertList->SignatureListSize;
+ }
+ ItemDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
+
+ DataSize = Offset;
+
+ Status = gRT->SetVariable(
+ VariableName,
+ VendorGuid,
+ Attr,
+ DataSize,
+ OldData
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to set variable, Status = %r\n", Status));
+ goto ON_EXIT;
+ }
+
+ON_EXIT:
+ if (Data != NULL) {
+ FreePool(Data);
+ }
+
+ if (OldData != NULL) {
+ FreePool(OldData);
+ }
+
+ return UpdateDeletePage (
+ Private,
+ VariableName,
+ VendorGuid,
+ LabelNumber,
+ FormId,
+ QuestionIdBase
+ );
+}
+
+
+/**
+ Close an open file handle.
+
+ @param[in] FileHandle The file handle to close.
+
+**/
+VOID
+CloseFile (
+ IN EFI_FILE_HANDLE FileHandle
+ )
+{
+ if (FileHandle != NULL) {
+ FileHandle->Close (FileHandle);
+ }
+}
+
+/**
+ Read file content into BufferPtr, the size of the allocate buffer
+ is *FileSize plus AddtionAllocateSize.
+
+ @param[in] FileHandle The file to be read.
+ @param[in, out] BufferPtr Pointers to the pointer of allocated buffer.
+ @param[out] FileSize Size of input file
+ @param[in] AddtionAllocateSize Addtion size the buffer need to be allocated.
+ In case the buffer need to contain others besides the file content.
+
+ @retval EFI_SUCCESS The file was read into the buffer.
+ @retval EFI_INVALID_PARAMETER A parameter was invalid.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+ @retval others Unexpected error.
+
+**/
+EFI_STATUS
+ReadFileContent (
+ IN EFI_FILE_HANDLE FileHandle,
+ IN OUT VOID **BufferPtr,
+ OUT UINTN *FileSize,
+ IN UINTN AddtionAllocateSize
+ )
+
+{
+ UINTN BufferSize;
+ UINT64 SourceFileSize;
+ VOID *Buffer;
+ EFI_STATUS Status;
+
+ if ((FileHandle == NULL) || (FileSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Buffer = NULL;
+
+ //
+ // Get the file size
+ //
+ Status = FileHandle->SetPosition (FileHandle, (UINT64) -1);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = FileHandle->GetPosition (FileHandle, &SourceFileSize);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = FileHandle->SetPosition (FileHandle, 0);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ BufferSize = (UINTN) SourceFileSize + AddtionAllocateSize;
+ Buffer = AllocateZeroPool(BufferSize);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ BufferSize = (UINTN) SourceFileSize;
+ *FileSize = BufferSize;
+
+ Status = FileHandle->Read (FileHandle, &BufferSize, Buffer);
+ if (EFI_ERROR (Status) || BufferSize != *FileSize) {
+ FreePool (Buffer);
+ Buffer = NULL;
+ Status = EFI_BAD_BUFFER_SIZE;
+ goto ON_EXIT;
+ }
+
+ON_EXIT:
+
+ *BufferPtr = Buffer;
+ return Status;
+}
+
+/**
+ This function will open a file or directory referenced by DevicePath.
+
+ This function opens a file with the open mode according to the file path. The
+ Attributes is valid only for EFI_FILE_MODE_CREATE.
+
+ @param[in, out] FilePath On input, the device path to the file.
+ On output, the remaining device path.
+ @param[out] FileHandle Pointer to the file handle.
+ @param[in] OpenMode The mode to open the file with.
+ @param[in] Attributes The file's file attributes.
+
+ @retval EFI_SUCCESS The information was set.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+ @retval EFI_UNSUPPORTED Could not open the file path.
+ @retval EFI_NOT_FOUND The specified file could not be found on the
+ device or the file system could not be found on
+ the device.
+ @retval EFI_NO_MEDIA The device has no medium.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the
+ medium is no longer supported.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_WRITE_PROTECTED The file or medium is write protected.
+ @retval EFI_ACCESS_DENIED The file was opened read only.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the
+ file.
+ @retval EFI_VOLUME_FULL The volume is full.
+**/
+EFI_STATUS
+EFIAPI
+OpenFileByDevicePath (
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,
+ OUT EFI_FILE_HANDLE *FileHandle,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *EfiSimpleFileSystemProtocol;
+ EFI_FILE_PROTOCOL *Handle1;
+ EFI_FILE_PROTOCOL *Handle2;
+ EFI_HANDLE DeviceHandle;
+
+ if ((FilePath == NULL || FileHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gBS->LocateDevicePath (
+ &gEfiSimpleFileSystemProtocolGuid,
+ FilePath,
+ &DeviceHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol(
+ DeviceHandle,
+ &gEfiSimpleFileSystemProtocolGuid,
+ (VOID**)&EfiSimpleFileSystemProtocol,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = EfiSimpleFileSystemProtocol->OpenVolume(EfiSimpleFileSystemProtocol, &Handle1);
+ if (EFI_ERROR (Status)) {
+ FileHandle = NULL;
+ return Status;
+ }
+
+ //
+ // go down directories one node at a time.
+ //
+ while (!IsDevicePathEnd (*FilePath)) {
+ //
+ // For file system access each node should be a file path component
+ //
+ if (DevicePathType (*FilePath) != MEDIA_DEVICE_PATH ||
+ DevicePathSubType (*FilePath) != MEDIA_FILEPATH_DP
+ ) {
+ FileHandle = NULL;
+ return (EFI_INVALID_PARAMETER);
+ }
+ //
+ // Open this file path node
+ //
+ Handle2 = Handle1;
+ Handle1 = NULL;
+
+ //
+ // Try to test opening an existing file
+ //
+ Status = Handle2->Open (
+ Handle2,
+ &Handle1,
+ ((FILEPATH_DEVICE_PATH*)*FilePath)->PathName,
+ OpenMode &~EFI_FILE_MODE_CREATE,
+ 0
+ );
+
+ //
+ // see if the error was that it needs to be created
+ //
+ if ((EFI_ERROR (Status)) && (OpenMode != (OpenMode &~EFI_FILE_MODE_CREATE))) {
+ Status = Handle2->Open (
+ Handle2,
+ &Handle1,
+ ((FILEPATH_DEVICE_PATH*)*FilePath)->PathName,
+ OpenMode,
+ Attributes
+ );
+ }
+ //
+ // Close the last node
+ //
+ Handle2->Close (Handle2);
+
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ //
+ // Get the next node
+ //
+ *FilePath = NextDevicePathNode (*FilePath);
+ }
+
+ //
+ // This is a weak spot since if the undefined SHELL_FILE_HANDLE format changes this must change also!
+ //
+ *FileHandle = (VOID*)Handle1;
+ return EFI_SUCCESS;
+}
+
+/**
+ This function converts an input device structure to a Unicode string.
+
+ @param[in] DevPath A pointer to the device path structure.
+
+ @return A new allocated Unicode string that represents the device path.
+
+**/
+CHAR16 *
+EFIAPI
+DevicePathToStr (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevPath
+ )
+{
+ return ConvertDevicePathToText (
+ DevPath,
+ FALSE,
+ TRUE
+ );
+}
+
+
+/**
+ Extract filename from device path. The returned buffer is allocated using AllocateCopyPool.
+ The caller is responsible for freeing the allocated buffer using FreePool(). If return NULL
+ means not enough memory resource.
+
+ @param DevicePath Device path.
+
+ @retval NULL Not enough memory resourece for AllocateCopyPool.
+ @retval Other A new allocated string that represents the file name.
+
+**/
+CHAR16 *
+ExtractFileNameFromDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ CHAR16 *String;
+ CHAR16 *MatchString;
+ CHAR16 *LastMatch;
+ CHAR16 *FileName;
+ UINTN Length;
+
+ ASSERT(DevicePath != NULL);
+
+ String = DevicePathToStr(DevicePath);
+ MatchString = String;
+ LastMatch = String;
+ FileName = NULL;
+
+ while(MatchString != NULL){
+ LastMatch = MatchString + 1;
+ MatchString = StrStr(LastMatch,L"\\");
+ }
+
+ Length = StrLen(LastMatch);
+ FileName = AllocateCopyPool ((Length + 1) * sizeof(CHAR16), LastMatch);
+ if (FileName != NULL) {
+ *(FileName + Length) = 0;
+ }
+
+ FreePool(String);
+
+ return FileName;
+}
+
+/**
+ Enroll a new X509 certificate into Variable.
+
+ @param[in] PrivateData The module's private data.
+ @param[in] VariableName Variable name of CA database.
+
+ @retval EFI_SUCCESS New X509 is enrolled successfully.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+
+**/
+EFI_STATUS
+EnrollX509toVariable (
+ IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private,
+ IN CHAR16 *VariableName
+ )
+{
+ EFI_STATUS Status;
+ UINTN X509DataSize;
+ VOID *X509Data;
+ EFI_SIGNATURE_LIST *CACert;
+ EFI_SIGNATURE_DATA *CACertData;
+ VOID *Data;
+ UINTN DataSize;
+ UINTN SigDataSize;
+ UINT32 Attr;
+
+ X509DataSize = 0;
+ SigDataSize = 0;
+ DataSize = 0;
+ X509Data = NULL;
+ CACert = NULL;
+ CACertData = NULL;
+ Data = NULL;
+
+ Status = ReadFileContent (
+ Private->FileContext->FHandle,
+ &X509Data,
+ &X509DataSize,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ ASSERT (X509Data != NULL);
+
+ SigDataSize = sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + X509DataSize;
+
+ Data = AllocateZeroPool (SigDataSize);
+ if (Data == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Fill Certificate Database parameters.
+ //
+ CACert = (EFI_SIGNATURE_LIST*) Data;
+ CACert->SignatureListSize = (UINT32) SigDataSize;
+ CACert->SignatureHeaderSize = 0;
+ CACert->SignatureSize = (UINT32) (sizeof(EFI_SIGNATURE_DATA) - 1 + X509DataSize);
+ CopyGuid (&CACert->SignatureType, &gEfiCertX509Guid);
+
+ CACertData = (EFI_SIGNATURE_DATA*) ((UINT8* ) CACert + sizeof (EFI_SIGNATURE_LIST));
+ CopyGuid (&CACertData->SignatureOwner, Private->CertGuid);
+ CopyMem ((UINT8* ) (CACertData->SignatureData), X509Data, X509DataSize);
+
+ //
+ // Check if signature database entry has been already existed.
+ // If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the
+ // new signature data to original variable
+ //
+ Attr = TLS_AUTH_CONFIG_VAR_BASE_ATTR;
+
+ Status = gRT->GetVariable(
+ VariableName,
+ &gEfiTlsCaCertificateGuid,
+ NULL,
+ &DataSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Attr |= EFI_VARIABLE_APPEND_WRITE;
+ } else if (Status != EFI_NOT_FOUND) {
+ goto ON_EXIT;
+ }
+
+ Status = gRT->SetVariable(
+ VariableName,
+ &gEfiTlsCaCertificateGuid,
+ Attr,
+ SigDataSize,
+ Data
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ON_EXIT:
+
+ CloseFile (Private->FileContext->FHandle);
+ if (Private->FileContext->FileName != NULL) {
+ FreePool(Private->FileContext->FileName);
+ Private->FileContext->FileName = NULL;
+ }
+
+ Private->FileContext->FHandle = NULL;
+
+ if (Private->CertGuid != NULL) {
+ FreePool (Private->CertGuid);
+ Private->CertGuid = NULL;
+ }
+
+ if (Data != NULL) {
+ FreePool (Data);
+ }
+
+ if (X509Data != NULL) {
+ FreePool (X509Data);
+ }
+
+ return Status;
+}
+
+/**
+ Enroll Cert into TlsCaCertificate. The GUID will be Private->CertGuid.
+
+ @param[in] PrivateData The module's private data.
+ @param[in] VariableName Variable name of signature database.
+
+ @retval EFI_SUCCESS New Cert enrolled successfully.
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_UNSUPPORTED The Cert file is unsupported type.
+ @retval others Fail to enroll Cert data.
+
+**/
+EFI_STATUS
+EnrollCertDatabase (
+ IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private,
+ IN CHAR16 *VariableName
+ )
+{
+ UINT16* FilePostFix;
+ UINTN NameLength;
+
+ if ((Private->FileContext->FileName == NULL) || (Private->FileContext->FHandle == NULL) || (Private->CertGuid == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Parse the file's postfix.
+ //
+ NameLength = StrLen (Private->FileContext->FileName);
+ if (NameLength <= 4) {
+ return EFI_INVALID_PARAMETER;
+ }
+ FilePostFix = Private->FileContext->FileName + NameLength - 4;
+
+ if (IsDerPemEncodeCertificate (FilePostFix)) {
+ //
+ // Supports DER-encoded X509 certificate.
+ //
+ return EnrollX509toVariable (Private, VariableName);
+ }
+
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Refresh the global UpdateData structure.
+
+**/
+VOID
+RefreshUpdateData (
+ VOID
+ )
+{
+ //
+ // Free current updated date
+ //
+ if (mStartOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (mStartOpCodeHandle);
+ }
+
+ //
+ // Create new OpCode Handle
+ //
+ mStartOpCodeHandle = HiiAllocateOpCodeHandle ();
+
+ //
+ // Create Hii Extend Label OpCode as the start opcode
+ //
+ mStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ mStartOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ mStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+}
+
+/**
+ Clean up the dynamic opcode at label and form specified by both LabelId.
+
+ @param[in] LabelId It is both the Form ID and Label ID for opcode deletion.
+ @param[in] PrivateData Module private data.
+
+**/
+VOID
+CleanUpPage (
+ IN UINT16 LabelId,
+ IN TLS_AUTH_CONFIG_PRIVATE_DATA *PrivateData
+ )
+{
+ RefreshUpdateData ();
+
+ //
+ // Remove all op-codes from dynamic page
+ //
+ mStartLabel->Number = LabelId;
+ HiiUpdateForm (
+ PrivateData->RegisteredHandle,
+ &gTlsAuthConfigGuid,
+ LabelId,
+ mStartOpCodeHandle, // Label LabelId
+ mEndOpCodeHandle // LABEL_END
+ );
+}
+
+/**
+ Update the form base on the selected file.
+
+ @param FilePath Point to the file path.
+ @param FormId The form need to display.
+
+ @retval TRUE Exit caller function.
+ @retval FALSE Not exit caller function.
+
+**/
+BOOLEAN
+UpdatePage(
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN EFI_FORM_ID FormId
+ )
+{
+ CHAR16 *FileName;
+ EFI_STRING_ID StringToken;
+
+ FileName = NULL;
+
+ if (FilePath != NULL) {
+ FileName = ExtractFileNameFromDevicePath(FilePath);
+ }
+ if (FileName == NULL) {
+ //
+ // FileName = NULL has two case:
+ // 1. FilePath == NULL, not select file.
+ // 2. FilePath != NULL, but ExtractFileNameFromDevicePath return NULL not enough memory resource.
+ // In these two case, no need to update the form, and exit the caller function.
+ //
+ return TRUE;
+ }
+ StringToken = HiiSetString (mTlsAuthPrivateData->RegisteredHandle, 0, FileName, NULL);
+
+ mTlsAuthPrivateData->FileContext->FileName = FileName;
+
+ OpenFileByDevicePath (
+ &FilePath,
+ &mTlsAuthPrivateData->FileContext->FHandle,
+ EFI_FILE_MODE_READ,
+ 0
+ );
+ //
+ // Create Subtitle op-code for the display string of the option.
+ //
+ RefreshUpdateData ();
+ mStartLabel->Number = FormId;
+
+ HiiCreateSubTitleOpCode (
+ mStartOpCodeHandle,
+ StringToken,
+ 0,
+ 0,
+ 0
+ );
+
+ HiiUpdateForm (
+ mTlsAuthPrivateData->RegisteredHandle,
+ &gTlsAuthConfigGuid,
+ FormId,
+ mStartOpCodeHandle, /// Label FormId
+ mEndOpCodeHandle /// LABEL_END
+ );
+
+ return TRUE;
+}
+
+/**
+ Update the form base on the input file path info.
+
+ @param FilePath Point to the file path.
+
+ @retval TRUE Exit caller function.
+ @retval FALSE Not exit caller function.
+**/
+BOOLEAN
+EFIAPI
+UpdateCAFromFile (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ return UpdatePage(FilePath, TLS_AUTH_CONFIG_FORMID4_FORM);
+}
+
+/**
+ Unload the configuration form, this includes: delete all the configuration
+ entries, uninstall the form callback protocol, and free the resources used.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is unloaded.
+ @retval Others Failed to unload the form.
+
+**/
+EFI_STATUS
+TlsAuthConfigFormUnload (
+ IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private
+ )
+{
+ if (Private->DriverHandle != NULL) {
+ //
+ // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Private->DriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ &mTlsAuthConfigHiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &Private->ConfigAccess,
+ NULL
+ );
+ Private->DriverHandle = NULL;
+ }
+
+ if (Private->RegisteredHandle != NULL) {
+ //
+ // Remove HII package list
+ //
+ HiiRemovePackages (Private->RegisteredHandle);
+ Private->RegisteredHandle = NULL;
+ }
+
+ if (Private->CertGuid != NULL) {
+ FreePool (Private->CertGuid);
+ }
+
+ if (Private->FileContext != NULL) {
+ FreePool (Private->FileContext);
+ }
+
+ FreePool (Private);
+
+ if (mStartOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (mStartOpCodeHandle);
+ }
+
+ if (mEndOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (mEndOpCodeHandle);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Initialize the configuration form.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+TlsAuthConfigFormInit (
+ IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ Private->Signature = TLS_AUTH_CONFIG_PRIVATE_DATA_SIGNATURE;
+
+ Private->ConfigAccess.ExtractConfig = TlsAuthConfigAccessExtractConfig;
+ Private->ConfigAccess.RouteConfig = TlsAuthConfigAccessRouteConfig;
+ Private->ConfigAccess.Callback = TlsAuthConfigAccessCallback;
+
+ //
+ // Install Device Path Protocol and Config Access protocol to driver handle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Private->DriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ &mTlsAuthConfigHiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &Private->ConfigAccess,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Publish our HII data.
+ //
+ Private->RegisteredHandle = HiiAddPackages (
+ &gTlsAuthConfigGuid,
+ Private->DriverHandle,
+ TlsAuthConfigDxeStrings,
+ TlsAuthConfigVfrBin,
+ NULL
+ );
+ if (Private->RegisteredHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ Private->FileContext = AllocateZeroPool (sizeof (TLS_AUTH_CONFIG_FILE_CONTEXT));
+ if (Private->FileContext == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ //
+ // Init OpCode Handle and Allocate space for creation of Buffer
+ //
+ mStartOpCodeHandle = HiiAllocateOpCodeHandle ();
+ if (mStartOpCodeHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ mEndOpCodeHandle = HiiAllocateOpCodeHandle ();
+ if (mEndOpCodeHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ //
+ // Create Hii Extend Label OpCode as the start opcode
+ //
+ mStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ mStartOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ mStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+
+ //
+ // Create Hii Extend Label OpCode as the end opcode
+ //
+ mEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ mEndOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ mEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ mEndLabel->Number = LABEL_END;
+
+ return EFI_SUCCESS;
+
+Error:
+ TlsAuthConfigFormUnload (Private);
+ 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 This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+
+ @param 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.
+ If a NULL is passed in for the Request field,
+ all of the settings being abstracted by this function
+ will be returned in the Results field. In addition,
+ if a ConfigHdr is passed in with no request elements,
+ all of the settings being abstracted for that particular
+ ConfigHdr reference will be returned in the Results Field.
+
+ @param 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 Results A null-terminated Unicode string in
+ <MultiConfigAltResp> 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_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
+TlsAuthConfigAccessExtractConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Request,
+ OUT EFI_STRING *Progress,
+ OUT EFI_STRING *Results
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ UINTN Size;
+ EFI_STRING ConfigRequest;
+ EFI_STRING ConfigRequestHdr;
+ TLS_AUTH_CONFIG_PRIVATE_DATA *Private;
+ BOOLEAN AllocatedRequest;
+
+ if (Progress == NULL || Results == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ AllocatedRequest = FALSE;
+ ConfigRequestHdr = NULL;
+ ConfigRequest = NULL;
+ Size = 0;
+
+ Private = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This);
+
+ BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA);
+ ZeroMem (&Private->TlsAuthConfigNvData, BufferSize);
+
+ *Progress = Request;
+
+ if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gTlsAuthConfigGuid, mTlsAuthConfigStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ ConfigRequest = Request;
+ if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
+ //
+ // Request is set to NULL or OFFSET is NULL, 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 (&gTlsAuthConfigGuid, mTlsAuthConfigStorageName, 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);
+ ConfigRequestHdr = NULL;
+ }
+
+ Status = gHiiConfigRouting->BlockToConfig (
+ gHiiConfigRouting,
+ ConfigRequest,
+ (UINT8 *) &Private->TlsAuthConfigNvData,
+ BufferSize,
+ Results,
+ Progress
+ );
+
+ //
+ // Free the allocated config request string.
+ //
+ if (AllocatedRequest) {
+ FreePool (ConfigRequest);
+ }
+
+ //
+ // 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 This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+
+ @param Configuration A null-terminated Unicode string in
+ <ConfigString> format.
+
+ @param 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_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
+TlsAuthConfigAccessRouteConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Configuration,
+ OUT EFI_STRING *Progress
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ TLS_AUTH_CONFIG_PRIVATE_DATA *Private;
+
+ if (Progress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ *Progress = Configuration;
+
+ if (Configuration == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check routing data in <ConfigHdr>.
+ // Note: there is no name for Name/Value storage, only GUID will be checked
+ //
+ if (!HiiIsConfigHdrMatch (Configuration, &gTlsAuthConfigGuid, mTlsAuthConfigStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ Private = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This);
+
+ BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA);
+ ZeroMem (&Private->TlsAuthConfigNvData, BufferSize);
+
+ Status = gHiiConfigRouting->ConfigToBlock (
+ gHiiConfigRouting,
+ Configuration,
+ (UINT8 *) &Private->TlsAuthConfigNvData,
+ &BufferSize,
+ Progress
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return Status;
+}
+
+/**
+
+ 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 This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param Action Specifies the type of action taken by the browser.
+ @param 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 Type The type of value for the question.
+ @param Value A pointer to the data being sent to the original
+ exporting driver.
+ @param 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
+TlsAuthConfigAccessCallback (
+ 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
+ )
+{
+ EFI_INPUT_KEY Key;
+ EFI_STATUS Status;
+ RETURN_STATUS RStatus;
+ TLS_AUTH_CONFIG_PRIVATE_DATA *Private;
+ UINTN BufferSize;
+ TLS_AUTH_CONFIG_IFR_NVDATA *IfrNvData;
+ UINT16 LabelId;
+ EFI_DEVICE_PATH_PROTOCOL *File;
+
+ Status = EFI_SUCCESS;
+ File = NULL;
+
+ if ((This == NULL) || (Value == NULL) || (ActionRequest == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = TLS_AUTH_CONFIG_PRIVATE_FROM_THIS (This);
+
+ mTlsAuthPrivateData = Private;
+
+ //
+ // Retrieve uncommitted data from Browser
+ //
+ BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA);
+ IfrNvData = AllocateZeroPool (BufferSize);
+ if (IfrNvData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ HiiGetBrowserData (&gTlsAuthConfigGuid, mTlsAuthConfigStorageName, BufferSize, (UINT8 *) IfrNvData);
+
+ if ((Action != EFI_BROWSER_ACTION_CHANGED) &&
+ (Action != EFI_BROWSER_ACTION_CHANGING)) {
+ Status = EFI_UNSUPPORTED;
+ goto EXIT;
+ }
+
+ if (Action == EFI_BROWSER_ACTION_CHANGING) {
+ switch (QuestionId) {
+ case KEY_TLS_AUTH_CONFIG_CLIENT_CERT:
+ case KEY_TLS_AUTH_CONFIG_SERVER_CA:
+ //
+ // Clear Cert GUID.
+ //
+ ZeroMem (IfrNvData->CertGuid, sizeof (IfrNvData->CertGuid));
+ if (Private->CertGuid == NULL) {
+ Private->CertGuid = (EFI_GUID *) AllocateZeroPool (sizeof (EFI_GUID));
+ if (Private->CertGuid == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+ if (QuestionId == KEY_TLS_AUTH_CONFIG_CLIENT_CERT) {
+ LabelId = TLS_AUTH_CONFIG_FORMID3_FORM;
+ } else {
+ LabelId = TLS_AUTH_CONFIG_FORMID4_FORM;
+ }
+
+ //
+ // Refresh selected file.
+ //
+ CleanUpPage (LabelId, Private);
+ break;
+ case KEY_TLS_AUTH_CONFIG_ENROLL_CERT_FROM_FILE:
+ ChooseFile( NULL, NULL, UpdateCAFromFile, &File);
+ break;
+
+ case KEY_TLS_AUTH_CONFIG_VALUE_SAVE_AND_EXIT:
+ Status = EnrollCertDatabase (Private, EFI_TLS_CA_CERTIFICATE_VARIABLE);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"ERROR: Enroll Cert Failure!",
+ NULL
+ );
+ }
+ break;
+
+ case KEY_TLS_AUTH_CONFIG_VALUE_NO_SAVE_AND_EXIT:
+ if (Private->FileContext->FHandle != NULL) {
+ CloseFile (Private->FileContext->FHandle);
+ Private->FileContext->FHandle = NULL;
+ if (Private->FileContext->FileName!= NULL){
+ FreePool(Private->FileContext->FileName);
+ Private->FileContext->FileName = NULL;
+ }
+ }
+
+ if (Private->CertGuid!= NULL) {
+ FreePool (Private->CertGuid);
+ Private->CertGuid = NULL;
+ }
+ break;
+
+ case KEY_TLS_AUTH_CONFIG_DELETE_CERT:
+ UpdateDeletePage (
+ Private,
+ EFI_TLS_CA_CERTIFICATE_VARIABLE,
+ &gEfiTlsCaCertificateGuid,
+ LABEL_CA_DELETE,
+ TLS_AUTH_CONFIG_FORMID5_FORM,
+ OPTION_DEL_CA_ESTION_ID
+ );
+ break;
+
+ default:
+ if ((QuestionId >= OPTION_DEL_CA_ESTION_ID) &&
+ (QuestionId < (OPTION_DEL_CA_ESTION_ID + OPTION_CONFIG_RANGE))) {
+ DeleteCert (
+ Private,
+ EFI_TLS_CA_CERTIFICATE_VARIABLE,
+ &gEfiTlsCaCertificateGuid,
+ LABEL_CA_DELETE,
+ TLS_AUTH_CONFIG_FORMID5_FORM,
+ OPTION_DEL_CA_ESTION_ID,
+ QuestionId - OPTION_DEL_CA_ESTION_ID
+ );
+ }
+ break;
+ }
+ } else if (Action == EFI_BROWSER_ACTION_CHANGED) {
+ switch (QuestionId) {
+ case KEY_TLS_AUTH_CONFIG_CERT_GUID:
+ ASSERT (Private->CertGuid != NULL);
+ RStatus = StrToGuid (
+ IfrNvData->CertGuid,
+ Private->CertGuid
+ );
+ if (RETURN_ERROR (RStatus) || (IfrNvData->CertGuid[GUID_STRING_LENGTH] != L'\0')) {
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
+ break;
+ default:
+ break;
+ }
+ }
+
+EXIT:
+
+ if (!EFI_ERROR (Status)) {
+ BufferSize = sizeof (TLS_AUTH_CONFIG_IFR_NVDATA);
+ HiiSetBrowserData (&gTlsAuthConfigGuid, mTlsAuthConfigStorageName, BufferSize, (UINT8*) IfrNvData, NULL);
+ }
+
+ FreePool (IfrNvData);
+
+ if (File != NULL){
+ FreePool(File);
+ File = NULL;
+ }
+
+ return EFI_SUCCESS;
+
+}
+
diff --git a/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigImpl.h b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigImpl.h
new file mode 100644
index 0000000000..f50d60d269
--- /dev/null
+++ b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigImpl.h
@@ -0,0 +1,282 @@
+/** @file
+ Header file of Miscellaneous Routines for TlsAuthConfigDxe driver.
+
+Copyright (c) 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 __TLS_AUTH_CONFIG_IMPL_H__
+#define __TLS_AUTH_CONFIG_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Protocol/HiiConfigAccess.h>
+#include <Protocol/SimpleFileSystem.h>
+
+//
+// Libraries
+//
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/HiiLib.h>
+#include <Library/UefiHiiServicesLib.h>
+#include <Library/FileExplorerLib.h>
+#include <Library/PrintLib.h>
+
+#include <Guid/MdeModuleHii.h>
+#include <Guid/ImageAuthentication.h>
+#include <Guid/TlsAuthentication.h>
+
+
+//
+// Include files with function prototypes
+//
+#include "TlsAuthConfigNvData.h"
+
+extern UINT8 TlsAuthConfigDxeStrings[];
+extern UINT8 TlsAuthConfigVfrBin[];
+
+#define TLS_AUTH_CONFIG_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('T', 'A', 'C', 'D')
+#define TLS_AUTH_CONFIG_PRIVATE_FROM_THIS(a) CR (a, TLS_AUTH_CONFIG_PRIVATE_DATA, ConfigAccess, TLS_AUTH_CONFIG_PRIVATE_DATA_SIGNATURE)
+
+#define TLS_AUTH_CONFIG_VAR_BASE_ATTR (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)
+
+typedef struct _TLS_AUTH_CONFIG_PRIVATE_DATA TLS_AUTH_CONFIG_PRIVATE_DATA;
+typedef struct _TLS_AUTH_CONFIG_FILE_CONTEXT TLS_AUTH_CONFIG_FILE_CONTEXT;
+
+///
+/// HII specific Vendor Device Path definition.
+///
+typedef struct {
+ VENDOR_DEVICE_PATH VendorDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} HII_VENDOR_DEVICE_PATH;
+
+struct _TLS_AUTH_CONFIG_FILE_CONTEXT {
+ EFI_FILE_HANDLE FHandle;
+ UINT16 *FileName;
+};
+
+struct _TLS_AUTH_CONFIG_PRIVATE_DATA {
+ UINTN Signature;
+
+ EFI_HANDLE DriverHandle;
+ EFI_HII_HANDLE RegisteredHandle;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess;
+ TLS_AUTH_CONFIG_IFR_NVDATA TlsAuthConfigNvData;
+
+ TLS_AUTH_CONFIG_FILE_CONTEXT *FileContext;
+
+ EFI_GUID *CertGuid;
+};
+
+/**
+ Unload the configuration form, this includes: delete all the configuration
+ entries, uninstall the form callback protocol, and free the resources used.
+ The form will only be unload completely when both IP4 and IP6 stack are stopped.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is unloaded.
+ @retval Others Failed to unload the form.
+
+**/
+EFI_STATUS
+TlsAuthConfigFormUnload (
+ IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private
+ );
+
+/**
+ Initialize the configuration form.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+TlsAuthConfigFormInit (
+ IN TLS_AUTH_CONFIG_PRIVATE_DATA *Private
+ );
+
+/**
+
+ 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 This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+
+ @param 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.
+ If a NULL is passed in for the Request field,
+ all of the settings being abstracted by this function
+ will be returned in the Results field. In addition,
+ if a ConfigHdr is passed in with no request elements,
+ all of the settings being abstracted for that particular
+ ConfigHdr reference will be returned in the Results Field.
+
+ @param 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 Results A null-terminated Unicode string in
+ <MultiConfigAltResp> 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_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
+TlsAuthConfigAccessExtractConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Request,
+ OUT EFI_STRING *Progress,
+ OUT EFI_STRING *Results
+ );
+
+/**
+
+ 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 This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+
+ @param Configuration A null-terminated Unicode string in
+ <ConfigString> format.
+
+ @param 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_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
+TlsAuthConfigAccessRouteConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Configuration,
+ OUT EFI_STRING *Progress
+ );
+
+/**
+
+ 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 This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param Action Specifies the type of action taken by the browser.
+ @param 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 Type The type of value for the question.
+ @param Value A pointer to the data being sent to the original
+ exporting driver.
+ @param 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
+TlsAuthConfigAccessCallback (
+ 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
+ );
+
+#endif
+
diff --git a/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigNvData.h b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigNvData.h
new file mode 100644
index 0000000000..80baa3836f
--- /dev/null
+++ b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigNvData.h
@@ -0,0 +1,50 @@
+/** @file
+ Header file for NV data structure definition.
+
+Copyright (c) 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 __TLS_AUTH_CONFIG_NV_DATA_H__
+#define __TLS_AUTH_CONFIG_NV_DATA_H__
+
+#include <Guid/TlsAuthConfigHii.h>
+
+#define TLS_AUTH_CONFIG_GUID_SIZE 36
+#define TLS_AUTH_CONFIG_GUID_STORAGE_SIZE 37
+
+#define TLS_AUTH_CONFIG_FORMID1_FORM 1
+#define TLS_AUTH_CONFIG_FORMID2_FORM 2
+#define TLS_AUTH_CONFIG_FORMID3_FORM 3
+#define TLS_AUTH_CONFIG_FORMID4_FORM 4
+#define TLS_AUTH_CONFIG_FORMID5_FORM 5
+
+
+#define KEY_TLS_AUTH_CONFIG_SERVER_CA 0x1000
+#define KEY_TLS_AUTH_CONFIG_CLIENT_CERT 0x1001
+#define KEY_TLS_AUTH_CONFIG_ENROLL_CERT 0x1002
+#define KEY_TLS_AUTH_CONFIG_DELETE_CERT 0x1003
+#define KEY_TLS_AUTH_CONFIG_ENROLL_CERT_FROM_FILE 0x1004
+#define KEY_TLS_AUTH_CONFIG_CERT_GUID 0x1005
+#define KEY_TLS_AUTH_CONFIG_VALUE_SAVE_AND_EXIT 0x1006
+#define KEY_TLS_AUTH_CONFIG_VALUE_NO_SAVE_AND_EXIT 0x1007
+
+#define OPTION_DEL_CA_ESTION_ID 0x2000
+#define OPTION_CONFIG_RANGE 0x1000
+
+#define LABEL_CA_DELETE 0x1101
+#define LABEL_END 0xffff
+
+typedef struct {
+ CHAR16 CertGuid[TLS_AUTH_CONFIG_GUID_STORAGE_SIZE];
+} TLS_AUTH_CONFIG_IFR_NVDATA;
+
+#endif
+
diff --git a/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigVfr.vfr b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigVfr.vfr
new file mode 100644
index 0000000000..9bca2c119f
--- /dev/null
+++ b/Core/NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigVfr.vfr
@@ -0,0 +1,153 @@
+/** @file
+ VFR file used by TlsAuthConfigDxe driver.
+
+ Copyright (c) 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 "TlsAuthConfigNvData.h"
+
+formset
+ guid = TLS_AUTH_CONFIG_GUID,
+ title = STRING_TOKEN(STR_TLS_AUTH_CONFIG_TITLE),
+ help = STRING_TOKEN(STR_TLS_AUTH_CONFIG_HELP),
+
+ varstore TLS_AUTH_CONFIG_IFR_NVDATA,
+ name = TLS_AUTH_CONFIG_IFR_NVDATA,
+ guid = TLS_AUTH_CONFIG_GUID;
+
+ //
+ // ##1 Form1: Main form for Tls Auth configration
+ //
+ form formid = TLS_AUTH_CONFIG_FORMID1_FORM,
+ title = STRING_TOKEN(STR_TLS_AUTH_CONFIG_TITLE);
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ //
+ // Display Server CA configration
+ //
+ goto TLS_AUTH_CONFIG_FORMID2_FORM,
+ prompt = STRING_TOKEN(STR_TLS_AUTH_CONFIG_SERVER_CA),
+ help = STRING_TOKEN(STR_TLS_AUTH_CONFIG_SERVER_CA_HELP),
+ flags = INTERACTIVE,
+ key = KEY_TLS_AUTH_CONFIG_SERVER_CA;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ //
+ // Display Client cert configration
+ //
+ grayoutif TRUE; /// Current unsupported.
+ goto TLS_AUTH_CONFIG_FORMID3_FORM,
+ prompt = STRING_TOKEN(STR_TLS_AUTH_CONFIG_CLIENT_CERT),
+ help = STRING_TOKEN(STR_TLS_AUTH_CONFIG_CLIENT_CERT_HELP),
+ flags = INTERACTIVE,
+ key = KEY_TLS_AUTH_CONFIG_CLIENT_CERT;
+ endif;
+ endform;
+
+ //
+ // ##2 Form2: CA configuration
+ //
+ form formid = TLS_AUTH_CONFIG_FORMID2_FORM,
+ title = STRING_TOKEN(STR_TLS_AUTH_CONFIG_SERVER_CA);
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ goto TLS_AUTH_CONFIG_FORMID4_FORM,
+ prompt = STRING_TOKEN(STR_TLS_AUTH_CONFIG_ENROLL_CERT),
+ help = STRING_TOKEN(STR_TLS_AUTH_CONFIG_ENROLL_CERT_HELP),
+ flags = INTERACTIVE,
+ key = KEY_TLS_AUTH_CONFIG_ENROLL_CERT;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ goto TLS_AUTH_CONFIG_FORMID5_FORM,
+ prompt = STRING_TOKEN(STR_TLS_AUTH_CONFIG_DELETE_CERT),
+ help = STRING_TOKEN(STR_TLS_AUTH_CONFIG_DELETE_CERT_HELP),
+ flags = INTERACTIVE,
+ key = KEY_TLS_AUTH_CONFIG_DELETE_CERT;
+ endform;
+
+ //
+ // ##3 Form3 : Client cert configuration
+ //
+ form formid = TLS_AUTH_CONFIG_FORMID3_FORM,
+ title = STRING_TOKEN(STR_TLS_AUTH_CONFIG_CLIENT_CERT);
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ //
+ // TODO...
+ //
+ endform;
+
+ //
+ // ##4 Form4: Enroll cert for CA
+ //
+ form formid = TLS_AUTH_CONFIG_FORMID4_FORM,
+ title = STRING_TOKEN(STR_TLS_AUTH_CONFIG_ENROLL_CERT);
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ goto TLS_AUTH_CONFIG_FORMID4_FORM,
+ prompt = STRING_TOKEN(STR_TLS_AUTH_CONFIG_ADD_CERT_FILE),
+ help = STRING_TOKEN(STR_TLS_AUTH_CONFIG_ADD_CERT_FILE),
+ flags = INTERACTIVE,
+ key = KEY_TLS_AUTH_CONFIG_ENROLL_CERT_FROM_FILE;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+ label TLS_AUTH_CONFIG_FORMID4_FORM;
+ label LABEL_END;
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ string varid = TLS_AUTH_CONFIG_IFR_NVDATA.CertGuid,
+ prompt = STRING_TOKEN(STR_TLS_AUTH_CONFIG_CERT_GUID),
+ help = STRING_TOKEN(STR_TLS_AUTH_CONFIG_CERT_GUID_HELP),
+ flags = INTERACTIVE,
+ key = KEY_TLS_AUTH_CONFIG_CERT_GUID,
+ minsize = TLS_AUTH_CONFIG_GUID_SIZE,
+ maxsize = TLS_AUTH_CONFIG_GUID_SIZE,
+ endstring;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ goto TLS_AUTH_CONFIG_FORMID1_FORM,
+ prompt = STRING_TOKEN(STR_TLS_AUTH_CONFIG_SAVE_AND_EXIT),
+ help = STRING_TOKEN(STR_TLS_AUTH_CONFIG_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_TLS_AUTH_CONFIG_VALUE_SAVE_AND_EXIT;
+
+ goto TLS_AUTH_CONFIG_FORMID1_FORM,
+ prompt = STRING_TOKEN(STR_TLS_AUTH_CONFIG_NO_SAVE_AND_EXIT),
+ help = STRING_TOKEN(STR_TLS_AUTH_CONFIG_NO_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_TLS_AUTH_CONFIG_VALUE_NO_SAVE_AND_EXIT;
+
+ endform;
+
+ //
+ // ##5 Form5: Delete cert for CA
+ //
+ form formid = TLS_AUTH_CONFIG_FORMID5_FORM,
+ title = STRING_TOKEN(STR_TLS_AUTH_CONFIG_DELETE_CERT);
+
+ label LABEL_CA_DELETE;
+ label LABEL_END;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ endform;
+
+endformset;
+
diff --git a/Core/NetworkPkg/TlsDxe/TlsConfigProtocol.c b/Core/NetworkPkg/TlsDxe/TlsConfigProtocol.c
new file mode 100644
index 0000000000..15a865e386
--- /dev/null
+++ b/Core/NetworkPkg/TlsDxe/TlsConfigProtocol.c
@@ -0,0 +1,153 @@
+/** @file
+ Implementation of EFI TLS Configuration Protocol Interfaces.
+
+ Copyright (c) 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 "TlsImpl.h"
+
+EFI_TLS_CONFIGURATION_PROTOCOL mTlsConfigurationProtocol = {
+ TlsConfigurationSetData,
+ TlsConfigurationGetData
+};
+
+/**
+ Set TLS configuration data.
+
+ The SetData() function sets TLS configuration to non-volatile storage or volatile
+ storage.
+
+ @param[in] This Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance.
+ @param[in] DataType Configuration data type.
+ @param[in] Data Pointer to configuration data.
+ @param[in] DataSize Total size of configuration data.
+
+ @retval EFI_SUCCESS The TLS configuration data is set successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ Data is NULL.
+ DataSize is 0.
+ @retval EFI_UNSUPPORTED The DataType is unsupported.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsConfigurationSetData (
+ IN EFI_TLS_CONFIGURATION_PROTOCOL *This,
+ IN EFI_TLS_CONFIG_DATA_TYPE DataType,
+ IN VOID *Data,
+ IN UINTN DataSize
+ )
+{
+ EFI_STATUS Status;
+ TLS_INSTANCE *Instance;
+ EFI_TPL OldTpl;
+
+ Status = EFI_SUCCESS;
+
+ if (This == NULL || Data == NULL || DataSize == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = TLS_INSTANCE_FROM_CONFIGURATION (This);
+
+ switch (DataType) {
+ case EfiTlsConfigDataTypeCACertificate:
+ Status = TlsSetCaCertificate (Instance->TlsConn, Data, DataSize);
+ break;
+ case EfiTlsConfigDataTypeHostPublicCert:
+ Status = TlsSetHostPublicCert (Instance->TlsConn, Data, DataSize);
+ break;
+ case EfiTlsConfigDataTypeHostPrivateKey:
+ Status = TlsSetHostPrivateKey (Instance->TlsConn, Data, DataSize);
+ break;
+ case EfiTlsConfigDataTypeCertRevocationList:
+ Status = TlsSetCertRevocationList (Data, DataSize);
+ break;
+ default:
+ Status = EFI_UNSUPPORTED;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Get TLS configuration data.
+
+ The GetData() function gets TLS configuration.
+
+ @param[in] This Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance.
+ @param[in] DataType Configuration data type.
+ @param[in, out] Data Pointer to configuration data.
+ @param[in, out] DataSize Total size of configuration data. On input, it means
+ the size of Data buffer. On output, it means the size
+ of copied Data buffer if EFI_SUCCESS, and means the
+ size of desired Data buffer if EFI_BUFFER_TOO_SMALL.
+
+ @retval EFI_SUCCESS The TLS configuration data is got successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ DataSize is NULL.
+ Data is NULL if *DataSize is not zero.
+ @retval EFI_UNSUPPORTED The DataType is unsupported.
+ @retval EFI_NOT_FOUND The TLS configuration data is not found.
+ @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the data.
+**/
+EFI_STATUS
+EFIAPI
+TlsConfigurationGetData (
+ IN EFI_TLS_CONFIGURATION_PROTOCOL *This,
+ IN EFI_TLS_CONFIG_DATA_TYPE DataType,
+ IN OUT VOID *Data, OPTIONAL
+ IN OUT UINTN *DataSize
+ )
+{
+ EFI_STATUS Status;
+ TLS_INSTANCE *Instance;
+
+ EFI_TPL OldTpl;
+
+ Status = EFI_SUCCESS;
+
+ if (This == NULL || DataSize == NULL || (Data == NULL && *DataSize != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = TLS_INSTANCE_FROM_CONFIGURATION (This);
+
+ switch (DataType) {
+ case EfiTlsConfigDataTypeCACertificate:
+ Status = TlsGetCaCertificate (Instance->TlsConn, Data, DataSize);
+ break;
+ case EfiTlsConfigDataTypeHostPublicCert:
+ Status = TlsGetHostPublicCert (Instance->TlsConn, Data, DataSize);
+ break;
+ case EfiTlsConfigDataTypeHostPrivateKey:
+ Status = TlsGetHostPrivateKey (Instance->TlsConn, Data, DataSize);
+ break;
+ case EfiTlsConfigDataTypeCertRevocationList:
+ Status = TlsGetCertRevocationList (Data, DataSize);
+ break;
+ default:
+ Status = EFI_UNSUPPORTED;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
diff --git a/Core/NetworkPkg/TlsDxe/TlsDriver.c b/Core/NetworkPkg/TlsDxe/TlsDriver.c
new file mode 100644
index 0000000000..29bc966c3e
--- /dev/null
+++ b/Core/NetworkPkg/TlsDxe/TlsDriver.c
@@ -0,0 +1,497 @@
+/** @file
+ The Driver Binding and Service Binding Protocol for TlsDxe driver.
+
+ Copyright (c) 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 "TlsImpl.h"
+
+EFI_SERVICE_BINDING_PROTOCOL mTlsServiceBinding = {
+ TlsServiceBindingCreateChild,
+ TlsServiceBindingDestroyChild
+};
+
+/**
+ Release all the resources used by the TLS instance.
+
+ @param[in] Instance The TLS instance data.
+
+**/
+VOID
+TlsCleanInstance (
+ IN TLS_INSTANCE *Instance
+ )
+{
+ if (Instance != NULL) {
+ if (Instance->TlsConn != NULL) {
+ TlsFree (Instance->TlsConn);
+ }
+
+ FreePool (Instance);
+ }
+}
+
+/**
+ Create the TLS instance and initialize it.
+
+ @param[in] Service The pointer to the TLS service.
+ @param[out] Instance The pointer to the TLS instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_SUCCESS The TLS instance is created.
+
+**/
+EFI_STATUS
+TlsCreateInstance (
+ IN TLS_SERVICE *Service,
+ OUT TLS_INSTANCE **Instance
+ )
+{
+ TLS_INSTANCE *TlsInstance;
+
+ *Instance = NULL;
+
+ TlsInstance = AllocateZeroPool (sizeof (TLS_INSTANCE));
+ if (TlsInstance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TlsInstance->Signature = TLS_INSTANCE_SIGNATURE;
+ InitializeListHead (&TlsInstance->Link);
+ TlsInstance->InDestroy = FALSE;
+ TlsInstance->Service = Service;
+
+ CopyMem (&TlsInstance->Tls, &mTlsProtocol, sizeof (TlsInstance->Tls));
+ CopyMem (&TlsInstance->TlsConfig, &mTlsConfigurationProtocol, sizeof (TlsInstance->TlsConfig));
+
+ TlsInstance->TlsSessionState = EfiTlsSessionNotStarted;
+
+ *Instance = TlsInstance;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Release all the resources used by the TLS service binding instance.
+
+ @param[in] Service The TLS service data.
+
+**/
+VOID
+TlsCleanService (
+ IN TLS_SERVICE *Service
+ )
+{
+ if (Service != NULL) {
+ if (Service->TlsCtx != NULL) {
+ TlsCtxFree (Service->TlsCtx);
+ }
+
+ FreePool (Service);
+ }
+}
+
+/**
+ Create then initialize a TLS service.
+
+ @param[in] Image ImageHandle of the TLS driver
+ @param[out] Service The service for TLS driver
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to create the service.
+ @retval EFI_SUCCESS The service is created for the driver.
+
+**/
+EFI_STATUS
+TlsCreateService (
+ IN EFI_HANDLE Image,
+ OUT TLS_SERVICE **Service
+ )
+{
+ TLS_SERVICE *TlsService;
+
+ ASSERT (Service != NULL);
+
+ *Service = NULL;
+
+ //
+ // Allocate a TLS Service Data
+ //
+ TlsService = AllocateZeroPool (sizeof (TLS_SERVICE));
+ if (TlsService == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize TLS Service Data
+ //
+ TlsService->Signature = TLS_SERVICE_SIGNATURE;
+ CopyMem (&TlsService->ServiceBinding, &mTlsServiceBinding, sizeof (TlsService->ServiceBinding));
+ TlsService->TlsChildrenNum = 0;
+ InitializeListHead (&TlsService->TlsChildrenList);
+ TlsService->ImageHandle = Image;
+
+ *Service = TlsService;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Unloads an image.
+
+ @param[in] 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
+TlsUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ UINTN HandleNum;
+ EFI_HANDLE *HandleBuffer;
+ UINT32 Index;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ TLS_SERVICE *TlsService;
+
+ HandleBuffer = NULL;
+ ServiceBinding = NULL;
+ TlsService = NULL;
+
+ //
+ // Locate all the handles with Tls service binding protocol.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiTlsServiceBindingProtocolGuid,
+ NULL,
+ &HandleNum,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ for (Index = 0; Index < HandleNum; Index++) {
+ //
+ // Firstly, find ServiceBinding interface
+ //
+ Status = gBS->OpenProtocol (
+ HandleBuffer[Index],
+ &gEfiTlsServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ TlsService = TLS_SERVICE_FROM_THIS (ServiceBinding);
+
+ //
+ // Then, uninstall ServiceBinding interface
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ HandleBuffer[Index],
+ &gEfiTlsServiceBindingProtocolGuid, ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ TlsCleanService (TlsService);
+ }
+
+ if (HandleBuffer != NULL) {
+ FreePool (HandleBuffer);
+ }
+
+ 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
+TlsDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ TLS_SERVICE *TlsService;
+
+ //
+ // Create TLS Service
+ //
+ Status = TlsCreateService (ImageHandle, &TlsService);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (TlsService != NULL);
+
+ //
+ // Initializes the OpenSSL library.
+ //
+ TlsInitialize ();
+
+ //
+ // Create a new SSL_CTX object as framework to establish TLS/SSL enabled
+ // connections. TLS 1.0 is used as the default version.
+ //
+ TlsService->TlsCtx = TlsCtxNew (TLS10_PROTOCOL_VERSION_MAJOR, TLS10_PROTOCOL_VERSION_MINOR);
+ if (TlsService->TlsCtx == NULL) {
+ FreePool (TlsService);
+ return EFI_ABORTED;
+ }
+
+ //
+ // Install the TlsServiceBinding Protocol onto Handle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &TlsService->Handle,
+ &gEfiTlsServiceBindingProtocolGuid,
+ &TlsService->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_CLEAN_SERVICE;
+ }
+
+ return Status;
+
+ON_CLEAN_SERVICE:
+ TlsCleanService (TlsService);
+
+ 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 available to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ TLS_SERVICE *TlsService;
+ TLS_INSTANCE *TlsInstance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TlsService = TLS_SERVICE_FROM_THIS (This);
+
+ Status = TlsCreateInstance (TlsService, &TlsInstance);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ASSERT (TlsInstance != NULL);
+
+ //
+ // Create a new TLS connection object.
+ //
+ TlsInstance->TlsConn = TlsNew (TlsService->TlsCtx);
+ if (TlsInstance->TlsConn == NULL) {
+ Status = EFI_ABORTED;
+ goto ON_ERROR;
+ }
+
+ //
+ // Set default ConnectionEnd to EfiTlsClient
+ //
+ Status = TlsSetConnectionEnd (TlsInstance->TlsConn, EfiTlsClient);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install TLS protocol and configuration protocol onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiTlsProtocolGuid,
+ &TlsInstance->Tls,
+ &gEfiTlsConfigurationProtocolGuid,
+ &TlsInstance->TlsConfig,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ TlsInstance->ChildHandle = *ChildHandle;
+
+ //
+ // Add it to the TLS service's child list.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&TlsService->TlsChildrenList, &TlsInstance->Link);
+ TlsService->TlsChildrenNum++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ TlsCleanInstance (TlsInstance);
+ 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 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
+TlsServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ TLS_SERVICE *TlsService;
+ TLS_INSTANCE *TlsInstance;
+
+ EFI_TLS_PROTOCOL *Tls;
+ EFI_TLS_CONFIGURATION_PROTOCOL *TlsConfig;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TlsService = TLS_SERVICE_FROM_THIS (This);
+
+ //
+ // Find TLS protocol interface installed in ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTlsProtocolGuid,
+ (VOID **) &Tls,
+ TlsService->ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Find TLS configuration protocol interface installed in ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTlsConfigurationProtocolGuid,
+ (VOID **) &TlsConfig,
+ TlsService->ImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ TlsInstance = TLS_INSTANCE_FROM_PROTOCOL (Tls);
+
+ if (TlsInstance->Service != TlsService) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TlsInstance->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ TlsInstance->InDestroy = TRUE;
+
+ //
+ // Uninstall the TLS protocol and TLS Configuration Protocol interface installed in ChildHandle.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiTlsProtocolGuid,
+ Tls,
+ &gEfiTlsConfigurationProtocolGuid,
+ TlsConfig,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ RemoveEntryList (&TlsInstance->Link);
+ TlsService->TlsChildrenNum--;
+
+ gBS->RestoreTPL (OldTpl);
+
+ TlsCleanInstance (TlsInstance);
+
+ return EFI_SUCCESS;
+}
+
diff --git a/Core/NetworkPkg/TlsDxe/TlsDriver.h b/Core/NetworkPkg/TlsDxe/TlsDriver.h
new file mode 100644
index 0000000000..950429af8f
--- /dev/null
+++ b/Core/NetworkPkg/TlsDxe/TlsDriver.h
@@ -0,0 +1,238 @@
+/** @file
+ Header file of the Driver Binding and Service Binding Protocol for TlsDxe driver.
+
+ Copyright (c) 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 __EFI_TLS_DRIVER_H__
+#define __EFI_TLS_DRIVER_H__
+
+#include <Uefi.h>
+
+//
+// Driver Protocols
+//
+#include <Protocol/ServiceBinding.h>
+
+//
+// Driver Version
+//
+#define TLS_VERSION 0x00000000
+
+#define TLS_SERVICE_SIGNATURE SIGNATURE_32 ('T', 'L', 'S', 'S')
+
+#define TLS_INSTANCE_SIGNATURE SIGNATURE_32 ('T', 'L', 'S', 'I')
+
+///
+/// TLS Service Data
+///
+typedef struct _TLS_SERVICE TLS_SERVICE;
+
+///
+/// TLS Instance Data
+///
+typedef struct _TLS_INSTANCE TLS_INSTANCE;
+
+
+struct _TLS_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+
+ UINT16 TlsChildrenNum;
+ LIST_ENTRY TlsChildrenList;
+
+ //
+ // Handle to install TlsServiceBinding protocol.
+ //
+ EFI_HANDLE Handle;
+ EFI_HANDLE ImageHandle;
+
+ //
+ // Main SSL Context object which is created by a server or client once per program
+ // life-time and which holds mainly default values for the SSL object which are later
+ // created for the connections.
+ //
+ VOID *TlsCtx;
+};
+
+struct _TLS_INSTANCE {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+
+ BOOLEAN InDestroy;
+
+ TLS_SERVICE *Service;
+ EFI_HANDLE ChildHandle;
+
+ EFI_TLS_PROTOCOL Tls;
+ EFI_TLS_CONFIGURATION_PROTOCOL TlsConfig;
+
+ EFI_TLS_SESSION_STATE TlsSessionState;
+
+ //
+ // Main SSL Connection which is created by a server or a client
+ // per established connection.
+ //
+ VOID *TlsConn;
+};
+
+
+#define TLS_SERVICE_FROM_THIS(a) \
+ CR (a, TLS_SERVICE, ServiceBinding, TLS_SERVICE_SIGNATURE)
+
+#define TLS_INSTANCE_FROM_PROTOCOL(a) \
+ CR (a, TLS_INSTANCE, Tls, TLS_INSTANCE_SIGNATURE)
+
+#define TLS_INSTANCE_FROM_CONFIGURATION(a) \
+ CR (a, TLS_INSTANCE, TlsConfig, TLS_INSTANCE_SIGNATURE)
+
+
+/**
+ Release all the resources used by the TLS instance.
+
+ @param[in] Instance The TLS instance data.
+
+**/
+VOID
+TlsCleanInstance (
+ IN TLS_INSTANCE *Instance
+ );
+
+/**
+ Create the TLS instance and initialize it.
+
+ @param[in] Service The pointer to the TLS service.
+ @param[out] Instance The pointer to the TLS instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_SUCCESS The TLS instance is created.
+
+**/
+EFI_STATUS
+TlsCreateInstance (
+ IN TLS_SERVICE *Service,
+ OUT TLS_INSTANCE **Instance
+ );
+
+/**
+ Release all the resources used by the TLS service binding instance.
+
+ @param[in] Service The TLS service data.
+
+**/
+VOID
+TlsCleanService (
+ IN TLS_SERVICE *Service
+ );
+
+/**
+ Create then initialize a TLS service.
+
+ @param[in] Image ImageHandle of the TLS driver
+ @param[out] Service The service for TLS driver
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to create the service.
+ @retval EFI_SUCCESS The service is created for the driver.
+
+**/
+EFI_STATUS
+TlsCreateService (
+ IN EFI_HANDLE Image,
+ OUT TLS_SERVICE **Service
+ );
+
+/**
+ Unloads an image.
+
+ @param[in] 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
+TlsUnload (
+ 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
+TlsDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+/**
+ 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 available to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+TlsServiceBindingCreateChild (
+ 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 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
+TlsServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
+
diff --git a/Core/NetworkPkg/TlsDxe/TlsDxe.inf b/Core/NetworkPkg/TlsDxe/TlsDxe.inf
new file mode 100644
index 0000000000..907feb735b
--- /dev/null
+++ b/Core/NetworkPkg/TlsDxe/TlsDxe.inf
@@ -0,0 +1,66 @@
+## @file
+# This module produces EFI TLS Protocol, EFI TLS Service Binding Protocol and
+# EFI TLS Configuration Protocol.
+#
+# This module produces EFI TLS (Transport Layer Security) Protocol and EFI TLS
+# Service Binding Protocol, to provide TLS services.
+#
+# Copyright (c) 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.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = TlsDxe
+ FILE_GUID = 3aceb0c0-3c72-11e4-9a56-74d435052646
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = TlsDriverEntryPoint
+ UNLOAD_IMAGE = TlsUnload
+ MODULE_UNI_FILE = TlsDxe.uni
+
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ CryptoPkg/CryptoPkg.dec
+
+[Sources]
+ TlsDriver.h
+ TlsDriver.c
+ TlsProtocol.c
+ TlsConfigProtocol.c
+ TlsImpl.h
+ TlsImpl.c
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ BaseLib
+ UefiLib
+ DebugLib
+ NetLib
+ BaseCryptLib
+ TlsLib
+
+[Protocols]
+ gEfiTlsServiceBindingProtocolGuid ## PRODUCES
+ gEfiTlsProtocolGuid ## PRODUCES
+ gEfiTlsConfigurationProtocolGuid ## PRODUCES
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ TlsDxeExtra.uni
+
diff --git a/Core/NetworkPkg/TlsDxe/TlsDxe.uni b/Core/NetworkPkg/TlsDxe/TlsDxe.uni
new file mode 100644
index 0000000000..e2b1f5cd0b
--- /dev/null
+++ b/Core/NetworkPkg/TlsDxe/TlsDxe.uni
@@ -0,0 +1,25 @@
+// /** @file
+// This module produces EFI TLS Protocol, EFI TLS Service Binding Protocol and
+// EFI TLS Configuration Protocol.
+//
+// This module produces EFI TLS (Transport Layer Security) Protocol, EFI TLS
+// Service Binding Protocol, and EFI TLS Configuration Protocol to provide TLS
+// services.
+//
+// Copyright (c) 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI TLS service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI TLS Protocol, EFI TLS Service Binding Protocol and EFI TLS Configuration Protocol to provide EFI TLS services."
+
diff --git a/Core/NetworkPkg/TlsDxe/TlsDxeExtra.uni b/Core/NetworkPkg/TlsDxe/TlsDxeExtra.uni
new file mode 100644
index 0000000000..a5663c3279
--- /dev/null
+++ b/Core/NetworkPkg/TlsDxe/TlsDxeExtra.uni
@@ -0,0 +1,19 @@
+// /** @file
+// TlsDxe Localized Strings and Content
+//
+// Copyright (c) 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"EFI TLS DXE Driver"
+
diff --git a/Core/NetworkPkg/TlsDxe/TlsImpl.c b/Core/NetworkPkg/TlsDxe/TlsImpl.c
new file mode 100644
index 0000000000..8e1238216b
--- /dev/null
+++ b/Core/NetworkPkg/TlsDxe/TlsImpl.c
@@ -0,0 +1,327 @@
+/** @file
+ The Miscellaneous Routines for TlsDxe driver.
+
+Copyright (c) 2016 - 2017, 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 "TlsImpl.h"
+
+/**
+ Encrypt the message listed in fragment.
+
+ @param[in] TlsInstance The pointer to the TLS instance.
+ @param[in, out] FragmentTable Pointer to a list of fragment.
+ On input these fragments contain the TLS header and
+ plain text TLS payload;
+ On output these fragments contain the TLS header and
+ cipher text TLS payload.
+ @param[in] FragmentCount Number of fragment.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_ABORTED TLS session state is incorrect.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+TlsEncryptPacket (
+ IN TLS_INSTANCE *TlsInstance,
+ IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT32 BytesCopied;
+ UINT32 BufferInSize;
+ UINT8 *BufferIn;
+ UINT8 *BufferInPtr;
+ TLS_RECORD_HEADER *RecordHeaderIn;
+ UINT16 ThisPlainMessageSize;
+ TLS_RECORD_HEADER *TempRecordHeader;
+ UINT16 ThisMessageSize;
+ UINT32 BufferOutSize;
+ UINT8 *BufferOut;
+ INTN Ret;
+
+ Status = EFI_SUCCESS;
+ BytesCopied = 0;
+ BufferInSize = 0;
+ BufferIn = NULL;
+ BufferInPtr = NULL;
+ RecordHeaderIn = NULL;
+ TempRecordHeader = NULL;
+ BufferOutSize = 0;
+ BufferOut = NULL;
+ Ret = 0;
+
+ //
+ // Calculate the size according to the fragment table.
+ //
+ for (Index = 0; Index < *FragmentCount; Index++) {
+ BufferInSize += (*FragmentTable)[Index].FragmentLength;
+ }
+
+ //
+ // Allocate buffer for processing data.
+ //
+ BufferIn = AllocateZeroPool (BufferInSize);
+ if (BufferIn == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR;
+ }
+
+ //
+ // Copy all TLS plain record header and payload into BufferIn.
+ //
+ for (Index = 0; Index < *FragmentCount; Index++) {
+ CopyMem (
+ (BufferIn + BytesCopied),
+ (*FragmentTable)[Index].FragmentBuffer,
+ (*FragmentTable)[Index].FragmentLength
+ );
+ BytesCopied += (*FragmentTable)[Index].FragmentLength;
+ }
+
+ BufferOut = AllocateZeroPool (MAX_BUFFER_SIZE);
+ if (BufferOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR;
+ }
+
+ //
+ // Parsing buffer.
+ //
+ BufferInPtr = BufferIn;
+ TempRecordHeader = (TLS_RECORD_HEADER *) BufferOut;
+ while ((UINTN) BufferInPtr < (UINTN) BufferIn + BufferInSize) {
+ RecordHeaderIn = (TLS_RECORD_HEADER *) BufferInPtr;
+
+ if (RecordHeaderIn->ContentType != TlsContentTypeApplicationData) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ERROR;
+ }
+
+ ThisPlainMessageSize = RecordHeaderIn->Length;
+
+ TlsWrite (TlsInstance->TlsConn, (UINT8 *) (RecordHeaderIn + 1), ThisPlainMessageSize);
+
+ Ret = TlsCtrlTrafficOut (TlsInstance->TlsConn, (UINT8 *)(TempRecordHeader), MAX_BUFFER_SIZE - BufferOutSize);
+
+ if (Ret > 0) {
+ ThisMessageSize = (UINT16) Ret;
+ } else {
+ //
+ // No data was successfully encrypted, continue to encrypt other messages.
+ //
+ DEBUG ((EFI_D_WARN, "TlsEncryptPacket: No data read from TLS object.\n"));
+
+ ThisMessageSize = 0;
+ }
+
+ BufferOutSize += ThisMessageSize;
+
+ BufferInPtr += RECORD_HEADER_LEN + ThisPlainMessageSize;
+ TempRecordHeader += ThisMessageSize;
+ }
+
+ FreePool (BufferIn);
+ BufferIn = NULL;
+
+ //
+ // The caller will be responsible to handle the original fragment table.
+ //
+ *FragmentTable = AllocateZeroPool (sizeof (EFI_TLS_FRAGMENT_DATA));
+ if (*FragmentTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR;
+ }
+
+ (*FragmentTable)[0].FragmentBuffer = BufferOut;
+ (*FragmentTable)[0].FragmentLength = BufferOutSize;
+ *FragmentCount = 1;
+
+ return Status;
+
+ERROR:
+
+ if (BufferIn != NULL) {
+ FreePool (BufferIn);
+ BufferIn = NULL;
+ }
+
+ if (BufferOut != NULL) {
+ FreePool (BufferOut);
+ BufferOut = NULL;
+ }
+
+ return Status;
+}
+
+/**
+ Decrypt the message listed in fragment.
+
+ @param[in] TlsInstance The pointer to the TLS instance.
+ @param[in, out] FragmentTable Pointer to a list of fragment.
+ On input these fragments contain the TLS header and
+ cipher text TLS payload;
+ On output these fragments contain the TLS header and
+ plain text TLS payload.
+ @param[in] FragmentCount Number of fragment.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_ABORTED TLS session state is incorrect.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+TlsDecryptPacket (
+ IN TLS_INSTANCE *TlsInstance,
+ IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT32 BytesCopied;
+ UINT8 *BufferIn;
+ UINT32 BufferInSize;
+ UINT8 *BufferInPtr;
+ TLS_RECORD_HEADER *RecordHeaderIn;
+ UINT16 ThisCipherMessageSize;
+ TLS_RECORD_HEADER *TempRecordHeader;
+ UINT16 ThisPlainMessageSize;
+ UINT8 *BufferOut;
+ UINT32 BufferOutSize;
+ INTN Ret;
+
+ Status = EFI_SUCCESS;
+ BytesCopied = 0;
+ BufferIn = NULL;
+ BufferInSize = 0;
+ BufferInPtr = NULL;
+ RecordHeaderIn = NULL;
+ TempRecordHeader = NULL;
+ BufferOut = NULL;
+ BufferOutSize = 0;
+ Ret = 0;
+
+ //
+ // Calculate the size according to the fragment table.
+ //
+ for (Index = 0; Index < *FragmentCount; Index++) {
+ BufferInSize += (*FragmentTable)[Index].FragmentLength;
+ }
+
+ //
+ // Allocate buffer for processing data
+ //
+ BufferIn = AllocateZeroPool (BufferInSize);
+ if (BufferIn == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR;
+ }
+
+ //
+ // Copy all TLS plain record header and payload to BufferIn
+ //
+ for (Index = 0; Index < *FragmentCount; Index++) {
+ CopyMem (
+ (BufferIn + BytesCopied),
+ (*FragmentTable)[Index].FragmentBuffer,
+ (*FragmentTable)[Index].FragmentLength
+ );
+ BytesCopied += (*FragmentTable)[Index].FragmentLength;
+ }
+
+ BufferOut = AllocateZeroPool (MAX_BUFFER_SIZE);
+ if (BufferOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR;
+ }
+
+ //
+ // Parsing buffer. Received packet may have multiple TLS record messages.
+ //
+ BufferInPtr = BufferIn;
+ TempRecordHeader = (TLS_RECORD_HEADER *) BufferOut;
+ while ((UINTN) BufferInPtr < (UINTN) BufferIn + BufferInSize) {
+ RecordHeaderIn = (TLS_RECORD_HEADER *) BufferInPtr;
+
+ if (RecordHeaderIn->ContentType != TlsContentTypeApplicationData) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ERROR;
+ }
+
+ ThisCipherMessageSize = NTOHS (RecordHeaderIn->Length);
+
+ Ret = TlsCtrlTrafficIn (TlsInstance->TlsConn, (UINT8 *) (RecordHeaderIn), RECORD_HEADER_LEN + ThisCipherMessageSize);
+ if (Ret != RECORD_HEADER_LEN + ThisCipherMessageSize) {
+ TlsInstance->TlsSessionState = EfiTlsSessionError;
+ Status = EFI_ABORTED;
+ goto ERROR;
+ }
+
+ Ret = 0;
+ Ret = TlsRead (TlsInstance->TlsConn, (UINT8 *) (TempRecordHeader + 1), MAX_BUFFER_SIZE - BufferOutSize);
+
+ if (Ret > 0) {
+ ThisPlainMessageSize = (UINT16) Ret;
+ } else {
+ //
+ // No data was successfully decrypted, continue to decrypt other messages.
+ //
+ DEBUG ((EFI_D_WARN, "TlsDecryptPacket: No data read from TLS object.\n"));
+
+ ThisPlainMessageSize = 0;
+ }
+
+ CopyMem (TempRecordHeader, RecordHeaderIn, RECORD_HEADER_LEN);
+ TempRecordHeader->Length = ThisPlainMessageSize;
+ BufferOutSize += RECORD_HEADER_LEN + ThisPlainMessageSize;
+
+ BufferInPtr += RECORD_HEADER_LEN + ThisCipherMessageSize;
+ TempRecordHeader += RECORD_HEADER_LEN + ThisPlainMessageSize;
+ }
+
+ FreePool (BufferIn);
+ BufferIn = NULL;
+
+ //
+ // The caller will be responsible to handle the original fragment table
+ //
+ *FragmentTable = AllocateZeroPool (sizeof (EFI_TLS_FRAGMENT_DATA));
+ if (*FragmentTable == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR;
+ }
+
+ (*FragmentTable)[0].FragmentBuffer = BufferOut;
+ (*FragmentTable)[0].FragmentLength = BufferOutSize;
+ *FragmentCount = 1;
+
+ return Status;
+
+ERROR:
+
+ if (BufferIn != NULL) {
+ FreePool (BufferIn);
+ BufferIn = NULL;
+ }
+
+ if (BufferOut != NULL) {
+ FreePool (BufferOut);
+ BufferOut = NULL;
+ }
+
+ return Status;
+}
+
diff --git a/Core/NetworkPkg/TlsDxe/TlsImpl.h b/Core/NetworkPkg/TlsDxe/TlsImpl.h
new file mode 100644
index 0000000000..3ae9d0d546
--- /dev/null
+++ b/Core/NetworkPkg/TlsDxe/TlsImpl.h
@@ -0,0 +1,316 @@
+/** @file
+ Header file of Miscellaneous Routines for TlsDxe driver.
+
+Copyright (c) 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 __EFI_TLS_IMPL_H__
+#define __EFI_TLS_IMPL_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>
+#include <Library/NetLib.h>
+#include <Library/BaseCryptLib.h>
+#include <Library/TlsLib.h>
+
+//
+// Consumed Protocols
+//
+#include <Protocol/Tls.h>
+#include <Protocol/TlsConfig.h>
+
+#include <IndustryStandard/Tls1.h>
+
+#include "TlsDriver.h"
+
+//
+// Protocol instances
+//
+extern EFI_SERVICE_BINDING_PROTOCOL mTlsServiceBinding;
+extern EFI_TLS_PROTOCOL mTlsProtocol;
+extern EFI_TLS_CONFIGURATION_PROTOCOL mTlsConfigurationProtocol;
+
+#define RECORD_HEADER_LEN 5 /// ContentType(1) + Version(2) + Length(2)
+
+#define MAX_BUFFER_SIZE 32768
+
+/**
+ Encrypt the message listed in fragment.
+
+ @param[in] TlsInstance The pointer to the TLS instance.
+ @param[in, out] FragmentTable Pointer to a list of fragment.
+ On input these fragments contain the TLS header and
+ plain text TLS payload;
+ On output these fragments contain the TLS header and
+ cipher text TLS payload.
+ @param[in] FragmentCount Number of fragment.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_ABORTED TLS session state is incorrect.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+TlsEncryptPacket (
+ IN TLS_INSTANCE *TlsInstance,
+ IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount
+ );
+
+/**
+ Decrypt the message listed in fragment.
+
+ @param[in] TlsInstance The pointer to the TLS instance.
+ @param[in, out] FragmentTable Pointer to a list of fragment.
+ On input these fragments contain the TLS header and
+ cipher text TLS payload;
+ On output these fragments contain the TLS header and
+ plain text TLS payload.
+ @param[in] FragmentCount Number of fragment.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
+ @retval EFI_ABORTED TLS session state is incorrect.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+TlsDecryptPacket (
+ IN TLS_INSTANCE *TlsInstance,
+ IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount
+ );
+
+/**
+ Set TLS session data.
+
+ The SetSessionData() function set data for a new TLS session. All session data should
+ be set before BuildResponsePacket() invoked.
+
+ @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[in] DataType TLS session data type.
+ @param[in] Data Pointer to session data.
+ @param[in] DataSize Total size of session data.
+
+ @retval EFI_SUCCESS The TLS session data is set successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ Data is NULL.
+ DataSize is 0.
+ @retval EFI_UNSUPPORTED The DataType is unsupported.
+ @retval EFI_ACCESS_DENIED If the DataType is one of below:
+ EfiTlsClientRandom
+ EfiTlsServerRandom
+ EfiTlsKeyMaterial
+ @retval EFI_NOT_READY Current TLS session state is NOT
+ EfiTlsSessionStateNotStarted.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+**/
+EFI_STATUS
+EFIAPI
+TlsSetSessionData (
+ IN EFI_TLS_PROTOCOL *This,
+ IN EFI_TLS_SESSION_DATA_TYPE DataType,
+ IN VOID *Data,
+ IN UINTN DataSize
+ );
+
+/**
+ Get TLS session data.
+
+ The GetSessionData() function return the TLS session information.
+
+ @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[in] DataType TLS session data type.
+ @param[in, out] Data Pointer to session data.
+ @param[in, out] DataSize Total size of session data. On input, it means
+ the size of Data buffer. On output, it means the size
+ of copied Data buffer if EFI_SUCCESS, and means the
+ size of desired Data buffer if EFI_BUFFER_TOO_SMALL.
+
+ @retval EFI_SUCCESS The TLS session data is got successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ DataSize is NULL.
+ Data is NULL if *DataSize is not zero.
+ @retval EFI_UNSUPPORTED The DataType is unsupported.
+ @retval EFI_NOT_FOUND The TLS session data is not found.
+ @retval EFI_NOT_READY The DataType is not ready in current session state.
+ @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the data.
+**/
+EFI_STATUS
+EFIAPI
+TlsGetSessionData (
+ IN EFI_TLS_PROTOCOL *This,
+ IN EFI_TLS_SESSION_DATA_TYPE DataType,
+ IN OUT VOID *Data, OPTIONAL
+ IN OUT UINTN *DataSize
+ );
+
+/**
+ Build response packet according to TLS state machine. This function is only valid for
+ alert, handshake and change_cipher_spec content type.
+
+ The BuildResponsePacket() function builds TLS response packet in response to the TLS
+ request packet specified by RequestBuffer and RequestSize. If RequestBuffer is NULL and
+ RequestSize is 0, and TLS session status is EfiTlsSessionNotStarted, the TLS session
+ will be initiated and the response packet needs to be ClientHello. If RequestBuffer is
+ NULL and RequestSize is 0, and TLS session status is EfiTlsSessionClosing, the TLS
+ session will be closed and response packet needs to be CloseNotify. If RequestBuffer is
+ NULL and RequestSize is 0, and TLS session status is EfiTlsSessionError, the TLS
+ session has errors and the response packet needs to be Alert message based on error
+ type.
+
+ @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[in] RequestBuffer Pointer to the most recently received TLS packet. NULL
+ means TLS need initiate the TLS session and response
+ packet need to be ClientHello.
+ @param[in] RequestSize Packet size in bytes for the most recently received TLS
+ packet. 0 is only valid when RequestBuffer is NULL.
+ @param[out] Buffer Pointer to the buffer to hold the built packet.
+ @param[in, out] BufferSize Pointer to the buffer size in bytes. On input, it is
+ the buffer size provided by the caller. On output, it
+ is the buffer size in fact needed to contain the
+ packet.
+
+ @retval EFI_SUCCESS The required TLS packet is built successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ RequestBuffer is NULL but RequestSize is NOT 0.
+ RequestSize is 0 but RequestBuffer is NOT NULL.
+ BufferSize is NULL.
+ Buffer is NULL if *BufferSize is not zero.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to hold the response packet.
+ @retval EFI_NOT_READY Current TLS session state is NOT ready to build
+ ResponsePacket.
+ @retval EFI_ABORTED Something wrong build response packet.
+**/
+EFI_STATUS
+EFIAPI
+TlsBuildResponsePacket (
+ IN EFI_TLS_PROTOCOL *This,
+ IN UINT8 *RequestBuffer, OPTIONAL
+ IN UINTN RequestSize, OPTIONAL
+ OUT UINT8 *Buffer, OPTIONAL
+ IN OUT UINTN *BufferSize
+ );
+
+/**
+ Decrypt or encrypt TLS packet during session. This function is only valid after
+ session connected and for application_data content type.
+
+ The ProcessPacket () function process each inbound or outbound TLS APP packet.
+
+ @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[in, out] FragmentTable Pointer to a list of fragment. The caller will take
+ responsible to handle the original FragmentTable while
+ it may be reallocated in TLS driver. If CryptMode is
+ EfiTlsEncrypt, on input these fragments contain the TLS
+ header and plain text TLS APP payload; on output these
+ fragments contain the TLS header and cipher text TLS
+ APP payload. If CryptMode is EfiTlsDecrypt, on input
+ these fragments contain the TLS header and cipher text
+ TLS APP payload; on output these fragments contain the
+ TLS header and plain text TLS APP payload.
+ @param[in] FragmentCount Number of fragment.
+ @param[in] CryptMode Crypt mode.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ FragmentTable is NULL.
+ FragmentCount is NULL.
+ CryptoMode is invalid.
+ @retval EFI_NOT_READY Current TLS session state is NOT
+ EfiTlsSessionDataTransferring.
+ @retval EFI_ABORTED Something wrong decryption the message. TLS session
+ status will become EfiTlsSessionError. The caller need
+ call BuildResponsePacket() to generate Error Alert
+ message and send it out.
+ @retval EFI_OUT_OF_RESOURCES No enough resource to finish the operation.
+**/
+EFI_STATUS
+EFIAPI
+TlsProcessPacket (
+ IN EFI_TLS_PROTOCOL *This,
+ IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ IN EFI_TLS_CRYPT_MODE CryptMode
+ );
+
+/**
+ Set TLS configuration data.
+
+ The SetData() function sets TLS configuration to non-volatile storage or volatile
+ storage.
+
+ @param[in] This Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance.
+ @param[in] DataType Configuration data type.
+ @param[in] Data Pointer to configuration data.
+ @param[in] DataSize Total size of configuration data.
+
+ @retval EFI_SUCCESS The TLS configuration data is set successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ Data is NULL.
+ DataSize is 0.
+ @retval EFI_UNSUPPORTED The DataType is unsupported.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+**/
+EFI_STATUS
+EFIAPI
+TlsConfigurationSetData (
+ IN EFI_TLS_CONFIGURATION_PROTOCOL *This,
+ IN EFI_TLS_CONFIG_DATA_TYPE DataType,
+ IN VOID *Data,
+ IN UINTN DataSize
+ );
+
+/**
+ Get TLS configuration data.
+
+ The GetData() function gets TLS configuration.
+
+ @param[in] This Pointer to the EFI_TLS_CONFIGURATION_PROTOCOL instance.
+ @param[in] DataType Configuration data type.
+ @param[in, out] Data Pointer to configuration data.
+ @param[in, out] DataSize Total size of configuration data. On input, it means
+ the size of Data buffer. On output, it means the size
+ of copied Data buffer if EFI_SUCCESS, and means the
+ size of desired Data buffer if EFI_BUFFER_TOO_SMALL.
+
+ @retval EFI_SUCCESS The TLS configuration data is got successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ DataSize is NULL.
+ Data is NULL if *DataSize is not zero.
+ @retval EFI_UNSUPPORTED The DataType is unsupported.
+ @retval EFI_NOT_FOUND The TLS configuration data is not found.
+ @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the data.
+**/
+EFI_STATUS
+EFIAPI
+TlsConfigurationGetData (
+ IN EFI_TLS_CONFIGURATION_PROTOCOL *This,
+ IN EFI_TLS_CONFIG_DATA_TYPE DataType,
+ IN OUT VOID *Data, OPTIONAL
+ IN OUT UINTN *DataSize
+ );
+
+#endif
+
diff --git a/Core/NetworkPkg/TlsDxe/TlsProtocol.c b/Core/NetworkPkg/TlsDxe/TlsProtocol.c
new file mode 100644
index 0000000000..ad4c922c60
--- /dev/null
+++ b/Core/NetworkPkg/TlsDxe/TlsProtocol.c
@@ -0,0 +1,633 @@
+/** @file
+ Implementation of EFI TLS Protocol Interfaces.
+
+ Copyright (c) 2016 - 2017, 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 "TlsImpl.h"
+
+EFI_TLS_PROTOCOL mTlsProtocol = {
+ TlsSetSessionData,
+ TlsGetSessionData,
+ TlsBuildResponsePacket,
+ TlsProcessPacket
+};
+
+/**
+ Set TLS session data.
+
+ The SetSessionData() function set data for a new TLS session. All session data should
+ be set before BuildResponsePacket() invoked.
+
+ @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[in] DataType TLS session data type.
+ @param[in] Data Pointer to session data.
+ @param[in] DataSize Total size of session data.
+
+ @retval EFI_SUCCESS The TLS session data is set successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ Data is NULL.
+ DataSize is 0.
+ @retval EFI_UNSUPPORTED The DataType is unsupported.
+ @retval EFI_ACCESS_DENIED If the DataType is one of below:
+ EfiTlsClientRandom
+ EfiTlsServerRandom
+ EfiTlsKeyMaterial
+ @retval EFI_NOT_READY Current TLS session state is NOT
+ EfiTlsSessionStateNotStarted.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+**/
+EFI_STATUS
+EFIAPI
+TlsSetSessionData (
+ IN EFI_TLS_PROTOCOL *This,
+ IN EFI_TLS_SESSION_DATA_TYPE DataType,
+ IN VOID *Data,
+ IN UINTN DataSize
+ )
+{
+ EFI_STATUS Status;
+ TLS_INSTANCE *Instance;
+ UINT16 *CipherId;
+ UINTN Index;
+
+ EFI_TPL OldTpl;
+
+ Status = EFI_SUCCESS;
+ CipherId = NULL;
+
+ if (This == NULL || Data == NULL || DataSize == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = TLS_INSTANCE_FROM_PROTOCOL (This);
+
+ if (DataType != EfiTlsSessionState && Instance->TlsSessionState != EfiTlsSessionNotStarted){
+ Status = EFI_NOT_READY;
+ goto ON_EXIT;
+ }
+
+ switch (DataType) {
+ //
+ // Session Configuration
+ //
+ case EfiTlsVersion:
+ if (DataSize != sizeof (EFI_TLS_VERSION)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Status = TlsSetVersion (Instance->TlsConn, ((EFI_TLS_VERSION *) Data)->Major, ((EFI_TLS_VERSION *) Data)->Minor);
+ break;
+ case EfiTlsConnectionEnd:
+ if (DataSize != sizeof (EFI_TLS_CONNECTION_END)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Status = TlsSetConnectionEnd (Instance->TlsConn, *((EFI_TLS_CONNECTION_END *) Data));
+ break;
+ case EfiTlsCipherList:
+ CipherId = AllocatePool (DataSize);
+ if (CipherId == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ for (Index = 0; Index < DataSize / sizeof (EFI_TLS_CIPHER); Index++) {
+ *(CipherId +Index) = HTONS (*(((UINT16 *) Data) + Index));
+ }
+
+ Status = TlsSetCipherList (Instance->TlsConn, CipherId, DataSize / sizeof (EFI_TLS_CIPHER));
+
+ FreePool (CipherId);
+ break;
+ case EfiTlsCompressionMethod:
+ //
+ // TLS seems only define one CompressionMethod.null, which specifies that data exchanged via the
+ // record protocol will not be compressed.
+ // More information from OpenSSL: http://www.openssl.org/docs/manmaster/ssl/SSL_COMP_add_compression_method.html
+ // The TLS RFC does however not specify compression methods or their corresponding identifiers,
+ // so there is currently no compatible way to integrate compression with unknown peers.
+ // It is therefore currently not recommended to integrate compression into applications.
+ // Applications for non-public use may agree on certain compression methods.
+ // Using different compression methods with the same identifier will lead to connection failure.
+ //
+ for (Index = 0; Index < DataSize / sizeof (EFI_TLS_COMPRESSION); Index++) {
+ Status = TlsSetCompressionMethod (*((UINT8 *) Data + Index));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ break;
+ case EfiTlsExtensionData:
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ case EfiTlsVerifyMethod:
+ if (DataSize != sizeof (EFI_TLS_VERIFY)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ TlsSetVerify (Instance->TlsConn, *((UINT32 *) Data));
+ break;
+ case EfiTlsSessionID:
+ if (DataSize != sizeof (EFI_TLS_SESSION_ID)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Status = TlsSetSessionId (
+ Instance->TlsConn,
+ ((EFI_TLS_SESSION_ID *) Data)->Data,
+ ((EFI_TLS_SESSION_ID *) Data)->Length
+ );
+ break;
+ case EfiTlsSessionState:
+ if (DataSize != sizeof (EFI_TLS_SESSION_STATE)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Instance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) Data;
+ break;
+ //
+ // Session information
+ //
+ case EfiTlsClientRandom:
+ Status = EFI_ACCESS_DENIED;
+ break;
+ case EfiTlsServerRandom:
+ Status = EFI_ACCESS_DENIED;
+ break;
+ case EfiTlsKeyMaterial:
+ Status = EFI_ACCESS_DENIED;
+ break;
+ //
+ // Unsupported type.
+ //
+ default:
+ Status = EFI_UNSUPPORTED;
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Get TLS session data.
+
+ The GetSessionData() function return the TLS session information.
+
+ @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[in] DataType TLS session data type.
+ @param[in, out] Data Pointer to session data.
+ @param[in, out] DataSize Total size of session data. On input, it means
+ the size of Data buffer. On output, it means the size
+ of copied Data buffer if EFI_SUCCESS, and means the
+ size of desired Data buffer if EFI_BUFFER_TOO_SMALL.
+
+ @retval EFI_SUCCESS The TLS session data is got successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ DataSize is NULL.
+ Data is NULL if *DataSize is not zero.
+ @retval EFI_UNSUPPORTED The DataType is unsupported.
+ @retval EFI_NOT_FOUND The TLS session data is not found.
+ @retval EFI_NOT_READY The DataType is not ready in current session state.
+ @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the data.
+**/
+EFI_STATUS
+EFIAPI
+TlsGetSessionData (
+ IN EFI_TLS_PROTOCOL *This,
+ IN EFI_TLS_SESSION_DATA_TYPE DataType,
+ IN OUT VOID *Data, OPTIONAL
+ IN OUT UINTN *DataSize
+ )
+{
+ EFI_STATUS Status;
+ TLS_INSTANCE *Instance;
+
+ EFI_TPL OldTpl;
+
+ Status = EFI_SUCCESS;
+
+ if (This == NULL || DataSize == NULL || (Data == NULL && *DataSize != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = TLS_INSTANCE_FROM_PROTOCOL (This);
+
+ if (Instance->TlsSessionState == EfiTlsSessionNotStarted &&
+ (DataType == EfiTlsSessionID || DataType == EfiTlsClientRandom ||
+ DataType == EfiTlsServerRandom || DataType == EfiTlsKeyMaterial)) {
+ Status = EFI_NOT_READY;
+ goto ON_EXIT;
+ }
+
+ switch (DataType) {
+ case EfiTlsVersion:
+ if (*DataSize < sizeof (EFI_TLS_VERSION)) {
+ *DataSize = sizeof (EFI_TLS_VERSION);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof (EFI_TLS_VERSION);
+ *((UINT16 *) Data) = HTONS (TlsGetVersion (Instance->TlsConn));
+ break;
+ case EfiTlsConnectionEnd:
+ if (*DataSize < sizeof (EFI_TLS_CONNECTION_END)) {
+ *DataSize = sizeof (EFI_TLS_CONNECTION_END);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof (EFI_TLS_CONNECTION_END);
+ *((UINT8 *) Data) = TlsGetConnectionEnd (Instance->TlsConn);
+ break;
+ case EfiTlsCipherList:
+ //
+ // Get the current session cipher suite.
+ //
+ if (*DataSize < sizeof (EFI_TLS_CIPHER)) {
+ *DataSize = sizeof (EFI_TLS_CIPHER);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof(EFI_TLS_CIPHER);
+ Status = TlsGetCurrentCipher (Instance->TlsConn, (UINT16 *) Data);
+ *((UINT16 *) Data) = HTONS (*((UINT16 *) Data));
+ break;
+ case EfiTlsCompressionMethod:
+ //
+ // Get the current session compression method.
+ //
+ if (*DataSize < sizeof (EFI_TLS_COMPRESSION)) {
+ *DataSize = sizeof (EFI_TLS_COMPRESSION);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof (EFI_TLS_COMPRESSION);
+ Status = TlsGetCurrentCompressionId (Instance->TlsConn, (UINT8 *) Data);
+ break;
+ case EfiTlsExtensionData:
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ case EfiTlsVerifyMethod:
+ if (*DataSize < sizeof (EFI_TLS_VERIFY)) {
+ *DataSize = sizeof (EFI_TLS_VERIFY);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof (EFI_TLS_VERIFY);
+ *((UINT32 *) Data) = TlsGetVerify (Instance->TlsConn);
+ break;
+ case EfiTlsSessionID:
+ if (*DataSize < sizeof (EFI_TLS_SESSION_ID)) {
+ *DataSize = sizeof (EFI_TLS_SESSION_ID);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof (EFI_TLS_SESSION_ID);
+ Status = TlsGetSessionId (
+ Instance->TlsConn,
+ ((EFI_TLS_SESSION_ID *) Data)->Data,
+ &(((EFI_TLS_SESSION_ID *) Data)->Length)
+ );
+ break;
+ case EfiTlsSessionState:
+ if (*DataSize < sizeof (EFI_TLS_SESSION_STATE)) {
+ *DataSize = sizeof (EFI_TLS_SESSION_STATE);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof (EFI_TLS_SESSION_STATE);
+ CopyMem (Data, &Instance->TlsSessionState, *DataSize);
+ break;
+ case EfiTlsClientRandom:
+ if (*DataSize < sizeof (EFI_TLS_RANDOM)) {
+ *DataSize = sizeof (EFI_TLS_RANDOM);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof (EFI_TLS_RANDOM);
+ TlsGetClientRandom (Instance->TlsConn, (UINT8 *) Data);
+ break;
+ case EfiTlsServerRandom:
+ if (*DataSize < sizeof (EFI_TLS_RANDOM)) {
+ *DataSize = sizeof (EFI_TLS_RANDOM);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof (EFI_TLS_RANDOM);
+ TlsGetServerRandom (Instance->TlsConn, (UINT8 *) Data);
+ break;
+ case EfiTlsKeyMaterial:
+ if (*DataSize < sizeof (EFI_TLS_MASTER_SECRET)) {
+ *DataSize = sizeof (EFI_TLS_MASTER_SECRET);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ *DataSize = sizeof (EFI_TLS_MASTER_SECRET);
+ Status = TlsGetKeyMaterial (Instance->TlsConn, (UINT8 *) Data);
+ break;
+ //
+ // Unsupported type.
+ //
+ default:
+ Status = EFI_UNSUPPORTED;
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Build response packet according to TLS state machine. This function is only valid for
+ alert, handshake and change_cipher_spec content type.
+
+ The BuildResponsePacket() function builds TLS response packet in response to the TLS
+ request packet specified by RequestBuffer and RequestSize. If RequestBuffer is NULL and
+ RequestSize is 0, and TLS session status is EfiTlsSessionNotStarted, the TLS session
+ will be initiated and the response packet needs to be ClientHello. If RequestBuffer is
+ NULL and RequestSize is 0, and TLS session status is EfiTlsSessionClosing, the TLS
+ session will be closed and response packet needs to be CloseNotify. If RequestBuffer is
+ NULL and RequestSize is 0, and TLS session status is EfiTlsSessionError, the TLS
+ session has errors and the response packet needs to be Alert message based on error
+ type.
+
+ @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[in] RequestBuffer Pointer to the most recently received TLS packet. NULL
+ means TLS need initiate the TLS session and response
+ packet need to be ClientHello.
+ @param[in] RequestSize Packet size in bytes for the most recently received TLS
+ packet. 0 is only valid when RequestBuffer is NULL.
+ @param[out] Buffer Pointer to the buffer to hold the built packet.
+ @param[in, out] BufferSize Pointer to the buffer size in bytes. On input, it is
+ the buffer size provided by the caller. On output, it
+ is the buffer size in fact needed to contain the
+ packet.
+
+ @retval EFI_SUCCESS The required TLS packet is built successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ RequestBuffer is NULL but RequestSize is NOT 0.
+ RequestSize is 0 but RequestBuffer is NOT NULL.
+ BufferSize is NULL.
+ Buffer is NULL if *BufferSize is not zero.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to hold the response packet.
+ @retval EFI_NOT_READY Current TLS session state is NOT ready to build
+ ResponsePacket.
+ @retval EFI_ABORTED Something wrong build response packet.
+**/
+EFI_STATUS
+EFIAPI
+TlsBuildResponsePacket (
+ IN EFI_TLS_PROTOCOL *This,
+ IN UINT8 *RequestBuffer, OPTIONAL
+ IN UINTN RequestSize, OPTIONAL
+ OUT UINT8 *Buffer, OPTIONAL
+ IN OUT UINTN *BufferSize
+ )
+{
+ EFI_STATUS Status;
+ TLS_INSTANCE *Instance;
+ EFI_TPL OldTpl;
+
+ Status = EFI_SUCCESS;
+
+ if ((This == NULL) || (BufferSize == NULL) ||
+ (RequestBuffer == NULL && RequestSize != 0) ||
+ (RequestBuffer != NULL && RequestSize == 0) ||
+ (Buffer == NULL && *BufferSize !=0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = TLS_INSTANCE_FROM_PROTOCOL (This);
+
+ if(RequestBuffer == NULL && RequestSize == 0) {
+ switch (Instance->TlsSessionState) {
+ case EfiTlsSessionNotStarted:
+ //
+ // ClientHello.
+ //
+ Status = TlsDoHandshake (
+ Instance->TlsConn,
+ NULL,
+ 0,
+ Buffer,
+ BufferSize
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // *BufferSize should not be zero when ClientHello.
+ //
+ if (*BufferSize == 0) {
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+
+ Instance->TlsSessionState = EfiTlsSessionHandShaking;
+
+ break;
+ case EfiTlsSessionClosing:
+ //
+ // TLS session will be closed and response packet needs to be CloseNotify.
+ //
+ Status = TlsCloseNotify (
+ Instance->TlsConn,
+ Buffer,
+ BufferSize
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // *BufferSize should not be zero when build CloseNotify message.
+ //
+ if (*BufferSize == 0) {
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+
+ break;
+ case EfiTlsSessionError:
+ //
+ // TLS session has errors and the response packet needs to be Alert
+ // message based on error type.
+ //
+ Status = TlsHandleAlert (
+ Instance->TlsConn,
+ NULL,
+ 0,
+ Buffer,
+ BufferSize
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ break;
+ default:
+ //
+ // Current TLS session state is NOT ready to build ResponsePacket.
+ //
+ Status = EFI_NOT_READY;
+ }
+ } else {
+ //
+ // 1. Received packet may have multiple TLS record messages.
+ // 2. One TLS record message may have multiple handshake protocol.
+ // 3. Some errors may be happened in handshake.
+ // TlsDoHandshake() can handle all of those cases.
+ //
+ if (TlsInHandshake (Instance->TlsConn)) {
+ Status = TlsDoHandshake (
+ Instance->TlsConn,
+ RequestBuffer,
+ RequestSize,
+ Buffer,
+ BufferSize
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (!TlsInHandshake (Instance->TlsConn)) {
+ Instance->TlsSessionState = EfiTlsSessionDataTransferring;
+ }
+ } else {
+ //
+ // Must be alert message, Decrypt it and build the ResponsePacket.
+ //
+ ASSERT (((TLS_RECORD_HEADER *) RequestBuffer)->ContentType == TlsContentTypeAlert);
+
+ Status = TlsHandleAlert (
+ Instance->TlsConn,
+ RequestBuffer,
+ RequestSize,
+ Buffer,
+ BufferSize
+ );
+ if (EFI_ERROR (Status)) {
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ Instance->TlsSessionState = EfiTlsSessionError;
+ }
+
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
+/**
+ Decrypt or encrypt TLS packet during session. This function is only valid after
+ session connected and for application_data content type.
+
+ The ProcessPacket () function process each inbound or outbound TLS APP packet.
+
+ @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
+ @param[in, out] FragmentTable Pointer to a list of fragment. The caller will take
+ responsible to handle the original FragmentTable while
+ it may be reallocated in TLS driver. If CryptMode is
+ EfiTlsEncrypt, on input these fragments contain the TLS
+ header and plain text TLS APP payload; on output these
+ fragments contain the TLS header and cipher text TLS
+ APP payload. If CryptMode is EfiTlsDecrypt, on input
+ these fragments contain the TLS header and cipher text
+ TLS APP payload; on output these fragments contain the
+ TLS header and plain text TLS APP payload.
+ @param[in] FragmentCount Number of fragment.
+ @param[in] CryptMode Crypt mode.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL.
+ FragmentTable is NULL.
+ FragmentCount is NULL.
+ CryptoMode is invalid.
+ @retval EFI_NOT_READY Current TLS session state is NOT
+ EfiTlsSessionDataTransferring.
+ @retval EFI_ABORTED Something wrong decryption the message. TLS session
+ status will become EfiTlsSessionError. The caller need
+ call BuildResponsePacket() to generate Error Alert
+ message and send it out.
+ @retval EFI_OUT_OF_RESOURCES No enough resource to finish the operation.
+**/
+EFI_STATUS
+EFIAPI
+TlsProcessPacket (
+ IN EFI_TLS_PROTOCOL *This,
+ IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable,
+ IN UINT32 *FragmentCount,
+ IN EFI_TLS_CRYPT_MODE CryptMode
+ )
+{
+ EFI_STATUS Status;
+ TLS_INSTANCE *Instance;
+
+ EFI_TPL OldTpl;
+
+ Status = EFI_SUCCESS;
+
+ if (This == NULL || FragmentTable == NULL || FragmentCount == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = TLS_INSTANCE_FROM_PROTOCOL (This);
+
+ if (Instance->TlsSessionState != EfiTlsSessionDataTransferring) {
+ Status = EFI_NOT_READY;
+ goto ON_EXIT;
+ }
+
+ //
+ // Packet sent or received may have multiple TLS record messages (Application data type).
+ // So,on input these fragments contain the TLS header and TLS APP payload;
+ // on output these fragments also contain the TLS header and TLS APP payload.
+ //
+ switch (CryptMode) {
+ case EfiTlsEncrypt:
+ Status = TlsEncryptPacket (Instance, FragmentTable, FragmentCount);
+ break;
+ case EfiTlsDecrypt:
+ Status = TlsDecryptPacket (Instance, FragmentTable, FragmentCount);
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
+
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..a4b1104d2b
--- /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 - 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 "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 available 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..af1d64c46b
--- /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 - 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 _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 available 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..5f1f19bdba
--- /dev/null
+++ b/Core/NetworkPkg/Udp6Dxe/Udp6Dxe.uni
@@ -0,0 +1,23 @@
+// /** @file
+// 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "UDP packet service based on IPv6 stack"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI UDPv6 Protocol which provides simple packet-oriented services to transmit and receive UDP packets."
+
diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni b/Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni
new file mode 100644
index 0000000000..1185113f3c
--- /dev/null
+++ b/Core/NetworkPkg/Udp6Dxe/Udp6DxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Udp6Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UDP6 DXE"
+
+
diff --git a/Core/NetworkPkg/Udp6Dxe/Udp6Impl.c b/Core/NetworkPkg/Udp6Dxe/Udp6Impl.c
new file mode 100644
index 0000000000..edf2c23976
--- /dev/null
+++ b/Core/NetworkPkg/Udp6Dxe/Udp6Impl.c
@@ -0,0 +1,1976 @@
+/** @file
+ Udp6 driver's whole implementation.
+
+ 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 "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 appropriate instances.
+
+ @param[in] Udp6Service Pointer to the udp service context data.
+ @param[in] NetSession Pointer to the EFI_NET_SESSION_DATA abstracted 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 abstracted 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;
+
+ if (Packet->TotalSize < sizeof (EFI_UDP_HEADER)) {
+ NetbufFree (Packet);
+ return;
+ }
+
+ //
+ // 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.
+ //
+ NetbufFree (Packet);
+ 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;
+
+ if (Packet->TotalSize < sizeof (EFI_UDP_HEADER)) {
+ NetbufFree (Packet);
+ return;
+ }
+
+ 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..fc50a82959
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
@@ -0,0 +1,1262 @@
+/** @file
+ Boot functions implementation for UefiPxeBc Driver.
+
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2016 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 "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,
+ MultU64x32 (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);
+
+ //
+ // Set the station address to IP layer.
+ //
+ Status = PxeBcSetIp6Address (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+
+ //
+ // Parse (m)tftp server ip address and bootfile name.
+ //
+ Status = PxeBcExtractBootFileUrl (
+ Private,
+ &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;
+ }
+
+ //
+ // 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 NBP file downloaded successfully.\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..5497390119
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c
@@ -0,0 +1,1754 @@
+/** @file
+ Functions implementation related with DHCPv4 for UefiPxeBc Driver.
+
+ 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 "PxeBcImpl.h"
+
+//
+// This is a map from the interested DHCP4 option tags' index to the tag value.
+//
+UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {
+ DHCP4_TAG_BOOTFILE_LEN,
+ DHCP4_TAG_VENDOR,
+ DHCP4_TAG_OVERLOAD,
+ DHCP4_TAG_MSG_TYPE,
+ DHCP4_TAG_SERVER_ID,
+ DHCP4_TAG_VENDOR_CLASS_ID,
+ 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 != DHCP4_TAG_EOP) {
+
+ if (Option->OpCode == OptTag) {
+ //
+ // Found the required option.
+ //
+ return Option;
+ }
+
+ //
+ // Skip the current option to the next.
+ //
+ if (Option->OpCode == 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 != 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 == 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 = 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 = 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);
+ 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 = DHCP4_TAG_PARA_LIST;
+ OptList[Index]->Length = 35;
+ OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;
+ OptEnt.Para->ParaList[0] = DHCP4_TAG_NETMASK;
+ OptEnt.Para->ParaList[1] = DHCP4_TAG_TIME_OFFSET;
+ OptEnt.Para->ParaList[2] = DHCP4_TAG_ROUTER;
+ OptEnt.Para->ParaList[3] = DHCP4_TAG_TIME_SERVER;
+ OptEnt.Para->ParaList[4] = DHCP4_TAG_NAME_SERVER;
+ OptEnt.Para->ParaList[5] = DHCP4_TAG_DNS_SERVER;
+ OptEnt.Para->ParaList[6] = DHCP4_TAG_HOSTNAME;
+ OptEnt.Para->ParaList[7] = DHCP4_TAG_BOOTFILE_LEN;
+ OptEnt.Para->ParaList[8] = DHCP4_TAG_DOMAINNAME;
+ OptEnt.Para->ParaList[9] = DHCP4_TAG_ROOTPATH;
+ OptEnt.Para->ParaList[10] = DHCP4_TAG_EXTEND_PATH;
+ OptEnt.Para->ParaList[11] = DHCP4_TAG_EMTU;
+ OptEnt.Para->ParaList[12] = DHCP4_TAG_TTL;
+ OptEnt.Para->ParaList[13] = DHCP4_TAG_BROADCAST;
+ OptEnt.Para->ParaList[14] = DHCP4_TAG_NIS_DOMAIN;
+ OptEnt.Para->ParaList[15] = DHCP4_TAG_NIS_SERVER;
+ OptEnt.Para->ParaList[16] = DHCP4_TAG_NTP_SERVER;
+ OptEnt.Para->ParaList[17] = DHCP4_TAG_VENDOR;
+ OptEnt.Para->ParaList[18] = DHCP4_TAG_REQUEST_IP;
+ OptEnt.Para->ParaList[19] = DHCP4_TAG_LEASE;
+ OptEnt.Para->ParaList[20] = DHCP4_TAG_SERVER_ID;
+ OptEnt.Para->ParaList[21] = DHCP4_TAG_T1;
+ OptEnt.Para->ParaList[22] = DHCP4_TAG_T2;
+ OptEnt.Para->ParaList[23] = DHCP4_TAG_VENDOR_CLASS_ID;
+ OptEnt.Para->ParaList[24] = DHCP4_TAG_TFTP;
+ OptEnt.Para->ParaList[25] = DHCP4_TAG_BOOTFILE;
+ OptEnt.Para->ParaList[26] = 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 = 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 = 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 = 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 = DHCP4_TAG_VENDOR_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] = 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.
+
+ @retval EFI_SUCCESS Packet is copied.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+PxeBcCacheDhcp4Packet (
+ IN EFI_DHCP4_PACKET *Dst,
+ IN EFI_DHCP4_PACKET *Src
+ )
+{
+ if (Dst->Size < Src->Length) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
+ Dst->Length = Src->Length;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ 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;
+ BOOLEAN FileFieldOverloaded;
+
+ IsProxyOffer = FALSE;
+ IsPxeOffer = FALSE;
+ FileFieldOverloaded = 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) {
+ FileFieldOverloaded = TRUE;
+ 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 (!FileFieldOverloaded && 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.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+PxeBcCopyDhcp4Ack (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PACKET *Ack,
+ IN BOOLEAN Verified
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+
+ Mode = Private->PxeBc.Mode;
+
+ Status = PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ 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.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+PxeBcCopyProxyOffer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT32 OfferIndex
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP4_PACKET *Offer;
+ EFI_STATUS Status;
+
+ 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.
+ //
+ Status = PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4);
+
+ //
+ // Store this packet into mode data.
+ //
+ CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length);
+ Mode->ProxyOfferReceived = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ 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.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval Others Operation failed.
+
+**/
+EFI_STATUS
+PxeBcCacheDhcp4Offer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PACKET *RcvdOffer
+ )
+{
+ PXEBC_DHCP4_PACKET_CACHE *Cache4;
+ EFI_DHCP4_PACKET *Offer;
+ PXEBC_OFFER_TYPE OfferType;
+ EFI_STATUS Status;
+
+ ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM);
+ Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;
+ Offer = &Cache4->Packet.Offer;
+
+ //
+ // Cache the content of DHCPv4 packet firstly.
+ //
+ Status = PxeBcCacheDhcp4Packet (Offer, RcvdOffer);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
+ // Validate the DHCPv4 packet, and parse the options and offer type.
+ //
+ if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // 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 EFI_ABORTED;
+ }
+ } 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 EFI_ABORTED;
+ }
+ } 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++;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ 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.
+ @retval EFI_BUFFER_TOO_SMALL Can't cache the offer pacet.
+
+**/
+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.
+ //
+ Status = 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;
+ }
+
+ Status = PxeBcCopyDhcp4Ack (Private, Ack, TRUE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ 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),
+ DHCP4_TAG_MAXMSG
+ );
+ if (MaxMsgSize != NULL) {
+ Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE);
+ 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:
+ if (Packet->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) {
+ //
+ // If the to be sent packet exceeds the maximum length, abort the DHCP process.
+ //
+ Status = EFI_ABORTED;
+ break;
+ }
+
+ //
+ // 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 (Packet->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) {
+ //
+ // If the to be sent packet exceeds the maximum length, abort the DHCP process.
+ //
+ Status = EFI_ABORTED;
+ break;
+ }
+
+ 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 (Packet->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) {
+ //
+ // Ignore the incoming packets which exceed the maximum length.
+ //
+ break;
+ }
+ if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
+ //
+ // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record
+ // the OfferIndex and OfferCount.
+ // If error happens, just ignore this packet and continue to wait more offer.
+ //
+ 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);
+
+ Status = PxeBcCopyDhcp4Ack (Private, Packet, FALSE);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_ABORTED;
+ }
+ 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 = 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] = 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) {
+ if (Response->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) {
+ SrvIndex = 0;
+ RepIndex++;
+ Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);
+ continue;
+ }
+
+ 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) {
+ Status = PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response);
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+ CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length);
+ } else {
+ Status = PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response);
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+ }
+ } else {
+ //
+ // Not found the right PXE reply packet.
+ //
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ON_EXIT:
+
+ if (Token.ResponseList != NULL) {
+ FreePool (Token.ResponseList);
+ }
+ if (Token.Packet != NULL) {
+ 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..27794c911a
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h
@@ -0,0 +1,371 @@
+/** @file
+ Functions declaration related with DHCPv4 for UefiPxeBc Driver.
+
+ 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 __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 (sizeof (EFI_PXE_BASE_CODE_PACKET))
+#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
+
+//
+// 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;
+
+#define PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE (OFFSET_OF (EFI_DHCP4_PACKET, Dhcp4) + PXEBC_DHCP4_PACKET_MAX_SIZE)
+
+typedef union {
+ EFI_DHCP4_PACKET Offer;
+ EFI_DHCP4_PACKET Ack;
+ UINT8 Buffer[PXEBC_CACHED_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..a295b827af
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
@@ -0,0 +1,2377 @@
+/** @file
+ Functions implementation related with DHCPv6 for UefiPxeBc Driver.
+
+ (C) Copyright 2014 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 "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 (DHCP6_OPT_ORO);
+ OptList[Index]->OpLen = HTONS (6);
+ OptEnt.Oro = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;
+ OptEnt.Oro->OpCode[0] = HTONS(DHCP6_OPT_BOOT_FILE_URL);
+ OptEnt.Oro->OpCode[1] = HTONS(DHCP6_OPT_BOOT_FILE_PARAM);
+ OptEnt.Oro->OpCode[2] = HTONS(DHCP6_OPT_DNS_SERVERS);
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client network device interface option
+ //
+ OptList[Index]->OpCode = HTONS (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 (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 (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.
+
+ @retval EFI_SUCCESS Packet is copied.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+PxeBcCacheDhcp6Packet (
+ IN EFI_DHCP6_PACKET *Dst,
+ IN EFI_DHCP6_PACKET *Src
+ )
+{
+ if (Dst->Size < Src->Length) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
+ Dst->Length = Src->Length;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ 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);
+ }
+}
+
+/**
+ Retrieve the boot server address using the EFI_DNS6_PROTOCOL.
+
+ @param[in] Private Pointer to PxeBc 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_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
+PxeBcDns6 (
+ IN PXEBC_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_IPv6_ADDRESS *DnsServerList;
+ BOOLEAN IsDone;
+
+ Dns6 = NULL;
+ Dns6Handle = NULL;
+ DnsServerList = Private->DnsServer;
+ ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));
+
+ //
+ // 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 = 1;
+ Dns6ConfigData.DnsServerList = DnsServerList;
+ Dns6ConfigData.EnableDnsCache = TRUE;
+ Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP;
+ IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &Private->TmpStationIp.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,
+ PxeBcCommonNotify,
+ &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:
+ FreePool (HostName);
+
+ 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;
+}
+
+/**
+ Parse the Boot File URL option.
+
+ @param[in] Private Pointer to PxeBc private data.
+ @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 (
+ IN PXEBC_PRIVATE_DATA *Private,
+ 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;
+ CHAR16 *HostName;
+ BOOLEAN IpExpressedUrl;
+ UINTN Len;
+ EFI_STATUS Status;
+
+ IpExpressedUrl = TRUE;
+ //
+ // 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.6, bootfile-url format can be
+ // tftp://[SERVER_ADDRESS]/BOOTFILE_NAME or tftp://domain_name/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) {
+ 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;
+ }
+
+ } else {
+ IpExpressedUrl = FALSE;
+ ServerAddress = ServerAddressOption;
+ while (*ServerAddress != '\0' && *ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {
+ ServerAddress++;
+ }
+
+ if (*ServerAddress != PXEBC_TFTP_URL_SEPARATOR) {
+ FreePool (TmpStr);
+ return EFI_INVALID_PARAMETER;
+ }
+ *ServerAddress = '\0';
+
+ Len = AsciiStrSize (ServerAddressOption);
+ HostName = AllocateZeroPool (Len * sizeof (CHAR16));
+ if (HostName == NULL) {
+ FreePool (TmpStr);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ AsciiStrToUnicodeStrS (
+ ServerAddressOption,
+ HostName,
+ Len
+ );
+
+ //
+ // Perform DNS resolution.
+ //
+ Status = PxeBcDns6 (Private,HostName, SrvAddr);
+ if (EFI_ERROR (Status)) {
+ FreePool (TmpStr);
+ return Status;
+ }
+ }
+
+ //
+ // Get the part of BOOTFILE_NAME string.
+ //
+ BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1);
+ if (IpExpressedUrl) {
+ 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) == DHCP6_OPT_IA_NA) {
+ Options[PXEBC_DHCP6_IDX_IA_NA] = Option;
+ } else if (NTOHS (Option->OpCode) == 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) == DHCP6_OPT_BOOT_FILE_PARAM) {
+ Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
+ } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) {
+ Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;
+ } else if (NTOHS (Option->OpCode) == DHCP6_OPT_DNS_SERVERS) {
+ Options[PXEBC_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[PXEBC_DHCP6_IDX_IA_NA];
+ if (Option != NULL) {
+ Option = PxeBcParseDhcp6Options (
+ Option->Data + 12,
+ NTOHS (Option->OpLen),
+ 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.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+PxeBcCopyDhcp6Ack (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP6_PACKET *Ack,
+ IN BOOLEAN Verified
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_STATUS Status;
+
+ Mode = Private->PxeBc.Mode;
+
+ Status = PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ 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.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+PxeBcCopyDhcp6Proxy (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN UINT32 OfferIndex
+ )
+{
+ EFI_PXE_BASE_CODE_MODE *Mode;
+ EFI_DHCP6_PACKET *Offer;
+ EFI_STATUS Status;
+
+ 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.
+ //
+ Status = PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);
+
+ //
+ // Store this packet into mode data.
+ //
+ CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);
+ Mode->ProxyOfferReceived = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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,
+ 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 != 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),
+ 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,
+ &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.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval Others Operation failed.
+**/
+EFI_STATUS
+PxeBcCacheDhcp6Offer (
+ IN PXEBC_PRIVATE_DATA *Private,
+ IN EFI_DHCP6_PACKET *RcvdOffer
+ )
+{
+ PXEBC_DHCP6_PACKET_CACHE *Cache6;
+ EFI_DHCP6_PACKET *Offer;
+ PXEBC_OFFER_TYPE OfferType;
+ EFI_STATUS Status;
+
+ Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
+ Offer = &Cache6->Packet.Offer;
+
+ //
+ // Cache the content of DHCPv6 packet firstly.
+ //
+ Status = PxeBcCacheDhcp6Packet (Offer, RcvdOffer);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Validate the DHCPv6 packet, and parse the options and offer type.
+ //
+ if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // 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 EFI_ABORTED;
+ }
+ } 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++;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ 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.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
+ @retval EFI_BUFFER_TOO_SMALL Can't cache the offer pacet.
+
+**/
+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;
+
+ //
+ // First try to cache DNS server address if DHCP6 offer provides.
+ //
+ if (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER] != NULL) {
+ Private->DnsServer = AllocateZeroPool (NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->OpLen));
+ if (Private->DnsServer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (Private->DnsServer, Cache6->OptList[PXEBC_DHCP6_IDX_DNS_SERVER]->Data, sizeof (EFI_IPv6_ADDRESS));
+ }
+
+ 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.
+ //
+ Status = 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.
+ //
+ Status = 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:
+ if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
+ //
+ // If the to be sent packet exceeds the maximum length, abort the DHCP process.
+ //
+ Status = EFI_ABORTED;
+ break;
+ }
+
+ //
+ // 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 (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
+ //
+ // Ignore the incoming packets which exceed the maximum length.
+ //
+ break;
+ }
+ 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:
+ if (Packet->Length > PXEBC_DHCP6_PACKET_MAX_SIZE) {
+ //
+ // If the to be sent packet exceeds the maximum length, abort the DHCP process.
+ //
+ Status = EFI_ABORTED;
+ break;
+ }
+
+ //
+ // 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);
+ Status = PxeBcCopyDhcp6Ack (Private, Packet, FALSE);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_ABORTED;
+ }
+ 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 != NULL) && (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));
+ if (Mode.ClientId != NULL) {
+ FreePool (Mode.ClientId);
+ }
+ if (Mode.Ia != NULL) {
+ FreePool (Mode.Ia);
+ }
+ //
+ // 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..39efcfaa77
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h
@@ -0,0 +1,285 @@
+/** @file
+ Functions declaration related with DHCPv6 for UefiPxeBc Driver.
+
+ 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 __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 (sizeof (EFI_PXE_BASE_CODE_PACKET))
+#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_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_DNS_SERVER 4
+#define PXEBC_DHCP6_IDX_MAX 5
+
+#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;
+
+#define PXEBC_CACHED_DHCP6_PACKET_MAX_SIZE (OFFSET_OF (EFI_DHCP6_PACKET, Dhcp6) + PXEBC_DHCP6_PACKET_MAX_SIZE)
+
+typedef union {
+ EFI_DHCP6_PACKET Offer;
+ EFI_DHCP6_PACKET Ack;
+ UINT8 Buffer[PXEBC_CACHED_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[in] Private Pointer to PxeBc private data.
+ @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 (
+ IN PXEBC_PRIVATE_DATA *Private,
+ 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..09c5753ad9
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c
@@ -0,0 +1,1849 @@
+/** @file
+ Driver Binding functions implementationfor for UefiPxeBc Driver.
+
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2007 - 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 "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;
+
+ 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);
+ }
+
+ //
+ // 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..ab9e494b56
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c
@@ -0,0 +1,2432 @@
+/** @file
+ This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.
+
+ Copyright (c) 2007 - 2017, 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 = 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_CACHED_DHCP6_PACKET_MAX_SIZE;
+ Private->DhcpAck.Dhcp6.Packet.Ack.Size = PXEBC_CACHED_DHCP6_PACKET_MAX_SIZE;
+ Private->PxeReply.Dhcp6.Packet.Ack.Size = PXEBC_CACHED_DHCP6_PACKET_MAX_SIZE;
+
+ for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {
+ Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_CACHED_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 = 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_CACHED_DHCP4_PACKET_MAX_SIZE;
+ Private->DhcpAck.Dhcp4.Packet.Ack.Size = PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE;
+ Private->PxeReply.Dhcp4.Packet.Ack.Size = PXEBC_CACHED_DHCP4_PACKET_MAX_SIZE;
+
+ for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {
+ Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_CACHED_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))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Config = NULL;
+ Status = EFI_DEVICE_ERROR;
+ Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+ Mode = Private->PxeBc.Mode;
+
+ if (Mode->UsingIpv6) {
+ if (!NetIp6IsValidUnicast (&ServerIp->v6)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ if (IP4_IS_UNSPECIFIED (NTOHL (ServerIp->Addr[0])) || IP4_IS_LOCAL_BROADCAST (NTOHL (ServerIp->Addr[0]))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ 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 && Mode->SubnetMask.Addr[0] != 0 &&
+ !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), EFI_NTOHL(Mode->SubnetMask))) {
+ //
+ // 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,
+ Private->Mode.TTL,
+ Private->Mode.ToS
+ );
+ }
+
+ 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 (Mode->UsingIpv6) {
+ if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&
+ NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6)) {
+ NeedPromiscuous = TRUE;
+ }
+ } else if ((EFI_NTOHL(Mode->StationIp) != 0) &&
+ (EFI_NTOHL(Mode->SubnetMask) != 0) &&
+ IP4_NET_EQUAL(EFI_NTOHL(Mode->StationIp), EFI_NTOHL(NewFilter->IpList[Index].v4), EFI_NTOHL(Mode->SubnetMask.v4)) &&
+ NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), EFI_NTOHL(Mode->SubnetMask)) &&
+ ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0)) {
+ 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 && !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->UsingIpv6 && NewStationIp != NULL) {
+ if (IP4_IS_UNSPECIFIED(NTOHL (NewStationIp->Addr[0])) ||
+ IP4_IS_LOCAL_BROADCAST(NTOHL (NewStationIp->Addr[0])) ||
+ (NewSubnetMask != NULL && NewSubnetMask->Addr[0] != 0 && !NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 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..2cdc8bf589
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h
@@ -0,0 +1,229 @@
+/** @file
+ This EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.
+ interfaces declaration.
+
+ Copyright (c) 2007 - 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 __EFI_PXEBC_IMPL_H__
+#define __EFI_PXEBC_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Guid/SmBios.h>
+#include <IndustryStandard/SmBios.h>
+#include <IndustryStandard/Dhcp.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/Dns6.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_DNS6_PROTOCOL *Dns6;
+
+ 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;
+ EFI_IPv6_ADDRESS *DnsServer;
+ 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..568360de0f
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
@@ -0,0 +1,1518 @@
+/** @file
+ Support functions implementation for UefiPxeBc Driver.
+
+ Copyright (c) 2007 - 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 "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 &&
+ (NTOHL (Mode->SubnetMask.Addr[0]) != 0) &&
+ IP4_NET_EQUAL (NTOHL(Mode->StationIp.Addr[0]), EFI_NTOHL (RxData->Header->SourceAddress), NTOHL (Mode->SubnetMask.Addr[0])) &&
+ !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), NTOHL (Mode->SubnetMask.Addr[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.
+ @param[in] Ttl The time to live field of the IP header.
+ @param[in] ToS The type of service field of the IP header.
+
+ @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,
+ IN UINT8 Ttl,
+ IN UINT8 ToS
+ )
+{
+ EFI_UDP4_CONFIG_DATA Udp4CfgData;
+ EFI_STATUS Status;
+
+ ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData));
+
+ Udp4CfgData.TransmitTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp4CfgData.ReceiveTimeout = PXEBC_DEFAULT_LIFETIME;
+ Udp4CfgData.TypeOfService = ToS;
+ Udp4CfgData.TimeToLive = 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;
+
+ for (; Length > 0; Length--) {
+ Remainder = Number % 10;
+ Number /= 10;
+ Buffer[Length - 1] = (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..b8519ae8c8
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h
@@ -0,0 +1,519 @@
+/** @file
+ Support functions declaration for UefiPxeBc Driver.
+
+ Copyright (c) 2007 - 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 __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 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.
+ @param[in] Ttl The time to live field of the IP header.
+ @param[in] ToS The type of service field of the IP header.
+
+ @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,
+ IN UINT8 Ttl,
+ IN UINT8 ToS
+ );
+
+
+/**
+ 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..6d1102b6b7
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
@@ -0,0 +1,111 @@
+## @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 - 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.
+#
+##
+
+[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
+ gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES
+ 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..1a1c9593a4
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni
@@ -0,0 +1,24 @@
+// /** @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 - 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.
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Access PXE-compatible devices for network access and network booting"
+
+#string STR_MODULE_DESCRIPTION #language en-US "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."
+
diff --git a/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni
new file mode 100644
index 0000000000..ef45972576
--- /dev/null
+++ b/Core/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni
@@ -0,0 +1,20 @@
+// /** @file
+// UefiPxeBcDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 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.
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Uefi PXE DXE"
+
+