Skip to content

Commit

Permalink
Merge pull request #2258 from andreyleskov/#2194__enforce_ip_family
Browse files Browse the repository at this point in the history
related to #2194
  • Loading branch information
Aaronontheweb authored Aug 22, 2016
2 parents 614f1f0 + 16dbf67 commit e7edb26
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 26 deletions.
21 changes: 21 additions & 0 deletions src/core/Akka.Remote.Tests/RemoteConfigSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,27 @@ public void Remoting_should_contain_correct_heliosTCP_values_in_ReferenceConf()
Assert.False(s.DnsUseIpv6);
}

[Fact]
public void When_remoting_works_in_Mono_ip_enforcement_should_be_defaulted_to_true()
{
HeliosTransportSettings.IsMono = true;
var c = ((RemoteActorRefProvider)((ActorSystemImpl)Sys).Provider).RemoteSettings.Config.GetConfig("akka.remote.helios.tcp");
var s = new HeliosTransportSettings(c);

Assert.True(s.EnforceIpFamily);
}

[Fact]
public void When_remoting_works_not_in_Mono_ip_enforcement_should_be_defaulted_to_false()
{
HeliosTransportSettings.IsMono = false;
var c = ((RemoteActorRefProvider)((ActorSystemImpl)Sys).Provider).RemoteSettings.Config.GetConfig("akka.remote.helios.tcp");
var s = new HeliosTransportSettings(c);

Assert.False(s.EnforceIpFamily);
}


[Fact]
public void Remoting_should_contain_correct_socket_worker_pool_configuration_values_in_ReferenceConf()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,13 @@ public HeliosTransportDnsResolutionSpec()
Arb.Register(typeof(EndpointGenerators));
}

public Config BuildConfig(string hostname, int? port = null, string publichostname = null, bool useIpv6 = false)
public Config BuildConfig(string hostname, int? port = null, string publichostname = null, bool useIpv6 = false, bool enforceIpFamily = false)
{
return ConfigurationFactory.ParseString(@"akka.actor.provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""")
.WithFallback("akka.remote.helios.tcp.hostname =\"" + hostname + "\"")
.WithFallback("akka.remote.helios.tcp.public-hostname =\"" + (publichostname ?? hostname) + "\"")
.WithFallback("akka.remote.helios.tcp.port = " + (port ?? 0))
.WithFallback("akka.remote.helios.tcp.enforce-ip-family = " + enforceIpFamily.ToString().ToLowerInvariant())
.WithFallback("akka.remote.helios.tcp.dns-use-ipv6 = " + useIpv6.ToString().ToLowerInvariant())
.WithFallback("akka.test.single-expect-default = 1s")
.WithFallback(Sys.Settings.Config);
Expand All @@ -88,10 +89,10 @@ public AssociationAcker()
}
}

private void Setup(string inboundHostname, string outboundHostname, string inboundPublicHostname = null, string outboundPublicHostname = null, bool useIpv6Dns = false)
private void Setup(string inboundHostname, string outboundHostname, string inboundPublicHostname = null, string outboundPublicHostname = null, bool useIpv6Dns = false, bool enforceIpFamily = false)
{
_inbound = ActorSystem.Create("Sys1", BuildConfig(inboundHostname, 0, inboundPublicHostname, useIpv6Dns));
_outbound = ActorSystem.Create("Sys2", BuildConfig(outboundHostname, 0, outboundPublicHostname, useIpv6Dns));
_inbound = ActorSystem.Create("Sys1", BuildConfig(inboundHostname, 0, inboundPublicHostname, useIpv6Dns, enforceIpFamily));
_outbound = ActorSystem.Create("Sys2", BuildConfig(outboundHostname, 0, outboundPublicHostname, useIpv6Dns, enforceIpFamily));

_inbound.ActorOf(Props.Create(() => new AssociationAcker()), "ack");
_outbound.ActorOf(Props.Create(() => new AssociationAcker()), "ack");
Expand Down Expand Up @@ -126,13 +127,28 @@ public static bool IsAnyIp(EndPoint ep)
return (ip.Address.Equals(IPAddress.Any) || ip.Address.Equals(IPAddress.IPv6Any));
}

[Property]
public Property HeliosTransport_Should_Resolve_DNS(EndPoint inbound, EndPoint outbound, bool dnsIpv6)
[Property()]
public Property HeliosTransport_Should_Resolve_DNS(EndPoint inbound, EndPoint outbound, bool dnsIpv6, bool enforceIpFamily, bool monoRuntime)
{
if (IsAnyIp(inbound) || IsAnyIp(outbound)) return true.Label("Can't connect directly to an ANY address");
try
{
Setup(EndpointGenerators.ParseAddress(inbound), EndpointGenerators.ParseAddress(outbound), useIpv6Dns:dnsIpv6);
try
{
Setup(EndpointGenerators.ParseAddress(inbound),
EndpointGenerators.ParseAddress(outbound),
useIpv6Dns: dnsIpv6,
enforceIpFamily: enforceIpFamily);
}
catch
{
//if ip family is enforced, there are some special cases when it is normal to unable
//to create actor system
if (enforceIpFamily && IsExpectedFailure(inbound, outbound, dnsIpv6))
return true.ToProperty();
throw;
}

var outboundReceivedAck = true;
var inboundReceivedAck = true;
_outbound.ActorSelection(_inboundAck).Tell("ping", _outboundProbe.Ref);
Expand All @@ -155,20 +171,40 @@ public Property HeliosTransport_Should_Resolve_DNS(EndPoint inbound, EndPoint ou
{
inboundReceivedAck = false;
}


return outboundReceivedAck.Label($"Expected (outbound: {RARP.For(_outbound).Provider.DefaultAddress}) to be able to successfully message and receive reply from (inbound: {RARP.For(_inbound).Provider.DefaultAddress})")
.And(inboundReceivedAck.Label($"Expected (inbound: {RARP.For(_inbound).Provider.DefaultAddress}) to be able to successfully message and receive reply from (outbound: {RARP.For(_outbound).Provider.DefaultAddress})"));
}
finally
{
Cleanup();
}
return outboundReceivedAck.Label($"Expected (outbound: {RARP.For(_outbound).Provider.DefaultAddress}) to be able to successfully message and receive reply from (inbound: {RARP.For(_inbound).Provider.DefaultAddress})")
.And(inboundReceivedAck.Label($"Expected (inbound: {RARP.For(_inbound).Provider.DefaultAddress}) to be able to successfully message and receive reply from (outbound: {RARP.For(_outbound).Provider.DefaultAddress})"));
}
finally
{
Cleanup();
}
}



private static bool IsExpectedFailure(EndPoint inbound,
EndPoint outbound,
bool dnsIpv6)
{
/*if ip family is enforced,
it is normal to unable to connect between ips
if any of them has not-enforced family
examples:
trying to use ipv4 on both sides when ipv6 is enforced
trying to use ipv4 + ipv6 when ipv4 or ipv6 is enforced
*/

var enforcedFamily = dnsIpv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
var endpointsIpFamilyMismatch = inbound.AddressFamily != enforcedFamily ||
outbound.AddressFamily != enforcedFamily;

return endpointsIpFamilyMismatch;
}

[Property]
public Property HeliosTransport_Should_Resolve_DNS_with_PublicHostname(IPEndPoint inbound, DnsEndPoint publicInbound,
IPEndPoint outbound, DnsEndPoint publicOutbound, bool dnsUseIpv6)
IPEndPoint outbound, DnsEndPoint publicOutbound, bool dnsUseIpv6, bool enforceIpFamily)
{
if (dnsUseIpv6 &&
(inbound.AddressFamily == AddressFamily.InterNetwork ||
Expand All @@ -179,10 +215,23 @@ public Property HeliosTransport_Should_Resolve_DNS_with_PublicHostname(IPEndPoin

try
{
Setup(EndpointGenerators.ParseAddress(inbound),
EndpointGenerators.ParseAddress(outbound),
EndpointGenerators.ParseAddress(publicInbound),
EndpointGenerators.ParseAddress(publicOutbound), dnsUseIpv6);
try
{
Setup(EndpointGenerators.ParseAddress(inbound),
EndpointGenerators.ParseAddress(outbound),
EndpointGenerators.ParseAddress(publicInbound),
EndpointGenerators.ParseAddress(publicOutbound),
dnsUseIpv6,
enforceIpFamily);
}
catch
{
//if ip family is enforced, there are some special cases when it is normal to unable
//to create actor system
if (enforceIpFamily && IsExpectedFailure(inbound, outbound, dnsUseIpv6))
return true.ToProperty();
throw;
}
var outboundReceivedAck = true;
var inboundReceivedAck = true;
_outbound.ActorSelection(_inboundAck).Tell("ping", _outboundProbe.Ref);
Expand All @@ -208,7 +257,7 @@ public Property HeliosTransport_Should_Resolve_DNS_with_PublicHostname(IPEndPoin


return outboundReceivedAck.Label($"Expected (outbound: {RARP.For(_outbound).Provider.DefaultAddress}) to be able to successfully message and receive reply from (inbound: {RARP.For(_inbound).Provider.DefaultAddress})")
.And(inboundReceivedAck.Label($"Expected (inbound: {RARP.For(_inbound).Provider.DefaultAddress}) to be able to successfully message and receive reply from (outbound: {RARP.For(_outbound).Provider.DefaultAddress})"));
.And(inboundReceivedAck.Label($"Expected (inbound: {RARP.For(_inbound).Provider.DefaultAddress}) to be able to successfully message and receive reply from (outbound: {RARP.For(_outbound).Provider.DefaultAddress})"));
}
finally
{
Expand Down
13 changes: 13 additions & 0 deletions src/core/Akka.Remote/Configuration/Remote.conf
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,19 @@ akka {
# Otherwise, we will use IPV4.
dns-use-ipv6 = false

# If set to true, we will enforce usage of IPV4 or IPV6 addresses upon DNS resolution for host names.
# If dns-use-ipv6 = true, we will use IPV6 enforcement
# Otherwise, we will use IPV4.
# Warning: when ip family is enforced, any connection between IPV4 and IPV6 is impossible
#
# enforce-ip-family setting is used only in some special cases, when default behaviour of
# underlying sockets leads to errors
# for 08/20/2016 there are two known cases: running under Mono and in Azure WebApp
# for them we will need enforce-ip-family = true, and for Azure dns-use-ipv6 = false
# if property is missed from config, it will be set to true if Mono runtime is presented

enforce-ip-family = ""

# Enables SSL support on this transport
enable-ssl = false

Expand Down
22 changes: 17 additions & 5 deletions src/core/Akka.Remote/Transport/Helios/HeliosTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ public TcpTransportException(string message, Exception cause = null) : base(mess
internal class HeliosTransportSettings
{
internal readonly Config Config;
internal static bool IsMono = Type.GetType("Mono.Runtime") != null;

public HeliosTransportSettings(Config config)
{
Config = config;
Init();
}

static HeliosTransportSettings()
{
// Disable STDOUT logging for Helios in release mode
Expand Down Expand Up @@ -96,6 +96,8 @@ private void Init()
var configHost = Config.GetString("hostname");
var publicConfigHost = Config.GetString("public-hostname");
DnsUseIpv6 = Config.GetBoolean("dns-use-ipv6");
EnforceIpFamily = string.IsNullOrEmpty(Config.GetString("enforce-ip-family")) ?
IsMono : Config.GetBoolean("enforce-ip-family");
Hostname = string.IsNullOrEmpty(configHost) ? IPAddress.Any.ToString() : configHost;
PublicHostname = string.IsNullOrEmpty(publicConfigHost) ? Hostname : publicConfigHost;
ServerSocketWorkerPoolSize = ComputeWps(Config.GetConfig("server-socket-worker-pool"));
Expand Down Expand Up @@ -134,6 +136,8 @@ private void Init()

public bool DnsUseIpv6 { get; private set; }

public bool EnforceIpFamily { get; private set; }

/// <summary>
/// The hostname that this server binds to
/// </summary>
Expand Down Expand Up @@ -283,15 +287,19 @@ protected ClientBootstrap ClientFactory(Address remoteAddres)
{
if (InternalTransport == TransportType.Tcp)
{
var addressFamily = Settings.DnsUseIpv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;

var client = new ClientBootstrap()
.Group(_clientEventLoopGroup)
.Option(ChannelOption.SoReuseaddr, Settings.TcpReuseAddr)
.Option(ChannelOption.SoKeepalive, Settings.TcpKeepAlive)
.Option(ChannelOption.TcpNodelay, Settings.TcpNoDelay)
.Option(ChannelOption.ConnectTimeout, Settings.ConnectTimeout)
.Option(ChannelOption.AutoRead, false)
.PreferredDnsResolutionFamily(Settings.DnsUseIpv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork)
.Channel<TcpSocketChannel>()
.PreferredDnsResolutionFamily(addressFamily)
.ChannelFactory(() => Settings.EnforceIpFamily ?
new TcpSocketChannel(addressFamily):
new TcpSocketChannel())
.Handler(
new ActionChannelInitializer<TcpSocketChannel>(
channel => SetClientPipeline(channel, remoteAddres)));
Expand Down Expand Up @@ -321,15 +329,19 @@ protected ServerBootstrap ServerFactory
{
if (InternalTransport == TransportType.Tcp)
{
var addressFamily = Settings.DnsUseIpv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;

var client = new ServerBootstrap()
.Channel<TcpServerSocketChannel>()
.Group(_serverEventLoopGroup)
.Option(ChannelOption.SoReuseaddr, Settings.TcpReuseAddr)
.ChildOption(ChannelOption.SoKeepalive, Settings.TcpKeepAlive)
.ChildOption(ChannelOption.TcpNodelay, Settings.TcpNoDelay)
.ChildOption(ChannelOption.AutoRead, false)
.Option(ChannelOption.SoBacklog, Settings.Backlog)
.PreferredDnsResolutionFamily(Settings.DnsUseIpv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork)
.PreferredDnsResolutionFamily(addressFamily)
.ChannelFactory(() => Settings.EnforceIpFamily ?
new TcpServerSocketChannel(addressFamily):
new TcpServerSocketChannel())
.ChildHandler(
new ActionChannelInitializer<TcpSocketChannel>(
SetServerPipeline));
Expand Down

0 comments on commit e7edb26

Please sign in to comment.