Skip to content

Commit e999002

Browse files
committed
refactor(ipv6): address PR #262 review comments and optimize IPv6 handling
1 parent 9289efa commit e999002

9 files changed

Lines changed: 49 additions & 109 deletions

File tree

api/settings.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package api
22

33
import (
4-
"net"
54
"net/http"
65

76
"github.com/labstack/echo/v4"
@@ -97,12 +96,8 @@ func (h *Handler) GetMyPeerInfo(c echo.Context) (err error) {
9796
}(),
9897
}
9998

100-
ipV6, maskV6 := h.conf.VPNLocalIPMaskV6()
101-
if ipV6 != nil && maskV6 != nil {
102-
ipNetV6 := &net.IPNet{IP: ipV6.Mask(maskV6), Mask: maskV6}
103-
if ipv6 := config.DeriveIPv6FromPeerID(h.p2p.PeerID(), ipNetV6); ipv6 != nil {
104-
peerInfo.VPN.IPv6Addr = ipv6.String()
105-
}
99+
if ipV6, _ := h.conf.VPNLocalIPMaskV6(); ipV6 != nil {
100+
peerInfo.VPN.IPv6Addr = ipV6.String()
106101
}
107102

108103
return c.JSON(http.StatusOK, peerInfo)

awldns/awldns.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,10 @@ func (r *Resolver) dnsLocalDomainHandler(resp dns.ResponseWriter, req *dns.Msg)
219219
qtype := question.Qtype
220220
hostnameLower := strings.ToLower(hostname)
221221
mappedIP, found := cfg.directMapping[hostnameLower]
222+
mappedIPv6, foundV6 := cfg.directMappingV6[hostnameLower]
222223

223224
switch qtype {
224225
case dns.TypeA, dns.TypeANY:
225-
_, foundV6 := cfg.directMappingV6[hostnameLower]
226226
if !found {
227227
if foundV6 {
228228
continue // domain exists but no A record, return NOERROR with 0 answers (NODATA)
@@ -243,10 +243,8 @@ func (r *Resolver) dnsLocalDomainHandler(resp dns.ResponseWriter, req *dns.Msg)
243243
})
244244
}
245245
case dns.TypeAAAA:
246-
_, foundV4 := cfg.directMapping[hostnameLower]
247-
mappedIPv6, foundV6 := cfg.directMappingV6[hostnameLower]
248246
if !foundV6 {
249-
if foundV4 {
247+
if found {
250248
continue // domain exists but no AAAA record, return NOERROR with 0 answers (NODATA)
251249
}
252250
m.SetRcode(req, dns.RcodeNameError)

cmd/gomobile-lib/main.go

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package anywherelan
55
import (
66
"context"
77
"fmt"
8-
"net"
98
"os"
109

1110
"golang.zx2c4.com/wireguard/tun"
@@ -48,57 +47,6 @@ func GetConfig() string {
4847
return string(data)
4948
}
5049

51-
func GetLocalIPv6() string {
52-
if globalDataDir == "" {
53-
panic("call to GetLocalIPv6 before Setup")
54-
}
55-
56-
conf, loadConfigErr := config.LoadConfig(appType, eventbus.NewBus())
57-
if loadConfigErr != nil {
58-
return ""
59-
}
60-
61-
ipV6, _ := conf.VPNLocalIPMaskV6()
62-
if ipV6 != nil {
63-
return ipV6.String()
64-
}
65-
return ""
66-
}
67-
68-
func GetVpnNetworkAddressV4() string {
69-
if globalDataDir == "" {
70-
panic("call to GetVpnNetworkAddressV4 before Setup")
71-
}
72-
73-
conf, loadConfigErr := config.LoadConfig(appType, eventbus.NewBus())
74-
if loadConfigErr != nil {
75-
return ""
76-
}
77-
78-
_, ipNet, err := net.ParseCIDR(conf.VPNConfig.IPNet)
79-
if err == nil && ipNet != nil {
80-
return ipNet.IP.String()
81-
}
82-
return ""
83-
}
84-
85-
func GetVpnNetworkAddressV6() string {
86-
if globalDataDir == "" {
87-
panic("call to GetVpnNetworkAddressV6 before Setup")
88-
}
89-
90-
conf, loadConfigErr := config.LoadConfig(appType, eventbus.NewBus())
91-
if loadConfigErr != nil {
92-
return ""
93-
}
94-
95-
_, ipNet, err := net.ParseCIDR(conf.VPNConfig.IPNetV6)
96-
if err == nil && ipNet != nil {
97-
return ipNet.IP.String()
98-
}
99-
return ""
100-
}
101-
10250
// SocketProtector is the interface that the Android host app must implement
10351
// when it wants AWL to mark libp2p sockets so they bypass the VPN. The
10452
// implementation should call android.net.VpnService.protect() under the hood.

config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ func (c *Config) SetIdentity(key crypto.PrivKey, id peer.ID) {
380380

381381
c.P2pNode.Identity = identity
382382
c.P2pNode.PeerID = id.String()
383+
c.ensureIPv6AddressLocked()
383384
c.Save()
384385
c.Unlock()
385386
}
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
package service
1+
package config
22

33
import (
44
"net"
55
"testing"
66

7-
"github.com/anywherelan/awl/config"
87
"github.com/libp2p/go-libp2p/core/peer"
98
"github.com/stretchr/testify/assert"
109
)
1110

1211
func TestDeriveIPv6FromPeerID(t *testing.T) {
13-
_, sub6, _ := net.ParseCIDR("fd00:66::/48")
14-
pid, _ := peer.Decode("12D3KooWNstM7Xq2VvMUPnBfN1Nhm64ZzCDBa64T8wY1oD2kKk8v")
12+
_, sub6, _ := net.ParseCIDR("fd00:66:0:7915:10ba:fb1c:ef80:180c/48")
13+
pid, _ := peer.Decode("12D3KooWBG3PFoGRgbr8ckoRPCpWQjdFj5tME2XBUot5s4uGZkiL")
1514

16-
addr := config.DeriveIPv6FromPeerID(pid, sub6)
15+
addr := DeriveIPv6FromPeerID(pid, sub6)
1716
assert.NotNil(t, addr)
1817

1918
// Ensure the address has the correct prefix
@@ -23,5 +22,5 @@ func TestDeriveIPv6FromPeerID(t *testing.T) {
2322
assert.NotEqual(t, sub6.IP, addr)
2423

2524
// Ensure the last bit logic works for all-zero hashes (hard to mock hash, but we test nil case)
26-
assert.Nil(t, config.DeriveIPv6FromPeerID(pid, nil))
25+
assert.Nil(t, DeriveIPv6FromPeerID(pid, nil))
2726
}

config/network_addr.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@ import (
55
"fmt"
66
"net"
77
"net/netip"
8-
9-
"github.com/libp2p/go-libp2p/core/peer"
108
)
119

1210
const (
1311
DefaultVPNInterfaceName = "awl0"
1412
// TODO: generate subnets if this has already taken
1513
DefaultVPNNetworkSubnet = "10.66.0.1/16"
16-
DefaultVPNNetworkSubnet6 = "fd00:66:0::1/48"
14+
DefaultVPNNetworkSubnet6 = "fd00:66:0::/48"
1715
)
1816

1917
func (c *Config) VPNLocalIPMask() (net.IP, net.IPMask) {
@@ -49,15 +47,6 @@ func (c *Config) VPNLocalIPMaskV6Unlocked() (net.IP, net.IPMask) {
4947
return nil, nil
5048
}
5149

52-
if c.P2pNode.PeerID != "" {
53-
pid, err := peer.Decode(c.P2pNode.PeerID)
54-
if err == nil {
55-
if derived := DeriveIPv6FromPeerID(pid, ipNet); derived != nil {
56-
return derived, ipNet.Mask
57-
}
58-
}
59-
}
60-
6150
return localIP.To16(), ipNet.Mask
6251
}
6352

@@ -74,7 +63,7 @@ func (c *Config) NetstackDNSIP() net.IP {
7463
}
7564

7665
// computeNetstackDNSIP derives the reserved DNS server IP from the current
77-
// config snapshot: broadcast shifted down until an address is free to
66+
// config snapshot: broadcast-1, shifted down until an address is free to
7867
// assign (CheckIPUnique rejects our own IP, the broadcast address and peers).
7968
// Deterministic; returns nil when the subnet has no free address. Not thread
8069
// safe — called from setDefaults at construction only, where netstackDNSIP is

config/other.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package config
33
import (
44
"encoding/json"
55
"fmt"
6+
"net"
67
"net/url"
78
"os"
89
"path/filepath"
@@ -11,6 +12,7 @@ import (
1112
"time"
1213

1314
"github.com/ipfs/go-log/v2"
15+
"github.com/libp2p/go-libp2p/core/peer"
1416
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
1517
"github.com/moby/sys/atomicwriter"
1618
"github.com/multiformats/go-multiaddr"
@@ -237,10 +239,11 @@ func setDefaults(conf *Config, bus awlevent.Bus) {
237239
if isEmptyConfig && conf.VPNConfig.IPNetV6 == "" {
238240
conf.VPNConfig.IPNetV6 = DefaultVPNNetworkSubnet6
239241
}
242+
conf.ensureIPv6AddressLocked()
240243
if ip, _ := conf.VPNLocalIPMask(); ip == nil {
241244
conf.VPNConfig.IPNet = DefaultVPNNetworkSubnet
242245
}
243-
if ip, _ := conf.VPNLocalIPMaskV6(); ip == nil {
246+
if ip, _ := conf.VPNLocalIPMaskV6(); conf.VPNConfig.IPNetV6 != "" && ip == nil {
244247
conf.VPNConfig.IPNetV6 = DefaultVPNNetworkSubnet6
245248
}
246249
if conf.VPNConfig.InterfaceName == "" {
@@ -355,3 +358,18 @@ func writeFileAtomic(path string, data []byte) error {
355358
ChownFileIfNeeded(path)
356359
return nil
357360
}
361+
362+
func (c *Config) ensureIPv6AddressLocked() {
363+
if c.VPNConfig.IPNetV6 == "" || c.P2pNode.PeerID == "" {
364+
return
365+
}
366+
localIP, ipNet, err := net.ParseCIDR(c.VPNConfig.IPNetV6)
367+
if err == nil && localIP.Equal(ipNet.IP) {
368+
if pid, err := peer.Decode(c.P2pNode.PeerID); err == nil {
369+
if derived := DeriveIPv6FromPeerID(pid, ipNet); derived != nil {
370+
maskLen, _ := ipNet.Mask.Size()
371+
c.VPNConfig.IPNetV6 = fmt.Sprintf("%s/%d", derived.String(), maskLen)
372+
}
373+
}
374+
}
375+
}

service/auth_status.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66
"maps"
7-
"net"
87
"strings"
98
"sync"
109
"time"
@@ -167,21 +166,17 @@ func (s *AuthStatus) createPeerInfo(peer config.KnownPeer, myPeerName string, de
167166
vpnGatewayServerEnabled := s.conf.VPNGateway.ServerEnabled
168167
s.conf.RUnlock()
169168

170-
myPeerInfo := protocol.PeerStatusInfo{
169+
var ipv6Addr string
170+
if ipV6, _ := s.conf.VPNLocalIPMaskV6(); ipV6 != nil {
171+
ipv6Addr = ipV6.String()
172+
}
173+
174+
return protocol.PeerStatusInfo{
171175
Name: myPeerName,
172176
AllowUsingAsExitNode: peer.WeAllowUsingAsExitNode,
173177
VPNGatewayServerEnabled: vpnGatewayServerEnabled,
178+
IPv6Addr: ipv6Addr,
174179
}
175-
176-
ipV6, maskV6 := s.conf.VPNLocalIPMaskV6()
177-
if ipV6 != nil && maskV6 != nil {
178-
ipNetV6 := &net.IPNet{IP: ipV6.Mask(maskV6), Mask: maskV6}
179-
if ipv6 := config.DeriveIPv6FromPeerID(s.p2p.PeerID(), ipNetV6); ipv6 != nil {
180-
myPeerInfo.IPv6Addr = ipv6.String()
181-
}
182-
}
183-
184-
return myPeerInfo
185180
}
186181

187182
// processPeerStatusInfo merges the status info received from peerID into the
@@ -220,7 +215,7 @@ func (s *AuthStatus) processPeerStatusInfo(peerID string, peerInfo protocol.Peer
220215
if peer.Alias == "" {
221216
peer.Alias = s.conf.GenUniqPeerAliasUnlocked(peer.Name, peer.Alias)
222217
}
223-
if peerInfo.IPv6Addr != "" && peer.IPAddrV6 == "" {
218+
if peerInfo.IPv6Addr != "" && peer.IPAddrV6 != peerInfo.IPv6Addr {
224219
peer.IPAddrV6 = peerInfo.IPv6Addr
225220
}
226221
peer.AllowedUsingAsExitNode = peerInfo.AllowUsingAsExitNode

service/tunnel.go

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -117,22 +117,9 @@ func (t *Tunnel) StreamHandler(stream network.Stream) {
117117
peerID := stream.Conn().RemotePeer()
118118

119119
defer func() {
120-
if r := recover(); r != nil {
121-
// This typically happens if vpnPeer.inboundCh is closed concurrently
122-
// during a tunnel restart or peer removal.
123-
t.logger.Debugf("StreamHandler recovered from panic (likely channel closed) for peer %s: %v", peerID, r)
124-
}
125120
_ = stream.Close()
126121
}()
127122

128-
t.peersLock.RLock()
129-
vpnPeer, ok := t.peerIDToPeer[peerID]
130-
t.peersLock.RUnlock()
131-
if !ok {
132-
t.logger.Infof("Unknown peer %s tried to tunnel packet", peerID)
133-
return
134-
}
135-
136123
wrappedStream := &io.LimitedReader{}
137124
for {
138125
packet := t.device.GetTempPacket()
@@ -154,13 +141,23 @@ func (t *Tunnel) StreamHandler(stream network.Stream) {
154141
}
155142
packet.GatewayDir = dir
156143

144+
t.peersLock.RLock()
145+
vpnPeer, ok := t.peerIDToPeer[peerID]
146+
if !ok {
147+
t.peersLock.RUnlock()
148+
t.logger.Infof("Unknown peer %s tried to tunnel packet", peerID)
149+
t.device.PutTempPacket(packet)
150+
return
151+
}
152+
157153
select {
158154
case vpnPeer.inboundCh <- packet:
159155
default:
160156
metrics.VPNPacketsDroppedTotal.WithLabelValues("inbound_channel_full").Inc()
161157
t.logger.Warnf("inbound reader dropped packet for peer %s", peerID)
162158
t.device.PutTempPacket(packet)
163159
}
160+
t.peersLock.RUnlock()
164161
}
165162
}
166163

0 commit comments

Comments
 (0)