Browse Source

1.10.6 merge to main (#1930)

* add note about forceTcpRelay

* Create a sample systemd unit for tcp proxy

* set gitattributes for rust & cargo so hashes dont conflict on Windows

* Revert "set gitattributes for rust & cargo so hashes dont conflict on Windows"

This reverts commit 032dc5c108195f6bbc2e224f00da5b785df4b7f9.

* Turn off autocrlf for rust source

Doesn't appear to play nice well when it comes to git and vendored cargo package hashes

* Fix #1883 (#1886)

Still unknown as to why, but the call to `nc->GetProperties()` can fail
when setting a friendly name on the Windows virtual ethernet adapter.
Ensure that `ncp` is not null before continuing and accessing the device
GUID.

* Don't vendor packages for zeroidc (#1885)

* Added docker environment way to join networks (#1871)

* add StringUtils

* fix headers
use recommended headers and remove unused headers

* move extern "C"
only JNI functions need to be exported

* cleanup

* fix ANDROID-50: RESULT_ERROR_BAD_PARAMETER typo

* fix typo in log message

* fix typos in JNI method signatures

* fix typo

* fix ANDROID-51: fieldName is uninitialized

* fix ANDROID-35: memory leak

* fix missing DeleteLocalRef in loops

* update to use unique error codes

* add GETENV macro

* add LOG_TAG defines

* ANDROID-48: add ZT_jnicache.cpp

* ANDROID-48: use ZT_jnicache.cpp and remove ZT_jnilookup.cpp and ZT_jniarray.cpp

* add Event.fromInt

* add PeerRole.fromInt

* add ResultCode.fromInt

* fix ANDROID-36: issues with ResultCode

* add VirtualNetworkConfigOperation.fromInt

* fix ANDROID-40: VirtualNetworkConfigOperation out-of-sync with ZT_VirtualNetworkConfigOperation enum

* add VirtualNetworkStatus.fromInt

* fix ANDROID-37: VirtualNetworkStatus out-of-sync with ZT_VirtualNetworkStatus enum

* add VirtualNetworkType.fromInt

* make NodeStatus a plain data class

* fix ANDROID-52: synchronization bug with nodeMap

* Node init work: separate Node construction and init

* add Node.toString

* make PeerPhysicalPath a plain data class

* remove unused PeerPhysicalPath.fixed

* add array functions

* make Peer a plain data class

* make Version a plain data class

* fix ANDROID-42: copy/paste error

* fix ANDROID-49: VirtualNetworkConfig.equals is wrong

* reimplement VirtualNetworkConfig.equals

* reimplement VirtualNetworkConfig.compareTo

* add VirtualNetworkConfig.hashCode

* make VirtualNetworkConfig a plain data class

* remove unused VirtualNetworkConfig.enabled

* reimplement VirtualNetworkDNS.equals

* add VirtualNetworkDNS.hashCode

* make VirtualNetworkDNS a plain data class

* reimplement VirtualNetworkRoute.equals

* reimplement VirtualNetworkRoute.compareTo

* reimplement VirtualNetworkRoute.toString

* add VirtualNetworkRoute.hashCode

* make VirtualNetworkRoute a plain data class

* add isSocketAddressEmpty

* add addressPort

* add fromSocketAddressObject

* invert logic in a couple of places and return early

* newInetAddress and newInetSocketAddress work
allow newInetSocketAddress to return NULL if given empty address

* fix ANDROID-38: stack corruption in onSendPacketRequested

* use GETENV macro

* JniRef work
JniRef does not use callbacks struct, so remove
fix NewGlobalRef / DeleteGlobalRef mismatch

* use PRId64 macros

* switch statement work

* comments and logging

* Modifier 'public' is redundant for interface members

* NodeException can be made a checked Exception

* 'NodeException' does not define a 'serialVersionUID' field

* 'finalize()' should not be overridden
this is fine to do because ZeroTierOneService calls close() when it is done

* error handling, error reporting, asserts, logging

* simplify loadLibrary

* rename Node.networks -> Node.networkConfigs

* Windows file permissions fix (#1887)

* Allow macOS interfaces to use multiple IP addresses (#1879)

Co-authored-by: Sean OMeara <[email protected]>
Co-authored-by: Grant Limberg <[email protected]>

* Fix condition where full HELLOs might not be sent when necessary (#1877)

Co-authored-by: Grant Limberg <[email protected]>

* 1.10.4 version bumps

* Add security policy to repo (#1889)

* [+] add e2k64 arch (#1890)

* temp fix for ANDROID-56: crash inside newNetworkConfig from too many args

* 1.10.4 release notes

* Windows 1.10.4 Advanced Installer bump

* Revert "temp fix for ANDROID-56: crash inside newNetworkConfig from too many args"

This reverts commit dd627cd7f44ad623a110bb14f72d0bea72a09e30.

* actual fix for ANDROID-56: crash inside newNetworkConfig
cast all arguments to varargs functions as good style

* Fix addIp being called with applied ips (#1897)

This was getting called outside of the check for existing ips
Because of the added ifdef and a brace getting moved to the
wrong place.

```
if (! n.tap()->addIp(*ip)) {
	fprintf(stderr, "ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf));
}
WinFWHelper::newICMPRule(*ip, n.config().nwid);

```

* 1.10.5 (#1905)

* 1.10.5 bump

* 1.10.5 for Windows

* 1.10.5

* Prevent path-learning loops (#1914)

* Prevent path-learning loops

* Only allow new overwrite if not bonded

* fix binding temporary ipv6 addresses on macos (#1910)

The check code wasn't running.

I don't know why !defined(TARGET_OS_IOS) would exclude code on
desktop macOS. I did a quick search and changed it to defined(TARGET_OS_MAC).
Not 100% sure what the most correct solution there is.

You can verify the old and new versions with

`ifconfig | grep temporary`

plus

`zerotier-cli info -j` -> listeningOn

* 1.10.6 (#1929)

* 1.10.5 bump

* 1.10.6

* 1.10.6 AIP for Windows.

---------

Co-authored-by: travis laduke <[email protected]>
Co-authored-by: Grant Limberg <[email protected]>
Co-authored-by: Grant Limberg <[email protected]>
Co-authored-by: Leonardo Amaral <[email protected]>
Co-authored-by: Brenton Bostick <[email protected]>
Co-authored-by: Sean OMeara <[email protected]>
Co-authored-by: Joseph Henry <[email protected]>
Co-authored-by: Roman Peshkichev <[email protected]>
Adam Ierymenko 2 years ago
parent
commit
a872cc3418

+ 4 - 0
RELEASE-NOTES.md

@@ -1,6 +1,10 @@
 ZeroTier Release Notes
 ======
 
+# 2023-03-10 -- Version 1.10.5
+
+ * Fix for high CPU usage bug on Windows
+
 # 2023-03-07 -- Version 1.10.4
 
  * SECURITY FIX (Windows): this version fixes a file permission problem on

+ 12 - 0
debian/changelog

@@ -1,3 +1,15 @@
+zerotier-one (1.10.6) unstable; urgency=medium
+
+  * See RELEASE-NOTES.md for release notes.
+
+ -- Adam Ierymenko <[email protected]>  Tue, 21 Mar 2023 01:00:00 -0700
+
+zerotier-one (1.10.5) unstable; urgency=medium
+
+  * See RELEASE-NOTES.md for release notes.
+
+ -- Adam Ierymenko <[email protected]>  Fri, 10 Mar 2023 01:00:00 -0700
+
 zerotier-one (1.10.4) unstable; urgency=medium
 
   * See RELEASE-NOTES.md for release notes.

+ 1 - 1
ext/installfiles/mac/ZeroTier One.pkgproj

@@ -701,7 +701,7 @@
 				<key>USE_HFS+_COMPRESSION</key>
 				<false/>
 				<key>VERSION</key>
-				<string>1.10.4</string>
+				<string>1.10.6</string>
 			</dict>
 			<key>TYPE</key>
 			<integer>0</integer>

+ 12 - 12
ext/installfiles/windows/ZeroTier One.aip

@@ -1,15 +1,13 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="20.0" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
+<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="20.4.1" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
   <COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
     <ROW Name="HiddenItems" Value="ActSyncAppComponent;CPLAppletComponent;AutorunComponent;GameUxComponent;SilverlightSlnComponent;SharePointSlnComponent;AppXAppDetailsComponent;FixupComponent;AppXCapabilitiesComponent;AppXDependenciesComponent;AppXProductDetailsComponent;AppXVisualAssetsComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent;MsiXDiffComponent;MsixManifestEditorComponent"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
     <ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
-    <ROW Property="AI_CURRENT_YEAR" Value="2022" ValueLocId="-"/>
     <ROW Property="AI_EMBD_MSI_EXTR_PATH" Value="[TempFolder]" ValueLocId="-"/>
     <ROW Property="AI_EXTERNALUIUNINSTALLERNAME" MultiBuildValue="DefaultBuild:aiui"/>
     <ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/>
-    <ROW Property="AI_PACKAGING_TOOL" Value="Advanced Installer 20.0 build e4706cc2" ValueLocId="-"/>
     <ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/>
     <ROW Property="AI_PREREQ_REPAIR_ENABLED" MultiBuildValue="ExeBuild:1"/>
     <ROW Property="AI_PRODUCTNAME_ARP" Value="ZeroTier One"/>
@@ -32,10 +30,10 @@
     <ROW Property="LIMITUI" MultiBuildValue="DefaultBuild:1"/>
     <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
     <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
-    <ROW Property="ProductCode" Value="1033:{49E28DB8-25E1-4B84-81B8-D6C7C8091A40} " Type="16"/>
+    <ROW Property="ProductCode" Value="1033:{8D8ECA43-43FF-4358-84E9-4629D8855C03} " Type="16"/>
     <ROW Property="ProductLanguage" Value="1033"/>
     <ROW Property="ProductName" Value="ZeroTier One"/>
-    <ROW Property="ProductVersion" Value="1.10.2"/>
+    <ROW Property="ProductVersion" Value="1.10.6"/>
     <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
     <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
     <ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/>
@@ -70,7 +68,7 @@
     <ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86" DirectoryOptions="12"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
-    <ROW Component="AI_CustomARPName" ComponentId="{578A0B34-2AAA-4452-B725-86FC90EB4C2B}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
+    <ROW Component="AI_CustomARPName" ComponentId="{D8521222-B184-44D4-84AE-8B6549555BD8}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
     <ROW Component="AI_DisableModify" ComponentId="{46FFA8C5-A0CB-4E05-9AD3-911D543DE8CA}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
     <ROW Component="AI_ExePath" ComponentId="{8E02B36C-7A19-429B-A93E-77A9261AC918}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
     <ROW Component="APPDIR" ComponentId="{4DD7907D-D7FE-4CD6-B1A0-B5C1625F5133}" Directory_="APPDIR" Attributes="0"/>
@@ -140,10 +138,10 @@
     <ROW TimeStampUrl="http://timestamp.digicert.com" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="5" UseSha256="1" KVTenantId="5300bf3b-0eff-4a5f-a63f-821e22ed1730" KVAppId="5f94d77e-b795-41fd-afe7-ec913b03c1d3" KVName="ZeroTier-CS" KVCertName="ZT-EV-CS" KVCertVersion="442c2d6f77874ff99eed4b36f5cb401c"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
-    <ROW FirewallException="ZeroTierOneUDP9993" Direction="1" DisplayName="ZeroTier UDP/9993 In" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
-    <ROW FirewallException="ZeroTierOnex64Binary" Direction="1" DisplayName="ZeroTier x64 Binary In" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="((?zerotierone_x64.exe=2) AND ($zerotierone_x64.exe=3))" Profiles="7" AppPath="[#zerotierone_x64.exe]" Port="*" Protocol="ANY"/>
-    <ROW FirewallException="ZeroTierOnex86Binary" Direction="1" DisplayName="ZeroTier x86 Binary In" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="((?zerotierone_x86.exe=2) AND ($zerotierone_x86.exe=3))" Profiles="7" AppPath="[#zerotierone_x86.exe]" Port="*" Protocol="ANY"/>
-    <ROW FirewallException="ZeroTierUDP9993Out" Direction="2" DisplayName="ZeroTier UDP/9993 Out" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
+    <ROW FirewallException="ZeroTierOneUDP9993" Direction="1" Action="1" DisplayName="ZeroTier UDP/9993 In" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
+    <ROW FirewallException="ZeroTierOnex64Binary" Direction="1" Action="1" DisplayName="ZeroTier x64 Binary In" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="((?zerotierone_x64.exe=2) AND ($zerotierone_x64.exe=3))" Profiles="7" AppPath="[#zerotierone_x64.exe]" Port="*" Protocol="ANY"/>
+    <ROW FirewallException="ZeroTierOnex86Binary" Direction="1" Action="1" DisplayName="ZeroTier x86 Binary In" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="((?zerotierone_x86.exe=2) AND ($zerotierone_x86.exe=3))" Profiles="7" AppPath="[#zerotierone_x86.exe]" Port="*" Protocol="ANY"/>
+    <ROW FirewallException="ZeroTierUDP9993Out" Direction="2" Action="1" DisplayName="ZeroTier UDP/9993 Out" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
     <ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
@@ -296,6 +294,7 @@
     <ROW Action="AI_GetArpIconPath" Type="1" Source="aicustact.dll" Target="GetArpIconPath"/>
     <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
     <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
+    <ROW Action="AI_PRESERVE_INSTALL_TYPE" Type="65" Source="aicustact.dll" Target="PreserveInstallType"/>
     <ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/>
     <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
     <ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
@@ -398,7 +397,7 @@
     <ROW Action="TerminateUINew" Sequence="1604"/>
     <ROW Action="AI_DATA_SETTER_5" Sequence="1603"/>
     <ROW Action="LaunchUI" Condition="( NOT Installed ) AND ( ZTHEADLESS = &quot;No&quot; )" Sequence="6606"/>
-    <ROW Action="AI_DETECT_MODERNWIN" Condition="(VersionNT &gt;= 603)" Sequence="54" MsiKey="AI_DETECT_MODERNWIN"/>
+    <ROW Action="AI_DETECT_MODERNWIN" Condition="(VersionNT &gt;= 603)" Sequence="55" MsiKey="AI_DETECT_MODERNWIN"/>
     <ROW Action="AI_ResolveLocalizedCredentials" Sequence="51"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
@@ -412,6 +411,7 @@
     <ROW Action="AI_EnableDebugLog" Sequence="52"/>
     <ROW Action="AI_AppSearchEx" Sequence="101"/>
     <ROW Action="AI_ResolveLocalizedCredentials" Sequence="51"/>
+    <ROW Action="AI_PRESERVE_INSTALL_TYPE" Sequence="199"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
     <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 600) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 600) OR (MsiNTProductType = 1)) AND ((VersionNT64 &lt;&gt; 601) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 601) OR (MsiNTProductType = 1)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
@@ -528,7 +528,7 @@
     <ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
-    <ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="2" UpdateIndexInParent="0"/>
+    <ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="6" UpdateIndexInParent="0"/>
     <ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false" UpdateIndexInParent="0"/>
     <ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1" UpdateIndexInParent="0"/>
     <ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="10" UpdateIndexInParent="0"/>

+ 1 - 9
java/jni/ZT_jnicache.cpp

@@ -176,15 +176,7 @@ void setupJNICache(JavaVM *vm) {
     EXCEPTIONANDNULLCHECK(Peer_ctor = env->GetMethodID(Peer_class, "<init>", "(JIIIILcom/zerotier/sdk/PeerRole;[Lcom/zerotier/sdk/PeerPhysicalPath;)V"));
     EXCEPTIONANDNULLCHECK(Version_ctor = env->GetMethodID(Version_class, "<init>", "(III)V"));
     EXCEPTIONANDNULLCHECK(VirtualNetworkConfigListener_onNetworkConfigurationUpdated_method = env->GetMethodID(VirtualNetworkConfigListener_class, "onNetworkConfigurationUpdated", "(JLcom/zerotier/sdk/VirtualNetworkConfigOperation;Lcom/zerotier/sdk/VirtualNetworkConfig;)I"));
-
-
-    //
-    // ANDROID-56: temporarily remove parameters to prevent crashing
-    //
-//    EXCEPTIONANDNULLCHECK(VirtualNetworkConfig_ctor = env->GetMethodID(VirtualNetworkConfig_class, "<init>", "(JJLjava/lang/String;Lcom/zerotier/sdk/VirtualNetworkStatus;Lcom/zerotier/sdk/VirtualNetworkType;IZZZIJ[Ljava/net/InetSocketAddress;[Lcom/zerotier/sdk/VirtualNetworkRoute;Lcom/zerotier/sdk/VirtualNetworkDNS;)V"));
-    EXCEPTIONANDNULLCHECK(VirtualNetworkConfig_ctor = env->GetMethodID(VirtualNetworkConfig_class, "<init>", "(JJLjava/lang/String;Lcom/zerotier/sdk/VirtualNetworkStatus;Lcom/zerotier/sdk/VirtualNetworkType;IZZZ[Ljava/net/InetSocketAddress;[Lcom/zerotier/sdk/VirtualNetworkRoute;Lcom/zerotier/sdk/VirtualNetworkDNS;)V"));
-
-
+    EXCEPTIONANDNULLCHECK(VirtualNetworkConfig_ctor = env->GetMethodID(VirtualNetworkConfig_class, "<init>", "(JJLjava/lang/String;Lcom/zerotier/sdk/VirtualNetworkStatus;Lcom/zerotier/sdk/VirtualNetworkType;IZZZIJ[Ljava/net/InetSocketAddress;[Lcom/zerotier/sdk/VirtualNetworkRoute;Lcom/zerotier/sdk/VirtualNetworkDNS;)V"));
     EXCEPTIONANDNULLCHECK(VirtualNetworkDNS_ctor = env->GetMethodID(VirtualNetworkDNS_class, "<init>", "(Ljava/lang/String;Ljava/util/ArrayList;)V"));
     EXCEPTIONANDNULLCHECK(VirtualNetworkFrameListener_onVirtualNetworkFrame_method = env->GetMethodID(VirtualNetworkFrameListener_class, "onVirtualNetworkFrame", "(JJJJJ[B)V"));
     EXCEPTIONANDNULLCHECK(VirtualNetworkRoute_ctor = env->GetMethodID(VirtualNetworkRoute_class, "<init>", "(Ljava/net/InetSocketAddress;Ljava/net/InetSocketAddress;II)V"));

+ 47 - 50
java/jni/ZT_jniutils.cpp

@@ -31,7 +31,7 @@
 
 jobject createResultObject(JNIEnv *env, ZT_ResultCode code)
 {
-    jobject resultObject = env->CallStaticObjectMethod(ResultCode_class, ResultCode_fromInt_method, code);
+    jobject resultObject = env->CallStaticObjectMethod(ResultCode_class, ResultCode_fromInt_method, (jint)code);
     if(env->ExceptionCheck() || resultObject == NULL) {
         LOGE("Error creating ResultCode object");
         return NULL;
@@ -43,7 +43,7 @@ jobject createResultObject(JNIEnv *env, ZT_ResultCode code)
 
 jobject createVirtualNetworkStatus(JNIEnv *env, ZT_VirtualNetworkStatus status)
 {
-    jobject statusObject = env->CallStaticObjectMethod(VirtualNetworkStatus_class, VirtualNetworkStatus_fromInt_method, status);
+    jobject statusObject = env->CallStaticObjectMethod(VirtualNetworkStatus_class, VirtualNetworkStatus_fromInt_method, (jint)status);
     if (env->ExceptionCheck() || statusObject == NULL) {
         LOGE("Error creating VirtualNetworkStatus object");
         return NULL;
@@ -54,7 +54,7 @@ jobject createVirtualNetworkStatus(JNIEnv *env, ZT_VirtualNetworkStatus status)
 
 jobject createEvent(JNIEnv *env, ZT_Event event)
 {
-    jobject eventObject = env->CallStaticObjectMethod(Event_class, Event_fromInt_method, event);
+    jobject eventObject = env->CallStaticObjectMethod(Event_class, Event_fromInt_method, (jint)event);
     if (env->ExceptionCheck() || eventObject == NULL) {
         LOGE("Error creating Event object");
         return NULL;
@@ -65,7 +65,7 @@ jobject createEvent(JNIEnv *env, ZT_Event event)
 
 jobject createPeerRole(JNIEnv *env, ZT_PeerRole role)
 {
-    jobject peerRoleObject = env->CallStaticObjectMethod(PeerRole_class, PeerRole_fromInt_method, role);
+    jobject peerRoleObject = env->CallStaticObjectMethod(PeerRole_class, PeerRole_fromInt_method, (jint)role);
     if (env->ExceptionCheck() || peerRoleObject == NULL) {
         LOGE("Error creating PeerRole object");
         return NULL;
@@ -76,7 +76,7 @@ jobject createPeerRole(JNIEnv *env, ZT_PeerRole role)
 
 jobject createVirtualNetworkType(JNIEnv *env, ZT_VirtualNetworkType type)
 {
-    jobject vntypeObject = env->CallStaticObjectMethod(VirtualNetworkType_class, VirtualNetworkType_fromInt_method, type);
+    jobject vntypeObject = env->CallStaticObjectMethod(VirtualNetworkType_class, VirtualNetworkType_fromInt_method, (jint)type);
     if (env->ExceptionCheck() || vntypeObject == NULL) {
         LOGE("Error creating VirtualNetworkType object");
         return NULL;
@@ -87,7 +87,7 @@ jobject createVirtualNetworkType(JNIEnv *env, ZT_VirtualNetworkType type)
 
 jobject createVirtualNetworkConfigOperation(JNIEnv *env, ZT_VirtualNetworkConfigOperation op)
 {
-    jobject vnetConfigOpObject = env->CallStaticObjectMethod(VirtualNetworkConfigOperation_class, VirtualNetworkConfigOperation_fromInt_method, op);
+    jobject vnetConfigOpObject = env->CallStaticObjectMethod(VirtualNetworkConfigOperation_class, VirtualNetworkConfigOperation_fromInt_method, (jint)op);
     if (env->ExceptionCheck() || vnetConfigOpObject == NULL) {
         LOGE("Error creating VirtualNetworkConfigOperation object");
         return NULL;
@@ -113,7 +113,7 @@ jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr)
             }
 
             inetAddressObj = env->CallStaticObjectMethod(
-                InetAddress_class, InetAddress_getByAddress_method, buff);
+                InetAddress_class, InetAddress_getByAddress_method, (jbyteArray)buff);
         }
         break;
         case AF_INET:
@@ -127,7 +127,7 @@ jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr)
             }
 
             inetAddressObj = env->CallStaticObjectMethod(
-                InetAddress_class, InetAddress_getByAddress_method, buff);
+                InetAddress_class, InetAddress_getByAddress_method, (jbyteArray)buff);
         }
         break;
         default:
@@ -190,7 +190,7 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr)
 
     int port = addressPort(addr);
 
-    jobject inetSocketAddressObject = env->NewObject(InetSocketAddress_class, InetSocketAddress_ctor, inetAddressObject, port);
+    jobject inetSocketAddressObject = env->NewObject(InetSocketAddress_class, InetSocketAddress_ctor, (jobject)inetAddressObject, (jint)port);
     if(env->ExceptionCheck() || inetSocketAddressObject == NULL) {
         LOGE("Error creating InetSocketAddress object");
         return NULL;
@@ -211,10 +211,10 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
     jobject pppObject = env->NewObject(
             PeerPhysicalPath_class,
             PeerPhysicalPath_ctor,
-            addressObject,
-            ppp.lastSend,
-            ppp.lastReceive,
-            ppp.preferred);
+            (jobject)addressObject,
+            (jlong)ppp.lastSend,
+            (jlong)ppp.lastReceive,
+            (jboolean)ppp.preferred); // ANDROID-56: cast to correct size
     if(env->ExceptionCheck() || pppObject == NULL)
     {
         LOGE("Error creating PPP object");
@@ -240,13 +240,13 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
     jobject peerObject = env->NewObject(
             Peer_class,
             Peer_ctor,
-            peer.address,
-            peer.versionMajor,
-            peer.versionMinor,
-            peer.versionRev,
-            peer.latency,
-            peerRoleObj,
-            arrayObject);
+            (jlong)peer.address,
+            (jint)peer.versionMajor,
+            (jint)peer.versionMinor,
+            (jint)peer.versionRev,
+            (jint)peer.latency,
+            (jobject)peerRoleObj,
+            (jobjectArray)arrayObject);
     if(env->ExceptionCheck() || peerObject == NULL)
     {
         LOGE("Error creating Peer object");
@@ -298,23 +298,20 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
     jobject vnetConfigObj = env->NewObject(
             VirtualNetworkConfig_class,
             VirtualNetworkConfig_ctor,
-            vnetConfig.nwid,
-            vnetConfig.mac,
-            nameStr,
-            statusObject,
-            typeObject,
-            vnetConfig.mtu,
-            vnetConfig.dhcp,
-            vnetConfig.bridge,
-            vnetConfig.broadcastEnabled,
-            //
-            // ANDROID-56: temporarily remove parameters to prevent crashing
-            //
-//            vnetConfig.portError,
-//            vnetConfig.netconfRevision,
-            assignedAddrArrayObj,
-            routesArrayObj,
-            dnsObj);
+            (jlong)vnetConfig.nwid,
+            (jlong)vnetConfig.mac,
+            (jstring)nameStr,
+            (jobject)statusObject,
+            (jobject)typeObject,
+            (jint)vnetConfig.mtu,
+            (jboolean)vnetConfig.dhcp, // ANDROID-56: cast to correct size
+            (jboolean)vnetConfig.bridge, // ANDROID-56: cast to correct size
+            (jboolean)vnetConfig.broadcastEnabled, // ANDROID-56: cast to correct size
+            (jint)vnetConfig.portError,
+            (jlong)vnetConfig.netconfRevision,
+            (jobjectArray)assignedAddrArrayObj,
+            (jobjectArray)routesArrayObj,
+            (jobject)dnsObj);
     if(env->ExceptionCheck() || vnetConfigObj == NULL)
     {
         LOGE("Error creating new VirtualNetworkConfig object");
@@ -327,7 +324,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
 jobject newVersion(JNIEnv *env, int major, int minor, int rev)
 {
     // create a com.zerotier.sdk.Version object
-    jobject versionObj = env->NewObject(Version_class, Version_ctor, major, minor, rev);
+    jobject versionObj = env->NewObject(Version_class, Version_ctor, (jint)major, (jint)minor, (jint)rev);
     if(env->ExceptionCheck() || versionObj == NULL)
     {
         LOGE("Error creating new Version object");
@@ -358,10 +355,10 @@ jobject newVirtualNetworkRoute(JNIEnv *env, const ZT_VirtualNetworkRoute &route)
     jobject routeObj = env->NewObject(
             VirtualNetworkRoute_class,
             VirtualNetworkRoute_ctor,
-            targetObj,
-            viaObj,
-            route.flags,
-            route.metric);
+            (jobject)targetObj,
+            (jobject)viaObj,
+            (jint)route.flags, // ANDROID-56: cast to correct size
+            (jint)route.metric); // ANDROID-56: cast to correct size
     if(env->ExceptionCheck() || routeObj == NULL)
     {
         LOGE("Exception creating VirtualNetworkRoute");
@@ -387,7 +384,7 @@ jobject newVirtualNetworkDNS(JNIEnv *env, const ZT_VirtualNetworkDNS &dns)
         return NULL;
     }
 
-    jobject addrList = env->NewObject(ArrayList_class, ArrayList_ctor, 0);
+    jobject addrList = env->NewObject(ArrayList_class, ArrayList_ctor, (jint)0);
     if (env->ExceptionCheck() || addrList == NULL) {
         LOGE("Exception creating new ArrayList");
         return NULL;
@@ -409,7 +406,7 @@ jobject newVirtualNetworkDNS(JNIEnv *env, const ZT_VirtualNetworkDNS &dns)
             continue;
         }
 
-        env->CallBooleanMethod(addrList, ArrayList_add_method, addr);
+        env->CallBooleanMethod(addrList, ArrayList_add_method, (jobject)addr);
         if(env->ExceptionCheck())
         {
             LOGE("Exception calling add");
@@ -422,8 +419,8 @@ jobject newVirtualNetworkDNS(JNIEnv *env, const ZT_VirtualNetworkDNS &dns)
     jobject dnsObj = env->NewObject(
             VirtualNetworkDNS_class,
             VirtualNetworkDNS_ctor,
-            domain,
-            addrList);
+            (jstring)domain,
+            (jobject)addrList);
     if (env->ExceptionCheck() || dnsObj == NULL) {
         LOGE("Exception creating new VirtualNetworkDNS");
         return NULL;
@@ -450,10 +447,10 @@ jobject newNodeStatus(JNIEnv *env, const ZT_NodeStatus &status) {
     jobject nodeStatusObj = env->NewObject(
             NodeStatus_class,
             NodeStatus_ctor,
-            status.address,
-            pubIdentStr,
-            secIdentStr,
-            status.online);
+            (jlong)status.address,
+            (jstring)pubIdentStr,
+            (jstring)secIdentStr,
+            (jboolean)status.online);
     if(env->ExceptionCheck() || nodeStatusObj == NULL) {
         LOGE("Exception creating new NodeStatus");
         return NULL;

+ 15 - 15
java/jni/com_zerotierone_sdk_Node.cpp

@@ -162,7 +162,7 @@ namespace {
         jint ret = env->CallIntMethod(
             ref->configListener,
             VirtualNetworkConfigListener_onNetworkConfigurationUpdated_method,
-            (jlong)nwid, operationObject, networkConfigObject);
+            (jlong)nwid, (jobject)operationObject, (jobject)networkConfigObject);
         if (env->ExceptionCheck()) {
             LOGE("Exception calling onNetworkConfigurationUpdated");
             return -105;
@@ -213,7 +213,7 @@ namespace {
             return;
         }
 
-        env->CallVoidMethod(ref->frameListener, VirtualNetworkFrameListener_onVirtualNetworkFrame_method, (jlong)nwid, (jlong)sourceMac, (jlong)destMac, (jlong)etherType, (jlong)vlanid, dataArray);
+        env->CallVoidMethod(ref->frameListener, VirtualNetworkFrameListener_onVirtualNetworkFrame_method, (jlong)nwid, (jlong)sourceMac, (jlong)destMac, (jlong)etherType, (jlong)vlanid, (jbyteArray)dataArray);
         if (env->ExceptionCheck()) {
             LOGE("Exception calling onVirtualNetworkFrame");
             return;
@@ -254,7 +254,7 @@ namespace {
         switch (event) {
             case ZT_EVENT_UP: {
                 LOGD("Event Up");
-                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject);
+                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, (jobject)eventObject);
                 if (env->ExceptionCheck()) {
                     LOGE("Exception calling onEvent");
                     return;
@@ -263,7 +263,7 @@ namespace {
             }
             case ZT_EVENT_OFFLINE: {
                 LOGD("Event Offline");
-                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject);
+                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, (jobject)eventObject);
                 if (env->ExceptionCheck()) {
                     LOGE("Exception calling onEvent");
                     return;
@@ -272,7 +272,7 @@ namespace {
             }
             case ZT_EVENT_ONLINE: {
                 LOGD("Event Online");
-                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject);
+                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, (jobject)eventObject);
                 if (env->ExceptionCheck()) {
                     LOGE("Exception calling onEvent");
                     return;
@@ -281,7 +281,7 @@ namespace {
             }
             case ZT_EVENT_DOWN: {
                 LOGD("Event Down");
-                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject);
+                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, (jobject)eventObject);
                 if (env->ExceptionCheck()) {
                     LOGE("Exception calling onEvent");
                     return;
@@ -291,7 +291,7 @@ namespace {
             case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION: {
                 LOGV("Identity Collision");
                 // call onEvent()
-                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject);
+                env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, (jobject)eventObject);
                 if (env->ExceptionCheck()) {
                     LOGE("Exception calling onEvent");
                     return;
@@ -311,7 +311,7 @@ namespace {
                     return;
                 }
 
-                env->CallVoidMethod(ref->eventListener, EventListener_onTrace_method, messageStr);
+                env->CallVoidMethod(ref->eventListener, EventListener_onTrace_method, (jstring)messageStr);
                 if (env->ExceptionCheck()) {
                     LOGE("Exception calling onTrace");
                     return;
@@ -398,7 +398,7 @@ namespace {
 
             int retval = env->CallIntMethod(ref->dataStorePutListener,
                                DataStorePutListener_onDataStorePut_method,
-                               nameStr, bufferObj, secure);
+                               (jstring)nameStr, (jbyteArray)bufferObj, (jboolean)secure);
             if (env->ExceptionCheck()) {
                 LOGE("Exception calling onDataStorePut");
                 return;
@@ -410,7 +410,7 @@ namespace {
 
         } else {
             LOGD("JNI: Delete file: %s", p);
-            int retval = env->CallIntMethod(ref->dataStorePutListener, DataStorePutListener_onDelete_method, nameStr);
+            int retval = env->CallIntMethod(ref->dataStorePutListener, DataStorePutListener_onDelete_method, (jstring)nameStr);
             if (env->ExceptionCheck()) {
                 LOGE("Exception calling onDelete");
                 return;
@@ -500,8 +500,8 @@ namespace {
         int retval = (int)env->CallLongMethod(
                 ref->dataStoreGetListener,
                 DataStoreGetListener_onDataStoreGet_method,
-                nameStr,
-                bufferObj);
+                (jstring)nameStr,
+                (jbyteArray)bufferObj);
         if (env->ExceptionCheck()) {
             LOGE("Exception calling onDataStoreGet");
             return -106;
@@ -570,7 +570,7 @@ namespace {
             return -103;
         }
         
-        int retval = env->CallIntMethod(ref->packetSender, PacketSender_onSendPacketRequested_method, localSocket, remoteAddressObj, bufferObj, 0);
+        int retval = env->CallIntMethod(ref->packetSender, PacketSender_onSendPacketRequested_method, (jlong)localSocket, (jobject)remoteAddressObj, (jbyteArray)bufferObj, (jint)0);
         if (env->ExceptionCheck()) {
             LOGE("Exception calling onSendPacketRequested");
             return -104;
@@ -616,7 +616,7 @@ namespace {
             return true;
         }
 
-        jboolean ret = env->CallBooleanMethod(ref->pathChecker, PathChecker_onPathCheck_method, address, localSocket, remoteAddressObj);
+        jboolean ret = env->CallBooleanMethod(ref->pathChecker, PathChecker_onPathCheck_method, (jlong)address, (jlong)localSocket, (jobject)remoteAddressObj);
         if (env->ExceptionCheck()) {
             LOGE("Exception calling onPathCheck");
             return true;
@@ -656,7 +656,7 @@ namespace {
         //
         // may be NULL
         //
-        jobject sockAddressObject = env->CallObjectMethod(ref->pathChecker, PathChecker_onPathLookup_method, address, ss_family);
+        jobject sockAddressObject = env->CallObjectMethod(ref->pathChecker, PathChecker_onPathLookup_method, (jlong)address, (jint)ss_family);
         if (env->ExceptionCheck()) {
             LOGE("Unable to call onPathLookup implementation");
             return false;

+ 29 - 32
java/src/com/zerotier/sdk/VirtualNetworkConfig.java

@@ -66,12 +66,9 @@ public class VirtualNetworkConfig implements Comparable<VirtualNetworkConfig> {
 
     private final boolean broadcastEnabled;
 
-    //
-    // ANDROID-56: temporarily remove parameters to prevent crashing
-    //
-//    private final int portError;
-//
-//    private final long netconfRevision;
+    private final int portError;
+
+    private final long netconfRevision;
 
     private final InetSocketAddress[] assignedAddresses;
 
@@ -79,7 +76,7 @@ public class VirtualNetworkConfig implements Comparable<VirtualNetworkConfig> {
 
     private final VirtualNetworkDNS dns;
 
-    public VirtualNetworkConfig(long nwid, long mac, String name, VirtualNetworkStatus status, VirtualNetworkType type, int mtu, boolean dhcp, boolean bridge, boolean broadcastEnabled, InetSocketAddress[] assignedAddresses, VirtualNetworkRoute[] routes, VirtualNetworkDNS dns) {
+    public VirtualNetworkConfig(long nwid, long mac, String name, VirtualNetworkStatus status, VirtualNetworkType type, int mtu, boolean dhcp, boolean bridge, boolean broadcastEnabled, int portError, long netconfRevision, InetSocketAddress[] assignedAddresses, VirtualNetworkRoute[] routes, VirtualNetworkDNS dns) {
         this.nwid = nwid;
         this.mac = mac;
         this.name = name;
@@ -92,11 +89,11 @@ public class VirtualNetworkConfig implements Comparable<VirtualNetworkConfig> {
         this.dhcp = dhcp;
         this.bridge = bridge;
         this.broadcastEnabled = broadcastEnabled;
-//        this.portError = portError;
-//        if (netconfRevision < 0) {
-//            throw new RuntimeException("netconfRevision < 0: " + netconfRevision);
-//        }
-//        this.netconfRevision = netconfRevision;
+        this.portError = portError;
+        if (netconfRevision < 0) {
+            throw new RuntimeException("netconfRevision < 0: " + netconfRevision);
+        }
+        this.netconfRevision = netconfRevision;
         this.assignedAddresses = assignedAddresses;
         this.routes = routes;
         this.dns = dns;
@@ -104,7 +101,7 @@ public class VirtualNetworkConfig implements Comparable<VirtualNetworkConfig> {
 
     @Override
     public String toString() {
-        return "VirtualNetworkConfig(" + StringUtils.networkIdToString(nwid) + ", " + StringUtils.macAddressToString(mac) + ", " + name + ", " + status + ", " + type + ", " + mtu + ", " + dhcp + ", " + bridge + ", " + broadcastEnabled + ", " + Arrays.toString(assignedAddresses) + ", " + Arrays.toString(routes) + ", " + dns + ")";
+        return "VirtualNetworkConfig(" + StringUtils.networkIdToString(nwid) + ", " + StringUtils.macAddressToString(mac) + ", " + name + ", " + status + ", " + type + ", " + mtu + ", " + dhcp + ", " + bridge + ", " + broadcastEnabled + ", " + portError + ", " + netconfRevision + ", " + Arrays.toString(assignedAddresses) + ", " + Arrays.toString(routes) + ", " + dns + ")";
     }
 
     @Override
@@ -171,17 +168,17 @@ public class VirtualNetworkConfig implements Comparable<VirtualNetworkConfig> {
             return false;
         }
 
-//        if (this.portError != cfg.portError) {
-//            Log.i(TAG, "Port Error Changed. Old: " + this.portError + ", New: " + cfg.portError);
-//
-//            return false;
-//        }
-//
-//        if (this.netconfRevision != cfg.netconfRevision) {
-//            Log.i(TAG, "NetConfRevision Changed. Old: " + this.netconfRevision + ", New: " + cfg.netconfRevision);
-//
-//            return false;
-//        }
+        if (this.portError != cfg.portError) {
+            Log.i(TAG, "Port Error Changed. Old: " + this.portError + ", New: " + cfg.portError);
+
+            return false;
+        }
+
+        if (this.netconfRevision != cfg.netconfRevision) {
+            Log.i(TAG, "NetConfRevision Changed. Old: " + this.netconfRevision + ", New: " + cfg.netconfRevision);
+
+            return false;
+        }
 
         if (!Arrays.equals(assignedAddresses, cfg.assignedAddresses)) {
 
@@ -280,8 +277,8 @@ public class VirtualNetworkConfig implements Comparable<VirtualNetworkConfig> {
         result = 37 * result + (dhcp ? 1 : 0);
         result = 37 * result + (bridge ? 1 : 0);
         result = 37 * result + (broadcastEnabled ? 1 : 0);
-//        result = 37 * result + portError;
-//        result = 37 * result + (int) (netconfRevision ^ (netconfRevision >>> 32));
+        result = 37 * result + portError;
+        result = 37 * result + (int) (netconfRevision ^ (netconfRevision >>> 32));
         result = 37 * result + Arrays.hashCode(assignedAddresses);
         result = 37 * result + Arrays.hashCode(routes);
         result = 37 * result + (dns == null ? 0 : dns.hashCode());
@@ -362,18 +359,18 @@ public class VirtualNetworkConfig implements Comparable<VirtualNetworkConfig> {
     /**
      * If the network is in PORT_ERROR state, this is the error most recently returned by the port config callback
      */
-//    public int getPortError() {
-//        return portError;
-//    }
+    public int getPortError() {
+        return portError;
+    }
 
     /**
      * Network config revision as reported by netconf master
      *
      * <p>If this is zero, it means we're still waiting for our netconf.</p>
      */
-//    public long getNetconfRevision() {
-//        return netconfRevision;
-//    }
+    public long getNetconfRevision() {
+        return netconfRevision;
+    }
 
     /**
      * ZeroTier-assigned addresses (in {@link InetSocketAddress} objects)

+ 40 - 49
node/Peer.cpp

@@ -109,6 +109,13 @@ void Peer::received(
 						havePath = true;
 						break;
 					}
+					// If same address on same interface then don't learn unless existing path isn't alive (prevents learning loop)
+					if (_paths[i].p->address().ipsEqual(path->address()) && _paths[i].p->localSocket() == path->localSocket()) {
+						if (_paths[i].p->alive(now) && !_bond) {
+							havePath = true;
+							break;
+						}
+					}
 				} else {
 					break;
 				}
@@ -116,69 +123,37 @@ void Peer::received(
 		}
 
 		if ( (!havePath) && RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()) ) {
-
-			/**
-			 * First, fill all free slots before attempting to replace a path
-			 *  - If the above fails, attempt to replace the path that has been dead the longest
-			 *  - If there are no free slots, and no dead paths (unlikely), then replace old path most similar to new path
-			 *  - If all of the above fails to yield a suitable replacement. Replace first path found to have lower `(quality / priority)`
-			 */
-
 			if (verb == Packet::VERB_OK) {
 				Mutex::Lock _l(_paths_m);
+				unsigned int oldestPathIdx = ZT_MAX_PEER_NETWORK_PATHS;
+				unsigned int oldestPathAge = 0;
 				unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS;
-				uint64_t maxScore = 0;
-				uint64_t currScore;
-				long replacePathQuality = 0;
-				bool foundFreeSlot = false;
 
 				for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
-					currScore = 0;
 					if (_paths[i].p) {
-						// Reward dead paths
-						if (!_paths[i].p->alive(now)) {
-							currScore = _paths[i].p->age(now) / 1000;
+						// Keep track of oldest path as a last resort option
+						unsigned int currAge = _paths[i].p->age(now);
+						if (currAge > oldestPathAge) {
+							oldestPathAge = currAge;
+							oldestPathIdx = i;
 						}
-						// Reward as similarity increases
 						if (_paths[i].p->address().ipsEqual(path->address())) {
-							currScore++;
-							if (_paths[i].p->address().port() == path->address().port()) {
-								currScore++;
-								if (_paths[i].p->localSocket() == path->localSocket()) {
-									currScore++; // max score (3)
+							if (_paths[i].p->localSocket() == path->localSocket()) {
+								if (!_paths[i].p->alive(now)) {
+									replacePath = i;
+									break;
 								}
 							}
 						}
-						// If best so far, mark for replacement
-						if (currScore > maxScore) {
-							maxScore = currScore;
-							replacePath = i;
-						}
 					}
 					else {
-						foundFreeSlot = true;
 						replacePath = i;
 						break;
 					}
 				}
-				if (!foundFreeSlot) {
-					if (maxScore > 3) {
-						// Do nothing. We found a dead path and have already marked it as a candidate
-					}
-					// If we couldn't find a replacement by matching, replacing a dead path, or taking a free slot, then replace by quality
-					else if (maxScore == 0) {
-						for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
-							if (_paths[i].p) {
-								const long q = _paths[i].p->quality(now) / _paths[i].priority;
-								if (q > replacePathQuality) {
-									replacePathQuality = q;
-									replacePath = i;
-								}
-							}
-						}
-					}
-				}
 
+				// If we didn't find a good candidate then resort to replacing oldest path
+				replacePath = (replacePath == ZT_MAX_PEER_NETWORK_PATHS) ? oldestPathIdx : replacePath;
 				if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) {
 					RR->t->peerLearnedNewPath(tPtr, networkId, *this, path, packetId);
 					_paths[replacePath].lr = now;
@@ -540,11 +515,15 @@ unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
 	// let those old links expire.
 	long maxPriority = 0;
 	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
-		if (_paths[i].p)
+		if (_paths[i].p) {
 			maxPriority = std::max(_paths[i].priority,maxPriority);
-		else break;
+		}
+		else {
+			break;
+		}
 	}
 
+	bool deletionOccurred = false;
 	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
 		if (_paths[i].p) {
 			// Clean expired and reduced priority paths
@@ -554,10 +533,22 @@ unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
 					_paths[i].p->sent(now);
 					sent |= (_paths[i].p->address().ss_family == AF_INET) ? 0x1 : 0x2;
 				}
-			} else {
+			}
+			else {
 				_paths[i] = _PeerPath();
+				deletionOccurred = true;
 			}
-		} else break;
+		}
+		if (!_paths[i].p || deletionOccurred) {
+			for(unsigned int j=i;j<ZT_MAX_PEER_NETWORK_PATHS;++j) {
+				if (_paths[j].p && i != j) {
+					_paths[i] = _paths[j];
+					_paths[j] = _PeerPath();
+					break;
+				}
+			}
+			deletionOccurred = false;
+		}
 	}
 	return sent;
 }

+ 3 - 3
osdep/Binder.hpp

@@ -42,7 +42,7 @@
 
 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK)
 #include <net/if.h>
-#if ! defined(TARGET_OS_IOS)
+#if defined(TARGET_OS_OSX)
 #include <netinet6/in6_var.h>
 #endif
 #include <sys/ioctl.h>
@@ -333,7 +333,7 @@ class Binder {
 					while (ifa) {
 						if ((ifa->ifa_name) && (ifa->ifa_addr)) {
 							InetAddress ip = *(ifa->ifa_addr);
-#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK) && !defined(TARGET_OS_IOS)
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__LINUX__) && !defined(ZT_SDK) && defined(TARGET_OS_OSX)
 							// Check if the address is an IPv6 Temporary Address, macOS/BSD version
 							if (ifa->ifa_addr->sa_family == AF_INET6) {
 								struct sockaddr_in6* sa6 = (struct sockaddr_in6*)ifa->ifa_addr;
@@ -349,8 +349,8 @@ class Binder {
 
 								// if this is a temporary IPv6 address, skip to the next address
 								if (flags & IN6_IFF_TEMPORARY) {
-									char buf[64];
 #ifdef ZT_TRACE
+									char buf[64];
 									fprintf(stderr, "skip binding to temporary IPv6 address: %s\n", ip.toIpString(buf));
 #endif
 									ifa = ifa->ifa_next;

+ 11 - 7
service/OneService.cpp

@@ -2307,8 +2307,10 @@ public:
 				auto same_subnet = [ip](InetAddress i){
 					return ip->network() == i.network();
 				};
+#endif
 
 				if (std::find(n.managedIps().begin(),n.managedIps().end(),*ip) == n.managedIps().end()) {
+#ifdef __APPLE__
 					// if same subnet as a previously added address
 					if (
 						std::find_if(n.managedIps().begin(),n.managedIps().end(), same_subnet) != n.managedIps().end() ||
@@ -2322,15 +2324,17 @@ public:
 					} else {
 						newManagedIps2.push_back(*ip);
 					}
-				}
 #endif
 
-					if (!n.tap()->addIp(*ip))
-						fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf));
-
-					#ifdef __WINDOWS__
-					WinFWHelper::newICMPRule(*ip, n.config().nwid);
-					#endif
+					if (! n.tap()->addIp(*ip)) {
+						fprintf(stderr, "ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf));
+					}
+					else {
+#ifdef __WINDOWS__
+						WinFWHelper::newICMPRule(*ip, n.config().nwid);
+#endif
+					}
+				}
 			}
 
 #ifdef __APPLE__

+ 1 - 1
version.h

@@ -27,7 +27,7 @@
 /**
  * Revision
  */
-#define ZEROTIER_ONE_VERSION_REVISION 4
+#define ZEROTIER_ONE_VERSION_REVISION 6
 
 /**
  * Build version

+ 7 - 1
zerotier-one.spec

@@ -1,5 +1,5 @@
 Name:           zerotier-one
-Version:        1.10.4
+Version:        1.10.6
 Release:        1%{?dist}
 Summary:        ZeroTier network virtualization service
 
@@ -137,6 +137,12 @@ chmod 0755 $RPM_BUILD_ROOT/etc/init.d/zerotier-one
 %endif
 
 %changelog
+* Tue Mar 21 2023 Adam Ierymenko <[email protected]> - 1.10.6
+- see https://github.com/zerotier/ZeroTierOne for release notes
+
+* Sat Mar 10 2023 Adam Ierymenko <[email protected]> - 1.10.5
+- see https://github.com/zerotier/ZeroTierOne for release notes
+
 * Sat Mar 06 2023 Adam Ierymenko <[email protected]> - 1.10.4
 - see https://github.com/zerotier/ZeroTierOne for release notes