ipn/ipnlocal: make peerapi listener on Android avoid the kernel

We intercept the peerapi port in netstack anyway, so there's no reason
the linux kernel on Android needs to know about it. It's only getting
in the way and causing problems for reasons we don't fully understand.
But we don't even need to understand it because it's not relevant
anymore.

Instead, provide a dummy net.Listener that just sits and blocks to
pacify the rest of the code that assumes it can be stuck in a
Listener.Accept call and call Listener.Close and Listener.Addr.

We'll likely do this for all platforms in the future, if/when we also
link in netstack on iOS.

Updates #4449
Updates #4293
Updates #3986

Change-Id: Ic2d3fe2f3cee60fc527356a3368830f17aeb75ae
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2022-04-20 11:57:34 -07:00 committed by Brad Fitzpatrick
parent cf9b9a7fec
commit 1b89662eff

View File

@ -386,6 +386,14 @@ func (s *peerAPIServer) OpenFile(baseName string) (rc io.ReadCloser, size int64,
}
func (s *peerAPIServer) listen(ip netaddr.IP, ifState *interfaces.State) (ln net.Listener, err error) {
// Android for whatever reason often has problems creating the peerapi listener.
// But since we started intercepting it with netstack, it's not even important that
// we have a real kernel-level listener. So just create a dummy listener on Android
// and let netstack intercept it.
if runtime.GOOS == "android" {
return newFakePeerAPIListener(ip), nil
}
ipStr := ip.String()
var lc net.ListenConfig
@ -1047,3 +1055,49 @@ func writePrettyDNSReply(w io.Writer, res []byte) (err error) {
w.Write(j)
return nil
}
// newFakePeerAPIListener creates a new net.Listener that acts like
// it's listening on the provided IP address and on TCP port 1.
//
// See docs on fakePeerAPIListener.
func newFakePeerAPIListener(ip netaddr.IP) net.Listener {
return &fakePeerAPIListener{
addr: netaddr.IPPortFrom(ip, 1).TCPAddr(),
closed: make(chan struct{}),
}
}
// fakePeerAPIListener is a net.Listener that has an Addr method returning a TCPAddr
// for a given IP on port 1 (arbitrary) and can be Closed, but otherwise Accept
// just blocks forever until closed. The purpose of this is to let the rest
// of the LocalBackend/PeerAPI code run and think it's talking to the kernel,
// even if the kernel isn't cooperating (like on Android: Issue 4449, 4293, etc)
// or we lack permission to listen on a port. It's okay to not actually listen via
// the kernel because on almost all platforms (except iOS as of 2022-04-20) we
// also intercept netstack TCP requests in to our peerapi port and hand it over
// directly to peerapi, without involving the kernel. So this doesn't need to be
// real. But the port number we return (1, in this case) is the port number we advertise
// to peers and they connect to. 1 seems pretty safe to use. Even if the kernel's
// using it, it doesn't matter, as we intercept it first in netstack and the kernel
// never notices.
//
// Eventually we'll remove this code and do this on all platforms, when iOS also uses
// netstack.
type fakePeerAPIListener struct {
addr net.Addr
closeOnce sync.Once
closed chan struct{}
}
func (fl *fakePeerAPIListener) Close() error {
fl.closeOnce.Do(func() { close(fl.closed) })
return nil
}
func (fl *fakePeerAPIListener) Accept() (net.Conn, error) {
<-fl.closed
return nil, io.EOF
}
func (fl *fakePeerAPIListener) Addr() net.Addr { return fl.addr }