JackDoan 7 месяцев назад
Родитель
Сommit
f7dd3c0ce4
2 измененных файлов с 51 добавлено и 4 удалено
  1. 14 4
      overlay/tun.go
  2. 37 0
      overlay/tun_test.go

+ 14 - 4
overlay/tun.go

@@ -3,6 +3,7 @@ package overlay
 import (
 	"crypto/rand"
 	"fmt"
+	"io"
 	"net"
 	"net/netip"
 
@@ -131,9 +132,18 @@ func selectGateway(dest netip.Prefix, gateways []netip.Prefix) (netip.Prefix, er
 	return netip.Prefix{}, fmt.Errorf("no gateway found for %v in the list of vpn networks", dest)
 }
 
-func genLinkLocal() netip.Prefix {
+// genLinkLocal generates a random IPv4 link-local address.
+// If randomizer is nil, it uses rand.Reader to find two random bytes
+func genLinkLocal(randomizer io.Reader) netip.Prefix {
+	if randomizer == nil {
+		randomizer = rand.Reader
+	}
 	octets := []byte{169, 254, 0, 0}
-	_, _ = rand.Read(octets[2:4])
+	_, _ = randomizer.Read(octets[2:4])
+	return coerceLinkLocal(octets)
+}
+
+func coerceLinkLocal(octets []byte) netip.Prefix {
 	if octets[3] == 0 {
 		octets[3] = 1 //please no .0 addresses
 	} else if octets[2] == 255 && octets[3] == 255 {
@@ -171,7 +181,7 @@ func prepareUnsafeOriginAddr(d Device, l *logrus.Logger, c *config.C, routes []R
 			return netip.PrefixFrom(out, 32)
 		}
 	}
-	return genLinkLocal()
+	return genLinkLocal(nil)
 }
 
 // prepareSnatAddr provides the address that an IPv6-only unsafe router should use to SNAT traffic before handing it to the operating system
@@ -201,5 +211,5 @@ func prepareSnatAddr(d Device, l *logrus.Logger, c *config.C) netip.Prefix {
 			return netip.PrefixFrom(out, 32)
 		}
 	}
-	return genLinkLocal()
+	return genLinkLocal(nil)
 }

+ 37 - 0
overlay/tun_test.go

@@ -0,0 +1,37 @@
+package overlay
+
+import (
+	"bytes"
+	"net/netip"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestLinkLocal(t *testing.T) {
+	r := bytes.NewReader([]byte{42, 99})
+	result := genLinkLocal(r)
+	assert.Equal(t, netip.MustParsePrefix("169.254.42.99/32"), result, "genLinkLocal with a deterministic randomizer")
+
+	result = genLinkLocal(nil)
+	assert.True(t, result.IsValid(), "genLinkLocal with nil randomizer should be valid")
+	assert.True(t, result.Addr().IsLinkLocalUnicast(), "genLinkLocal with nil randomizer should be link-local")
+
+	result = coerceLinkLocal([]byte{169, 254, 100, 50})
+	assert.Equal(t, netip.MustParsePrefix("169.254.100.50/32"), result, "coerceLinkLocal should pass through normal values")
+
+	result = coerceLinkLocal([]byte{169, 254, 0, 0})
+	assert.Equal(t, netip.MustParsePrefix("169.254.0.1/32"), result, "coerceLinkLocal should bump .0 last octet to .1")
+
+	result = coerceLinkLocal([]byte{169, 254, 255, 255})
+	assert.Equal(t, netip.MustParsePrefix("169.254.255.254/32"), result, "coerceLinkLocal should bump broadcast 255.255 to 255.254")
+
+	result = coerceLinkLocal([]byte{169, 254, 0, 1})
+	assert.Equal(t, netip.MustParsePrefix("169.254.0.1/32"), result, "coerceLinkLocal should leave .1 last octet unchanged")
+
+	result = coerceLinkLocal([]byte{169, 254, 255, 254})
+	assert.Equal(t, netip.MustParsePrefix("169.254.255.254/32"), result, "coerceLinkLocal should leave 255.254 unchanged")
+
+	result = coerceLinkLocal([]byte{169, 254, 255, 100})
+	assert.Equal(t, netip.MustParsePrefix("169.254.255.100/32"), result, "coerceLinkLocal should leave 255.100 unchanged")
+}