net/tstun: finish wiring IPv6 NAT support
Updates https://github.com/tailscale/corp/issues/11202 Updates ENG-991 Signed-off-by: Tom DNetto <tom@tailscale.com>
This commit is contained in:
parent
52e4f24c58
commit
da1b917575
|
@ -98,8 +98,8 @@ type Wrapper struct {
|
|||
// timeNow, if non-nil, will be used to obtain the current time.
|
||||
timeNow func() time.Time
|
||||
|
||||
// natV4Config stores the current IPv4 NAT configuration.
|
||||
natV4Config atomic.Pointer[natV4Config]
|
||||
// natConfig stores the current NAT configuration.
|
||||
natConfig atomic.Pointer[natConfig]
|
||||
|
||||
// vectorBuffer stores the oldest unconsumed packet vector from tdev. It is
|
||||
// allocated in wrap() and the underlying arrays should never grow.
|
||||
|
@ -481,14 +481,9 @@ func (t *Wrapper) sendVectorOutbound(r tunVectorReadResult) {
|
|||
t.vectorOutbound <- r
|
||||
}
|
||||
|
||||
// snatV4 does SNAT on p if it's an IPv4 packet and the destination
|
||||
// address requires a different source address.
|
||||
func (t *Wrapper) snatV4(p *packet.Parsed) {
|
||||
if p.IPVersion != 4 {
|
||||
return
|
||||
}
|
||||
|
||||
nc := t.natV4Config.Load()
|
||||
// snat does SNAT on p if the destination address requires a different source address.
|
||||
func (t *Wrapper) snat(p *packet.Parsed) {
|
||||
nc := t.natConfig.Load()
|
||||
oldSrc := p.Src.Addr()
|
||||
newSrc := nc.selectSrcIP(oldSrc, p.Dst.Addr())
|
||||
if oldSrc != newSrc {
|
||||
|
@ -496,13 +491,9 @@ func (t *Wrapper) snatV4(p *packet.Parsed) {
|
|||
}
|
||||
}
|
||||
|
||||
// dnatV4 does destination NAT on p if it's an IPv4 packet.
|
||||
func (t *Wrapper) dnatV4(p *packet.Parsed) {
|
||||
if p.IPVersion != 4 {
|
||||
return
|
||||
}
|
||||
|
||||
nc := t.natV4Config.Load()
|
||||
// dnat does destination NAT on p.
|
||||
func (t *Wrapper) dnat(p *packet.Parsed) {
|
||||
nc := t.natConfig.Load()
|
||||
oldDst := p.Dst.Addr()
|
||||
newDst := nc.mapDstIP(oldDst)
|
||||
if newDst != oldDst {
|
||||
|
@ -521,15 +512,79 @@ func findV4(addrs []netip.Prefix) netip.Addr {
|
|||
return netip.Addr{}
|
||||
}
|
||||
|
||||
// natV4Config is the configuration for IPv4 NAT.
|
||||
// findV6 returns the first Tailscale IPv6 address in addrs.
|
||||
func findV6(addrs []netip.Prefix) netip.Addr {
|
||||
for _, ap := range addrs {
|
||||
a := ap.Addr()
|
||||
if a.Is6() && tsaddr.IsTailscaleIP(a) {
|
||||
return a
|
||||
}
|
||||
}
|
||||
return netip.Addr{}
|
||||
}
|
||||
|
||||
// natConfig is the configuration for NAT.
|
||||
// It should be treated as immutable.
|
||||
//
|
||||
// The nil value is a valid configuration.
|
||||
type natV4Config struct {
|
||||
// nativeAddr is the IPv4 Tailscale Address of the current node.
|
||||
type natConfig struct {
|
||||
v4, v6 *natFamilyConfig
|
||||
}
|
||||
|
||||
func (c *natConfig) String() string {
|
||||
if c == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("natConfig{")
|
||||
fmt.Fprintf(&b, "v4: %v, ", c.v4)
|
||||
fmt.Fprintf(&b, "v6: %v", c.v6)
|
||||
b.WriteString("}")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// mapDstIP returns the destination IP to use for a packet to dst.
|
||||
// If dst is not one of the listen addresses, it is returned as-is,
|
||||
// otherwise the native address is returned.
|
||||
func (c *natConfig) mapDstIP(oldDst netip.Addr) netip.Addr {
|
||||
if c == nil {
|
||||
return oldDst
|
||||
}
|
||||
if oldDst.Is4() {
|
||||
return c.v4.mapDstIP(oldDst)
|
||||
}
|
||||
if oldDst.Is6() {
|
||||
return c.v6.mapDstIP(oldDst)
|
||||
}
|
||||
return oldDst
|
||||
}
|
||||
|
||||
// selectSrcIP returns the source IP to use for a packet to dst.
|
||||
// If the packet is not from the native address, it is returned as-is.
|
||||
func (c *natConfig) selectSrcIP(oldSrc, dst netip.Addr) netip.Addr {
|
||||
if c == nil {
|
||||
return oldSrc
|
||||
}
|
||||
if oldSrc.Is4() {
|
||||
return c.v4.selectSrcIP(oldSrc, dst)
|
||||
}
|
||||
if oldSrc.Is6() {
|
||||
return c.v6.selectSrcIP(oldSrc, dst)
|
||||
}
|
||||
return oldSrc
|
||||
}
|
||||
|
||||
// natFamilyConfig is the NAT configuration for a particular
|
||||
// address family.
|
||||
// It should be treated as immutable.
|
||||
//
|
||||
// The nil value is a valid configuration.
|
||||
type natFamilyConfig struct {
|
||||
// nativeAddr is the Tailscale Address of the current node.
|
||||
nativeAddr netip.Addr
|
||||
|
||||
// listenAddrs is the set of IPv4 addresses that should be
|
||||
// listenAddrs is the set of addresses that should be
|
||||
// mapped to the native address. These are the addresses that
|
||||
// peers will use to connect to this node.
|
||||
listenAddrs views.Map[netip.Addr, struct{}] // masqAddr -> struct{}
|
||||
|
@ -545,12 +600,12 @@ type natV4Config struct {
|
|||
dstAddrToPeerKeyMapper *table.RoutingTable
|
||||
}
|
||||
|
||||
func (c *natV4Config) String() string {
|
||||
func (c *natFamilyConfig) String() string {
|
||||
if c == nil {
|
||||
return "<nil>"
|
||||
return "natFamilyConfig(nil)"
|
||||
}
|
||||
var b strings.Builder
|
||||
b.WriteString("natV4Config{")
|
||||
b.WriteString("natFamilyConfig{")
|
||||
fmt.Fprintf(&b, "nativeAddr: %v, ", c.nativeAddr)
|
||||
fmt.Fprint(&b, "listenAddrs: [")
|
||||
|
||||
|
@ -586,7 +641,7 @@ func (c *natV4Config) String() string {
|
|||
// mapDstIP returns the destination IP to use for a packet to dst.
|
||||
// If dst is not one of the listen addresses, it is returned as-is,
|
||||
// otherwise the native address is returned.
|
||||
func (c *natV4Config) mapDstIP(oldDst netip.Addr) netip.Addr {
|
||||
func (c *natFamilyConfig) mapDstIP(oldDst netip.Addr) netip.Addr {
|
||||
if c == nil {
|
||||
return oldDst
|
||||
}
|
||||
|
@ -598,7 +653,7 @@ func (c *natV4Config) mapDstIP(oldDst netip.Addr) netip.Addr {
|
|||
|
||||
// selectSrcIP returns the source IP to use for a packet to dst.
|
||||
// If the packet is not from the native address, it is returned as-is.
|
||||
func (c *natV4Config) selectSrcIP(oldSrc, dst netip.Addr) netip.Addr {
|
||||
func (c *natFamilyConfig) selectSrcIP(oldSrc, dst netip.Addr) netip.Addr {
|
||||
if c == nil {
|
||||
return oldSrc
|
||||
}
|
||||
|
@ -615,16 +670,25 @@ func (c *natV4Config) selectSrcIP(oldSrc, dst netip.Addr) netip.Addr {
|
|||
return oldSrc
|
||||
}
|
||||
|
||||
// natV4ConfigFromWGConfig generates a natV4Config from nm.
|
||||
// If v4 NAT is not required, it returns nil.
|
||||
func natV4ConfigFromWGConfig(wcfg *wgcfg.Config) *natV4Config {
|
||||
// natConfigFromWGConfig generates a natFamilyConfig from nm,
|
||||
// for the indicated address family.
|
||||
// If NAT is not required for that address family, it returns nil.
|
||||
func natConfigFromWGConfig(wcfg *wgcfg.Config, addrFam ipproto.IPProtoVersion) *natFamilyConfig {
|
||||
if wcfg == nil {
|
||||
return nil
|
||||
}
|
||||
nativeAddr := findV4(wcfg.Addresses)
|
||||
|
||||
var nativeAddr netip.Addr
|
||||
switch addrFam {
|
||||
case ipproto.IPProtoVersion4:
|
||||
nativeAddr = findV4(wcfg.Addresses)
|
||||
case ipproto.IPProtoVersion6:
|
||||
nativeAddr = findV6(wcfg.Addresses)
|
||||
}
|
||||
if !nativeAddr.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
rt table.RoutingTableBuilder
|
||||
dstMasqAddrs map[key.NodePublic]netip.Addr
|
||||
|
@ -637,17 +701,25 @@ func natV4ConfigFromWGConfig(wcfg *wgcfg.Config) *natV4Config {
|
|||
exitNodeRequiresMasq := false // true if using an exit node and it requires masquerading
|
||||
for _, p := range wcfg.Peers {
|
||||
isExitNode := slices.Contains(p.AllowedIPs, tsaddr.AllIPv4()) || slices.Contains(p.AllowedIPs, tsaddr.AllIPv6())
|
||||
if isExitNode && p.V4MasqAddr != nil && p.V4MasqAddr.IsValid() {
|
||||
exitNodeRequiresMasq = true
|
||||
if isExitNode {
|
||||
hasMasqAddrsForFamily := false ||
|
||||
(addrFam == ipproto.IPProtoVersion4 && p.V4MasqAddr != nil && p.V4MasqAddr.IsValid()) ||
|
||||
(addrFam == ipproto.IPProtoVersion6 && p.V6MasqAddr != nil && p.V6MasqAddr.IsValid())
|
||||
if hasMasqAddrsForFamily {
|
||||
exitNodeRequiresMasq = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := range wcfg.Peers {
|
||||
p := &wcfg.Peers[i]
|
||||
var addrToUse netip.Addr
|
||||
if p.V4MasqAddr != nil && p.V4MasqAddr.IsValid() {
|
||||
if addrFam == ipproto.IPProtoVersion4 && p.V4MasqAddr != nil && p.V4MasqAddr.IsValid() {
|
||||
addrToUse = *p.V4MasqAddr
|
||||
mak.Set(&listenAddrs, addrToUse, struct{}{})
|
||||
} else if addrFam == ipproto.IPProtoVersion6 && p.V6MasqAddr != nil && p.V6MasqAddr.IsValid() {
|
||||
addrToUse = *p.V6MasqAddr
|
||||
mak.Set(&listenAddrs, addrToUse, struct{}{})
|
||||
} else if exitNodeRequiresMasq {
|
||||
addrToUse = nativeAddr
|
||||
} else {
|
||||
|
@ -659,7 +731,7 @@ func natV4ConfigFromWGConfig(wcfg *wgcfg.Config) *natV4Config {
|
|||
if len(listenAddrs) == 0 && len(dstMasqAddrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &natV4Config{
|
||||
return &natFamilyConfig{
|
||||
nativeAddr: nativeAddr,
|
||||
listenAddrs: views.MapOf(listenAddrs),
|
||||
dstMasqAddrs: views.MapOf(dstMasqAddrs),
|
||||
|
@ -668,10 +740,14 @@ func natV4ConfigFromWGConfig(wcfg *wgcfg.Config) *natV4Config {
|
|||
}
|
||||
|
||||
// SetNetMap is called when a new NetworkMap is received.
|
||||
// It currently (2023-03-01) only updates the IPv4 NAT configuration.
|
||||
func (t *Wrapper) SetWGConfig(wcfg *wgcfg.Config) {
|
||||
cfg := natV4ConfigFromWGConfig(wcfg)
|
||||
old := t.natV4Config.Swap(cfg)
|
||||
v4, v6 := natConfigFromWGConfig(wcfg, ipproto.IPProtoVersion4), natConfigFromWGConfig(wcfg, ipproto.IPProtoVersion6)
|
||||
var cfg *natConfig
|
||||
if v4 != nil || v6 != nil {
|
||||
cfg = &natConfig{v4: v4, v6: v6}
|
||||
}
|
||||
|
||||
old := t.natConfig.Swap(cfg)
|
||||
if !reflect.DeepEqual(old, cfg) {
|
||||
t.logf("nat config: %v", cfg)
|
||||
}
|
||||
|
@ -786,7 +862,7 @@ func (t *Wrapper) Read(buffs [][]byte, sizes []int, offset int) (int, error) {
|
|||
for _, data := range res.data {
|
||||
p.Decode(data[res.dataOffset:])
|
||||
|
||||
t.snatV4(p)
|
||||
t.snat(p)
|
||||
if m := t.destIPActivity.Load(); m != nil {
|
||||
if fn := m[p.Dst.Addr()]; fn != nil {
|
||||
fn()
|
||||
|
@ -843,7 +919,7 @@ func (t *Wrapper) injectedRead(res tunInjectedRead, buf []byte, offset int) (int
|
|||
p := parsedPacketPool.Get().(*packet.Parsed)
|
||||
defer parsedPacketPool.Put(p)
|
||||
p.Decode(buf[offset : offset+n])
|
||||
t.snatV4(p)
|
||||
t.snat(p)
|
||||
|
||||
if m := t.destIPActivity.Load(); m != nil {
|
||||
if fn := m[p.Dst.Addr()]; fn != nil {
|
||||
|
@ -965,7 +1041,7 @@ func (t *Wrapper) Write(buffs [][]byte, offset int) (int, error) {
|
|||
captHook := t.captureHook.Load()
|
||||
for _, buff := range buffs {
|
||||
p.Decode(buff[offset:])
|
||||
t.dnatV4(p)
|
||||
t.dnat(p)
|
||||
if !t.disableFilter {
|
||||
if t.filterPacketInboundFromWireGuard(p, captHook) != filter.Accept {
|
||||
metricPacketInDrop.Add(1)
|
||||
|
@ -1030,7 +1106,7 @@ func (t *Wrapper) InjectInboundPacketBuffer(pkt stack.PacketBufferPtr) error {
|
|||
if captHook != nil {
|
||||
captHook(capture.SynthesizedToLocal, t.now(), p.Buffer(), p.CaptureMeta)
|
||||
}
|
||||
t.dnatV4(p)
|
||||
t.dnat(p)
|
||||
|
||||
return t.InjectInboundDirect(buf, PacketStartOffset)
|
||||
}
|
||||
|
|
|
@ -608,191 +608,217 @@ func TestNATCfg(t *testing.T) {
|
|||
AllowedIPs: []netip.Prefix{
|
||||
netip.PrefixFrom(ip, ip.BitLen()),
|
||||
},
|
||||
V4MasqAddr: ptr.To(masqIP),
|
||||
}
|
||||
if masqIP.Is4() {
|
||||
p.V4MasqAddr = ptr.To(masqIP)
|
||||
} else {
|
||||
p.V6MasqAddr = ptr.To(masqIP)
|
||||
}
|
||||
p.AllowedIPs = append(p.AllowedIPs, otherAllowedIPs...)
|
||||
return p
|
||||
}
|
||||
var (
|
||||
noIP netip.Addr
|
||||
test := func(addrFam ipproto.IPProtoVersion) {
|
||||
var (
|
||||
noIP netip.Addr
|
||||
|
||||
selfNativeIP = netip.MustParseAddr("100.64.0.1")
|
||||
selfEIP1 = netip.MustParseAddr("100.64.1.1")
|
||||
selfEIP2 = netip.MustParseAddr("100.64.1.2")
|
||||
selfAddrs = []netip.Prefix{netip.PrefixFrom(selfNativeIP, selfNativeIP.BitLen())}
|
||||
selfNativeIP = netip.MustParseAddr("100.64.0.1")
|
||||
selfEIP1 = netip.MustParseAddr("100.64.1.1")
|
||||
selfEIP2 = netip.MustParseAddr("100.64.1.2")
|
||||
selfAddrs = []netip.Prefix{netip.PrefixFrom(selfNativeIP, selfNativeIP.BitLen())}
|
||||
|
||||
peer1IP = netip.MustParseAddr("100.64.0.2")
|
||||
peer2IP = netip.MustParseAddr("100.64.0.3")
|
||||
peer1IP = netip.MustParseAddr("100.64.0.2")
|
||||
peer2IP = netip.MustParseAddr("100.64.0.3")
|
||||
|
||||
subnet = netip.MustParsePrefix("192.168.0.0/24")
|
||||
subnetIP = netip.MustParseAddr("192.168.0.1")
|
||||
subnet = netip.MustParsePrefix("192.168.0.0/24")
|
||||
subnetIP = netip.MustParseAddr("192.168.0.1")
|
||||
|
||||
exitRoute = netip.MustParsePrefix("0.0.0.0/0")
|
||||
publicIP = netip.MustParseAddr("8.8.8.8")
|
||||
)
|
||||
exitRoute = netip.MustParsePrefix("0.0.0.0/0")
|
||||
publicIP = netip.MustParseAddr("8.8.8.8")
|
||||
)
|
||||
if addrFam == ipproto.IPProtoVersion6 {
|
||||
selfNativeIP = netip.MustParseAddr("fd7a:115c:a1e0::a")
|
||||
selfEIP1 = netip.MustParseAddr("fd7a:115c:a1e0::1a")
|
||||
selfEIP2 = netip.MustParseAddr("fd7a:115c:a1e0::1b")
|
||||
selfAddrs = []netip.Prefix{netip.PrefixFrom(selfNativeIP, selfNativeIP.BitLen())}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
wcfg *wgcfg.Config
|
||||
snatMap map[netip.Addr]netip.Addr // dst -> src
|
||||
dnatMap map[netip.Addr]netip.Addr
|
||||
}{
|
||||
{
|
||||
name: "no-cfg",
|
||||
wcfg: nil,
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfNativeIP,
|
||||
peer2IP: selfNativeIP,
|
||||
subnetIP: selfNativeIP,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfEIP1,
|
||||
selfEIP2: selfEIP2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single-peer-requires-nat",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, noIP),
|
||||
node(peer2IP, selfEIP1),
|
||||
peer1IP = netip.MustParseAddr("fd7a:115c:a1e0::b")
|
||||
peer2IP = netip.MustParseAddr("fd7a:115c:a1e0::c")
|
||||
|
||||
subnet = netip.MustParsePrefix("2001:db8::/32")
|
||||
subnetIP = netip.MustParseAddr("2001:db8::FFFF")
|
||||
|
||||
exitRoute = netip.MustParsePrefix("::/0")
|
||||
publicIP = netip.MustParseAddr("2001:4860:4860::8888")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
wcfg *wgcfg.Config
|
||||
snatMap map[netip.Addr]netip.Addr // dst -> src
|
||||
dnatMap map[netip.Addr]netip.Addr
|
||||
}{
|
||||
{
|
||||
name: "no-cfg",
|
||||
wcfg: nil,
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfNativeIP,
|
||||
peer2IP: selfNativeIP,
|
||||
subnetIP: selfNativeIP,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfEIP1,
|
||||
selfEIP2: selfEIP2,
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfNativeIP,
|
||||
peer2IP: selfEIP1,
|
||||
subnetIP: selfNativeIP,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfNativeIP,
|
||||
selfEIP2: selfEIP2,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple-peers-require-nat",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, selfEIP1),
|
||||
node(peer2IP, selfEIP2),
|
||||
{
|
||||
name: "single-peer-requires-nat",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, noIP),
|
||||
node(peer2IP, selfEIP1),
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfNativeIP,
|
||||
peer2IP: selfEIP1,
|
||||
subnetIP: selfNativeIP,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfNativeIP,
|
||||
selfEIP2: selfEIP2,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfEIP1,
|
||||
peer2IP: selfEIP2,
|
||||
subnetIP: selfNativeIP,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfNativeIP,
|
||||
selfEIP2: selfNativeIP,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple-peers-require-nat-with-subnet",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, selfEIP1),
|
||||
node(peer2IP, selfEIP2, subnet),
|
||||
{
|
||||
name: "multiple-peers-require-nat",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, selfEIP1),
|
||||
node(peer2IP, selfEIP2),
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfEIP1,
|
||||
peer2IP: selfEIP2,
|
||||
subnetIP: selfNativeIP,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfNativeIP,
|
||||
selfEIP2: selfNativeIP,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfEIP1,
|
||||
peer2IP: selfEIP2,
|
||||
subnetIP: selfEIP2,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfNativeIP,
|
||||
selfEIP2: selfNativeIP,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple-peers-require-nat-with-default-route",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, selfEIP1),
|
||||
node(peer2IP, selfEIP2, exitRoute),
|
||||
{
|
||||
name: "multiple-peers-require-nat-with-subnet",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, selfEIP1),
|
||||
node(peer2IP, selfEIP2, subnet),
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfEIP1,
|
||||
peer2IP: selfEIP2,
|
||||
subnetIP: selfEIP2,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfNativeIP,
|
||||
selfEIP2: selfNativeIP,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfEIP1,
|
||||
peer2IP: selfEIP2,
|
||||
publicIP: selfEIP2,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfNativeIP,
|
||||
selfEIP2: selfNativeIP,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-nat",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, noIP),
|
||||
node(peer2IP, noIP),
|
||||
{
|
||||
name: "multiple-peers-require-nat-with-default-route",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, selfEIP1),
|
||||
node(peer2IP, selfEIP2, exitRoute),
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfEIP1,
|
||||
peer2IP: selfEIP2,
|
||||
publicIP: selfEIP2,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfNativeIP,
|
||||
selfEIP2: selfNativeIP,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfNativeIP,
|
||||
peer2IP: selfNativeIP,
|
||||
subnetIP: selfNativeIP,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfEIP1,
|
||||
selfEIP2: selfEIP2,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exit-node-require-nat-peer-doesnt",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, noIP),
|
||||
node(peer2IP, selfEIP2, exitRoute),
|
||||
{
|
||||
name: "no-nat",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, noIP),
|
||||
node(peer2IP, noIP),
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfNativeIP,
|
||||
peer2IP: selfNativeIP,
|
||||
subnetIP: selfNativeIP,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP1: selfEIP1,
|
||||
selfEIP2: selfEIP2,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfNativeIP,
|
||||
peer2IP: selfEIP2,
|
||||
publicIP: selfEIP2,
|
||||
{
|
||||
name: "exit-node-require-nat-peer-doesnt",
|
||||
wcfg: &wgcfg.Config{
|
||||
Addresses: selfAddrs,
|
||||
Peers: []wgcfg.Peer{
|
||||
node(peer1IP, noIP),
|
||||
node(peer2IP, selfEIP2, exitRoute),
|
||||
},
|
||||
},
|
||||
snatMap: map[netip.Addr]netip.Addr{
|
||||
peer1IP: selfNativeIP,
|
||||
peer2IP: selfEIP2,
|
||||
publicIP: selfEIP2,
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP2: selfNativeIP,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
dnatMap: map[netip.Addr]netip.Addr{
|
||||
selfNativeIP: selfNativeIP,
|
||||
selfEIP2: selfNativeIP,
|
||||
subnetIP: subnetIP,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ncfg := natV4ConfigFromWGConfig(tc.wcfg)
|
||||
for peer, want := range tc.snatMap {
|
||||
if got := ncfg.selectSrcIP(selfNativeIP, peer); got != want {
|
||||
t.Errorf("selectSrcIP[%v]: got %v; want %v", peer, got, want)
|
||||
for _, tc := range tests {
|
||||
t.Run(fmt.Sprintf("%v/%v", addrFam, tc.name), func(t *testing.T) {
|
||||
ncfg := natConfigFromWGConfig(tc.wcfg, addrFam)
|
||||
for peer, want := range tc.snatMap {
|
||||
if got := ncfg.selectSrcIP(selfNativeIP, peer); got != want {
|
||||
t.Errorf("selectSrcIP[%v]: got %v; want %v", peer, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
for dstIP, want := range tc.dnatMap {
|
||||
if got := ncfg.mapDstIP(dstIP); got != want {
|
||||
t.Errorf("mapDstIP[%v]: got %v; want %v", dstIP, got, want)
|
||||
for dstIP, want := range tc.dnatMap {
|
||||
if got := ncfg.mapDstIP(dstIP); got != want {
|
||||
t.Errorf("mapDstIP[%v]: got %v; want %v", dstIP, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if t.Failed() {
|
||||
t.Logf("%v", ncfg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
test(ipproto.IPProtoVersion4)
|
||||
test(ipproto.IPProtoVersion6)
|
||||
}
|
||||
|
||||
// TestCaptureHook verifies that the Wrapper.captureHook callback is called
|
||||
|
|
|
@ -117,7 +117,8 @@ type CapabilityVersion int
|
|||
// - 74: 2023-09-18: Client understands NodeCapMap
|
||||
// - 75: 2023-09-12: Client understands NodeAttrDNSForwarderDisableTCPRetries
|
||||
// - 76: 2023-09-20: Client understands ExitNodeDNSResolvers for IsWireGuardOnly nodes
|
||||
const CurrentCapabilityVersion CapabilityVersion = 76
|
||||
// - 77: 2023-10-03: Client understands Peers[].SelfNodeV6MasqAddrForThisPeer
|
||||
const CurrentCapabilityVersion CapabilityVersion = 77
|
||||
|
||||
type StableID string
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ func TestOneNodeUpNoAuth(t *testing.T) {
|
|||
n1.AwaitResponding()
|
||||
n1.MustUp()
|
||||
|
||||
t.Logf("Got IP: %v", n1.AwaitIP())
|
||||
t.Logf("Got IP: %v", n1.AwaitIP4())
|
||||
n1.AwaitRunning()
|
||||
|
||||
d1.MustCleanShutdown(t)
|
||||
|
@ -130,7 +130,7 @@ func TestControlKnobs(t *testing.T) {
|
|||
n1.AwaitResponding()
|
||||
n1.MustUp()
|
||||
|
||||
t.Logf("Got IP: %v", n1.AwaitIP())
|
||||
t.Logf("Got IP: %v", n1.AwaitIP4())
|
||||
n1.AwaitRunning()
|
||||
|
||||
cmd := n1.Tailscale("debug", "control-knobs")
|
||||
|
@ -212,7 +212,7 @@ func TestStateSavedOnStart(t *testing.T) {
|
|||
n1.AwaitResponding()
|
||||
n1.MustUp()
|
||||
|
||||
t.Logf("Got IP: %v", n1.AwaitIP())
|
||||
t.Logf("Got IP: %v", n1.AwaitIP4())
|
||||
n1.AwaitRunning()
|
||||
|
||||
p1 := n1.diskPrefs()
|
||||
|
@ -271,7 +271,7 @@ func TestOneNodeUpAuth(t *testing.T) {
|
|||
if err := cmd.Run(); err != nil {
|
||||
t.Fatalf("up: %v", err)
|
||||
}
|
||||
t.Logf("Got IP: %v", n1.AwaitIP())
|
||||
t.Logf("Got IP: %v", n1.AwaitIP4())
|
||||
|
||||
n1.AwaitRunning()
|
||||
|
||||
|
@ -574,7 +574,7 @@ func TestNoControlConnWhenDown(t *testing.T) {
|
|||
|
||||
// Come up the first time.
|
||||
n1.MustUp()
|
||||
ip1 := n1.AwaitIP()
|
||||
ip1 := n1.AwaitIP4()
|
||||
n1.AwaitRunning()
|
||||
|
||||
// Then bring it down and stop the daemon.
|
||||
|
@ -590,7 +590,7 @@ func TestNoControlConnWhenDown(t *testing.T) {
|
|||
t.Fatalf("after restart, state = %q; want %q", got, want)
|
||||
}
|
||||
|
||||
ip2 := n1.AwaitIP()
|
||||
ip2 := n1.AwaitIP4()
|
||||
if ip1 != ip2 {
|
||||
t.Errorf("IPs different: %q vs %q", ip1, ip2)
|
||||
}
|
||||
|
@ -615,7 +615,7 @@ func TestOneNodeUpWindowsStyle(t *testing.T) {
|
|||
n1.AwaitResponding()
|
||||
n1.MustUp("--unattended")
|
||||
|
||||
t.Logf("Got IP: %v", n1.AwaitIP())
|
||||
t.Logf("Got IP: %v", n1.AwaitIP4())
|
||||
n1.AwaitRunning()
|
||||
|
||||
d1.MustCleanShutdown(t)
|
||||
|
@ -625,111 +625,128 @@ func TestOneNodeUpWindowsStyle(t *testing.T) {
|
|||
// tries to do bi-directional pings between them.
|
||||
func TestNATPing(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newTestEnv(t)
|
||||
registerNode := func() (*testNode, key.NodePublic) {
|
||||
n := newTestNode(t, env)
|
||||
n.StartDaemon()
|
||||
n.AwaitListening()
|
||||
n.MustUp()
|
||||
n.AwaitRunning()
|
||||
k := n.MustStatus().Self.PublicKey
|
||||
return n, k
|
||||
}
|
||||
n1, k1 := registerNode()
|
||||
n2, k2 := registerNode()
|
||||
for _, v6 := range []bool{false, true} {
|
||||
env := newTestEnv(t)
|
||||
registerNode := func() (*testNode, key.NodePublic) {
|
||||
n := newTestNode(t, env)
|
||||
n.StartDaemon()
|
||||
n.AwaitListening()
|
||||
n.MustUp()
|
||||
n.AwaitRunning()
|
||||
k := n.MustStatus().Self.PublicKey
|
||||
return n, k
|
||||
}
|
||||
n1, k1 := registerNode()
|
||||
n2, k2 := registerNode()
|
||||
|
||||
n1IP := n1.AwaitIP()
|
||||
n2IP := n2.AwaitIP()
|
||||
var n1IP, n2IP netip.Addr
|
||||
if v6 {
|
||||
n1IP = n1.AwaitIP6()
|
||||
n2IP = n2.AwaitIP6()
|
||||
} else {
|
||||
n1IP = n1.AwaitIP4()
|
||||
n2IP = n2.AwaitIP4()
|
||||
}
|
||||
|
||||
n1ExternalIP := netip.MustParseAddr("100.64.1.1")
|
||||
n2ExternalIP := netip.MustParseAddr("100.64.2.1")
|
||||
n1ExternalIP := netip.MustParseAddr("100.64.1.1")
|
||||
n2ExternalIP := netip.MustParseAddr("100.64.2.1")
|
||||
if v6 {
|
||||
n1ExternalIP = netip.MustParseAddr("fd7a:115c:a1e0::1a")
|
||||
n2ExternalIP = netip.MustParseAddr("fd7a:115c:a1e0::1b")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pairs []testcontrol.MasqueradePair
|
||||
n1SeesN2IP netip.Addr
|
||||
n2SeesN1IP netip.Addr
|
||||
}{
|
||||
{
|
||||
name: "no_nat",
|
||||
n1SeesN2IP: n2IP,
|
||||
n2SeesN1IP: n1IP,
|
||||
},
|
||||
{
|
||||
name: "n1_has_external_ip",
|
||||
pairs: []testcontrol.MasqueradePair{
|
||||
{
|
||||
Node: k1,
|
||||
Peer: k2,
|
||||
NodeMasqueradesAs: n1ExternalIP,
|
||||
},
|
||||
tests := []struct {
|
||||
name string
|
||||
pairs []testcontrol.MasqueradePair
|
||||
n1SeesN2IP netip.Addr
|
||||
n2SeesN1IP netip.Addr
|
||||
}{
|
||||
{
|
||||
name: "no_nat",
|
||||
n1SeesN2IP: n2IP,
|
||||
n2SeesN1IP: n1IP,
|
||||
},
|
||||
n1SeesN2IP: n2IP,
|
||||
n2SeesN1IP: n1ExternalIP,
|
||||
},
|
||||
{
|
||||
name: "n2_has_external_ip",
|
||||
pairs: []testcontrol.MasqueradePair{
|
||||
{
|
||||
Node: k2,
|
||||
Peer: k1,
|
||||
NodeMasqueradesAs: n2ExternalIP,
|
||||
{
|
||||
name: "n1_has_external_ip",
|
||||
pairs: []testcontrol.MasqueradePair{
|
||||
{
|
||||
Node: k1,
|
||||
Peer: k2,
|
||||
NodeMasqueradesAs: n1ExternalIP,
|
||||
},
|
||||
},
|
||||
n1SeesN2IP: n2IP,
|
||||
n2SeesN1IP: n1ExternalIP,
|
||||
},
|
||||
n1SeesN2IP: n2ExternalIP,
|
||||
n2SeesN1IP: n1IP,
|
||||
},
|
||||
{
|
||||
name: "both_have_external_ips",
|
||||
pairs: []testcontrol.MasqueradePair{
|
||||
{
|
||||
Node: k1,
|
||||
Peer: k2,
|
||||
NodeMasqueradesAs: n1ExternalIP,
|
||||
},
|
||||
{
|
||||
Node: k2,
|
||||
Peer: k1,
|
||||
NodeMasqueradesAs: n2ExternalIP,
|
||||
{
|
||||
name: "n2_has_external_ip",
|
||||
pairs: []testcontrol.MasqueradePair{
|
||||
{
|
||||
Node: k2,
|
||||
Peer: k1,
|
||||
NodeMasqueradesAs: n2ExternalIP,
|
||||
},
|
||||
},
|
||||
n1SeesN2IP: n2ExternalIP,
|
||||
n2SeesN1IP: n1IP,
|
||||
},
|
||||
n1SeesN2IP: n2ExternalIP,
|
||||
n2SeesN1IP: n1ExternalIP,
|
||||
},
|
||||
}
|
||||
{
|
||||
name: "both_have_external_ips",
|
||||
pairs: []testcontrol.MasqueradePair{
|
||||
{
|
||||
Node: k1,
|
||||
Peer: k2,
|
||||
NodeMasqueradesAs: n1ExternalIP,
|
||||
},
|
||||
{
|
||||
Node: k2,
|
||||
Peer: k1,
|
||||
NodeMasqueradesAs: n2ExternalIP,
|
||||
},
|
||||
},
|
||||
n1SeesN2IP: n2ExternalIP,
|
||||
n2SeesN1IP: n1ExternalIP,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
env.Control.SetMasqueradeAddresses(tc.pairs)
|
||||
for _, tc := range tests {
|
||||
t.Run(fmt.Sprintf("v6=%t/%v", v6, tc.name), func(t *testing.T) {
|
||||
env.Control.SetMasqueradeAddresses(tc.pairs)
|
||||
|
||||
s1 := n1.MustStatus()
|
||||
n2AsN1Peer := s1.Peer[k2]
|
||||
if got := n2AsN1Peer.TailscaleIPs[0]; got != tc.n1SeesN2IP {
|
||||
t.Fatalf("n1 sees n2 as %v; want %v", got, tc.n1SeesN2IP)
|
||||
}
|
||||
ipIdx := 0
|
||||
if v6 {
|
||||
ipIdx = 1
|
||||
}
|
||||
|
||||
s2 := n2.MustStatus()
|
||||
n1AsN2Peer := s2.Peer[k1]
|
||||
if got := n1AsN2Peer.TailscaleIPs[0]; got != tc.n2SeesN1IP {
|
||||
t.Fatalf("n2 sees n1 as %v; want %v", got, tc.n2SeesN1IP)
|
||||
}
|
||||
s1 := n1.MustStatus()
|
||||
n2AsN1Peer := s1.Peer[k2]
|
||||
if got := n2AsN1Peer.TailscaleIPs[ipIdx]; got != tc.n1SeesN2IP {
|
||||
t.Fatalf("n1 sees n2 as %v; want %v", got, tc.n1SeesN2IP)
|
||||
}
|
||||
|
||||
if err := n1.Tailscale("ping", tc.n1SeesN2IP.String()).Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s2 := n2.MustStatus()
|
||||
n1AsN2Peer := s2.Peer[k1]
|
||||
if got := n1AsN2Peer.TailscaleIPs[ipIdx]; got != tc.n2SeesN1IP {
|
||||
t.Fatalf("n2 sees n1 as %v; want %v", got, tc.n2SeesN1IP)
|
||||
}
|
||||
|
||||
if err := n1.Tailscale("ping", "-peerapi", tc.n1SeesN2IP.String()).Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := n1.Tailscale("ping", tc.n1SeesN2IP.String()).Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := n2.Tailscale("ping", tc.n2SeesN1IP.String()).Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := n1.Tailscale("ping", "-peerapi", tc.n1SeesN2IP.String()).Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := n2.Tailscale("ping", "-peerapi", tc.n2SeesN1IP.String()).Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
if err := n2.Tailscale("ping", tc.n2SeesN1IP.String()).Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := n2.Tailscale("ping", "-peerapi", tc.n2SeesN1IP.String()).Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -743,7 +760,7 @@ func TestLogoutRemovesAllPeers(t *testing.T) {
|
|||
nodes[i].StartDaemon()
|
||||
nodes[i].AwaitResponding()
|
||||
nodes[i].MustUp()
|
||||
nodes[i].AwaitIP()
|
||||
nodes[i].AwaitIP4()
|
||||
nodes[i].AwaitRunning()
|
||||
}
|
||||
expectedPeers := len(nodes) - 1
|
||||
|
@ -758,7 +775,7 @@ func TestLogoutRemovesAllPeers(t *testing.T) {
|
|||
if err := tstest.WaitFor(20*time.Second, func() error {
|
||||
return nodes[i].Ping(nodes[j])
|
||||
}); err != nil {
|
||||
t.Fatalf("ping %v -> %v: %v", nodes[i].AwaitIP(), nodes[j].AwaitIP(), err)
|
||||
t.Fatalf("ping %v -> %v: %v", nodes[i].AwaitIP4(), nodes[j].AwaitIP4(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -783,7 +800,7 @@ func TestLogoutRemovesAllPeers(t *testing.T) {
|
|||
nodes[0].MustUp() // This will create a new node
|
||||
expectedPeers++
|
||||
|
||||
nodes[0].AwaitIP()
|
||||
nodes[0].AwaitIP4()
|
||||
wantNode0PeerCount(expectedPeers) // all existing peers and the new node
|
||||
}
|
||||
|
||||
|
@ -1107,8 +1124,8 @@ func (n *testNode) MustLogOut() {
|
|||
|
||||
func (n *testNode) Ping(otherNode *testNode) error {
|
||||
t := n.env.t
|
||||
ip := otherNode.AwaitIP().String()
|
||||
t.Logf("Running ping %v (from %v)...", ip, n.AwaitIP())
|
||||
ip := otherNode.AwaitIP4().String()
|
||||
t.Logf("Running ping %v (from %v)...", ip, n.AwaitIP4())
|
||||
return n.Tailscale("ping", ip).Run()
|
||||
}
|
||||
|
||||
|
@ -1162,14 +1179,22 @@ func (n *testNode) AwaitIPs() []netip.Addr {
|
|||
return addrs
|
||||
}
|
||||
|
||||
// AwaitIP returns the IP address of n.
|
||||
func (n *testNode) AwaitIP() netip.Addr {
|
||||
// AwaitIP4 returns the IPv4 address of n.
|
||||
func (n *testNode) AwaitIP4() netip.Addr {
|
||||
t := n.env.t
|
||||
t.Helper()
|
||||
ips := n.AwaitIPs()
|
||||
return ips[0]
|
||||
}
|
||||
|
||||
// AwaitIP6 returns the IPv6 address of n.
|
||||
func (n *testNode) AwaitIP6() netip.Addr {
|
||||
t := n.env.t
|
||||
t.Helper()
|
||||
ips := n.AwaitIPs()
|
||||
return ips[1]
|
||||
}
|
||||
|
||||
// AwaitRunning waits for n to reach the IPN state "Running".
|
||||
func (n *testNode) AwaitRunning() {
|
||||
t := n.env.t
|
||||
|
|
|
@ -900,8 +900,13 @@ func (s *Server) MapResponse(req *tailcfg.MapRequest) (res *tailcfg.MapResponse,
|
|||
peerAddress := s.masquerades[p.Key][node.Key]
|
||||
s.mu.Unlock()
|
||||
if peerAddress.IsValid() {
|
||||
p.Addresses[0] = netip.PrefixFrom(peerAddress, peerAddress.BitLen())
|
||||
p.AllowedIPs[0] = netip.PrefixFrom(peerAddress, peerAddress.BitLen())
|
||||
if peerAddress.Is6() {
|
||||
p.Addresses[1] = netip.PrefixFrom(peerAddress, peerAddress.BitLen())
|
||||
p.AllowedIPs[1] = netip.PrefixFrom(peerAddress, peerAddress.BitLen())
|
||||
} else {
|
||||
p.Addresses[0] = netip.PrefixFrom(peerAddress, peerAddress.BitLen())
|
||||
p.AllowedIPs[0] = netip.PrefixFrom(peerAddress, peerAddress.BitLen())
|
||||
}
|
||||
}
|
||||
res.Peers = append(res.Peers, p)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,26 @@ package ipproto
|
|||
|
||||
import "fmt"
|
||||
|
||||
// IPProtoVersion describes the IP address version.
|
||||
type IPProtoVersion uint8
|
||||
|
||||
// Valid IPProtoVersion values.
|
||||
const (
|
||||
IPProtoVersion4 = 4
|
||||
IPProtoVersion6 = 6
|
||||
)
|
||||
|
||||
func (p IPProtoVersion) String() string {
|
||||
switch p {
|
||||
case IPProtoVersion4:
|
||||
return "IPv4"
|
||||
case IPProtoVersion6:
|
||||
return "IPv6"
|
||||
default:
|
||||
return fmt.Sprintf("IPProtoVersion-%d", int(p))
|
||||
}
|
||||
}
|
||||
|
||||
// Proto is an IP subprotocol as defined by the IANA protocol
|
||||
// numbers list
|
||||
// (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml),
|
||||
|
|
Loading…
Reference in New Issue