cmd/derper: add a STUN server

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/106/head
Brad Fitzpatrick 5 years ago
parent 0e128f8f22
commit 00ad93ec25

@ -13,9 +13,11 @@ import (
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path/filepath"
"time"
"github.com/tailscale/wireguard-go/wgcfg"
"golang.org/x/crypto/acme/autocert"
@ -23,6 +25,7 @@ import (
"tailscale.com/derp"
"tailscale.com/derp/derphttp"
"tailscale.com/logpolicy"
"tailscale.com/stun"
"tailscale.com/tsweb"
"tailscale.com/types/key"
)
@ -35,6 +38,7 @@ var (
hostname = flag.String("hostname", "derp.tailscale.com", "LetsEncrypt host name, if addr's port is :443")
mbps = flag.Int("mbps", 5, "Mbps (mebibit/s) per-client rate limit; 0 means unlimited")
logCollection = flag.String("logcollection", "", "If non-empty, logtail collection to log to")
runSTUN = flag.Bool("stun", false, "also run a STUN server")
)
type config struct {
@ -135,6 +139,10 @@ func main() {
}
}))
if *runSTUN {
go serveSTUN()
}
httpsrv := &http.Server{
Addr: *addr,
Handler: mux,
@ -190,3 +198,58 @@ func debugHandler(s *derp.Server) http.Handler {
`)
})
}
func serveSTUN() {
pc, err := net.ListenPacket("udp", ":3478")
if err != nil {
log.Fatalf("failed to open STUN listener: %v", err)
}
log.Printf("running STUN server on %v", pc.LocalAddr())
var (
stunReadErrors = expvar.NewInt("stun-read-error")
stunWriteErrors = expvar.NewInt("stun-write-error")
stunReadNotSTUN = expvar.NewInt("stun-read-not-stun")
stunReadNotSTUNValid = expvar.NewInt("stun-read-not-stun-valid")
stunReadIPv4 = expvar.NewInt("stun-read-ipv4")
stunReadIPv6 = expvar.NewInt("stun-read-ipv6")
stunWrite = expvar.NewInt("stun-write")
)
var buf [64 << 10]byte
for {
n, addr, err := pc.ReadFrom(buf[:])
if err != nil {
log.Printf("STUN ReadFrom: %v", err)
time.Sleep(time.Second)
stunReadErrors.Add(1)
continue
}
ua, ok := addr.(*net.UDPAddr)
if !ok {
log.Printf("STUN unexpected address %T %v", addr, addr)
stunReadErrors.Add(1)
continue
}
pkt := buf[:n]
if !stun.Is(pkt) {
stunReadNotSTUN.Add(1)
continue
}
txid, err := stun.ParseBindingRequest(pkt)
if err != nil {
stunReadNotSTUNValid.Add(1)
continue
}
if ua.IP.To4() != nil {
stunReadIPv4.Add(1)
} else {
stunReadIPv6.Add(1)
}
res := stun.Response(txid, ua.IP, uint16(ua.Port))
_, err = pc.WriteTo(res, addr)
if err != nil {
stunWriteErrors.Add(1)
} else {
stunWrite.Add(1)
}
}
}

Loading…
Cancel
Save