Skip to content

Commit fa435ce

Browse files
committed
Add IPv6 dual-stack support
- Expand TunAdapter.ConfigureInterface to accept IPv6 address - Add IPv6 address to VPN builder when available - Display peer IPv6 addresses in peer list - Add Disable IPv6 toggle in Advanced settings - Point netbird submodule to IPv6 branch
1 parent 98a2ee7 commit fa435ce

13 files changed

Lines changed: 112 additions & 13 deletions

File tree

app/src/main/java/io/netbird/client/ui/advanced/AdvancedFragment.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ private void initializeEngineConfigSwitches() {
220220
binding.switchDisableFirewall.setChecked(goPreferences.getDisableFirewall());
221221
binding.switchAllowSsh.setChecked(goPreferences.getServerSSHAllowed());
222222
binding.switchBlockInbound.setChecked(goPreferences.getBlockInbound());
223+
binding.switchDisableIpv6.setChecked(goPreferences.getDisableIPv6());
223224

224225
// Set up change listeners
225226
binding.switchDisableClientRoutes.setOnCheckedChangeListener((buttonView, isChecked) -> {
@@ -275,7 +276,16 @@ private void initializeEngineConfigSwitches() {
275276
Log.e(LOGTAG, "Failed to set block inbound", e);
276277
}
277278
});
278-
279+
280+
binding.switchDisableIpv6.setOnCheckedChangeListener((buttonView, isChecked) -> {
281+
try {
282+
goPreferences.setDisableIPv6(isChecked);
283+
goPreferences.commit();
284+
} catch (Exception e) {
285+
Log.e(LOGTAG, "Failed to set disable IPv6", e);
286+
}
287+
});
288+
279289
// Make parent layouts clickable to toggle switches (for TV remote)
280290
binding.layoutAllowSsh.setOnClickListener(v -> {
281291
binding.switchAllowSsh.toggle();
@@ -301,6 +311,10 @@ private void initializeEngineConfigSwitches() {
301311
binding.switchDisableFirewall.toggle();
302312
});
303313

314+
binding.layoutDisableIpv6.setOnClickListener(v -> {
315+
binding.switchDisableIpv6.toggle();
316+
});
317+
304318
} catch (Exception e) {
305319
Log.e(LOGTAG, "Failed to initialize engine config switches", e);
306320
}

app/src/main/java/io/netbird/client/ui/home/Peer.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
public class Peer {
44
private final Status status;
55
private final String ip;
6+
private final String ipv6;
67
private final String fqdn;
78

8-
public Peer(Status status, String ip, String fqdn) {
9+
public Peer(Status status, String ip, String ipv6, String fqdn) {
910
this.status = status;
1011
this.ip = ip;
12+
this.ipv6 = ipv6;
1113
this.fqdn = fqdn;
1214
}
1315

@@ -19,7 +21,11 @@ public String getIp() {
1921
return ip;
2022
}
2123

24+
public String getIpv6() {
25+
return ipv6;
26+
}
27+
2228
public String getFqdn() {
2329
return fqdn;
2430
}
25-
}
31+
}

app/src/main/java/io/netbird/client/ui/home/PeersAdapter.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ private static void showPopup(View view, Peer peer) {
120120
PopupMenu popup = new PopupMenu(view.getContext(), view);
121121
popup.getMenuInflater().inflate(R.menu.peer_clipboard_menu, popup.getMenu());
122122

123+
boolean hasIpv6 = peer.getIpv6() != null && !peer.getIpv6().isEmpty();
124+
popup.getMenu().findItem(R.id.copy_ipv6).setVisible(hasIpv6);
125+
123126
popup.setOnMenuItemClickListener(menuItem -> {
124127
int id = menuItem.getItemId();
125128
if (id == R.id.copy_fqdn) {
@@ -128,6 +131,9 @@ private static void showPopup(View view, Peer peer) {
128131
} else if (id == R.id.copy_ip) {
129132
copyToClipboard(view.getContext(), "IP Address", peer.getIp());
130133
return true;
134+
} else if (id == R.id.copy_ipv6) {
135+
copyToClipboard(view.getContext(), "IPv6 Address", peer.getIpv6());
136+
return true;
131137
}
132138
return false;
133139
});
@@ -165,7 +171,11 @@ public PeerViewHolder(ListItemPeerBinding binding) {
165171

166172
public void bind(Peer peer) {
167173
binding.status.setText(peer.getStatus().toString());
168-
binding.ip.setText(peer.getIp());
174+
String ipDisplay = peer.getIp();
175+
if (peer.getIpv6() != null && !peer.getIpv6().isEmpty()) {
176+
ipDisplay = ipDisplay + "\n" + peer.getIpv6();
177+
}
178+
binding.ip.setText(ipDisplay);
169179
binding.fqdn.setText(peer.getFqdn());
170180

171181
if (peer.getStatus() == Status.CONNECTED) {

app/src/main/java/io/netbird/client/ui/home/PeersFragmentViewModel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private List<Peer> getPeers(PeerInfoArray peersInfo) {
6161
}
6262

6363
status = Status.fromString(connStatus);
64-
peers.add(new Peer(status, peerInfo.getIP(), peerInfo.getFQDN()));
64+
peers.add(new Peer(status, peerInfo.getIP(), peerInfo.getIPv6(), peerInfo.getFQDN()));
6565
}
6666
return peers;
6767
}

app/src/main/java/io/netbird/client/ui/home/Resource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public Status getStatus() {
3636
}
3737

3838
public boolean isExitNode() {
39-
return address.equals("0.0.0.0/0");
39+
return address.contains("0.0.0.0/0") || address.equals("::/0");
4040
}
4141

4242
public boolean isSelected() {

app/src/main/res/layout/fragment_advanced.xml

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,55 @@
519519

520520
</LinearLayout>
521521

522+
<LinearLayout
523+
android:id="@+id/layout_disable_ipv6"
524+
android:layout_width="0dp"
525+
android:layout_height="wrap_content"
526+
android:layout_marginTop="16dp"
527+
android:background="@drawable/focus_highlight"
528+
android:clickable="true"
529+
android:focusable="true"
530+
android:focusableInTouchMode="false"
531+
android:orientation="vertical"
532+
android:padding="12dp"
533+
app:layout_constraintEnd_toEndOf="parent"
534+
app:layout_constraintStart_toStartOf="parent"
535+
app:layout_constraintTop_toBottomOf="@id/layout_disable_firewall">
536+
537+
<LinearLayout
538+
android:layout_width="match_parent"
539+
android:layout_height="wrap_content"
540+
android:gravity="center_vertical"
541+
android:orientation="horizontal">
542+
543+
<TextView
544+
android:layout_width="0dp"
545+
android:layout_height="wrap_content"
546+
android:layout_weight="1"
547+
android:text="@string/advanced_disable_ipv6"
548+
android:textColor="@color/nb_txt_light" />
549+
550+
<com.google.android.material.switchmaterial.SwitchMaterial
551+
android:id="@+id/switch_disable_ipv6"
552+
android:layout_width="wrap_content"
553+
android:layout_height="wrap_content"
554+
android:clickable="false"
555+
android:focusable="false" />
556+
557+
</LinearLayout>
558+
559+
<TextView
560+
android:layout_width="match_parent"
561+
android:layout_height="wrap_content"
562+
android:layout_marginStart="0dp"
563+
android:layout_marginTop="2dp"
564+
android:layout_marginEnd="48dp"
565+
android:text="@string/advanced_disable_ipv6_desc"
566+
android:textColor="@color/nb_txt_light"
567+
android:textSize="12sp" />
568+
569+
</LinearLayout>
570+
522571
<include
523572
android:id="@+id/layout_force_relay_connection"
524573
layout="@layout/component_switch"
@@ -528,7 +577,7 @@
528577
android:orientation="vertical"
529578
app:layout_constraintEnd_toEndOf="parent"
530579
app:layout_constraintStart_toStartOf="parent"
531-
app:layout_constraintTop_toBottomOf="@id/layout_disable_firewall" />
580+
app:layout_constraintTop_toBottomOf="@id/layout_disable_ipv6" />
532581

533582
<LinearLayout
534583
android:id="@+id/layout_theme"

app/src/main/res/menu/peer_clipboard_menu.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@
55
<item
66
android:id="@+id/copy_ip"
77
android:title="@string/peers_clipboard_copy_ip" />
8+
<item
9+
android:id="@+id/copy_ipv6"
10+
android:title="@string/peers_clipboard_copy_ipv6" />
811
</menu>

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<string name="peer_filter_connecting">Connecting</string>
4040
<string name="peer_filter_connected">Connected</string>
4141
<string name="peers_clipboard_copy_ip">Copy IP</string>
42+
<string name="peers_clipboard_copy_ipv6">Copy IPv6</string>
4243
<string name="peers_clipboard_copy_fqdn">Copy FQDN</string>
4344
<string name="peers_desc_filter">Filter</string>
4445

@@ -88,6 +89,8 @@
8889
<string name="advanced_disable_dns_desc">Disables NetBird DNS configuration on this device</string>
8990
<string name="advanced_disable_firewall">Disable firewall</string>
9091
<string name="advanced_disable_firewall_desc">Disables the NetBird firewall</string>
92+
<string name="advanced_disable_ipv6">Disable IPv6</string>
93+
<string name="advanced_disable_ipv6_desc">Disables IPv6 overlay addressing on the tunnel interface</string>
9194
<string name="advanced_allow_ssh">Enable SSH</string>
9295
<string name="advanced_allow_ssh_desc">Allows SSH connections to this device</string>
9396
<string name="advanced_block_inbound">Block inbound connections</string>

netbird

Submodule netbird updated 733 files

tool/src/main/java/io/netbird/client/tool/IFace.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,26 @@ public IFace(VPNService vpnService) {
2828
}
2929

3030
@Override
31-
public long configureInterface(String address, long mtu, String dns, String searchDomainsString, String routesString) throws Exception {
31+
public long configureInterface(String address, String addressV6, long mtu, String dns, String searchDomainsString, String routesString) throws Exception {
3232
String[] searchDomains = toSearchDomains(searchDomainsString);
3333
LinkedList<Route> routes = toRoutes(routesString);
3434

3535
InetNetwork addr = InetNetwork.parse(address);
36+
InetNetwork addrV6 = null;
37+
if (addressV6 != null && !addressV6.isEmpty()) {
38+
addrV6 = InetNetwork.parse(addressV6);
39+
}
3640
long fd = -1;
3741

3842
try {
39-
fd = createTun(addr.getAddress().getHostAddress(), addr.getMask(), (int) mtu, dns, searchDomains, routes);
43+
fd = createTun(addr.getAddress().getHostAddress(), addr.getMask(), addrV6, (int) mtu, dns, searchDomains, routes);
4044
} catch (Exception e) {
4145
Log.e(LOGTAG, "failed to create tunnel", e);
4246
}
4347

4448
// only set the currently used TUN parameters if createTun didn't throw exceptions
4549
if (fd != -1) {
46-
this.vpnService.setCurrentTUNParameters(new TUNParameters(address, mtu, dns, searchDomainsString, routesString));
50+
this.vpnService.setCurrentTUNParameters(new TUNParameters(address, addressV6, mtu, dns, searchDomainsString, routesString));
4751
}
4852

4953
return fd;
@@ -57,9 +61,13 @@ public boolean protectSocket(int fd) {
5761
return true;
5862
}
5963

60-
private int createTun(String ip, int prefixLength, int mtu, String dns, String[] searchDomains, LinkedList<Route> routes) throws Exception {
64+
private int createTun(String ip, int prefixLength, InetNetwork addrV6, int mtu, String dns, String[] searchDomains, LinkedList<Route> routes) throws Exception {
6165
VpnService.Builder builder = vpnService.getBuilder();
6266
builder.addAddress(ip, prefixLength);
67+
if (addrV6 != null) {
68+
builder.addAddress(addrV6.getAddress().getHostAddress(), addrV6.getMask());
69+
Log.d(LOGTAG, "add IPv6 address: " + addrV6.getAddress().getHostAddress() + "/" + addrV6.getMask());
70+
}
6371
builder.allowFamily(OsConstants.AF_INET);
6472
builder.allowFamily(OsConstants.AF_INET6);
6573
builder.setMtu(mtu);

0 commit comments

Comments
 (0)