wgengine{,/magicsock}: add DERP hooks for filtering+sending packets

Add two small APIs to support out-of-tree projects to exchange custom
signaling messages over DERP without requiring disco protocol
extensions:

- OnDERPRecv callback on magicsock.Options / wgengine.Config: called for
  every non-disco DERP packet before the peer map lookup, allowing callers
  to intercept packets from unknown peers that would otherwise be dropped.

- SendDERPPacketTo method on magicsock.Conn: sends arbitrary bytes to a
  node key via a DERP region, creating the connection if needed. Thin
  wrapper around the existing internal sendAddr.

Also allow netstack.Start to accept a nil LocalBackend for use cases
that wire up TCP/UDP handlers directly without a full LocalBackend.

Updates tailscale/corp#24454

Change-Id: I99a523ef281625b8c0024a963f5f5bf5d8792c17
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2026-03-11 13:29:06 +00:00
committed by Brad Fitzpatrick
parent 4c7c1091ba
commit 073a9a8c9e
4 changed files with 51 additions and 11 deletions
+17 -7
View File
@@ -603,15 +603,25 @@ type LocalBackend = any
// Start sets up all the handlers so netstack can start working. Implements
// wgengine.FakeImpl.
//
// The provided LocalBackend interface can be either nil, for special case users
// of netstack that don't have a LocalBackend, or a non-nil
// *ipnlocal.LocalBackend. Any other type will cause Start to panic.
//
// Start currently (2026-03-11) never returns a non-nil error, but maybe it did
// in the past and maybe it will in the future.
func (ns *Impl) Start(b LocalBackend) error {
if b == nil {
panic("nil LocalBackend interface")
switch b := b.(type) {
case nil:
// No backend, so just continue with ns.lb unset.
case *ipnlocal.LocalBackend:
if b == nil {
panic("nil LocalBackend")
}
ns.lb = b
default:
panic(fmt.Sprintf("unexpected type for LocalBackend: %T", b))
}
lb := b.(*ipnlocal.LocalBackend)
if lb == nil {
panic("nil LocalBackend")
}
ns.lb = lb
tcpFwd := tcp.NewForwarder(ns.ipstack, tcpRXBufDefSize, maxInFlightConnectionAttempts(), ns.acceptTCP)
udpFwd := udp.NewForwarder(ns.ipstack, ns.acceptUDPNoICMP)
ns.ipstack.SetTransportProtocolHandler(tcp.ProtocolNumber, ns.wrapTCPProtocolHandler(tcpFwd.HandlePacket))