Compare commits

..

11 Commits

Author SHA1 Message Date
Denton Gentry
1dc90404f3 cmd/tailscale{,d}: combine into a single binary
To reduce size, combine tailscaled and tailscale into a single
binary which will figure out what it should do based on argv[0].

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2021-05-17 08:16:50 -07:00
Josh Bleecher Snyder
25df067dd0 all: adapt to opaque netaddr types
This commit is a mishmash of automated edits using gofmt:

gofmt -r 'netaddr.IPPort{IP: a, Port: b} -> netaddr.IPPortFrom(a, b)' -w .
gofmt -r 'netaddr.IPPrefix{IP: a, Port: b} -> netaddr.IPPrefixFrom(a, b)' -w .

gofmt -r 'a.IP.Is4 -> a.IP().Is4' -w .
gofmt -r 'a.IP.As16 -> a.IP().As16' -w .
gofmt -r 'a.IP.Is6 -> a.IP().Is6' -w .
gofmt -r 'a.IP.As4 -> a.IP().As4' -w .
gofmt -r 'a.IP.String -> a.IP().String' -w .

And regexps:

\w*(.*)\.Port = (.*)  ->  $1 = $1.WithPort($2)
\w*(.*)\.IP = (.*)  ->  $1 = $1.WithIP($2)

And lots of manual fixups.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2021-05-16 14:52:00 -07:00
David Anderson
4f92f405ee scripts: fix up installer script comments.
Signed-off-by: David Anderson <danderson@tailscale.com>
2021-05-14 14:13:31 -07:00
David Anderson
0e9ea9f779 scripts: detect curl vs. wget and use the right one.
Signed-off-by: David Anderson <danderson@tailscale.com>
2021-05-14 14:12:31 -07:00
David Anderson
783f125003 scripts: use codenames for ubuntu, since that's what our repo uses.
Signed-off-by: David Anderson <danderson@tailscale.com>
2021-05-14 14:12:28 -07:00
David Anderson
01a359cec9 scripts: add an install script.
The script detects one of the supported OS/version combos, and issues
the right install instructions for it.

Co-authored-by: Christine Dodrill <xe@tailscale.com>
Signed-off-by: David Anderson <danderson@tailscale.com>
2021-05-14 13:40:41 -07:00
Brad Fitzpatrick
5b52b64094 tsnet: add Tailscale-as-a-library package
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2021-05-14 12:46:42 -07:00
Josh Bleecher Snyder
6f62bbae79 cmd/tailscale: make ping --until-direct require direct connection to exit 0
If --until-direct is set, the goal is to make a direct connection.
If we failed at that, say so, and exit with an error.

RELNOTE=tailscale ping --until-direct (the default) now exits with
a non-zero exit code if no direct connection was established.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2021-05-13 15:07:19 -07:00
Avery Pennarun
6fd4e8d244 ipnlocal: fix switching users while logged in + Stopped.
This code path is very tricky since it was originally designed for the
"re-authenticate to refresh my keys" use case, which didn't want to
lose the original session even if the refresh cycle failed. This is why
it acts differently from the Logout(); Login(); case.

Maybe that's too fancy, considering that it probably never quite worked
at all, for switching between users without logging out first. But it
works now.

This was more invasive than I hoped, but the necessary fixes actually
removed several other suspicious BUG: lines from state_test.go, so I'm
pretty confident this is a significant net improvement.

Fixes tailscale/corp#1756.

Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-12 23:21:22 -04:00
Avery Pennarun
6307a9285d controlclient: update Persist.LoginName when it changes.
Well, that was anticlimactic.

Fixes tailscale/corp#461.

Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-12 23:21:11 -04:00
Avery Pennarun
285d0e3b4d ipnlocal: fix deadlock in RequestEngineStatusAndWait() error path.
If the engine was shutting down from a previous session
(e.closing=true), it would return an error code when trying to get
status. In that case, ipnlocal would never unblock any callers that
were waiting on the status.

Not sure if this ever happened in real life, but I accidentally
triggered it while writing a test.

Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
2021-05-12 23:21:11 -04:00
73 changed files with 1233 additions and 448 deletions

View File

@@ -112,13 +112,13 @@ func tailscaleIP(who *apitype.WhoIsResponse) string {
return ""
}
for _, nodeIP := range who.Node.Addresses {
if nodeIP.IP.Is4() && nodeIP.IsSingleIP() {
return nodeIP.IP.String()
if nodeIP.IP().Is4() && nodeIP.IsSingleIP() {
return nodeIP.IP().String()
}
}
for _, nodeIP := range who.Node.Addresses {
if nodeIP.IsSingleIP() {
return nodeIP.IP.String()
return nodeIP.IP().String()
}
}
return ""

View File

@@ -194,7 +194,7 @@ func discoverPeerAPIBase(ctx context.Context, ipStr string) (base string, lastSe
for _, ft := range fts {
n := ft.Node
for _, a := range n.Addresses {
if a.IP != ip {
if a.IP() != ip {
continue
}
if n.LastSeen != nil {
@@ -301,7 +301,7 @@ func runCpTargets(ctx context.Context, args []string) error {
if detail != "" {
detail = "\t" + detail
}
fmt.Printf("%s\t%s%s\n", n.Addresses[0].IP, n.ComputedName, detail)
fmt.Printf("%s\t%s%s\n", n.Addresses[0].IP(), n.ComputedName, detail)
}
return nil
}

View File

@@ -139,6 +139,9 @@ func runPing(ctx context.Context, args []string) error {
if !anyPong {
return errors.New("no reply")
}
if pingArgs.untilDirect {
return errors.New("direct connection not established")
}
return nil
}
}

View File

@@ -164,10 +164,10 @@ func prefsFromUpArgs(upArgs upArgsT, warnf logger.Logf, st *ipnstate.Status, goo
routes = append(routes, r)
}
sort.Slice(routes, func(i, j int) bool {
if routes[i].Bits != routes[j].Bits {
return routes[i].Bits < routes[j].Bits
if routes[i].Bits() != routes[j].Bits() {
return routes[i].Bits() < routes[j].Bits()
}
return routes[i].IP.Less(routes[j].IP)
return routes[i].IP().Less(routes[j].IP())
})
var exitNodeIP netaddr.IP
@@ -723,10 +723,10 @@ func fmtFlagValueArg(flagName string, val interface{}) string {
func hasExitNodeRoutes(rr []netaddr.IPPrefix) bool {
var v4, v6 bool
for _, r := range rr {
if r.Bits == 0 {
if r.IP.Is4() {
if r.Bits() == 0 {
if r.IP().Is4() {
v4 = true
} else if r.IP.Is6() {
} else if r.IP().Is6() {
v6 = true
}
}
@@ -743,7 +743,7 @@ func withoutExitNodes(rr []netaddr.IPPrefix) []netaddr.IPPrefix {
}
var out []netaddr.IPPrefix
for _, r := range rr {
if r.Bits > 0 {
if r.Bits() > 0 {
out = append(out, r)
}
}

16
cmd/tailscaled/main.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"os"
"strings"
)
func main() {
if strings.HasSuffix(os.Args[0], "tailscaled") {
tailscaled_main()
} else if strings.HasSuffix(os.Args[0], "tailscale") {
tailscale_main()
} else {
panic(os.Args[0])
}
}

View File

@@ -4,7 +4,7 @@
// The tailscale command is the Tailscale command-line client. It interacts
// with the tailscaled node agent.
package main // import "tailscale.com/cmd/tailscale"
package main // import "tailscale.com/cmd/tailscaled"
import (
"fmt"
@@ -12,10 +12,10 @@ import (
"path/filepath"
"strings"
"tailscale.com/cmd/tailscale/cli"
"tailscale.com/cmd/tailscaled/cli"
)
func main() {
func tailscale_main() {
args := os.Args[1:]
if name, _ := os.Executable(); strings.HasSuffix(filepath.Base(name), ".cgi") {
args = []string{"web", "-cgi"}

View File

@@ -101,7 +101,7 @@ var subCommands = map[string]*func([]string) error{
"debug": &debugModeFunc,
}
func main() {
func tailscaled_main() {
// We aren't very performance sensitive, and the parts that are
// performance sensitive (wireguard) try hard not to do any memory
// allocations. So let's be aggressive about garbage collection,
@@ -266,7 +266,7 @@ func run() error {
if err != nil {
return nil, err
}
if ns != nil && useNetstackForIP(ipp.IP) {
if ns != nil && useNetstackForIP(ipp.IP()) {
return ns.DialContextTCP(ctx, addr)
}
var d net.Dialer

View File

@@ -1091,7 +1091,7 @@ func ipForwardingBroken(routes []netaddr.IPPrefix, state *interfaces.State) bool
localIPs := map[netaddr.IP]bool{}
for _, addrs := range state.InterfaceIPs {
for _, pfx := range addrs {
localIPs[pfx.IP] = true
localIPs[pfx.IP()] = true
}
}
@@ -1100,10 +1100,10 @@ func ipForwardingBroken(routes []netaddr.IPPrefix, state *interfaces.State) bool
// It's possible to advertise a route to one of the local
// machine's local IPs. IP forwarding isn't required for this
// to work, so we shouldn't warn for such exports.
if r.IsSingleIP() && localIPs[r.IP] {
if r.IsSingleIP() && localIPs[r.IP()] {
continue
}
if r.IP.Is4() {
if r.IP().Is4() {
v4Routes = true
} else {
v6Routes = true

View File

@@ -86,7 +86,7 @@ func TestNewDirect(t *testing.T) {
func fakeEndpoints(ports ...uint16) (ret []tailcfg.Endpoint) {
for _, port := range ports {
ret = append(ret, tailcfg.Endpoint{
Addr: netaddr.IPPort{Port: port},
Addr: netaddr.IPPortFrom(netaddr.IP{}, port),
})
}
return

View File

@@ -147,9 +147,9 @@ const epLength = 16 + 2 // 16 byte IP address + 2 byte port
func (m *CallMeMaybe) AppendMarshal(b []byte) []byte {
ret, p := appendMsgHeader(b, TypeCallMeMaybe, v0, epLength*len(m.MyNumber))
for _, ipp := range m.MyNumber {
a := ipp.IP.As16()
a := ipp.IP().As16()
copy(p[:], a[:])
binary.BigEndian.PutUint16(p[16:], ipp.Port)
binary.BigEndian.PutUint16(p[16:], ipp.Port())
p = p[epLength:]
}
return ret
@@ -164,10 +164,9 @@ func parseCallMeMaybe(ver uint8, p []byte) (m *CallMeMaybe, err error) {
for len(p) > 0 {
var a [16]byte
copy(a[:], p)
m.MyNumber = append(m.MyNumber, netaddr.IPPort{
IP: netaddr.IPFrom16(a),
Port: binary.BigEndian.Uint16(p[16:18]),
})
m.MyNumber = append(m.MyNumber, netaddr.IPPortFrom(
netaddr.IPFrom16(a),
binary.BigEndian.Uint16(p[16:18])))
p = p[epLength:]
}
return m, nil
@@ -187,9 +186,9 @@ const pongLen = 12 + 16 + 2
func (m *Pong) AppendMarshal(b []byte) []byte {
ret, d := appendMsgHeader(b, TypePong, v0, pongLen)
d = d[copy(d, m.TxID[:]):]
ip16 := m.Src.IP.As16()
ip16 := m.Src.IP().As16()
d = d[copy(d, ip16[:]):]
binary.BigEndian.PutUint16(d, m.Src.Port)
binary.BigEndian.PutUint16(d, m.Src.Port())
return ret
}
@@ -201,10 +200,10 @@ func parsePong(ver uint8, p []byte) (m *Pong, err error) {
copy(m.TxID[:], p)
p = p[12:]
m.Src.IP, _ = netaddr.FromStdIP(net.IP(p[:16]))
srcIP, _ := netaddr.FromStdIP(net.IP(p[:16]))
p = p[16:]
m.Src.Port = binary.BigEndian.Uint16(p)
port := binary.BigEndian.Uint16(p)
m.Src = netaddr.IPPortFrom(srcIP, port)
return m, nil
}

4
go.mod
View File

@@ -40,10 +40,10 @@ require (
golang.zx2c4.com/wireguard/windows v0.1.2-0.20201113162609-9b85be97fdf8
gopkg.in/yaml.v2 v2.2.8 // indirect
honnef.co/go/tools v0.1.0
inet.af/netaddr v0.0.0-20210511181906-37180328850c
inet.af/netaddr v0.0.0-20210515010201-ad03edc7c841
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
inet.af/peercred v0.0.0-20210302202138-56e694897155
inet.af/wf v0.0.0-20210424212123-eaa011a774a4
inet.af/wf v0.0.0-20210516214145-a5343001b756
rsc.io/goversion v1.2.0
)

6
go.sum
View File

@@ -258,11 +258,17 @@ inet.af/netaddr v0.0.0-20210508014949-da1c2a70a83d h1:9tuJMxDV7THGfXWirKBD/v9rbs
inet.af/netaddr v0.0.0-20210508014949-da1c2a70a83d/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
inet.af/netaddr v0.0.0-20210511181906-37180328850c h1:rzDy/tC8LjEdN94+i0Bu22tTo/qE9cvhKyfD0HMU0NU=
inet.af/netaddr v0.0.0-20210511181906-37180328850c/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
inet.af/netaddr v0.0.0-20210515010201-ad03edc7c841 h1:2HpK+rC0Arcu98JukIlyVfEaE2OsvtmBFc8rs/2SJYs=
inet.af/netaddr v0.0.0-20210515010201-ad03edc7c841/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22 h1:DNtszwGa6w76qlIr+PbPEnlBJdiRV8SaxeigOy0q1gg=
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22/go.mod h1:GVx+5OZtbG4TVOW5ilmyRZAZXr1cNwfqUEkTOtWK0PM=
inet.af/peercred v0.0.0-20210302202138-56e694897155 h1:KojYNEYqDkZ2O3LdyTstR1l13L3ePKTIEM2h7ONkfkE=
inet.af/peercred v0.0.0-20210302202138-56e694897155/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU=
inet.af/wf v0.0.0-20210424212123-eaa011a774a4 h1:g1VVXY1xRKoO17aKY3g9KeJxDW0lGx1n2Y+WPSWkOL8=
inet.af/wf v0.0.0-20210424212123-eaa011a774a4/go.mod h1:56/0QVlZ4NmPRh1QuU2OfrKqjSgt5P39R534gD2JMpQ=
inet.af/wf v0.0.0-20210515021317-09f8efa8ac30 h1:TLxVVv7rmErJW7l81tbbR2BkOIYBI3YdxbJbEs/HJt8=
inet.af/wf v0.0.0-20210515021317-09f8efa8ac30/go.mod h1:ViGMZRA6+RA318D7GCncrjv5gHUrPYrNDejjU12tikA=
inet.af/wf v0.0.0-20210516214145-a5343001b756 h1:muIT3C1rH3/xpvIH8blKkMvhctV7F+OtZqs7kcwHDBQ=
inet.af/wf v0.0.0-20210516214145-a5343001b756/go.mod h1:ViGMZRA6+RA318D7GCncrjv5gHUrPYrNDejjU12tikA=
rsc.io/goversion v1.2.0 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w=
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=

View File

@@ -33,7 +33,7 @@ func getVal() []interface{} {
return []interface{}{
&wgcfg.Config{
Name: "foo",
Addresses: []netaddr.IPPrefix{{Bits: 5, IP: netaddr.IPFrom16([16]byte{3: 3})}},
Addresses: []netaddr.IPPrefix{netaddr.IPPrefixFrom(netaddr.IPFrom16([16]byte{3: 3}), 5)},
Peers: []wgcfg.Peer{
{
Endpoints: wgcfg.Endpoints{

View File

@@ -356,14 +356,14 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
var tailAddr4 string
var tailscaleIPs = make([]netaddr.IP, 0, len(p.Addresses))
for _, addr := range p.Addresses {
if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.IP) {
if addr.IP.Is4() && tailAddr4 == "" {
if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.IP()) {
if addr.IP().Is4() && tailAddr4 == "" {
// The peer struct previously only allowed a single
// Tailscale IP address. For compatibility for a few releases starting
// with 1.8, keep it pulled out as IPv4-only for a bit.
tailAddr4 = addr.IP.String()
tailAddr4 = addr.IP().String()
}
tailscaleIPs = append(tailscaleIPs, addr.IP)
tailscaleIPs = append(tailscaleIPs, addr.IP())
}
}
sb.AddPeer(key.Public(p.Key), &ipnstate.PeerStatus{
@@ -390,10 +390,10 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
func (b *LocalBackend) WhoIs(ipp netaddr.IPPort) (n *tailcfg.Node, u tailcfg.UserProfile, ok bool) {
b.mu.Lock()
defer b.mu.Unlock()
n, ok = b.nodeByAddr[ipp.IP]
n, ok = b.nodeByAddr[ipp.IP()]
if !ok {
var ip netaddr.IP
if ipp.Port != 0 {
if ipp.Port() != 0 {
ip, ok = b.e.WhoIsIPPort(ipp)
}
if !ok {
@@ -434,14 +434,15 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
}
return
}
if st.LoginFinished != nil {
b.mu.Lock()
wasBlocked := b.blocked
b.mu.Unlock()
if st.LoginFinished != nil && wasBlocked {
// Auth completed, unblock the engine
b.blockEngineUpdates(false)
b.authReconfig()
b.EditPrefs(&ipn.MaskedPrefs{
LoggedOutSet: true,
Prefs: ipn.Prefs{LoggedOut: false},
})
b.send(ipn.Notify{LoginFinished: &empty.Message{}})
}
@@ -480,11 +481,15 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
b.authURL = st.URL
b.authURLSticky = st.URL
}
if b.state == ipn.NeedsLogin {
if !b.prefs.WantRunning {
if wasBlocked && st.LoginFinished != nil {
// Interactive login finished successfully (URL visited).
// After an interactive login, the user always wants
// WantRunning.
if !b.prefs.WantRunning || b.prefs.LoggedOut {
prefsChanged = true
}
b.prefs.WantRunning = true
b.prefs.LoggedOut = false
}
// Prefs will be written out; this is not safe unless locked or cloned.
if prefsChanged {
@@ -547,7 +552,7 @@ func (b *LocalBackend) findExitNodeIDLocked(nm *netmap.NetworkMap) (prefsChanged
for _, peer := range nm.Peers {
for _, addr := range peer.Addresses {
if !addr.IsSingleIP() || addr.IP != b.prefs.ExitNodeIP {
if !addr.IsSingleIP() || addr.IP() != b.prefs.ExitNodeIP {
continue
}
// Found the node being referenced, upgrade prefs to
@@ -886,7 +891,7 @@ func (b *LocalBackend) updateFilter(netMap *netmap.NetworkMap, prefs *ipn.Prefs)
}
if prefs != nil {
for _, r := range prefs.AdvertiseRoutes {
if r.Bits == 0 {
if r.Bits() == 0 {
// When offering a default route to the world, we
// filter out locally reachable LANs, so that the
// default route effectively appears to be a "guest
@@ -954,13 +959,13 @@ var removeFromDefaultRoute = []netaddr.IPPrefix{
func interfaceRoutes() (ips *netaddr.IPSet, hostIPs []netaddr.IP, err error) {
var b netaddr.IPSetBuilder
if err := interfaces.ForeachInterfaceAddress(func(_ interfaces.Interface, pfx netaddr.IPPrefix) {
if tsaddr.IsTailscaleIP(pfx.IP) {
if tsaddr.IsTailscaleIP(pfx.IP()) {
return
}
if pfx.IsSingleIP() {
return
}
hostIPs = append(hostIPs, pfx.IP)
hostIPs = append(hostIPs, pfx.IP())
b.AddPrefix(pfx)
}); err != nil {
return nil, nil, err
@@ -1746,10 +1751,10 @@ func (b *LocalBackend) authReconfig() {
// https://github.com/tailscale/tailscale/issues/1152
// tracks adding the right capability reporting to
// enable AAAA in MagicDNS.
if addr.IP.Is6() {
if addr.IP().Is6() {
continue
}
ips = append(ips, addr.IP)
ips = append(ips, addr.IP())
}
dcfg.Hosts[fqdn] = ips
}
@@ -1804,10 +1809,7 @@ func parseResolver(cfg tailcfg.DNSResolver) (netaddr.IPPort, error) {
if err != nil {
return netaddr.IPPort{}, fmt.Errorf("[unexpected] non-IP resolver %q", cfg.Addr)
}
return netaddr.IPPort{
IP: ip,
Port: 53,
}, nil
return netaddr.IPPortFrom(ip, 53), nil
}
// tailscaleVarRoot returns the root directory of Tailscale's writable
@@ -1865,7 +1867,7 @@ func (b *LocalBackend) initPeerAPIListener() {
if len(b.netMap.Addresses) == len(b.peerAPIListeners) {
allSame := true
for i, pln := range b.peerAPIListeners {
if pln.ip != b.netMap.Addresses[i].IP {
if pln.ip != b.netMap.Addresses[i].IP() {
allSame = false
break
}
@@ -1910,7 +1912,7 @@ func (b *LocalBackend) initPeerAPIListener() {
var err error
skipListen := i > 0 && isNetstack
if !skipListen {
ln, err = ps.listen(a.IP, b.prevIfState)
ln, err = ps.listen(a.IP(), b.prevIfState)
if err != nil {
if runtime.GOOS == "windows" {
// Expected for now. See Issue 1620.
@@ -1924,7 +1926,7 @@ func (b *LocalBackend) initPeerAPIListener() {
}
pln := &peerAPIListener{
ps: ps,
ip: a.IP,
ip: a.IP(),
ln: ln, // nil for 2nd+ on netstack
lb: b,
}
@@ -1933,7 +1935,7 @@ func (b *LocalBackend) initPeerAPIListener() {
} else {
pln.port = ln.Addr().(*net.TCPAddr).Port
}
pln.urlStr = "http://" + net.JoinHostPort(a.IP.String(), strconv.Itoa(pln.port))
pln.urlStr = "http://" + net.JoinHostPort(a.IP().String(), strconv.Itoa(pln.port))
b.logf("peerapi: serving on %s", pln.urlStr)
go pln.serve()
b.peerAPIListeners = append(b.peerAPIListeners, pln)
@@ -1984,14 +1986,14 @@ func peerRoutes(peers []wgcfg.Peer, cgnatThreshold int) (routes []netaddr.IPPref
for _, aip := range peer.AllowedIPs {
aip = unmapIPPrefix(aip)
// Only add the Tailscale IPv6 ULA once, if we see anybody using part of it.
if aip.IP.Is6() && aip.IsSingleIP() && tsULA.Contains(aip.IP) {
if aip.IP().Is6() && aip.IsSingleIP() && tsULA.Contains(aip.IP()) {
if !didULA {
didULA = true
routes = append(routes, tsULA)
}
continue
}
if aip.IsSingleIP() && cgNAT.Contains(aip.IP) {
if aip.IsSingleIP() && cgNAT.Contains(aip.IP()) {
cgNATIPs = append(cgNATIPs, aip)
} else {
routes = append(routes, aip)
@@ -2058,16 +2060,13 @@ func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs) *router
}
}
rs.Routes = append(rs.Routes, netaddr.IPPrefix{
IP: tsaddr.TailscaleServiceIP(),
Bits: 32,
})
rs.Routes = append(rs.Routes, netaddr.IPPrefixFrom(tsaddr.TailscaleServiceIP(), 32))
return rs
}
func unmapIPPrefix(ipp netaddr.IPPrefix) netaddr.IPPrefix {
return netaddr.IPPrefix{IP: ipp.IP.Unmap(), Bits: ipp.Bits}
return netaddr.IPPrefixFrom(ipp.IP().Unmap(), ipp.Bits())
}
func unmapIPPrefixes(ippsList ...[]netaddr.IPPrefix) (ret []netaddr.IPPrefix) {
@@ -2121,8 +2120,8 @@ func (b *LocalBackend) enterState(newState ipn.State) {
if oldState == newState {
return
}
b.logf("Switching ipn state %v -> %v (WantRunning=%v)",
oldState, newState, prefs.WantRunning)
b.logf("Switching ipn state %v -> %v (WantRunning=%v, nm=%v)",
oldState, newState, prefs.WantRunning, netMap != nil)
health.SetIPNState(newState.String(), prefs.WantRunning)
b.send(ipn.Notify{State: &newState})
@@ -2151,7 +2150,7 @@ func (b *LocalBackend) enterState(newState ipn.State) {
case ipn.Running:
var addrs []string
for _, addr := range b.netMap.Addresses {
addrs = append(addrs, addr.IP.String())
addrs = append(addrs, addr.IP().String())
}
systemd.Status("Connected; %s; %s", activeLogin, strings.Join(addrs, " "))
default:
@@ -2178,13 +2177,14 @@ func (b *LocalBackend) nextState() ipn.State {
cc = b.cc
netMap = b.netMap
state = b.state
blocked = b.blocked
wantRunning = b.prefs.WantRunning
loggedOut = b.prefs.LoggedOut
)
b.mu.Unlock()
switch {
case !wantRunning && !loggedOut && b.hasNodeKey():
case !wantRunning && !loggedOut && !blocked && b.hasNodeKey():
return ipn.Stopped
case netMap == nil:
if cc.AuthCantContinue() || loggedOut {
@@ -2418,7 +2418,7 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
addNode := func(n *tailcfg.Node) {
for _, ipp := range n.Addresses {
if ipp.IsSingleIP() {
b.nodeByAddr[ipp.IP] = n
b.nodeByAddr[ipp.IP()] = n
}
}
}
@@ -2570,9 +2570,9 @@ func peerAPIBase(nm *netmap.NetworkMap, peer *tailcfg.Node) string {
continue
}
switch {
case a.IP.Is4():
case a.IP().Is4():
have4 = true
case a.IP.Is6():
case a.IP().Is6():
have6 = true
}
}
@@ -2588,11 +2588,11 @@ func peerAPIBase(nm *netmap.NetworkMap, peer *tailcfg.Node) string {
var ipp netaddr.IPPort
switch {
case have4 && p4 != 0:
ipp = netaddr.IPPort{IP: nodeIP(peer, netaddr.IP.Is4), Port: p4}
ipp = netaddr.IPPortFrom(nodeIP(peer, netaddr.IP.Is4), p4)
case have6 && p6 != 0:
ipp = netaddr.IPPort{IP: nodeIP(peer, netaddr.IP.Is6), Port: p6}
ipp = netaddr.IPPortFrom(nodeIP(peer, netaddr.IP.Is6), p6)
}
if ipp.IP.IsZero() {
if ipp.IP().IsZero() {
return ""
}
return fmt.Sprintf("http://%v", ipp)
@@ -2600,8 +2600,8 @@ func peerAPIBase(nm *netmap.NetworkMap, peer *tailcfg.Node) string {
func nodeIP(n *tailcfg.Node, pred func(netaddr.IP) bool) netaddr.IP {
for _, a := range n.Addresses {
if a.IsSingleIP() && pred(a.IP) {
return a.IP
if a.IsSingleIP() && pred(a.IP()) {
return a.IP()
}
}
return netaddr.IP{}

View File

@@ -171,7 +171,7 @@ func TestShrinkDefaultRoute(t *testing.T) {
out: []string{
"fe80::1",
"ff00::1",
tsaddr.TailscaleULARange().IP.String(),
tsaddr.TailscaleULARange().IP().String(),
},
localIPFn: func(ip netaddr.IP) bool { return !inRemove(ip) && ip.Is6() },
},

View File

@@ -510,7 +510,7 @@ func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
<body>
<h1>Hello, %s (%v)</h1>
This is my Tailscale device. Your device is %v.
`, html.EscapeString(who), h.remoteAddr.IP, html.EscapeString(h.peerNode.ComputedName))
`, html.EscapeString(who), h.remoteAddr.IP(), html.EscapeString(h.peerNode.ComputedName))
if h.isSelf {
fmt.Fprintf(w, "<p>You are the owner of this node.\n")

View File

@@ -368,13 +368,11 @@ func TestStateMachine(t *testing.T) {
{
c.Assert(cc.getCalls(), qt.DeepEquals, []string{"Login"})
notifies.drain(0)
// BUG: this should immediately set WantRunning to true.
// Users don't log in if they don't want to also connect.
// (Generally, we're inconsistent about who is supposed to
// update Prefs at what time. But the overall philosophy is:
// update it when the user's intent changes. This is clearly
// at the time the user *requests* Login, not at the time
// the login finishes.)
// Note: WantRunning isn't true yet. It'll switch to true
// after a successful login finishes.
// (This behaviour is needed so that b.Login() won't
// start connecting to an old account right away, if one
// exists when you launch another login.)
}
// Attempted non-interactive login with no key; indicate that
@@ -384,18 +382,16 @@ func TestStateMachine(t *testing.T) {
url1 := "http://localhost:1/1"
cc.send(nil, url1, false, nil)
{
c.Assert(cc.getCalls(), qt.HasLen, 0)
c.Assert(cc.getCalls(), qt.DeepEquals, []string{})
// ...but backend eats that notification, because the user
// didn't explicitly request interactive login yet, and
// we're already in NeedsLogin state.
nn := notifies.drain(1)
// Trying to log in automatically sets WantRunning.
// BUG: that should have happened right after Login().
c.Assert(nn[0].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[0].Prefs.LoggedOut, qt.IsFalse)
c.Assert(nn[0].Prefs.WantRunning, qt.IsTrue)
c.Assert(nn[0].Prefs.WantRunning, qt.IsFalse)
}
// Now we'll try an interactive login.
@@ -451,11 +447,12 @@ func TestStateMachine(t *testing.T) {
// same time.
// The backend should propagate this upward for the UI.
t.Logf("\n\nLoginFinished")
notifies.expect(2)
notifies.expect(3)
cc.setAuthBlocked(false)
cc.persist.LoginName = "user1"
cc.send(nil, "", true, &netmap.NetworkMap{})
{
nn := notifies.drain(2)
nn := notifies.drain(3)
// BUG: still too soon for UpdateEndpoints.
//
// Arguably it makes sense to unpause now, since the machine
@@ -468,15 +465,12 @@ func TestStateMachine(t *testing.T) {
// it's visible in the logs)
c.Assert([]string{"unpause", "UpdateEndpoints"}, qt.DeepEquals, cc.getCalls())
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
c.Assert(nn[1].State, qt.Not(qt.IsNil))
c.Assert(ipn.NeedsMachineAuth, qt.Equals, *nn[1].State)
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[2].State, qt.Not(qt.IsNil))
c.Assert(nn[1].Prefs.Persist.LoginName, qt.Equals, "user1")
c.Assert(ipn.NeedsMachineAuth, qt.Equals, *nn[2].State)
}
// TODO: check that the logged-in username propagates from control
// through to the UI notifications. I think it's used as a hint
// for future logins, to pre-fill the username box? Not really sure
// how it works.
// Pretend that the administrator has authorized our machine.
t.Logf("\n\nMachineAuthorized")
notifies.expect(1)
@@ -581,77 +575,72 @@ func TestStateMachine(t *testing.T) {
// Let's make the logout succeed.
t.Logf("\n\nLogout (async) - succeed")
notifies.expect(1)
notifies.expect(0)
cc.setAuthBlocked(true)
cc.send(nil, "", false, nil)
{
nn := notifies.drain(1)
notifies.drain(0)
c.Assert(cc.getCalls(), qt.HasLen, 0)
c.Assert(nn[0].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[0].Prefs.LoggedOut, qt.IsTrue)
// BUG: WantRunning should be false after manual logout.
c.Assert(nn[0].Prefs.WantRunning, qt.IsTrue)
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
}
// A second logout should do nothing, since the prefs haven't changed.
t.Logf("\n\nLogout2 (async)")
notifies.expect(1)
notifies.expect(0)
b.Logout()
{
nn := notifies.drain(1)
notifies.drain(0)
// BUG: the backend has already called StartLogout, and we're
// still logged out. So it shouldn't call it again.
c.Assert([]string{"StartLogout"}, qt.DeepEquals, cc.getCalls())
// BUG: Prefs should not change here. Already logged out.
c.Assert(nn[0].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[0].Prefs.LoggedOut, qt.IsTrue)
c.Assert(nn[0].Prefs.WantRunning, qt.IsFalse)
c.Assert(cc.getCalls(), qt.HasLen, 0)
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
}
// Let's acknowledge the second logout too.
t.Logf("\n\nLogout2 (async) - succeed")
notifies.expect(1)
notifies.expect(0)
cc.setAuthBlocked(true)
cc.send(nil, "", false, nil)
{
nn := notifies.drain(1)
notifies.drain(0)
c.Assert(cc.getCalls(), qt.HasLen, 0)
c.Assert(nn[0].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[0].Prefs.LoggedOut, qt.IsTrue)
// BUG: second logout shouldn't cause WantRunning->true !!
c.Assert(nn[0].Prefs.WantRunning, qt.IsTrue)
c.Assert(cc.getCalls(), qt.HasLen, 0)
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
}
// Try the synchronous logout feature.
t.Logf("\n\nLogout3 (sync)")
notifies.expect(1)
notifies.expect(0)
b.LogoutSync(context.Background())
// NOTE: This returns as soon as cc.Logout() returns, which is okay
// I guess, since that's supposed to be synchronous.
{
nn := notifies.drain(1)
notifies.drain(0)
c.Assert([]string{"Logout"}, qt.DeepEquals, cc.getCalls())
c.Assert(nn[0].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[0].Prefs.LoggedOut, qt.IsTrue)
c.Assert(nn[0].Prefs.WantRunning, qt.IsFalse)
c.Assert(cc.getCalls(), qt.HasLen, 0)
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
}
// Generate the third logout event.
t.Logf("\n\nLogout3 (sync) - succeed")
notifies.expect(1)
notifies.expect(0)
cc.setAuthBlocked(true)
cc.send(nil, "", false, nil)
{
nn := notifies.drain(1)
notifies.drain(0)
c.Assert(cc.getCalls(), qt.HasLen, 0)
c.Assert(nn[0].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[0].Prefs.LoggedOut, qt.IsTrue)
// BUG: third logout shouldn't cause WantRunning->true !!
c.Assert(nn[0].Prefs.WantRunning, qt.IsTrue)
c.Assert(cc.getCalls(), qt.HasLen, 0)
c.Assert(b.Prefs().LoggedOut, qt.IsTrue)
c.Assert(b.Prefs().WantRunning, qt.IsFalse)
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
}
@@ -669,10 +658,6 @@ func TestStateMachine(t *testing.T) {
// happens if the user exits and restarts while logged out.
// Note that it's explicitly okay to call b.Start() over and over
// again, every time the frontend reconnects.
//
// BUG: WantRunning is true here (because of the bug above).
// We'll have to adjust the following test's expectations if we
// fix that.
// TODO: test user switching between statekeys.
@@ -691,7 +676,7 @@ func TestStateMachine(t *testing.T) {
c.Assert(nn[0].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[1].State, qt.Not(qt.IsNil))
c.Assert(nn[0].Prefs.LoggedOut, qt.IsTrue)
c.Assert(nn[0].Prefs.WantRunning, qt.IsTrue)
c.Assert(nn[0].Prefs.WantRunning, qt.IsFalse)
c.Assert(ipn.NeedsLogin, qt.Equals, *nn[1].State)
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
}
@@ -703,16 +688,20 @@ func TestStateMachine(t *testing.T) {
t.Logf("\n\nLoginFinished3")
notifies.expect(3)
cc.setAuthBlocked(false)
cc.persist.LoginName = "user2"
cc.send(nil, "", true, &netmap.NetworkMap{
MachineStatus: tailcfg.MachineAuthorized,
})
{
nn := notifies.drain(3)
c.Assert([]string{"unpause"}, qt.DeepEquals, cc.getCalls())
c.Assert(nn[0].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[1].LoginFinished, qt.Not(qt.IsNil))
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[2].State, qt.Not(qt.IsNil))
c.Assert(nn[0].Prefs.LoggedOut, qt.IsFalse)
// Prefs after finishing the login, so LoginName updated.
c.Assert(nn[1].Prefs.Persist.LoginName, qt.Equals, "user2")
c.Assert(nn[1].Prefs.LoggedOut, qt.IsFalse)
c.Assert(nn[1].Prefs.WantRunning, qt.IsTrue)
c.Assert(ipn.Starting, qt.Equals, *nn[2].State)
}
@@ -773,6 +762,63 @@ func TestStateMachine(t *testing.T) {
c.Assert(ipn.Starting, qt.Equals, *nn[0].State)
}
// Disconnect.
t.Logf("\n\nStop")
notifies.expect(2)
b.EditPrefs(&ipn.MaskedPrefs{
WantRunningSet: true,
Prefs: ipn.Prefs{WantRunning: false},
})
{
nn := notifies.drain(2)
c.Assert([]string{"unpause"}, qt.DeepEquals, cc.getCalls())
// BUG: I would expect Prefs to change first, and state after.
c.Assert(nn[0].State, qt.Not(qt.IsNil))
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
c.Assert(ipn.Stopped, qt.Equals, *nn[0].State)
}
// We want to try logging in as a different user, while Stopped.
// First, start the login process (without logging out first).
t.Logf("\n\nLoginDifferent")
notifies.expect(2)
b.StartLoginInteractive()
url3 := "http://localhost:1/3"
cc.send(nil, url3, false, nil)
{
nn := notifies.drain(2)
// It might seem like WantRunning should switch to true here,
// but that would be risky since we already have a valid
// user account. It might try to reconnect to the old account
// before the new one is ready. So no change yet.
c.Assert([]string{"Login", "unpause"}, qt.DeepEquals, cc.getCalls())
c.Assert(nn[0].BrowseToURL, qt.Not(qt.IsNil))
c.Assert(nn[1].State, qt.Not(qt.IsNil))
c.Assert(*nn[0].BrowseToURL, qt.Equals, url3)
c.Assert(ipn.NeedsLogin, qt.Equals, *nn[1].State)
}
// Now, let's say the interactive login completed, using a different
// user account than before.
t.Logf("\n\nLoginDifferent URL visited")
notifies.expect(3)
cc.persist.LoginName = "user3"
cc.send(nil, "", true, &netmap.NetworkMap{
MachineStatus: tailcfg.MachineAuthorized,
})
{
nn := notifies.drain(3)
c.Assert([]string{"unpause"}, qt.DeepEquals, cc.getCalls())
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
c.Assert(nn[1].Prefs, qt.Not(qt.IsNil))
c.Assert(nn[2].State, qt.Not(qt.IsNil))
// Prefs after finishing the login, so LoginName updated.
c.Assert(nn[1].Prefs.Persist.LoginName, qt.Equals, "user3")
c.Assert(nn[1].Prefs.LoggedOut, qt.IsFalse)
c.Assert(nn[1].Prefs.WantRunning, qt.IsTrue)
c.Assert(ipn.Starting, qt.Equals, *nn[2].State)
}
// The last test case is the most common one: restarting when both
// logged in and WantRunning.
t.Logf("\n\nStart5")
@@ -793,17 +839,18 @@ func TestStateMachine(t *testing.T) {
// Control server accepts our valid key from before.
t.Logf("\n\nLoginFinished5")
notifies.expect(2)
notifies.expect(1)
cc.setAuthBlocked(false)
cc.send(nil, "", true, &netmap.NetworkMap{
MachineStatus: tailcfg.MachineAuthorized,
})
{
nn := notifies.drain(2)
nn := notifies.drain(1)
c.Assert([]string{"unpause"}, qt.DeepEquals, cc.getCalls())
c.Assert(nn[0].LoginFinished, qt.Not(qt.IsNil))
c.Assert(nn[1].State, qt.Not(qt.IsNil))
c.Assert(ipn.Starting, qt.Equals, *nn[1].State)
// NOTE: No LoginFinished message since no interactive
// login was needed.
c.Assert(nn[0].State, qt.Not(qt.IsNil))
c.Assert(ipn.Starting, qt.Equals, *nn[0].State)
// NOTE: No prefs change this time. WantRunning stays true.
// We were in Starting in the first place, so that doesn't
// change either.

View File

@@ -147,7 +147,7 @@ func (s *server) getConnIdentity(c net.Conn) (ci connIdentity, err error) {
if err != nil {
return ci, fmt.Errorf("parsing local remote: %w", err)
}
if !la.IP.IsLoopback() || !ra.IP.IsLoopback() {
if !la.IP().IsLoopback() || !ra.IP().IsLoopback() {
return ci, errors.New("non-loopback connection")
}
tab, err := netstat.Get()

View File

@@ -211,7 +211,7 @@ func (m *Manager) compileConfig(cfg Config) (resolver.Config, OSConfig, error) {
func toIPsOnly(ipps []netaddr.IPPort) (ret []netaddr.IP) {
ret = make([]netaddr.IP, 0, len(ipps))
for _, ipp := range ipps {
ret = append(ret, ipp.IP)
ret = append(ret, ipp.IP())
}
return ret
}
@@ -219,7 +219,7 @@ func toIPsOnly(ipps []netaddr.IPPort) (ret []netaddr.IP) {
func toIPPorts(ips []netaddr.IP) (ret []netaddr.IPPort) {
ret = make([]netaddr.IPPort, 0, len(ips))
for _, ip := range ips {
ret = append(ret, netaddr.IPPort{IP: ip, Port: 53})
ret = append(ret, netaddr.IPPortFrom(ip, 53))
}
return ret
}

View File

@@ -368,11 +368,12 @@ func TestManager(t *testing.T) {
if err := m.Set(test.in); err != nil {
t.Fatalf("m.Set: %v", err)
}
tr := cmp.Transformer("ipStr", func(ip netaddr.IP) string { return ip.String() })
if diff := cmp.Diff(f.OSConfig, test.os, tr, cmpopts.EquateEmpty()); diff != "" {
trIP := cmp.Transformer("ipStr", func(ip netaddr.IP) string { return ip.String() })
trIPPort := cmp.Transformer("ippStr", func(ipp netaddr.IPPort) string { return ipp.String() })
if diff := cmp.Diff(f.OSConfig, test.os, trIP, trIPPort, cmpopts.EquateEmpty()); diff != "" {
t.Errorf("wrong OSConfig (-got+want)\n%s", diff)
}
if diff := cmp.Diff(f.ResolverConfig, test.rs, tr, cmpopts.EquateEmpty()); diff != "" {
if diff := cmp.Diff(f.ResolverConfig, test.rs, trIP, trIPPort, cmpopts.EquateEmpty()); diff != "" {
t.Errorf("wrong resolver.Config (-got+want)\n%s", diff)
}
})

View File

@@ -433,8 +433,8 @@ func TestDelegateCollision(t *testing.T) {
qtype dns.Type
addr netaddr.IPPort
}{
{"test.site.", dns.TypeA, netaddr.IPPort{IP: netaddr.IPv4(1, 1, 1, 1), Port: 1001}},
{"test.site.", dns.TypeAAAA, netaddr.IPPort{IP: netaddr.IPv4(1, 1, 1, 1), Port: 1002}},
{"test.site.", dns.TypeA, netaddr.IPPortFrom(netaddr.IPv4(1, 1, 1, 1), 1001)},
{"test.site.", dns.TypeAAAA, netaddr.IPPortFrom(netaddr.IPv4(1, 1, 1, 1), 1002)},
}
// packets will have the same dns txid.

View File

@@ -195,7 +195,7 @@ func ForeachInterface(fn func(Interface, []netaddr.IPPrefix)) error {
}
}
sort.Slice(pfxs, func(i, j int) bool {
return pfxs[i].IP.Less(pfxs[j].IP)
return pfxs[i].IP().Less(pfxs[j].IP())
})
fn(Interface{iface}, pfxs)
}
@@ -264,7 +264,7 @@ func (s *State) String() string {
fmt.Fprintf(&sb, "%s:[", ifName)
needSpace := false
for _, pfx := range s.InterfaceIPs[ifName] {
if !isInterestingIP(pfx.IP) {
if !isInterestingIP(pfx.IP()) {
continue
}
if needSpace {
@@ -367,7 +367,7 @@ func (s *State) AnyInterfaceUp() bool {
func hasTailscaleIP(pfxs []netaddr.IPPrefix) bool {
for _, pfx := range pfxs {
if tsaddr.IsTailscaleIP(pfx.IP) {
if tsaddr.IsTailscaleIP(pfx.IP()) {
return true
}
}
@@ -407,11 +407,11 @@ func GetState() (*State, error) {
return
}
for _, pfx := range pfxs {
if pfx.IP.IsLoopback() || pfx.IP.IsLinkLocalUnicast() {
if pfx.IP().IsLoopback() || pfx.IP().IsLinkLocalUnicast() {
continue
}
s.HaveV6Global = s.HaveV6Global || isGlobalV6(pfx.IP)
s.HaveV4 = s.HaveV4 || pfx.IP.Is4()
s.HaveV6Global = s.HaveV6Global || isGlobalV6(pfx.IP())
s.HaveV4 = s.HaveV4 || pfx.IP().Is4()
}
}); err != nil {
return nil, err
@@ -447,7 +447,7 @@ func HTTPOfListener(ln net.Listener) string {
var goodIP string
var privateIP string
ForeachInterfaceAddress(func(i Interface, pfx netaddr.IPPrefix) {
ip := pfx.IP
ip := pfx.IP()
if isPrivateIP(ip) {
if privateIP == "" {
privateIP = ip.String()
@@ -484,7 +484,7 @@ func LikelyHomeRouterIP() (gateway, myIP netaddr.IP, ok bool) {
return
}
ForeachInterfaceAddress(func(i Interface, pfx netaddr.IPPrefix) {
ip := pfx.IP
ip := pfx.IP()
if !i.IsUp() || ip.IsZero() || !myIP.IsZero() {
return
}
@@ -528,7 +528,7 @@ var (
// isInterestingIP.
func anyInterestingIP(pfxs []netaddr.IPPrefix) bool {
for _, pfx := range pfxs {
if isInterestingIP(pfx.IP) {
if isInterestingIP(pfx.IP()) {
return true
}
}

View File

@@ -625,7 +625,7 @@ func (rs *reportState) stopTimers() {
func (rs *reportState) addNodeLatency(node *tailcfg.DERPNode, ipp netaddr.IPPort, d time.Duration) {
var ipPortStr string
if ipp != (netaddr.IPPort{}) {
ipPortStr = net.JoinHostPort(ipp.IP.String(), fmt.Sprint(ipp.Port))
ipPortStr = net.JoinHostPort(ipp.IP().String(), fmt.Sprint(ipp.Port()))
}
rs.mu.Lock()
@@ -650,13 +650,13 @@ func (rs *reportState) addNodeLatency(node *tailcfg.DERPNode, ipp netaddr.IPPort
}
switch {
case ipp.IP.Is6():
case ipp.IP().Is6():
updateLatency(ret.RegionV6Latency, node.RegionID, d)
ret.IPv6 = true
ret.GlobalV6 = ipPortStr
// TODO: track MappingVariesByDestIP for IPv6
// too? Would be sad if so, but who knows.
case ipp.IP.Is4():
case ipp.IP().Is4():
updateLatency(ret.RegionV4Latency, node.RegionID, d)
ret.IPv4 = true
if rs.gotEP4 == "" {
@@ -1172,7 +1172,7 @@ func (c *Client) nodeAddr(ctx context.Context, n *tailcfg.DERPNode, proto probeP
if proto == probeIPv6 && ip.Is4() {
return nil
}
return netaddr.IPPort{IP: ip, Port: uint16(port)}.UDPAddr()
return netaddr.IPPortFrom(ip, uint16(port)).UDPAddr()
}
switch proto {
@@ -1182,7 +1182,7 @@ func (c *Client) nodeAddr(ctx context.Context, n *tailcfg.DERPNode, proto probeP
if !ip.Is4() {
return nil
}
return netaddr.IPPort{IP: ip, Port: uint16(port)}.UDPAddr()
return netaddr.IPPortFrom(ip, uint16(port)).UDPAddr()
}
case probeIPv6:
if n.IPv6 != "" {
@@ -1190,7 +1190,7 @@ func (c *Client) nodeAddr(ctx context.Context, n *tailcfg.DERPNode, proto probeP
if !ip.Is6() {
return nil
}
return netaddr.IPPort{IP: ip, Port: uint16(port)}.UDPAddr()
return netaddr.IPPortFrom(ip, uint16(port)).UDPAddr()
}
default:
return nil

View File

@@ -157,10 +157,9 @@ func ipport4(addr uint32, port uint16) netaddr.IPPort {
if !endian.Big {
addr = bits.ReverseBytes32(addr)
}
return netaddr.IPPort{
IP: netaddr.IPv4(byte(addr>>24), byte(addr>>16), byte(addr>>8), byte(addr)),
Port: port,
}
return netaddr.IPPortFrom(
netaddr.IPv4(byte(addr>>24), byte(addr>>16), byte(addr>>8), byte(addr)),
port)
}
func ipport6(addr [16]byte, scope uint32, port uint16) netaddr.IPPort {
@@ -169,10 +168,7 @@ func ipport6(addr [16]byte, scope uint32, port uint16) netaddr.IPPort {
// TODO: something better here?
ip = ip.WithZone(fmt.Sprint(scope))
}
return netaddr.IPPort{
IP: ip,
Port: port,
}
return netaddr.IPPortFrom(ip, port)
}
func port(v *uint32) uint16 {

View File

@@ -76,8 +76,8 @@ func (p *Parsed) String() string {
//
// TODO: make netaddr more efficient in this area, and retire this func.
func writeIPPort(sb *strbuilder.Builder, ipp netaddr.IPPort) {
if ipp.IP.Is4() {
raw := ipp.IP.As4()
if ipp.IP().Is4() {
raw := ipp.IP().As4()
sb.WriteUint(uint64(raw[0]))
sb.WriteByte('.')
sb.WriteUint(uint64(raw[1]))
@@ -88,10 +88,10 @@ func writeIPPort(sb *strbuilder.Builder, ipp netaddr.IPPort) {
sb.WriteByte(':')
} else {
sb.WriteByte('[')
sb.WriteString(ipp.IP.String()) // TODO: faster?
sb.WriteString(ipp.IP().String()) // TODO: faster?
sb.WriteString("]:")
}
sb.WriteUint(uint64(ipp.Port))
sb.WriteUint(uint64(ipp.Port()))
}
// Decode extracts data from the packet in b into q.
@@ -142,8 +142,8 @@ func (q *Parsed) decode4(b []byte) {
}
// If it's valid IPv4, then the IP addresses are valid
q.Src.IP = netaddr.IPv4(b[12], b[13], b[14], b[15])
q.Dst.IP = netaddr.IPv4(b[16], b[17], b[18], b[19])
q.Src = q.Src.WithIP(netaddr.IPv4(b[12], b[13], b[14], b[15]))
q.Dst = q.Dst.WithIP(netaddr.IPv4(b[16], b[17], b[18], b[19]))
q.subofs = int((b[0] & 0x0F) << 2)
if q.subofs > q.length {
@@ -185,8 +185,8 @@ func (q *Parsed) decode4(b []byte) {
q.IPProto = unknown
return
}
q.Src.Port = 0
q.Dst.Port = 0
q.Src = q.Src.WithPort(0)
q.Dst = q.Dst.WithPort(0)
q.dataofs = q.subofs + icmp4HeaderLength
return
case ipproto.IGMP:
@@ -198,8 +198,8 @@ func (q *Parsed) decode4(b []byte) {
q.IPProto = unknown
return
}
q.Src.Port = binary.BigEndian.Uint16(sub[0:2])
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
q.Src = q.Src.WithPort(binary.BigEndian.Uint16(sub[0:2]))
q.Dst = q.Dst.WithPort(binary.BigEndian.Uint16(sub[2:4]))
q.TCPFlags = TCPFlag(sub[13]) & 0x3F
headerLength := (sub[12] & 0xF0) >> 2
q.dataofs = q.subofs + int(headerLength)
@@ -209,8 +209,8 @@ func (q *Parsed) decode4(b []byte) {
q.IPProto = unknown
return
}
q.Src.Port = binary.BigEndian.Uint16(sub[0:2])
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
q.Src = q.Src.WithPort(binary.BigEndian.Uint16(sub[0:2]))
q.Dst = q.Dst.WithPort(binary.BigEndian.Uint16(sub[2:4]))
q.dataofs = q.subofs + udpHeaderLength
return
case ipproto.SCTP:
@@ -218,8 +218,8 @@ func (q *Parsed) decode4(b []byte) {
q.IPProto = unknown
return
}
q.Src.Port = binary.BigEndian.Uint16(sub[0:2])
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
q.Src = q.Src.WithPort(binary.BigEndian.Uint16(sub[0:2]))
q.Dst = q.Dst.WithPort(binary.BigEndian.Uint16(sub[2:4]))
return
case ipproto.TSMP:
// Inter-tailscale messages.
@@ -265,8 +265,10 @@ func (q *Parsed) decode6(b []byte) {
// okay to ignore `ok` here, because IPs pulled from packets are
// always well-formed stdlib IPs.
q.Src.IP, _ = netaddr.FromStdIP(net.IP(b[8:24]))
q.Dst.IP, _ = netaddr.FromStdIP(net.IP(b[24:40]))
srcIP, _ := netaddr.FromStdIP(net.IP(b[8:24]))
dstIP, _ := netaddr.FromStdIP(net.IP(b[24:40]))
q.Src = q.Src.WithIP(srcIP)
q.Dst = q.Dst.WithIP(dstIP)
// We don't support any IPv6 extension headers. Don't try to
// be clever. Therefore, the IP subprotocol always starts at
@@ -290,16 +292,16 @@ func (q *Parsed) decode6(b []byte) {
q.IPProto = unknown
return
}
q.Src.Port = 0
q.Dst.Port = 0
q.Src = q.Src.WithPort(0)
q.Dst = q.Dst.WithPort(0)
q.dataofs = q.subofs + icmp6HeaderLength
case ipproto.TCP:
if len(sub) < tcpHeaderLength {
q.IPProto = unknown
return
}
q.Src.Port = binary.BigEndian.Uint16(sub[0:2])
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
q.Src = q.Src.WithPort(binary.BigEndian.Uint16(sub[0:2]))
q.Dst = q.Dst.WithPort(binary.BigEndian.Uint16(sub[2:4]))
q.TCPFlags = TCPFlag(sub[13]) & 0x3F
headerLength := (sub[12] & 0xF0) >> 2
q.dataofs = q.subofs + int(headerLength)
@@ -309,16 +311,16 @@ func (q *Parsed) decode6(b []byte) {
q.IPProto = unknown
return
}
q.Src.Port = binary.BigEndian.Uint16(sub[0:2])
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
q.Src = q.Src.WithPort(binary.BigEndian.Uint16(sub[0:2]))
q.Dst = q.Dst.WithPort(binary.BigEndian.Uint16(sub[2:4]))
q.dataofs = q.subofs + udpHeaderLength
case ipproto.SCTP:
if len(sub) < sctpHeaderLength {
q.IPProto = unknown
return
}
q.Src.Port = binary.BigEndian.Uint16(sub[0:2])
q.Dst.Port = binary.BigEndian.Uint16(sub[2:4])
q.Src = q.Src.WithPort(binary.BigEndian.Uint16(sub[0:2]))
q.Dst = q.Dst.WithPort(binary.BigEndian.Uint16(sub[2:4]))
return
case ipproto.TSMP:
// Inter-tailscale messages.
@@ -338,8 +340,8 @@ func (q *Parsed) IP4Header() IP4Header {
return IP4Header{
IPID: ipid,
IPProto: q.IPProto,
Src: q.Src.IP,
Dst: q.Dst.IP,
Src: q.Src.IP(),
Dst: q.Dst.IP(),
}
}
@@ -351,8 +353,8 @@ func (q *Parsed) IP6Header() IP6Header {
return IP6Header{
IPID: ipid,
IPProto: q.IPProto,
Src: q.Src.IP,
Dst: q.Dst.IP,
Src: q.Src.IP(),
Dst: q.Dst.IP(),
}
}
@@ -373,8 +375,8 @@ func (q *Parsed) UDP4Header() UDP4Header {
}
return UDP4Header{
IP4Header: q.IP4Header(),
SrcPort: q.Src.Port,
DstPort: q.Dst.Port,
SrcPort: q.Src.Port(),
DstPort: q.Dst.Port(),
}
}

View File

@@ -143,7 +143,7 @@ func (h TailscaleRejectedHeader) Marshal(buf []byte) error {
if len(buf) > maxPacketLength {
return errLargePacket
}
if h.Src.IP.Is4() {
if h.Src.IP().Is4() {
iph := IP4Header{
IPProto: ipproto.TSMP,
Src: h.IPSrc,
@@ -151,7 +151,7 @@ func (h TailscaleRejectedHeader) Marshal(buf []byte) error {
}
iph.Marshal(buf)
buf = buf[ip4HeaderLength:]
} else if h.Src.IP.Is6() {
} else if h.Src.IP().Is6() {
iph := IP6Header{
IPProto: ipproto.TSMP,
Src: h.IPSrc,
@@ -165,8 +165,8 @@ func (h TailscaleRejectedHeader) Marshal(buf []byte) error {
buf[0] = byte(TSMPTypeRejectedConn)
buf[1] = byte(h.Proto)
buf[2] = byte(h.Reason)
binary.BigEndian.PutUint16(buf[3:5], h.Src.Port)
binary.BigEndian.PutUint16(buf[5:7], h.Dst.Port)
binary.BigEndian.PutUint16(buf[3:5], h.Src.Port())
binary.BigEndian.PutUint16(buf[5:7], h.Dst.Port())
if h.hasFlags() {
var flags byte
@@ -190,10 +190,10 @@ func (pp *Parsed) AsTailscaleRejectedHeader() (h TailscaleRejectedHeader, ok boo
h = TailscaleRejectedHeader{
Proto: ipproto.Proto(p[1]),
Reason: TailscaleRejectReason(p[2]),
IPSrc: pp.Src.IP,
IPDst: pp.Dst.IP,
Src: netaddr.IPPort{IP: pp.Dst.IP, Port: binary.BigEndian.Uint16(p[3:5])},
Dst: netaddr.IPPort{IP: pp.Src.IP, Port: binary.BigEndian.Uint16(p[5:7])},
IPSrc: pp.Src.IP(),
IPDst: pp.Dst.IP(),
Src: netaddr.IPPortFrom(pp.Dst.IP(), binary.BigEndian.Uint16(p[3:5])),
Dst: netaddr.IPPortFrom(pp.Src.IP(), binary.BigEndian.Uint16(p[5:7])),
}
if len(p) > 7 {
flags := p[7]

View File

@@ -84,7 +84,7 @@ type pmpMapping struct {
// externalValid reports whether m.external is valid, with both its IP and Port populated.
func (m *pmpMapping) externalValid() bool {
return !m.external.IP.IsZero() && m.external.Port != 0
return !m.external.IP().IsZero() && m.external.Port() != 0
}
// release does a best effort fire-and-forget release of the PMP mapping m.
@@ -94,8 +94,8 @@ func (m *pmpMapping) release() {
return
}
defer uc.Close()
pkt := buildPMPRequestMappingPacket(m.internal.Port, m.external.Port, pmpMapLifetimeDelete)
uc.WriteTo(pkt, netaddr.IPPort{IP: m.gw, Port: pmpPort}.UDPAddr())
pkt := buildPMPRequestMappingPacket(m.internal.Port(), m.external.Port(), pmpMapLifetimeDelete)
uc.WriteTo(pkt, netaddr.IPPortFrom(m.gw, pmpPort).UDPAddr())
}
// NewClient returns a new portmapping client.
@@ -256,7 +256,7 @@ func (c *Client) CreateOrGetMapping(ctx context.Context) (external netaddr.IPPor
localPort := c.localPort
m := &pmpMapping{
gw: gw,
internal: netaddr.IPPort{IP: myIP, Port: localPort},
internal: netaddr.IPPortFrom(myIP, localPort),
}
// prevPort is the port we had most previously, if any. We try
@@ -271,7 +271,7 @@ func (c *Client) CreateOrGetMapping(ctx context.Context) (external netaddr.IPPor
return m.external, nil
}
// The mapping might still be valid, so just try to renew it.
prevPort = m.external.Port
prevPort = m.external.Port()
}
// If we just did a Probe (e.g. via netchecker) but didn't
@@ -279,7 +279,7 @@ func (c *Client) CreateOrGetMapping(ctx context.Context) (external netaddr.IPPor
// again. Cuts down latency for most clients.
haveRecentPMP := c.sawPMPRecentlyLocked()
if haveRecentPMP {
m.external.IP = c.pmpPubIP
m.external = m.external.WithIP(c.pmpPubIP)
}
if c.lastProbe.After(now.Add(-5*time.Second)) && !haveRecentPMP {
c.mu.Unlock()
@@ -297,11 +297,11 @@ func (c *Client) CreateOrGetMapping(ctx context.Context) (external netaddr.IPPor
uc.SetReadDeadline(time.Now().Add(portMapServiceTimeout))
defer closeCloserOnContextDone(ctx, uc)()
pmpAddr := netaddr.IPPort{IP: gw, Port: pmpPort}
pmpAddr := netaddr.IPPortFrom(gw, pmpPort)
pmpAddru := pmpAddr.UDPAddr()
// Ask for our external address if needed.
if m.external.IP.IsZero() {
if m.external.IP().IsZero() {
if _, err := uc.WriteTo(pmpReqExternalAddrPacket, pmpAddru); err != nil {
return netaddr.IPPort{}, err
}
@@ -337,10 +337,10 @@ func (c *Client) CreateOrGetMapping(ctx context.Context) (external netaddr.IPPor
return netaddr.IPPort{}, NoMappingError{fmt.Errorf("PMP response Op=0x%x,Res=0x%x", pres.OpCode, pres.ResultCode)}
}
if pres.OpCode == pmpOpReply|pmpOpMapPublicAddr {
m.external.IP = pres.PublicAddr
m.external = m.external.WithIP(pres.PublicAddr)
}
if pres.OpCode == pmpOpReply|pmpOpMapUDP {
m.external.Port = pres.ExternalPort
m.external = m.external.WithPort(pres.ExternalPort)
d := time.Duration(pres.MappingValidSeconds) * time.Second
d /= 2 // renew in half the time
m.useUntil = time.Now().Add(d)
@@ -468,9 +468,9 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
defer cancel()
defer closeCloserOnContextDone(ctx, uc)()
pcpAddr := netaddr.IPPort{IP: gw, Port: pcpPort}.UDPAddr()
pmpAddr := netaddr.IPPort{IP: gw, Port: pmpPort}.UDPAddr()
upnpAddr := netaddr.IPPort{IP: gw, Port: upnpPort}.UDPAddr()
pcpAddr := netaddr.IPPortFrom(gw, pcpPort).UDPAddr()
pmpAddr := netaddr.IPPortFrom(gw, pmpPort).UDPAddr()
upnpAddr := netaddr.IPPortFrom(gw, upnpPort).UDPAddr()
// Don't send probes to services that we recently learned (for
// the same gw/myIP) are available. See

View File

@@ -92,7 +92,7 @@ func TailscaleEphemeral6Range() netaddr.IPPrefix {
// Currently used to work around a Windows limitation when programming
// IPv6 routes in corner cases.
func Tailscale4To6Placeholder() netaddr.IP {
return Tailscale4To6Range().IP
return Tailscale4To6Range().IP()
}
// Tailscale4To6 returns a Tailscale IPv6 address that maps 1:1 to the
@@ -102,7 +102,7 @@ func Tailscale4To6(ipv4 netaddr.IP) netaddr.IP {
if !ipv4.Is4() || !IsTailscaleIP(ipv4) {
return netaddr.IP{}
}
ret := Tailscale4To6Range().IP.As16()
ret := Tailscale4To6Range().IP().As16()
v4 := ipv4.As4()
copy(ret[13:], v4[1:])
return netaddr.IPFrom16(ret)
@@ -172,16 +172,16 @@ func NewContainsIPFunc(addrs []netaddr.IPPrefix) func(ip netaddr.IP) bool {
// Fast paths for 1 and 2 IPs:
if len(addrs) == 1 {
a := addrs[0]
return func(ip netaddr.IP) bool { return ip == a.IP }
return func(ip netaddr.IP) bool { return ip == a.IP() }
}
if len(addrs) == 2 {
a, b := addrs[0], addrs[1]
return func(ip netaddr.IP) bool { return ip == a.IP || ip == b.IP }
return func(ip netaddr.IP) bool { return ip == a.IP() || ip == b.IP() }
}
// General case:
m := map[netaddr.IP]bool{}
for _, a := range addrs {
m[a.IP] = true
m[a.IP()] = true
}
return func(ip netaddr.IP) bool { return m[ip] }
}

View File

@@ -352,7 +352,7 @@ func (t *Wrapper) Read(buf []byte, offset int) (int, error) {
p.Decode(buf[offset : offset+n])
if m, ok := t.destIPActivity.Load().(map[netaddr.IP]func()); ok {
if fn := m[p.Dst.IP]; fn != nil {
if fn := m[p.Dst.IP()]; fn != nil {
fn()
}
}
@@ -412,7 +412,7 @@ func (t *Wrapper) filterIn(buf []byte) filter.Response {
p.IPProto == ipproto.TCP &&
p.TCPFlags&packet.TCPSyn != 0 &&
t.PeerAPIPort != nil {
if port, ok := t.PeerAPIPort(p.Dst.IP); ok && port == p.Dst.Port {
if port, ok := t.PeerAPIPort(p.Dst.IP()); ok && port == p.Dst.Port() {
outcome = filter.Accept
}
}
@@ -425,8 +425,8 @@ func (t *Wrapper) filterIn(buf []byte) filter.Response {
// can show them a rejection history with reasons.
if p.IPVersion == 4 && p.IPProto == ipproto.TCP && p.TCPFlags&packet.TCPSyn != 0 && !t.disableTSMPRejected {
rj := packet.TailscaleRejectedHeader{
IPSrc: p.Dst.IP,
IPDst: p.Src.IP,
IPSrc: p.Dst.IP(),
IPDst: p.Src.IP(),
Src: p.Src,
Dst: p.Dst,
Proto: p.IPProto,
@@ -536,7 +536,7 @@ func (t *Wrapper) injectOutboundPong(pp *packet.Parsed, req packet.TSMPPingReque
Data: req.Data,
}
if t.PeerAPIPort != nil {
pong.PeerAPIPort, _ = t.PeerAPIPort(pp.Dst.IP)
pong.PeerAPIPort, _ = t.PeerAPIPort(pp.Dst.IP())
}
switch pp.IPVersion {
case 4:

View File

@@ -82,7 +82,7 @@ func nets(nets ...string) (ret []netaddr.IPPrefix) {
if ip.Is6() {
bits = 128
}
ret = append(ret, netaddr.IPPrefix{IP: ip, Bits: bits})
ret = append(ret, netaddr.IPPrefixFrom(ip, bits))
} else {
pfx, err := netaddr.ParseIPPrefix(s)
if err != nil {

399
scripts/installer.sh Executable file
View File

@@ -0,0 +1,399 @@
#!/bin/sh
# Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
#
# This script detects the current operating system, and installs
# Tailscale according to that OS's conventions.
set -eu
# All the code is wrapped in a main function that gets called at the
# bottom of the file, so that a truncated partial download doesn't end
# up executing half a script.
main() {
# Step 1: detect the current linux distro, version, and packaging system.
#
# We rely on a combination of 'uname' and /etc/os-release to find
# an OS name and version, and from there work out what
# installation method we should be using.
#
# The end result of this step is that the following three
# variables are populated, if detection was successful.
OS=""
VERSION=""
PACKAGETYPE=""
if [ -f /etc/os-release ]; then
# /etc/os-release populates a number of shell variables. We care about the following:
# - ID: the short name of the OS (e.g. "debian", "freebsd")
# - VERSION_ID: the numeric release version for the OS, if any (e.g. "18.04")
# - VERSION_CODENAME: the codename of the OS release, if any (e.g. "buster")
. /etc/os-release
case "$ID" in
ubuntu)
OS="$ID"
VERSION="$VERSION_CODENAME"
PACKAGETYPE="apt"
;;
debian)
OS="$ID"
VERSION="$VERSION_CODENAME"
PACKAGETYPE="apt"
;;
raspbian)
OS="$ID"
VERSION="$VERSION_CODENAME"
PACKAGETYPE="apt"
;;
centos)
OS="$ID"
VERSION="$VERSION_ID"
PACKAGETYPE="dnf"
if [ "$VERSION" = "7" ]; then
PACKAGETYPE="yum"
fi
;;
rhel)
OS="$ID"
VERSION="$(echo "$VERSION_ID" | cut -f1 -d.)"
PACKAGETYPE="dnf"
;;
fedora)
OS="$ID"
VERSION=""
PACKAGETYPE="dnf"
;;
amzn)
OS="amazon-linux"
VERSION="$VERSION_ID"
PACKAGETYPE="yum"
;;
opensuse-leap)
OS="opensuse"
VERSION="leap/$VERSION_ID"
PACKAGETYPE="zypper"
;;
opensuse-tumbleweed)
OS="opensuse"
VERSION="tumbleweed"
PACKAGETYPE="zypper"
;;
arch)
OS="$ID"
VERSION="" # rolling release
PACKAGETYPE="pacman"
;;
manjaro)
OS="$ID"
VERSION="" # rolling release
PACKAGETYPE="pacman"
;;
alpine)
OS="$ID"
VERSION="$VERSION_ID"
PACKAGETYPE="apk"
;;
nixos)
echo "Please add Tailscale to your NixOS configuration directly:"
echo
echo "services.tailscale.enable = true;"
exit 1
;;
void)
OS="$ID"
VERSION="" # rolling release
PACKAGETYPE="xbps"
;;
gentoo)
OS="$ID"
VERSION="" # rolling release
PACKAGETYPE="emerge"
;;
freebsd)
OS="$ID"
VERSION="$(echo "$VERSION_ID" | cut -f1 -d.)"
PACKAGETYPE="pkg"
;;
# TODO: wsl?
# TODO: synology? qnap?
esac
fi
# If we failed to detect something through os-release, consult
# uname and try to infer things from that.
if [ -z "$OS" ]; then
if type uname >/dev/null 2>&1; then
case "$(uname)" in
FreeBSD)
# FreeBSD before 12.2 doesn't have
# /etc/os-release, so we wouldn't have found it in
# the os-release probing above.
OS="freebsd"
VERSION="$(freebsd-version | cut -f1 -d.)"
PACKAGETYPE="pkg"
;;
OpenBSD)
OS="openbsd"
VERSION="$(uname -r)"
PACKAGETYPE=""
;;
Darwin)
OS="macos"
VERSION="$(sw_vers -productVersion | cut -f1-2 -d.)"
PACKAGETYPE="appstore"
;;
Linux)
OS="other-linux"
VERSION=""
PACKAGETYPE=""
;;
esac
fi
fi
# Step 2: having detected an OS we support, is it one of the
# versions we support?
OS_UNSUPPORTED=
case "$OS" in
ubuntu)
if [ "$VERSION" != "xenial" ] && \
[ "$VERSION" != "bionic" ] && \
[ "$VERSION" != "eoan" ] && \
[ "$VERSION" != "focal" ] && \
[ "$VERSION" != "groovy" ] && \
[ "$VERSION" != "hirsute" ]
then
OS_UNSUPPORTED=1
fi
;;
debian)
if [ "$VERSION" != "stretch" ] && \
[ "$VERSION" != "buster" ] && \
[ "$VERSION" != "bullseye" ] && \
[ "$VERSION" != "sid" ]
then
OS_UNSUPPORTED=1
fi
;;
raspbian)
if [ "$VERSION" != "buster" ]
then
OS_UNSUPPORTED=1
fi
;;
centos)
if [ "$VERSION" != "7" ] && \
[ "$VERSION" != "8" ]
then
OS_UNSUPPORTED=1
fi
;;
rhel)
if [ "$VERSION" != "8" ]
then
OS_UNSUPPORTED=1
fi
;;
amazon-linux)
if [ "$VERSION" != "2" ]
then
OS_UNSUPPORTED=1
fi
;;
opensuse)
if [ "$VERSION" != "leap/15.1" ] && \
[ "$VERSION" != "leap/15.2" ] && \
[ "$VERSION" != "tumbleweed" ]
then
OS_UNSUPPORTED=1
fi
;;
arch)
# Rolling release, no version checking needed.
;;
manjaro)
# Rolling release, no version checking needed.
;;
alpine)
# All versions supported, no version checking needed.
# TODO: is that true? When was tailscale packaged?
;;
void)
# Rolling release, no version checking needed.
;;
gentoo)
# Rolling release, no version checking needed.
;;
freebsd)
if [ "$VERSION" != "12" ] && \
[ "$VERSION" != "13" ]
then
OS_UNSUPPORTED=1
fi
;;
openbsd)
OS_UNSUPPORTED=1
;;
macos)
# We delegate macOS installation to the app store, it will
# perform version checks for us.
;;
other-linux)
OS_UNSUPPORTED=1
;;
*)
OS_UNSUPPORTED=1
;;
esac
if [ "$OS_UNSUPPORTED" = "1" ]; then
case "$OS" in
other-linux)
echo "Couldn't determine what kind of Linux is running."
echo "You could try the static binaries at:"
echo "https://pkgs.tailscale.com/stable/#static"
;;
"")
echo "Couldn't determine what operating system you're running."
;;
*)
echo "$OS $VERSION isn't supported by this script yet."
;;
esac
echo
echo "If you'd like us to support your system better, please email support@tailscale.com"
echo "and tell us what OS you're running."
echo
echo "Please include the following information we gathered from your system:"
echo
echo "OS=$OS"
echo "VERSION=$VERSION"
echo "PACKAGETYPE=$PACKAGETYPE"
if type uname >/dev/null 2>&1; then
echo "UNAME=$(uname -a)"
else
echo "UNAME="
fi
echo
if [ -f /etc/os-release ]; then
cat /etc/os-release
else
echo "No /etc/os-release"
fi
exit 1
fi
# Step 3: work out if we can run privileged commands, and if so,
# how.
CAN_ROOT=
SUDO=
if [ "$(id -u)" = 0 ]; then
CAN_ROOT=1
SUDO=""
elif type sudo >/dev/null; then
CAN_ROOT=1
SUDO="sudo"
elif type doas >/dev/null; then
CAN_ROOT=1
SUDO="doas"
fi
if [ "$CAN_ROOT" != "1" ]; then
echo "This installer needs to run commands as root."
echo "We tried looking for 'sudo' and 'doas', but couldn't find them."
echo "Either re-run this script as root, or set up sudo/doas."
exit 1
fi
# Step 4: run the installation.
echo "Installing Tailscale for $OS $VERSION, using method $PACKAGETYPE"
case "$PACKAGETYPE" in
apt)
# Ideally we want to use curl, but on some installs we
# only have wget. Detect and use what's available.
CURL=
if type curl >/dev/null; then
CURL="curl -fsSL"
elif type wget >/dev/null; then
CURL="wget -q -O-"
fi
if [ -z "$CURL" ]; then
echo "The installer needs either curl or wget to download files."
echo "Please install either curl or wget to proceed."
exit 1
fi
# TODO: use newfangled per-repo signature scheme
set -x
$CURL "https://pkgs.tailscale.com/stable/$OS/$VERSION.gpg" | $SUDO apt-key add -
$CURL "https://pkgs.tailscale.com/stable/$OS/$VERSION.list" | $SUDO tee /etc/apt/sources.list.d/tailscale.list
$SUDO apt-get update
$SUDO apt-get install tailscale
set +x
;;
yum)
set -x
$SUDO yum install yum-utils
$SUDO yum-config-manager --add-repo "https://pkgs.tailscale.com/stable/$OS/$VERSION/tailscale.repo"
$SUDO yum install tailscale
$SUDO systemctl enable --now tailscaled
set +x
;;
dnf)
set -x
$SUDO dnf config-manager --add-repo "https://pkgs.tailscale.com/stable/$OS/$VERSION/tailscale.repo"
$SUDO dnf install tailscale
$SUDO systemctl enable --now tailscaled
set +x
;;
zypper)
set -x
$SUDO zypper ar -g -r "https://pkgs.tailscale.com/stable/$OS/$VERSION/tailscale.repo"
$SUDO zypper ref
$SUDO zypper in tailscale
$SUDO systemctl enable --now tailscaled
set +x
;;
pacman)
set -x
$SUDO pacman -S tailscale
$SUDO systemctl enable --now tailscaled
set +x
;;
apk)
set -x
$SUDO apk add tailscale
$SUDO rc-update add tailscale
set +x
;;
xbps)
set -x
$SUDO xbps-install tailscale
set +x
;;
emerge)
set -x
$SUDO emerge net-vpn/tailscale
set +x
;;
appstore)
set -x
open "https://apps.apple.com/us/app/tailscale/id1475387142"
set +x
;;
*)
echo "unexpected: unknown package type $PACKAGETYPE"
exit 1
;;
esac
echo "Installation complete! Log in to start using Tailscale by running:"
echo
if [ -z "$SUDO" ]; then
echo "tailscale up"
else
echo "$SUDO tailscale up"
fi
}
main

View File

@@ -0,0 +1,43 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The tshello server demonstrates how to use Tailscale as a library.
package main
import (
"fmt"
"html"
"log"
"net/http"
"strings"
"tailscale.com/tsnet"
)
func main() {
s := new(tsnet.Server)
ln, err := s.Listen("tcp", ":80")
if err != nil {
log.Fatal(err)
}
log.Fatal(http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
who, ok := s.WhoIs(r.RemoteAddr)
if !ok {
http.Error(w, "WhoIs failed", 500)
return
}
fmt.Fprintf(w, "<html><body><h1>Hello, world!</h1>\n")
fmt.Fprintf(w, "<p>You are <b>%s</b> from <b>%s</b> (%s)</p>",
html.EscapeString(who.UserProfile.LoginName),
html.EscapeString(firstLabel(who.Node.ComputedName)),
r.RemoteAddr)
})))
}
func firstLabel(s string) string {
if i := strings.Index(s, "."); i != -1 {
return s[:i]
}
return s
}

274
tsnet/tsnet.go Normal file
View File

@@ -0,0 +1,274 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package tsnet provides Tailscale as a library.
//
// It is an experimental work in progress.
package tsnet
import (
"errors"
"fmt"
"log"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"inet.af/netaddr"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/control/controlclient"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/smallzstd"
"tailscale.com/types/logger"
"tailscale.com/wgengine"
"tailscale.com/wgengine/monitor"
"tailscale.com/wgengine/netstack"
)
// Server is an embedded Tailscale server.
//
// Its exported fields may be changed until the first call to Listen.
type Server struct {
// Dir specifies the name of the directory to use for
// state. If empty, a directory is selected automatically
// under os.UserConfigDir (https://golang.org/pkg/os/#UserConfigDir).
// based on the name of the binary.
Dir string
// Hostname is the hostname to present to the control server.
// If empty, the binary name is used.l
Hostname string
// Logf, if non-nil, specifies the logger to use. By default,
// log.Printf is used.
Logf logger.Logf
initOnce sync.Once
initErr error
lb *ipnlocal.LocalBackend
// the state directory
dir string
hostname string
mu sync.Mutex
listeners map[listenKey]*listener
}
// WhoIs reports the node and user who owns the node with the given
// address. The addr may be an ip:port (as from an
// http.Request.RemoteAddr) or just an IP address.
func (s *Server) WhoIs(addr string) (w *apitype.WhoIsResponse, ok bool) {
ipp, err := netaddr.ParseIPPort(addr)
if err != nil {
ip, err := netaddr.ParseIP(addr)
if err != nil {
return nil, false
}
ipp = ipp.WithIP(ip)
}
n, up, ok := s.lb.WhoIs(ipp)
if !ok {
return nil, false
}
return &apitype.WhoIsResponse{
Node: n,
UserProfile: &up,
}, true
}
func (s *Server) doInit() {
if err := s.start(); err != nil {
s.initErr = fmt.Errorf("tsnet: %w", err)
}
}
func (s *Server) start() error {
if v, _ := strconv.ParseBool(os.Getenv("TAILSCALE_USE_WIP_CODE")); !v {
return errors.New("code disabled without environment variable TAILSCALE_USE_WIP_CODE set true")
}
exe, err := os.Executable()
if err != nil {
return err
}
prog := strings.TrimSuffix(strings.ToLower(filepath.Base(exe)), ".exe")
s.hostname = s.Hostname
if s.hostname == "" {
s.hostname = prog
}
s.dir = s.Dir
if s.dir == "" {
confDir, err := os.UserConfigDir()
if err != nil {
return err
}
s.dir = filepath.Join(confDir, "tslib-"+prog)
if err := os.MkdirAll(s.dir, 0700); err != nil {
return err
}
}
if fi, err := os.Stat(s.dir); err != nil {
return err
} else if !fi.IsDir() {
return fmt.Errorf("%v is not a directory", s.dir)
}
logf := s.Logf
if logf == nil {
logf = log.Printf
}
// TODO(bradfitz): start logtail? don't use filch, perhaps?
// only upload plumbed Logf?
linkMon, err := monitor.New(logf)
if err != nil {
return err
}
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
ListenPort: 0,
LinkMonitor: linkMon,
})
if err != nil {
return err
}
tunDev, magicConn, ok := eng.(wgengine.InternalsGetter).GetInternals()
if !ok {
return fmt.Errorf("%T is not a wgengine.InternalsGetter", eng)
}
ns, err := netstack.Create(logf, tunDev, eng, magicConn, false)
if err != nil {
return fmt.Errorf("netstack.Create: %w", err)
}
ns.ForwardTCPIn = s.forwardTCP
if err := ns.Start(); err != nil {
return fmt.Errorf("failed to start netstack: %w", err)
}
statePath := filepath.Join(s.dir, "tailscaled.state")
store, err := ipn.NewFileStore(statePath)
if err != nil {
return err
}
logid := "tslib-TODO"
lb, err := ipnlocal.NewLocalBackend(logf, logid, store, eng)
if err != nil {
return fmt.Errorf("NewLocalBackend: %v", err)
}
s.lb = lb
lb.SetDecompressor(func() (controlclient.Decompressor, error) {
return smallzstd.NewDecoder(nil)
})
prefs := ipn.NewPrefs()
prefs.Hostname = s.hostname
prefs.WantRunning = true
err = lb.Start(ipn.Options{
StateKey: ipn.GlobalDaemonStateKey,
UpdatePrefs: prefs,
})
if err != nil {
return fmt.Errorf("starting backend: %w", err)
}
if os.Getenv("TS_LOGIN") == "1" {
s.lb.StartLoginInteractive()
}
return nil
}
func (s *Server) forwardTCP(c net.Conn, port uint16) {
s.mu.Lock()
ln, ok := s.listeners[listenKey{"tcp", "", fmt.Sprint(port)}]
s.mu.Unlock()
if !ok {
c.Close()
return
}
t := time.NewTimer(time.Second)
defer t.Stop()
select {
case ln.conn <- c:
case <-t.C:
c.Close()
}
}
func (s *Server) Listen(network, addr string) (net.Listener, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, fmt.Errorf("tsnet: %w", err)
}
s.initOnce.Do(s.doInit)
if s.initErr != nil {
return nil, s.initErr
}
key := listenKey{network, host, port}
ln := &listener{
s: s,
key: key,
addr: addr,
conn: make(chan net.Conn),
}
s.mu.Lock()
if s.listeners == nil {
s.listeners = map[listenKey]*listener{}
}
if _, ok := s.listeners[key]; ok {
s.mu.Unlock()
return nil, fmt.Errorf("tsnet: listener already open for %s, %s", network, addr)
}
s.listeners[key] = ln
s.mu.Unlock()
return ln, nil
}
type listenKey struct {
network string
host string
port string
}
type listener struct {
s *Server
key listenKey
addr string
conn chan net.Conn
}
func (ln *listener) Accept() (net.Conn, error) {
c, ok := <-ln.conn
if !ok {
return nil, fmt.Errorf("tsnet: %w", net.ErrClosed)
}
return c, nil
}
func (ln *listener) Addr() net.Addr { return addr{ln} }
func (ln *listener) Close() error {
ln.s.mu.Lock()
defer ln.s.mu.Unlock()
if v, ok := ln.s.listeners[ln.key]; ok && v == ln {
delete(ln.s.listeners, ln.key)
close(ln.conn)
}
return nil
}
type addr struct{ ln *listener }
func (a addr) Network() string { return a.ln.key.network }
func (a addr) String() string { return a.ln.addr }

View File

@@ -650,7 +650,7 @@ func keepClientEndpoint(ep string) bool {
// the incoming JSON response.
return false
}
ip := ipp.IP
ip := ipp.IP()
if ip.Zone() != "" {
return false
}

View File

@@ -52,7 +52,7 @@ func (s FirewallType) key(src, dst netaddr.IPPort) fwKey {
switch s {
case EndpointIndependentFirewall:
case AddressDependentFirewall:
k.dst.IP = dst.IP
k.dst = k.dst.WithIP(dst.IP())
case AddressAndPortDependentFirewall:
k.dst = dst
default:

View File

@@ -62,7 +62,7 @@ func (t NATType) key(src, dst netaddr.IPPort) natKey {
switch t {
case EndpointIndependentNAT:
case AddressDependentNAT:
k.dst.IP = dst.IP
k.dst = k.dst.WithIP(dst.IP())
case AddressAndPortDependentNAT:
k.dst = dst
default:
@@ -171,7 +171,7 @@ func (n *SNAT44) HandleIn(p *Packet, iif *Interface) *Packet {
func (n *SNAT44) HandleForward(p *Packet, iif, oif *Interface) *Packet {
switch {
case oif == n.ExternalInterface:
if p.Src.IP == oif.V4() {
if p.Src.IP() == oif.V4() {
// Packet already NATed and is just retraversing Forward,
// don't touch it again.
return p
@@ -237,10 +237,7 @@ func (n *SNAT44) allocateMappedPort() (net.PacketConn, netaddr.IPPort) {
if err != nil {
panic(fmt.Sprintf("ran out of NAT ports: %v", err))
}
addr := netaddr.IPPort{
IP: ip,
Port: uint16(pc.LocalAddr().(*net.UDPAddr).Port),
}
addr := netaddr.IPPortFrom(ip, uint16(pc.LocalAddr().(*net.UDPAddr).Port))
return pc, addr
}

View File

@@ -138,7 +138,7 @@ func (n *Network) allocIPv4(iface *Interface) netaddr.IP {
return netaddr.IP{}
}
if n.lastV4.IsZero() {
n.lastV4 = n.Prefix4.IP
n.lastV4 = n.Prefix4.IP()
}
a := n.lastV4.As16()
addOne(&a, 15)
@@ -157,7 +157,7 @@ func (n *Network) allocIPv6(iface *Interface) netaddr.IP {
return netaddr.IP{}
}
if n.lastV6.IsZero() {
n.lastV6 = n.Prefix6.IP
n.lastV6 = n.Prefix6.IP()
}
a := n.lastV6.As16()
addOne(&a, 15)
@@ -183,15 +183,15 @@ func (n *Network) write(p *Packet) (num int, err error) {
n.mu.Lock()
defer n.mu.Unlock()
iface, ok := n.machine[p.Dst.IP]
iface, ok := n.machine[p.Dst.IP()]
if !ok {
// If the destination is within the network's authoritative
// range, no route to host.
if p.Dst.IP.Is4() && n.Prefix4.Contains(p.Dst.IP) {
if p.Dst.IP().Is4() && n.Prefix4.Contains(p.Dst.IP()) {
p.Trace("no route to %v", p.Dst.IP)
return len(p.Payload), nil
}
if p.Dst.IP.Is6() && n.Prefix6.Contains(p.Dst.IP) {
if p.Dst.IP().Is6() && n.Prefix6.Contains(p.Dst.IP()) {
p.Trace("no route to %v", p.Dst.IP)
return len(p.Payload), nil
}
@@ -363,7 +363,7 @@ func (m *Machine) isLocalIP(ip netaddr.IP) bool {
func (m *Machine) deliverIncomingPacket(p *Packet, iface *Interface) {
p.setLocator("mach=%s if=%s", m.Name, iface.name)
if m.isLocalIP(p.Dst.IP) {
if m.isLocalIP(p.Dst.IP()) {
m.deliverLocalPacket(p, iface)
} else {
m.forwardPacket(p, iface)
@@ -391,13 +391,13 @@ func (m *Machine) deliverLocalPacket(p *Packet, iface *Interface) {
defer m.mu.Unlock()
conns := m.conns4
if p.Dst.IP.Is6() {
if p.Dst.IP().Is6() {
conns = m.conns6
}
possibleDsts := []netaddr.IPPort{
p.Dst,
netaddr.IPPort{IP: v6unspec, Port: p.Dst.Port},
netaddr.IPPort{IP: v4unspec, Port: p.Dst.Port},
netaddr.IPPortFrom(v6unspec, p.Dst.Port()),
netaddr.IPPortFrom(v4unspec, p.Dst.Port()),
}
for _, dest := range possibleDsts {
c, ok := conns[dest]
@@ -417,7 +417,7 @@ func (m *Machine) deliverLocalPacket(p *Packet, iface *Interface) {
}
func (m *Machine) forwardPacket(p *Packet, iif *Interface) {
oif, err := m.interfaceForIP(p.Dst.IP)
oif, err := m.interfaceForIP(p.Dst.IP())
if err != nil {
p.Trace("%v", err)
return
@@ -501,7 +501,7 @@ func (m *Machine) Attach(interfaceName string, n *Network) *Interface {
}
}
sort.Slice(m.routes, func(i, j int) bool {
return m.routes[i].prefix.Bits > m.routes[j].prefix.Bits
return m.routes[i].prefix.Bits() > m.routes[j].prefix.Bits()
})
return f
@@ -515,33 +515,33 @@ var (
func (m *Machine) writePacket(p *Packet) (n int, err error) {
p.setLocator("mach=%s", m.Name)
iface, err := m.interfaceForIP(p.Dst.IP)
iface, err := m.interfaceForIP(p.Dst.IP())
if err != nil {
p.Trace("%v", err)
return 0, err
}
origSrcIP := p.Src.IP
origSrcIP := p.Src.IP()
switch {
case p.Src.IP == v4unspec:
case p.Src.IP() == v4unspec:
p.Trace("assigning srcIP=%s", iface.V4())
p.Src.IP = iface.V4()
case p.Src.IP == v6unspec:
p.Src = p.Src.WithIP(iface.V4())
case p.Src.IP() == v6unspec:
// v6unspec in Go means "any src, but match address families"
if p.Dst.IP.Is6() {
if p.Dst.IP().Is6() {
p.Trace("assigning srcIP=%s", iface.V6())
p.Src.IP = iface.V6()
} else if p.Dst.IP.Is4() {
p.Src = p.Src.WithIP(iface.V6())
} else if p.Dst.IP().Is4() {
p.Trace("assigning srcIP=%s", iface.V4())
p.Src.IP = iface.V4()
p.Src = p.Src.WithIP(iface.V4())
}
default:
if !iface.Contains(p.Src.IP) {
err := fmt.Errorf("can't send to %v with src %v on interface %v", p.Dst.IP, p.Src.IP, iface)
if !iface.Contains(p.Src.IP()) {
err := fmt.Errorf("can't send to %v with src %v on interface %v", p.Dst.IP(), p.Src.IP(), iface)
p.Trace("%v", err)
return 0, err
}
}
if p.Src.IP.IsZero() {
if p.Src.IP().IsZero() {
err := fmt.Errorf("no matching address for address family for %v", origSrcIP)
p.Trace("%v", err)
return 0, err
@@ -602,12 +602,12 @@ func (m *Machine) pickEphemPort() (port uint16, err error) {
func (m *Machine) portInUseLocked(port uint16) bool {
for ipp := range m.conns4 {
if ipp.Port == port {
if ipp.Port() == port {
return true
}
}
for ipp := range m.conns6 {
if ipp.Port == port {
if ipp.Port() == port {
return true
}
}
@@ -617,7 +617,7 @@ func (m *Machine) portInUseLocked(port uint16) bool {
func (m *Machine) registerConn4(c *conn) error {
m.mu.Lock()
defer m.mu.Unlock()
if c.ipp.IP.Is6() && c.ipp.IP != v6unspec {
if c.ipp.IP().Is6() && c.ipp.IP() != v6unspec {
return fmt.Errorf("registerConn4 got IPv6 %s", c.ipp)
}
return registerConn(&m.conns4, c)
@@ -632,7 +632,7 @@ func (m *Machine) unregisterConn4(c *conn) {
func (m *Machine) registerConn6(c *conn) error {
m.mu.Lock()
defer m.mu.Unlock()
if c.ipp.IP.Is4() {
if c.ipp.IP().Is4() {
return fmt.Errorf("registerConn6 got IPv4 %s", c.ipp)
}
return registerConn(&m.conns6, c)
@@ -707,7 +707,7 @@ func (m *Machine) ListenPacket(ctx context.Context, network, address string) (ne
return nil, nil
}
}
ipp := netaddr.IPPort{IP: ip, Port: port}
ipp := netaddr.IPPortFrom(ip, port)
c := &conn{
m: m,

View File

@@ -49,8 +49,8 @@ func TestSendPacket(t *testing.T) {
ifFoo := foo.Attach("eth0", internet)
ifBar := bar.Attach("enp0s1", internet)
fooAddr := netaddr.IPPort{IP: ifFoo.V4(), Port: 123}
barAddr := netaddr.IPPort{IP: ifBar.V4(), Port: 456}
fooAddr := netaddr.IPPortFrom(ifFoo.V4(), 123)
barAddr := netaddr.IPPortFrom(ifBar.V4(), 456)
ctx := context.Background()
fooPC, err := foo.ListenPacket(ctx, "udp4", fooAddr.String())
@@ -111,10 +111,10 @@ func TestMultiNetwork(t *testing.T) {
t.Fatal(err)
}
clientAddr := netaddr.IPPort{IP: ifClient.V4(), Port: 123}
natLANAddr := netaddr.IPPort{IP: ifNATLAN.V4(), Port: 456}
natWANAddr := netaddr.IPPort{IP: ifNATWAN.V4(), Port: 456}
serverAddr := netaddr.IPPort{IP: ifServer.V4(), Port: 789}
clientAddr := netaddr.IPPortFrom(ifClient.V4(), 123)
natLANAddr := netaddr.IPPortFrom(ifNATLAN.V4(), 456)
natWANAddr := netaddr.IPPortFrom(ifNATWAN.V4(), 456)
serverAddr := netaddr.IPPortFrom(ifServer.V4(), 789)
const msg1, msg2 = "hello", "world"
if _, err := natPC.WriteTo([]byte(msg1), clientAddr.UDPAddr()); err != nil {
@@ -154,8 +154,8 @@ type trivialNAT struct {
}
func (n *trivialNAT) HandleIn(p *Packet, iface *Interface) *Packet {
if iface == n.wanIf && p.Dst.IP == n.wanIf.V4() {
p.Dst.IP = n.clientIP
if iface == n.wanIf && p.Dst.IP() == n.wanIf.V4() {
p.Dst = p.Dst.WithIP(n.clientIP)
}
return p
}
@@ -167,13 +167,13 @@ func (n trivialNAT) HandleOut(p *Packet, iface *Interface) *Packet {
func (n *trivialNAT) HandleForward(p *Packet, iif, oif *Interface) *Packet {
// Outbound from LAN -> apply NAT, continue
if iif == n.lanIf && oif == n.wanIf {
if p.Src.IP == n.clientIP {
p.Src.IP = n.wanIf.V4()
if p.Src.IP() == n.clientIP {
p.Src = p.Src.WithIP(n.wanIf.V4())
}
return p
}
// Return traffic to LAN, allow if right dst.
if iif == n.wanIf && oif == n.lanIf && p.Dst.IP == n.clientIP {
if iif == n.wanIf && oif == n.lanIf && p.Dst.IP() == n.clientIP {
return p
}
// Else drop.
@@ -216,7 +216,7 @@ func TestPacketHandler(t *testing.T) {
}
const msg = "some message"
serverAddr := netaddr.IPPort{IP: ifServer.V4(), Port: 456}
serverAddr := netaddr.IPPortFrom(ifServer.V4(), 456)
if _, err := clientPC.WriteTo([]byte(msg), serverAddr.UDPAddr()); err != nil {
t.Fatal(err)
}
@@ -230,7 +230,7 @@ func TestPacketHandler(t *testing.T) {
if string(buf) != msg {
t.Errorf("read %q; want %q", buf, msg)
}
mappedAddr := netaddr.IPPort{IP: ifNATWAN.V4(), Port: 123}
mappedAddr := netaddr.IPPortFrom(ifNATWAN.V4(), 123)
if addr.String() != mappedAddr.String() {
t.Errorf("addr = %q; want %q", addr, mappedAddr)
}

View File

@@ -250,7 +250,7 @@ func TestConciseDiffFrom(t *testing.T) {
DERP: "127.3.3.40:2",
Endpoints: []string{"192.168.0.100:41641", "1.1.1.1:41641"},
DiscoKey: testDiscoKey("f00f00f00f"),
AllowedIPs: []netaddr.IPPrefix{{IP: netaddr.IPv4(100, 102, 103, 104), Bits: 32}},
AllowedIPs: []netaddr.IPPrefix{netaddr.IPPrefixFrom(netaddr.IPv4(100, 102, 103, 104), 32)},
},
},
},
@@ -263,7 +263,7 @@ func TestConciseDiffFrom(t *testing.T) {
DERP: "127.3.3.40:2",
Endpoints: []string{"192.168.0.100:41641", "1.1.1.1:41641"},
DiscoKey: testDiscoKey("ba4ba4ba4b"),
AllowedIPs: []netaddr.IPPrefix{{IP: netaddr.IPv4(100, 102, 103, 104), Bits: 32}},
AllowedIPs: []netaddr.IPPrefix{netaddr.IPPrefixFrom(netaddr.IPv4(100, 102, 103, 104), 32)},
},
},
},

View File

@@ -218,7 +218,7 @@ func (f *Firewall) UpdatePermittedRoutes(newRoutes []netaddr.IPPrefix) error {
},
}
var p protocol
if r.IP.Is4() {
if r.IP().Is4() {
p = protocolV4
} else {
p = protocolV6

View File

@@ -87,7 +87,7 @@ func main() {
}
logf("initialized ok.")
traf.Start(Addr1.IP, Addr2.IP, PayloadSize+ICMPMinSize, 0)
traf.Start(Addr1.IP(), Addr2.IP(), PayloadSize+ICMPMinSize, 0)
var cur, prev Snapshot
var pps int64

View File

@@ -78,7 +78,7 @@ func runOnce(b *testing.B, setup SetupFunc, payload int) {
logf("initialized. (n=%v)", b.N)
b.SetBytes(int64(payload))
traf.Start(Addr1.IP, Addr2.IP, payload, int64(b.N))
traf.Start(Addr1.IP(), Addr2.IP(), payload, int64(b.N))
var cur, prev Snapshot
var pps int64

View File

@@ -98,8 +98,8 @@ const (
// everything. Use in tests only, as it permits some kinds of spoofing
// attacks to reach the OS network stack.
func NewAllowAllForTest(logf logger.Logf) *Filter {
any4 := netaddr.IPPrefix{IP: netaddr.IPv4(0, 0, 0, 0), Bits: 0}
any6 := netaddr.IPPrefix{IP: netaddr.IPFrom16([16]byte{}), Bits: 0}
any4 := netaddr.IPPrefixFrom(netaddr.IPv4(0, 0, 0, 0), 0)
any6 := netaddr.IPPrefixFrom(netaddr.IPFrom16([16]byte{}), 0)
ms := []Match{
{
Srcs: []netaddr.IPPrefix{any4},
@@ -185,12 +185,12 @@ func matchesFamily(ms matches, keep func(netaddr.IP) bool) matches {
var retm Match
retm.IPProto = m.IPProto
for _, src := range m.Srcs {
if keep(src.IP) {
if keep(src.IP()) {
retm.Srcs = append(retm.Srcs, src)
}
}
for _, dst := range m.Dsts {
if keep(dst.Net.IP) {
if keep(dst.Net.IP()) {
retm.Dsts = append(retm.Dsts, dst)
}
}
@@ -266,12 +266,10 @@ func (f *Filter) CheckTCP(srcIP, dstIP netaddr.IP, dstPort uint16) Response {
default:
panic("unreachable")
}
pkt.Src.IP = srcIP
pkt.Dst.IP = dstIP
pkt.Src = netaddr.IPPortFrom(srcIP, 0)
pkt.Dst = netaddr.IPPortFrom(dstIP, dstPort)
pkt.IPProto = ipproto.TCP
pkt.TCPFlags = packet.TCPSyn
pkt.Src.Port = 0
pkt.Dst.Port = dstPort
return f.RunIn(pkt, 0)
}
@@ -321,7 +319,7 @@ func (f *Filter) runIn4(q *packet.Parsed) (r Response, why string) {
// A compromised peer could try to send us packets for
// destinations we didn't explicitly advertise. This check is to
// prevent that.
if !f.local.Contains(q.Dst.IP) {
if !f.local.Contains(q.Dst.IP()) {
return Drop, "destination not allowed"
}
@@ -378,7 +376,7 @@ func (f *Filter) runIn6(q *packet.Parsed) (r Response, why string) {
// A compromised peer could try to send us packets for
// destinations we didn't explicitly advertise. This check is to
// prevent that.
if !f.local.Contains(q.Dst.IP) {
if !f.local.Contains(q.Dst.IP()) {
return Drop, "destination not allowed"
}
@@ -480,11 +478,11 @@ func (f *Filter) pre(q *packet.Parsed, rf RunFlags, dir direction) Response {
return Drop
}
if q.Dst.IP.IsMulticast() {
if q.Dst.IP().IsMulticast() {
f.logRateLimit(rf, q, dir, Drop, "multicast")
return Drop
}
if q.Dst.IP.IsLinkLocalUnicast() && q.Dst.IP != gcpDNSAddr {
if q.Dst.IP().IsLinkLocalUnicast() && q.Dst.IP() != gcpDNSAddr {
f.logRateLimit(rf, q, dir, Drop, "link-local-unicast")
return Drop
}
@@ -506,7 +504,7 @@ func (f *Filter) pre(q *packet.Parsed, rf RunFlags, dir direction) Response {
// loggingAllowed reports whether p can appear in logs at all.
func (f *Filter) loggingAllowed(p *packet.Parsed) bool {
return f.logIPs.Contains(p.Src.IP) && f.logIPs.Contains(p.Dst.IP)
return f.logIPs.Contains(p.Src.IP()) && f.logIPs.Contains(p.Dst.IP())
}
// omitDropLogging reports whether packet p, which has already been
@@ -518,5 +516,5 @@ func omitDropLogging(p *packet.Parsed, dir direction) bool {
return false
}
return p.Dst.IP.IsMulticast() || (p.Dst.IP.IsLinkLocalUnicast() && p.Dst.IP != gcpDNSAddr) || p.IPProto == ipproto.IGMP
return p.Dst.IP().IsMulticast() || (p.Dst.IP().IsLinkLocalUnicast() && p.Dst.IP() != gcpDNSAddr) || p.IPProto == ipproto.IGMP
}

View File

@@ -120,9 +120,9 @@ func TestFilter(t *testing.T) {
if test.p.IPProto == ipproto.TCP {
var got Response
if test.p.IPVersion == 4 {
got = acl.CheckTCP(test.p.Src.IP, test.p.Dst.IP, test.p.Dst.Port)
got = acl.CheckTCP(test.p.Src.IP(), test.p.Dst.IP(), test.p.Dst.Port())
} else {
got = acl.CheckTCP(test.p.Src.IP, test.p.Dst.IP, test.p.Dst.Port)
got = acl.CheckTCP(test.p.Src.IP(), test.p.Dst.IP(), test.p.Dst.Port())
}
if test.want != got {
t.Errorf("#%d CheckTCP got=%v want=%v packet:%v", i, got, test.want, test.p)
@@ -254,7 +254,9 @@ func TestParseIPSet(t *testing.T) {
}
t.Errorf("parseIPSet(%q, %v) error: %v; want error %q", tt.host, tt.bits, err, tt.wantErr)
}
if diff := cmp.Diff(got, tt.want, cmp.Comparer(func(a, b netaddr.IP) bool { return a == b })); diff != "" {
compareIP := cmp.Comparer(func(a, b netaddr.IP) bool { return a == b })
compareIPPrefix := cmp.Comparer(func(a, b netaddr.IPPrefix) bool { return a == b })
if diff := cmp.Diff(got, tt.want, compareIP, compareIPPrefix); diff != "" {
t.Errorf("parseIPSet(%q, %v) = %s; want %s", tt.host, tt.bits, got, tt.want)
continue
}
@@ -425,10 +427,10 @@ func TestLoggingPrivacy(t *testing.T) {
f.logIPs = logB.IPSet()
var (
ts4 = netaddr.IPPort{IP: tsaddr.CGNATRange().IP.Next(), Port: 1234}
internet4 = netaddr.IPPort{IP: netaddr.MustParseIP("8.8.8.8"), Port: 1234}
ts6 = netaddr.IPPort{IP: tsaddr.TailscaleULARange().IP.Next(), Port: 1234}
internet6 = netaddr.IPPort{IP: netaddr.MustParseIP("2001::1"), Port: 1234}
ts4 = netaddr.IPPortFrom(tsaddr.CGNATRange().IP().Next(), 1234)
internet4 = netaddr.IPPortFrom(netaddr.MustParseIP("8.8.8.8"), 1234)
ts6 = netaddr.IPPortFrom(tsaddr.TailscaleULARange().IP().Next(), 1234)
internet6 = netaddr.IPPortFrom(netaddr.MustParseIP("2001::1"), 1234)
)
tests := []struct {
@@ -545,10 +547,8 @@ func parsed(proto ipproto.Proto, src, dst string, sport, dport uint16) packet.Pa
var ret packet.Parsed
ret.Decode(dummyPacket)
ret.IPProto = proto
ret.Src.IP = sip
ret.Src.Port = sport
ret.Dst.IP = dip
ret.Dst.Port = dport
ret.Src = netaddr.IPPortFrom(sip, sport)
ret.Dst = netaddr.IPPortFrom(dip, dport)
ret.TCPFlags = packet.TCPSyn
if sip.Is4() {
@@ -674,7 +674,7 @@ func nets(nets ...string) (ret []netaddr.IPPrefix) {
if ip.Is6() {
bits = 128
}
ret = append(ret, netaddr.IPPrefix{IP: ip, Bits: bits})
ret = append(ret, netaddr.IPPrefixFrom(ip, bits))
} else {
pfx, err := netaddr.ParseIPPrefix(s)
if err != nil {

View File

@@ -85,14 +85,14 @@ func (ms matches) match(q *packet.Parsed) bool {
if !protoInList(q.IPProto, m.IPProto) {
continue
}
if !ipInList(q.Src.IP, m.Srcs) {
if !ipInList(q.Src.IP(), m.Srcs) {
continue
}
for _, dst := range m.Dsts {
if !dst.Net.Contains(q.Dst.IP) {
if !dst.Net.Contains(q.Dst.IP()) {
continue
}
if !dst.Ports.contains(q.Dst.Port) {
if !dst.Ports.contains(q.Dst.Port()) {
continue
}
return true
@@ -103,11 +103,11 @@ func (ms matches) match(q *packet.Parsed) bool {
func (ms matches) matchIPsOnly(q *packet.Parsed) bool {
for _, m := range ms {
if !ipInList(q.Src.IP, m.Srcs) {
if !ipInList(q.Src.IP(), m.Srcs) {
continue
}
for _, dst := range m.Dsts {
if dst.Net.Contains(q.Dst.IP) {
if dst.Net.Contains(q.Dst.IP()) {
return true
}
}

View File

@@ -99,8 +99,8 @@ func parseIPSet(arg string, bits *int) ([]netaddr.IPPrefix, error) {
if arg == "*" {
// User explicitly requested wildcard.
return []netaddr.IPPrefix{
{IP: zeroIP4, Bits: 0},
{IP: zeroIP6, Bits: 0},
netaddr.IPPrefixFrom(zeroIP4, 0),
netaddr.IPPrefixFrom(zeroIP6, 0),
}, nil
}
if strings.Contains(arg, "/") {
@@ -124,7 +124,7 @@ func parseIPSet(arg string, bits *int) ([]netaddr.IPPrefix, error) {
if err != nil {
return nil, err
}
r := netaddr.IPRange{From: ip1, To: ip2}
r := netaddr.IPRangeFrom(ip1, ip2)
if !r.Valid() {
return nil, fmt.Errorf("invalid IP range %q", arg)
}
@@ -141,5 +141,5 @@ func parseIPSet(arg string, bits *int) ([]netaddr.IPPrefix, error) {
}
bits8 = uint8(*bits)
}
return []netaddr.IPPrefix{{IP: ip, Bits: bits8}}, nil
return []netaddr.IPPrefix{netaddr.IPPrefixFrom(ip, bits8)}, nil
}

View File

@@ -62,7 +62,7 @@ func (c *Conn) createLegacyEndpointLocked(pk key.Public, addrs wgcfg.IPPortSet,
// Add entries to c.addrsByUDP.
for _, ipp := range a.ipPorts {
if ipp.IP == derpMagicIPAddr {
if ipp.IP() == derpMagicIPAddr {
continue
}
c.addrsByUDP[ipp] = a
@@ -70,7 +70,7 @@ func (c *Conn) createLegacyEndpointLocked(pk key.Public, addrs wgcfg.IPPortSet,
// Remove previous c.addrsByUDP entries that are no longer in the new set.
for _, ipp := range oldIPP {
if ipp.IP != derpMagicIPAddr && c.addrsByUDP[ipp] != a {
if ipp.IP() != derpMagicIPAddr && c.addrsByUDP[ipp] != a {
delete(c.addrsByUDP, ipp)
}
}
@@ -388,8 +388,8 @@ type addrSet struct {
// derpID returns this addrSet's home DERP node, or 0 if none is found.
func (as *addrSet) derpID() int {
for _, ua := range as.ipPorts {
if ua.IP == derpMagicIPAddr {
return int(ua.Port)
if ua.IP() == derpMagicIPAddr {
return int(ua.Port())
}
}
return 0
@@ -428,7 +428,7 @@ func (a *addrSet) DstToString() string {
return a.rawdst
}
func (a *addrSet) DstIP() net.IP {
return a.dst().IP.IPAddr().IP // TODO: add netaddr accessor to cut an alloc here?
return a.dst().IP().IPAddr().IP // TODO: add netaddr accessor to cut an alloc here?
}
func (a *addrSet) SrcIP() net.IP { return nil }
func (a *addrSet) SrcToString() string { return "" }
@@ -437,7 +437,7 @@ func (a *addrSet) ClearSrc() {}
// updateDst records receipt of a packet from new. This is used to
// potentially update the transmit address used for this addrSet.
func (a *addrSet) updateDst(new netaddr.IPPort) error {
if new.IP == derpMagicIPAddr {
if new.IP() == derpMagicIPAddr {
// Never consider DERP addresses as a viable candidate for
// either curAddr or roamAddr. It's only ever a last resort
// choice, never a preferred choice.
@@ -539,7 +539,7 @@ func (as *addrSet) populatePeerStatus(ps *ipnstate.PeerStatus) {
ps.LastWrite = as.lastSend
for i, ua := range as.ipPorts {
if ua.IP == derpMagicIPAddr {
if ua.IP() == derpMagicIPAddr {
continue
}
uaStr := ua.String()

View File

@@ -832,7 +832,7 @@ func (c *Conn) Ping(peer *tailcfg.Node, res *ipnstate.PingResult, cb func(*ipnst
return
}
if len(peer.Addresses) > 0 {
res.NodeIP = peer.Addresses[0].IP.String()
res.NodeIP = peer.Addresses[0].IP().String()
}
res.NodeName = peer.Name // prefer DNS name
if res.NodeName == "" {
@@ -878,11 +878,11 @@ func (c *Conn) Ping(peer *tailcfg.Node, res *ipnstate.PingResult, cb func(*ipnst
// c.mu must be held
func (c *Conn) populateCLIPingResponseLocked(res *ipnstate.PingResult, latency time.Duration, ep netaddr.IPPort) {
res.LatencySeconds = latency.Seconds()
if ep.IP != derpMagicIPAddr {
if ep.IP() != derpMagicIPAddr {
res.Endpoint = ep.String()
return
}
regionID := int(ep.Port)
regionID := int(ep.Port())
res.DERPRegionID = regionID
if c.derpMap != nil {
if dr, ok := c.derpMap.Regions[regionID]; ok {
@@ -965,7 +965,7 @@ func (c *Conn) goDerpConnect(node int) {
if node == 0 {
return
}
go c.derpWriteChanOfAddr(netaddr.IPPort{IP: derpMagicIPAddr, Port: uint16(node)}, key.Public{})
go c.derpWriteChanOfAddr(netaddr.IPPortFrom(derpMagicIPAddr, uint16(node)), key.Public{})
}
// determineEndpoints returns the machine's endpoint addresses. It
@@ -1037,7 +1037,7 @@ func (c *Conn) determineEndpoints(ctx context.Context) ([]tailcfg.Endpoint, erro
ips = loopback
}
for _, ip := range ips {
addAddr(netaddr.IPPort{IP: ip, Port: uint16(localAddr.Port)}, tailcfg.EndpointLocal)
addAddr(netaddr.IPPortFrom(ip, uint16(localAddr.Port)), tailcfg.EndpointLocal)
}
} else {
// Our local endpoint is bound to a particular address.
@@ -1169,7 +1169,7 @@ func (c *Conn) sendUDPStd(addr *net.UDPAddr, b []byte) (sent bool, err error) {
// IPv6 address when the local machine doesn't have IPv6 support
// returns (false, nil); it's not an error, but nothing was sent.
func (c *Conn) sendAddr(addr netaddr.IPPort, pubKey key.Public, b []byte) (sent bool, err error) {
if addr.IP != derpMagicIPAddr {
if addr.IP() != derpMagicIPAddr {
return c.sendUDP(addr, b)
}
@@ -1211,10 +1211,10 @@ const bufferedDerpWritesBeforeDrop = 32
// If peer is non-zero, it can be used to find an active reverse
// path, without using addr.
func (c *Conn) derpWriteChanOfAddr(addr netaddr.IPPort, peer key.Public) chan<- derpWriteRequest {
if addr.IP != derpMagicIPAddr {
if addr.IP() != derpMagicIPAddr {
return nil
}
regionID := int(addr.Port)
regionID := int(addr.Port())
if c.networkDown() {
return nil
@@ -1402,7 +1402,7 @@ func (c *Conn) runDerpReader(ctx context.Context, derpFakeAddr netaddr.IPPort, d
}
didCopy := make(chan struct{}, 1)
regionID := int(derpFakeAddr.Port)
regionID := int(derpFakeAddr.Port())
res := derpReadResult{regionID: regionID}
var pkt derp.ReceivedPacket
res.copyBuf = func(dst []byte) int {
@@ -1676,7 +1676,7 @@ func (c *Conn) processDERPReadResult(dm derpReadResult, b []byte) (n int, ep con
return 0, nil
}
ipp := netaddr.IPPort{IP: derpMagicIPAddr, Port: uint16(regionID)}
ipp := netaddr.IPPortFrom(derpMagicIPAddr, uint16(regionID))
if c.handleDiscoMessage(b[:n], ipp) {
return 0, nil
}
@@ -1922,7 +1922,7 @@ func (c *Conn) handleDiscoMessage(msg []byte, src netaddr.IPPort) (isDiscoMsg bo
}
de.handlePongConnLocked(dm, src)
case *disco.CallMeMaybe:
if src.IP != derpMagicIPAddr {
if src.IP() != derpMagicIPAddr {
// CallMeMaybe messages should only come via DERP.
c.logf("[unexpected] CallMeMaybe packets should only come via DERP")
return
@@ -2722,7 +2722,7 @@ func (c *Conn) resetEndpointStates() {
// packIPPort packs an IPPort into the form wanted by WireGuard.
func packIPPort(ua netaddr.IPPort) []byte {
ip := ua.IP.Unmap()
ip := ua.IP().Unmap()
a := ip.As16()
ipb := a[:]
if ip.Is4() {
@@ -2730,8 +2730,8 @@ func packIPPort(ua netaddr.IPPort) []byte {
}
b := make([]byte, 0, len(ipb)+2)
b = append(b, ipb...)
b = append(b, byte(ua.Port))
b = append(b, byte(ua.Port>>8))
b = append(b, byte(ua.Port()))
b = append(b, byte(ua.Port()>>8))
return b
}
@@ -2972,15 +2972,15 @@ func peerShort(k key.Public) string {
}
func sbPrintAddr(sb *strings.Builder, a netaddr.IPPort) {
is6 := a.IP.Is6()
is6 := a.IP().Is6()
if is6 {
sb.WriteByte('[')
}
fmt.Fprintf(sb, "%s", a.IP)
fmt.Fprintf(sb, "%s", a.IP())
if is6 {
sb.WriteByte(']')
}
fmt.Fprintf(sb, ":%d", a.Port)
fmt.Fprintf(sb, ":%d", a.Port())
}
func (c *Conn) derpRegionCodeOfAddrLocked(ipPort string) string {
@@ -3017,15 +3017,15 @@ func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) {
if !addr.IsSingleIP() {
continue
}
sb.AddTailscaleIP(addr.IP)
sb.AddTailscaleIP(addr.IP())
// TailAddr previously only allowed for a
// single Tailscale IP. For compatibility for
// a couple releases starting with 1.8, keep
// that field pulled out separately.
if addr.IP.Is4() {
tailAddr4 = addr.IP.String()
if addr.IP().Is4() {
tailAddr4 = addr.IP().String()
}
tailscaleIPs = append(tailscaleIPs, addr.IP)
tailscaleIPs = append(tailscaleIPs, addr.IP())
}
}
@@ -3084,8 +3084,8 @@ func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) {
}
func ippDebugString(ua netaddr.IPPort) string {
if ua.IP == derpMagicIPAddr {
return fmt.Sprintf("derp-%d", ua.Port)
if ua.IP() == derpMagicIPAddr {
return fmt.Sprintf("derp-%d", ua.Port())
}
return ua.String()
}
@@ -3254,10 +3254,7 @@ func (de *discoEndpoint) initFakeUDPAddr() {
addr[0] = 0xfd
addr[1] = 0x00
binary.BigEndian.PutUint64(addr[2:], uint64(reflect.ValueOf(de).Pointer()))
de.fakeWGAddr = netaddr.IPPort{
IP: netaddr.IPFrom16(addr),
Port: 12345,
}
de.fakeWGAddr = netaddr.IPPortFrom(netaddr.IPFrom16(addr), 12345)
}
// isFirstRecvActivityInAwhile notes that receive activity has occured for this
@@ -3632,7 +3629,7 @@ func (de *discoEndpoint) handlePongConnLocked(m *disco.Pong, src netaddr.IPPort)
de.mu.Lock()
defer de.mu.Unlock()
isDerp := src.IP == derpMagicIPAddr
isDerp := src.IP() == derpMagicIPAddr
sp, ok := de.sentPing[m.TxID]
if !ok {
@@ -3708,13 +3705,13 @@ func betterAddr(a, b addrLatency) bool {
if a.IsZero() {
return false
}
if a.IP.Is6() && b.IP.Is4() {
if a.IP().Is6() && b.IP().Is4() {
// Prefer IPv6 for being a bit more robust, as long as
// the latencies are roughly equivalent.
if a.latency/10*9 < b.latency {
return true
}
} else if a.IP.Is4() && b.IP.Is6() {
} else if a.IP().Is4() && b.IP().Is6() {
if betterAddr(b, a) {
return false
}
@@ -3754,7 +3751,7 @@ func (de *discoEndpoint) handleCallMeMaybe(m *disco.CallMeMaybe) {
}
var newEPs []netaddr.IPPort
for _, ep := range m.MyNumber {
if ep.IP.Is6() && ep.IP.IsLinkLocalUnicast() {
if ep.IP().Is6() && ep.IP().IsLinkLocalUnicast() {
// We send these out, but ignore them for now.
// TODO: teach the ping code to ping on all interfaces
// for these.

View File

@@ -252,13 +252,13 @@ func meshStacks(logf logger.Logf, ms []*magicStack) (cleanup func()) {
nm := &netmap.NetworkMap{
PrivateKey: me.privateKey,
NodeKey: tailcfg.NodeKey(me.privateKey.Public()),
Addresses: []netaddr.IPPrefix{{IP: netaddr.IPv4(1, 0, 0, byte(myIdx+1)), Bits: 32}},
Addresses: []netaddr.IPPrefix{netaddr.IPPrefixFrom(netaddr.IPv4(1, 0, 0, byte(myIdx+1)), 32)},
}
for i, peer := range ms {
if i == myIdx {
continue
}
addrs := []netaddr.IPPrefix{{IP: netaddr.IPv4(1, 0, 0, byte(i+1)), Bits: 32}}
addrs := []netaddr.IPPrefix{netaddr.IPPrefixFrom(netaddr.IPv4(1, 0, 0, byte(i+1)), 32)}
peer := &tailcfg.Node{
ID: tailcfg.NodeID(i + 1),
Name: fmt.Sprintf("node%d", i+1),
@@ -433,7 +433,7 @@ func TestPickDERPFallback(t *testing.T) {
// But move if peers are elsewhere.
const otherNode = 789
c.addrsByKey = map[key.Public]*addrSet{
{1}: {ipPorts: []netaddr.IPPort{{IP: derpMagicIPAddr, Port: otherNode}}},
{1}: {ipPorts: []netaddr.IPPort{netaddr.IPPortFrom(derpMagicIPAddr, otherNode)}},
}
if got := c.pickDERPFallback(); got != otherNode {
t.Errorf("didn't join peers: got %v; want %v", got, someNode)
@@ -887,8 +887,8 @@ func testTwoDevicePing(t *testing.T, d *devices) {
defer m2.Close()
addrs := []netaddr.IPPort{
{IP: d.m1IP, Port: m1.conn.LocalPort()},
{IP: d.m2IP, Port: m2.conn.LocalPort()},
netaddr.IPPortFrom(d.m1IP, m1.conn.LocalPort()),
netaddr.IPPortFrom(d.m2IP, m2.conn.LocalPort()),
}
cfgs := makeConfigs(t, addrs)
@@ -1555,7 +1555,7 @@ func TestEndpointSetsEqual(t *testing.T) {
s := func(ports ...uint16) (ret []tailcfg.Endpoint) {
for _, port := range ports {
ret = append(ret, tailcfg.Endpoint{
Addr: netaddr.IPPort{Port: port},
Addr: netaddr.IPPortFrom(netaddr.IP{}, port),
})
}
return

View File

@@ -130,11 +130,11 @@ func netaddrIP(std net.IP) netaddr.IP {
func netaddrIPPrefix(std net.IP, bits uint8) netaddr.IPPrefix {
ip, _ := netaddr.FromStdIP(std)
return netaddr.IPPrefix{IP: ip, Bits: bits}
return netaddr.IPPrefixFrom(ip, bits)
}
func condNetAddrPrefix(ipp netaddr.IPPrefix) string {
if ipp.IP.IsZero() {
if ipp.IP().IsZero() {
return ""
}
return ipp.String()
@@ -157,7 +157,7 @@ type newRouteMessage struct {
const tsTable = 52
func (m *newRouteMessage) ignore() bool {
return m.Table == tsTable || tsaddr.IsTailscaleIP(m.Dst.IP)
return m.Table == tsTable || tsaddr.IsTailscaleIP(m.Dst.IP())
}
// newAddrMessage is a message for a new address being added.

View File

@@ -48,6 +48,12 @@ const debugNetstack = false
// and implements wgengine.FakeImpl to act as a userspace network
// stack when Tailscale is running in fake mode.
type Impl struct {
// ForwardTCPIn, if non-nil, handles forwarding an inbound TCP
// connection.
// TODO(bradfitz): provide mechanism for tsnet to reject a
// port other than accepting it and closing it.
ForwardTCPIn func(c net.Conn, port uint16)
ipstack *stack.Stack
linkEP *channel.Endpoint
tundev *tstun.Wrapper
@@ -173,7 +179,7 @@ func DNSMapFromNetworkMap(nm *netmap.NetworkMap) DNSMap {
suffix := nm.MagicDNSSuffix()
if nm.Name != "" && len(nm.Addresses) > 0 {
ip := nm.Addresses[0].IP
ip := nm.Addresses[0].IP()
ret[strings.TrimRight(nm.Name, ".")] = ip
if dnsname.HasSuffix(nm.Name, suffix) {
ret[dnsname.TrimSuffix(nm.Name, suffix)] = ip
@@ -181,7 +187,7 @@ func DNSMapFromNetworkMap(nm *netmap.NetworkMap) DNSMap {
}
for _, p := range nm.Peers {
if p.Name != "" && len(p.Addresses) > 0 {
ip := p.Addresses[0].IP
ip := p.Addresses[0].IP()
ret[strings.TrimRight(p.Name, ".")] = ip
if dnsname.HasSuffix(p.Name, suffix) {
ret[dnsname.TrimSuffix(p.Name, suffix)] = ip
@@ -221,8 +227,8 @@ func (ns *Impl) removeSubnetAddress(ip netaddr.IP) {
func ipPrefixToAddressWithPrefix(ipp netaddr.IPPrefix) tcpip.AddressWithPrefix {
return tcpip.AddressWithPrefix{
Address: tcpip.Address(ipp.IP.IPAddr().IP),
PrefixLen: int(ipp.Bits),
Address: tcpip.Address(ipp.IP().IPAddr().IP),
PrefixLen: int(ipp.Bits()),
}
}
@@ -316,7 +322,7 @@ func (m DNSMap) Resolve(ctx context.Context, addr string) (netaddr.IPPort, error
// Try MagicDNS first, else otherwise a real DNS lookup.
ip := m[host]
if !ip.IsZero() {
return netaddr.IPPort{IP: ip, Port: uint16(port16)}, nil
return netaddr.IPPortFrom(ip, uint16(port16)), nil
}
// No MagicDNS name so try real DNS.
@@ -329,7 +335,7 @@ func (m DNSMap) Resolve(ctx context.Context, addr string) (netaddr.IPPort, error
return netaddr.IPPort{}, fmt.Errorf("DNS lookup returned no results for %q", host)
}
ip, _ = netaddr.FromStdIP(ips[0])
return netaddr.IPPort{IP: ip, Port: uint16(port16)}, nil
return netaddr.IPPortFrom(ip, uint16(port16)), nil
}
func (ns *Impl) DialContextTCP(ctx context.Context, addr string) (*gonet.TCPConn, error) {
@@ -343,11 +349,11 @@ func (ns *Impl) DialContextTCP(ctx context.Context, addr string) (*gonet.TCPConn
}
remoteAddress := tcpip.FullAddress{
NIC: nicID,
Addr: tcpip.Address(remoteIPPort.IP.IPAddr().IP),
Port: remoteIPPort.Port,
Addr: tcpip.Address(remoteIPPort.IP().IPAddr().IP),
Port: remoteIPPort.Port(),
}
var ipType tcpip.NetworkProtocolNumber
if remoteIPPort.IP.Is4() {
if remoteIPPort.IP().Is4() {
ipType = ipv4.ProtocolNumber
} else {
ipType = ipv6.ProtocolNumber
@@ -389,7 +395,7 @@ func (ns *Impl) isLocalIP(ip netaddr.IP) bool {
}
func (ns *Impl) injectInbound(p *packet.Parsed, t *tstun.Wrapper) filter.Response {
if ns.onlySubnets && ns.isLocalIP(p.Dst.IP) {
if ns.onlySubnets && ns.isLocalIP(p.Dst.IP()) {
// In hybrid ("only subnets") mode, bail out early if
// the traffic is destined for an actual Tailscale
// address. The real host OS interface will handle it.
@@ -441,11 +447,15 @@ func (ns *Impl) acceptTCP(r *tcp.ForwarderRequest) {
r.Complete(true)
return
}
r.Complete(false)
c := gonet.NewTCPConn(&wq, ep)
if ns.ForwardTCPIn != nil {
ns.ForwardTCPIn(c, reqDetails.LocalPort)
return
}
if isTailscaleIP {
dialAddr = tcpip.Address(net.ParseIP("127.0.0.1")).To4()
}
r.Complete(false)
c := gonet.NewTCPConn(&wq, ep)
ns.forwardTCP(c, &wq, dialAddr, reqDetails.LocalPort)
}

View File

@@ -115,8 +115,8 @@ func (e *userspaceEngine) trackOpenPostFilterOut(pp *packet.Parsed, t *tstun.Wra
// Don't start timers tracking those. They won't succeed anyway. Avoids log spam
// like:
// open-conn-track: timeout opening (100.115.73.60:52501 => 17.125.252.5:443); no associated peer node
if runtime.GOOS == "ios" && flow.Dst.Port == 443 && !tsaddr.IsTailscaleIP(flow.Dst.IP) {
if _, err := e.peerForIP(flow.Dst.IP); err != nil {
if runtime.GOOS == "ios" && flow.Dst.Port() == 443 && !tsaddr.IsTailscaleIP(flow.Dst.IP()) {
if _, err := e.peerForIP(flow.Dst.IP()); err != nil {
return
}
}
@@ -156,7 +156,7 @@ func (e *userspaceEngine) onOpenTimeout(flow flowtrack.Tuple) {
}
// Diagnose why it might've timed out.
n, err := e.peerForIP(flow.Dst.IP)
n, err := e.peerForIP(flow.Dst.IP())
if err != nil {
e.logf("open-conn-track: timeout opening %v; peerForIP: %v", flow, err)
return
@@ -193,7 +193,7 @@ func (e *userspaceEngine) onOpenTimeout(flow flowtrack.Tuple) {
if ps == nil {
onlyZeroRoute := true // whether peerForIP returned n only because its /0 route matched
for _, r := range n.AllowedIPs {
if r.Bits != 0 && r.Contains(flow.Dst.IP) {
if r.Bits() != 0 && r.Contains(flow.Dst.IP()) {
onlyZeroRoute = false
break
}

View File

@@ -324,16 +324,16 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) (retErr error) {
var firstGateway6 *net.IP
addresses := make([]*net.IPNet, 0, len(cfg.LocalAddrs))
for _, addr := range cfg.LocalAddrs {
if (addr.IP.Is4() && ipif4 == nil) || (addr.IP.Is6() && ipif6 == nil) {
if (addr.IP().Is4() && ipif4 == nil) || (addr.IP().Is6() && ipif6 == nil) {
// Can't program addresses for disabled protocol.
continue
}
ipnet := addr.IPNet()
addresses = append(addresses, ipnet)
gateway := ipnet.IP
if addr.IP.Is4() && firstGateway4 == nil {
if addr.IP().Is4() && firstGateway4 == nil {
firstGateway4 = &gateway
} else if addr.IP.Is6() && firstGateway6 == nil {
} else if addr.IP().Is6() && firstGateway6 == nil {
firstGateway6 = &gateway
}
}
@@ -342,12 +342,12 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) (retErr error) {
foundDefault4 := false
foundDefault6 := false
for _, route := range cfg.Routes {
if (route.IP.Is4() && ipif4 == nil) || (route.IP.Is6() && ipif6 == nil) {
if (route.IP().Is4() && ipif4 == nil) || (route.IP().Is6() && ipif6 == nil) {
// Can't program routes for disabled protocol.
continue
}
if route.IP.Is6() && firstGateway6 == nil {
if route.IP().Is6() && firstGateway6 == nil {
// Windows won't let us set IPv6 routes without having an
// IPv6 local address set. However, when we've configured
// a default route, we want to forcibly grab IPv6 traffic
@@ -357,16 +357,16 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) (retErr error) {
ipnet := &net.IPNet{tsaddr.Tailscale4To6Placeholder().IPAddr().IP, net.CIDRMask(128, 128)}
addresses = append(addresses, ipnet)
firstGateway6 = &ipnet.IP
} else if route.IP.Is4() && firstGateway4 == nil {
} else if route.IP().Is4() && firstGateway4 == nil {
// TODO: do same dummy behavior as v6?
return errors.New("due to a Windows limitation, one cannot have interface routes without an interface address")
}
ipn := route.IPNet()
var gateway net.IP
if route.IP.Is4() {
if route.IP().Is4() {
gateway = *firstGateway4
} else if route.IP.Is6() {
} else if route.IP().Is6() {
gateway = *firstGateway6
}
r := winipcfg.RouteData{
@@ -385,13 +385,13 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) (retErr error) {
// then the interface's IP won't be pingable.
continue
}
if route.IP.Is4() {
if route.Bits == 0 {
if route.IP().Is4() {
if route.Bits() == 0 {
foundDefault4 = true
}
r.NextHop = *firstGateway4
} else if route.IP.Is6() {
if route.Bits == 0 {
} else if route.IP().Is6() {
if route.Bits() == 0 {
foundDefault6 = true
}
r.NextHop = *firstGateway6
@@ -760,11 +760,8 @@ func filterRoutes(routes []*winipcfg.RouteData, dontDelete []netaddr.IPPrefix) [
if nr.IsSingleIP() {
continue
}
lastIP := nr.Range().To
ddm[netaddr.IPPrefix{
IP: lastIP,
Bits: lastIP.BitLen(),
}] = true
lastIP := nr.Range().To()
ddm[netaddr.IPPrefixFrom(lastIP, lastIP.BitLen())] = true
}
filtered := make([]*winipcfg.RouteData, 0, len(routes))
for _, r := range routes {

View File

@@ -360,7 +360,7 @@ func (r *linuxRouter) setNetfilterMode(mode preftype.NetfilterMode) error {
}
for cidr := range r.addrs {
if err := r.addLoopbackRule(cidr.IP); err != nil {
if err := r.addLoopbackRule(cidr.IP()); err != nil {
return err
}
}
@@ -372,13 +372,13 @@ func (r *linuxRouter) setNetfilterMode(mode preftype.NetfilterMode) error {
// address is already assigned to the interface, or if the addition
// fails.
func (r *linuxRouter) addAddress(addr netaddr.IPPrefix) error {
if !r.v6Available && addr.IP.Is6() {
if !r.v6Available && addr.IP().Is6() {
return nil
}
if err := r.cmd.run("ip", "addr", "add", addr.String(), "dev", r.tunname); err != nil {
return fmt.Errorf("adding address %q to tunnel interface: %w", addr, err)
}
if err := r.addLoopbackRule(addr.IP); err != nil {
if err := r.addLoopbackRule(addr.IP()); err != nil {
return err
}
return nil
@@ -388,10 +388,10 @@ func (r *linuxRouter) addAddress(addr netaddr.IPPrefix) error {
// the address is not assigned to the interface, or if the removal
// fails.
func (r *linuxRouter) delAddress(addr netaddr.IPPrefix) error {
if !r.v6Available && addr.IP.Is6() {
if !r.v6Available && addr.IP().Is6() {
return nil
}
if err := r.delLoopbackRule(addr.IP); err != nil {
if err := r.delLoopbackRule(addr.IP()); err != nil {
return err
}
if err := r.cmd.run("ip", "addr", "del", addr.String(), "dev", r.tunname); err != nil {
@@ -463,7 +463,7 @@ func (r *linuxRouter) addThrowRoute(cidr netaddr.IPPrefix) error {
}
func (r *linuxRouter) addRouteDef(routeDef []string, cidr netaddr.IPPrefix) error {
if !r.v6Available && cidr.IP.Is6() {
if !r.v6Available && cidr.IP().Is6() {
return nil
}
args := append([]string{"ip", "route", "add"}, routeDef...)
@@ -490,7 +490,7 @@ func (r *linuxRouter) delThrowRoute(cidr netaddr.IPPrefix) error {
}
func (r *linuxRouter) delRouteDef(routeDef []string, cidr netaddr.IPPrefix) error {
if !r.v6Available && cidr.IP.Is6() {
if !r.v6Available && cidr.IP().Is6() {
return nil
}
args := append([]string{"ip", "route", "del"}, routeDef...)
@@ -520,7 +520,7 @@ func dashFam(ip netaddr.IP) string {
}
func (r *linuxRouter) hasRoute(routeDef []string, cidr netaddr.IPPrefix) (bool, error) {
args := append([]string{"ip", dashFam(cidr.IP), "route", "show"}, routeDef...)
args := append([]string{"ip", dashFam(cidr.IP()), "route", "show"}, routeDef...)
if r.ipRuleAvailable {
args = append(args, "table", tailscaleRouteTable)
}

View File

@@ -69,11 +69,11 @@ func (r *openbsdRouter) Set(cfg *Config) error {
localAddr4 := netaddr.IPPrefix{}
localAddr6 := netaddr.IPPrefix{}
for _, addr := range cfg.LocalAddrs {
if addr.IP.Is4() {
if addr.IP().Is4() {
numIPv4++
localAddr4 = addr
}
if addr.IP.Is6() {
if addr.IP().Is6() {
numIPv6++
localAddr6 = addr
}
@@ -98,7 +98,7 @@ func (r *openbsdRouter) Set(cfg *Config) error {
routedel := []string{"route", "-q", "-n",
"del", "-inet", r.local4.String(),
"-iface", r.local4.IP.String()}
"-iface", r.local4.IP().String()}
if out, err := cmd(routedel...).CombinedOutput(); err != nil {
r.logf("route del failed: %v: %v\n%s", routedel, err, out)
if errq == nil {
@@ -120,7 +120,7 @@ func (r *openbsdRouter) Set(cfg *Config) error {
routeadd := []string{"route", "-q", "-n",
"add", "-inet", localAddr4.String(),
"-iface", localAddr4.IP.String()}
"-iface", localAddr4.IP().String()}
if out, err := cmd(routeadd...).CombinedOutput(); err != nil {
r.logf("route add failed: %v: %v\n%s", routeadd, err, out)
if errq == nil {
@@ -134,7 +134,7 @@ func (r *openbsdRouter) Set(cfg *Config) error {
// in https://github.com/tailscale/tailscale/issues/1307 we made
// FreeBSD use a /48 for IPv6 addresses, which is nice because we
// don't need to additionally add routing entries. Do that here too.
localAddr6 = netaddr.IPPrefix{localAddr6.IP, 48}
localAddr6 = netaddr.IPPrefixFrom(localAddr6.IP(), 48)
}
if localAddr6 != r.local6 {
@@ -171,10 +171,10 @@ func (r *openbsdRouter) Set(cfg *Config) error {
if _, keep := newRoutes[route]; !keep {
net := route.IPNet()
nip := net.IP.Mask(net.Mask)
nstr := fmt.Sprintf("%v/%d", nip, route.Bits)
nstr := fmt.Sprintf("%v/%d", nip, route.Bits())
routedel := []string{"route", "-q", "-n",
"del", "-inet", nstr,
"-iface", localAddr4.IP.String()}
"-iface", localAddr4.IP().String()}
out, err := cmd(routedel...).CombinedOutput()
if err != nil {
r.logf("route del failed: %v: %v\n%s", routedel, err, out)
@@ -188,10 +188,10 @@ func (r *openbsdRouter) Set(cfg *Config) error {
if _, exists := r.routes[route]; !exists {
net := route.IPNet()
nip := net.IP.Mask(net.Mask)
nstr := fmt.Sprintf("%v/%d", nip, route.Bits)
nstr := fmt.Sprintf("%v/%d", nip, route.Bits())
routeadd := []string{"route", "-q", "-n",
"add", "-inet", nstr,
"-iface", localAddr4.IP.String()}
"-iface", localAddr4.IP().String()}
out, err := cmd(routeadd...).CombinedOutput()
if err != nil {
r.logf("addr add failed: %v: %v\n%s", routeadd, err, out)

View File

@@ -87,7 +87,7 @@ func (r *userspaceBSDRouter) Up() error {
}
func inet(p netaddr.IPPrefix) string {
if p.IP.Is6() {
if p.IP().Is6() {
return "inet6"
}
return "inet"
@@ -116,15 +116,15 @@ func (r *userspaceBSDRouter) Set(cfg *Config) (reterr error) {
}
for _, addr := range r.addrsToAdd(cfg.LocalAddrs) {
var arg []string
if runtime.GOOS == "freebsd" && addr.IP.Is6() && addr.Bits == 128 {
if runtime.GOOS == "freebsd" && addr.IP().Is6() && addr.Bits() == 128 {
// FreeBSD rejects tun addresses of the form fc00::1/128 -> fc00::1,
// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=218508
// Instead add our whole /48, which works because we use a /48 route.
// Full history: https://github.com/tailscale/tailscale/issues/1307
tmp := netaddr.IPPrefix{IP: addr.IP, Bits: 48}
tmp := netaddr.IPPrefixFrom(addr.IP(), 48)
arg = []string{"ifconfig", r.tunname, inet(tmp), tmp.String()}
} else {
arg = []string{"ifconfig", r.tunname, inet(addr), addr.String(), addr.IP.String()}
arg = []string{"ifconfig", r.tunname, inet(addr), addr.String(), addr.IP().String()}
}
out, err := cmd(arg...).CombinedOutput()
if err != nil {
@@ -148,7 +148,7 @@ func (r *userspaceBSDRouter) Set(cfg *Config) (reterr error) {
if _, keep := newRoutes[route]; !keep {
net := route.IPNet()
nip := net.IP.Mask(net.Mask)
nstr := fmt.Sprintf("%v/%d", nip, route.Bits)
nstr := fmt.Sprintf("%v/%d", nip, route.Bits())
del := "del"
if version.OS() == "macOS" {
del = "delete"
@@ -168,7 +168,7 @@ func (r *userspaceBSDRouter) Set(cfg *Config) (reterr error) {
if _, exists := r.routes[route]; !exists {
net := route.IPNet()
nip := net.IP.Mask(net.Mask)
nstr := fmt.Sprintf("%v/%d", nip, route.Bits)
nstr := fmt.Sprintf("%v/%d", nip, route.Bits())
routeadd := []string{"route", "-q", "-n",
"add", "-" + inet(route), nstr,
"-iface", r.tunname}

View File

@@ -91,7 +91,7 @@ func (r *winRouter) Set(cfg *Config) error {
func hasDefaultRoute(routes []netaddr.IPPrefix) bool {
for _, route := range routes {
if route.Bits == 0 {
if route.Bits() == 0 {
return true
}
}

View File

@@ -399,7 +399,7 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper)
isLocalAddr, ok := e.isLocalAddr.Load().(func(netaddr.IP) bool)
if !ok {
e.logf("[unexpected] e.isLocalAddr was nil, can't check for loopback packet")
} else if isLocalAddr(p.Dst.IP) {
} else if isLocalAddr(p.Dst.IP()) {
// macOS NetworkExtension directs packets destined to the
// tunnel's local IP address into the tunnel, instead of
// looping back within the kernel network stack. We have to
@@ -415,7 +415,7 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper)
// handleDNS is an outbound pre-filter resolving Tailscale domains.
func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.Wrapper) filter.Response {
if p.Dst.IP == magicDNSIP && p.Dst.Port == magicDNSPort && p.IPProto == ipproto.UDP {
if p.Dst.IP() == magicDNSIP && p.Dst.Port() == magicDNSPort && p.IPProto == ipproto.UDP {
err := e.dns.EnqueueRequest(append([]byte(nil), p.Payload()...), p.Src)
if err != nil {
e.logf("dns: enqueue: %v", err)
@@ -440,10 +440,10 @@ func (e *userspaceEngine) pollResolver() {
h := packet.UDP4Header{
IP4Header: packet.IP4Header{
Src: magicDNSIP,
Dst: to.IP,
Dst: to.IP(),
},
SrcPort: magicDNSPort,
DstPort: to.Port,
DstPort: to.Port(),
}
hlen := h.Len()
@@ -620,8 +620,8 @@ func (e *userspaceEngine) maybeReconfigWireguardLocked(discoChanged map[key.Publ
trackDisco = append(trackDisco, dk)
recentlyActive := false
for _, cidr := range p.AllowedIPs {
trackIPs = append(trackIPs, cidr.IP)
recentlyActive = recentlyActive || e.isActiveSince(dk, cidr.IP, activeCutoff)
trackIPs = append(trackIPs, cidr.IP())
recentlyActive = recentlyActive || e.isActiveSince(dk, cidr.IP(), activeCutoff)
}
if recentlyActive {
min.Peers = append(min.Peers, *p)
@@ -1156,8 +1156,8 @@ func (e *userspaceEngine) mySelfIPMatchingFamily(dst netaddr.IP) (src netaddr.IP
return netaddr.IP{}, errors.New("no netmap")
}
for _, a := range e.netMap.Addresses {
if a.IsSingleIP() && a.IP.BitLen() == dst.BitLen() {
return a.IP, nil
if a.IsSingleIP() && a.IP().BitLen() == dst.BitLen() {
return a.IP(), nil
}
}
if len(e.netMap.Addresses) == 0 {
@@ -1293,7 +1293,7 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, err error)
var bestInNM *tailcfg.Node
for _, p := range nm.Peers {
for _, a := range p.Addresses {
if a.IP == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
if a.IP() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
return p, nil
}
}
@@ -1301,7 +1301,7 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, err error)
if !cidr.Contains(ip) {
continue
}
if bestInNMPrefix.IsZero() || cidr.Bits > bestInNMPrefix.Bits {
if bestInNMPrefix.IsZero() || cidr.Bits() > bestInNMPrefix.Bits() {
bestInNMPrefix = cidr
bestInNM = p
}
@@ -1319,7 +1319,7 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, err error)
if !cidr.Contains(ip) {
continue
}
if best.IsZero() || cidr.Bits > best.Bits {
if best.IsZero() || cidr.Bits() > best.Bits() {
best = cidr
bestKey = tailcfg.NodeKey(p.PublicKey)
}
@@ -1337,7 +1337,7 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, err error)
if bestInNM == nil {
return nil, nil
}
if bestInNMPrefix.Bits == 0 {
if bestInNMPrefix.Bits() == 0 {
return nil, errors.New("exit node found but not enabled")
}
return nil, fmt.Errorf("node %q found, but not using its %v route", bestInNM.ComputedNameWithHost, bestInNMPrefix)

View File

@@ -102,7 +102,7 @@ func TestUserspaceEngineReconfig(t *testing.T) {
Peers: []wgcfg.Peer{
{
AllowedIPs: []netaddr.IPPrefix{
{IP: netaddr.IPv4(100, 100, 99, 1), Bits: 32},
netaddr.IPPrefixFrom(netaddr.IPv4(100, 100, 99, 1), 32),
},
Endpoints: wgcfg.Endpoints{DiscoKey: dkFromHex(discoHex)},
},

View File

@@ -37,7 +37,7 @@ func nodeDebugName(n *tailcfg.Node) string {
// cidrIsSubnet reports whether cidr is a non-default-route subnet
// exported by node that is not one of its own self addresses.
func cidrIsSubnet(node *tailcfg.Node, cidr netaddr.IPPrefix) bool {
if cidr.Bits == 0 {
if cidr.Bits() == 0 {
return false
}
if !cidr.IsSingleIP() {
@@ -93,7 +93,7 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags,
}
didExitNodeWarn := false
for _, allowedIP := range peer.AllowedIPs {
if allowedIP.Bits == 0 && peer.StableID != exitNode {
if allowedIP.Bits() == 0 && peer.StableID != exitNode {
if didExitNodeWarn {
// Don't log about both the IPv4 /0 and IPv6 /0.
continue
@@ -104,11 +104,11 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags,
}
fmt.Fprintf(skippedUnselected, "%q (%v)", nodeDebugName(peer), peer.Key.ShortString())
continue
} else if allowedIP.IsSingleIP() && tsaddr.IsTailscaleIP(allowedIP.IP) && (flags&netmap.AllowSingleHosts) == 0 {
} else if allowedIP.IsSingleIP() && tsaddr.IsTailscaleIP(allowedIP.IP()) && (flags&netmap.AllowSingleHosts) == 0 {
if skippedIPs.Len() > 0 {
skippedIPs.WriteString(", ")
}
fmt.Fprintf(skippedIPs, "%v from %q (%v)", allowedIP.IP, nodeDebugName(peer), peer.Key.ShortString())
fmt.Fprintf(skippedIPs, "%v from %q (%v)", allowedIP.IP(), nodeDebugName(peer), peer.Key.ShortString())
continue
} else if cidrIsSubnet(peer, allowedIP) {
if (flags & netmap.AllowSubnetRoutes) == 0 {