cmd/derper: add a STUN server

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

@ -13,9 +13,11 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"time"
"github.com/tailscale/wireguard-go/wgcfg" "github.com/tailscale/wireguard-go/wgcfg"
"golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/acme/autocert"
@ -23,6 +25,7 @@ import (
"tailscale.com/derp" "tailscale.com/derp"
"tailscale.com/derp/derphttp" "tailscale.com/derp/derphttp"
"tailscale.com/logpolicy" "tailscale.com/logpolicy"
"tailscale.com/stun"
"tailscale.com/tsweb" "tailscale.com/tsweb"
"tailscale.com/types/key" "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") 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") 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") 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 { type config struct {
@ -135,6 +139,10 @@ func main() {
} }
})) }))
if *runSTUN {
go serveSTUN()
}
httpsrv := &http.Server{ httpsrv := &http.Server{
Addr: *addr, Addr: *addr,
Handler: mux, 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