tools: Tailscale status

This commit is contained in:
世界 2026-04-10 09:24:42 +08:00
parent eade67726a
commit 524578a635
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
10 changed files with 523 additions and 176 deletions

View file

@ -0,0 +1,16 @@
//go:build with_gvisor && tvos
package tailscale
import (
_ "unsafe"
"github.com/sagernet/tailscale/types/lazy"
)
//go:linkname isAppleTV github.com/sagernet/tailscale/version.isAppleTV
var isAppleTV lazy.SyncValue[bool]
func init() {
isAppleTV.Set(true)
}

View file

@ -0,0 +1,55 @@
//go:build with_gvisor
package tailscale
import (
"context"
"net/netip"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/tailscale/ipn/ipnstate"
"github.com/sagernet/tailscale/tailcfg"
)
func (t *Endpoint) StartTailscalePing(ctx context.Context, peerIP string, fn func(*adapter.TailscalePingResult)) error {
ip, err := netip.ParseAddr(peerIP)
if err != nil {
return err
}
localClient, err := t.server.LocalClient()
if err != nil {
return err
}
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
result, pingErr := localClient.Ping(ctx, ip, tailcfg.PingDisco)
if ctx.Err() != nil {
return ctx.Err()
}
if pingErr != nil {
fn(&adapter.TailscalePingResult{
Error: pingErr.Error(),
})
} else {
fn(convertPingResult(result))
}
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
}
}
}
func convertPingResult(result *ipnstate.PingResult) *adapter.TailscalePingResult {
return &adapter.TailscalePingResult{
LatencyMs: result.LatencySeconds * 1000,
IsDirect: result.Endpoint != "",
Endpoint: result.Endpoint,
DERPRegionID: int32(result.DERPRegionID),
DERPRegionCode: result.DERPRegionCode,
Error: result.Err,
}
}

View file

@ -4,14 +4,14 @@ package tailscale
import (
"context"
"slices"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/tailscale/ipn"
"github.com/sagernet/tailscale/ipn/ipnstate"
"github.com/sagernet/tailscale/tailcfg"
)
var _ adapter.TailscaleStatusProvider = (*Endpoint)(nil)
var _ adapter.TailscaleEndpoint = (*Endpoint)(nil)
func (t *Endpoint) SubscribeTailscaleStatus(ctx context.Context, fn func(*adapter.TailscaleEndpointStatus)) error {
localBackend := t.server.ExportLocalBackend()
@ -46,13 +46,35 @@ func convertTailscaleStatus(status *ipnstate.Status) *adapter.TailscaleEndpointS
if status.Self != nil {
result.Self = convertTailscalePeer(status.Self)
}
result.Users = make(map[int64]*adapter.TailscaleUser, len(status.User))
for userID, profile := range status.User {
result.Users[int64(userID)] = convertTailscaleUser(userID, profile)
groupIndex := make(map[int64]*adapter.TailscaleUserGroup)
for _, peerKey := range status.Peers() {
peer := status.Peer[peerKey]
userID := int64(peer.UserID)
group, loaded := groupIndex[userID]
if !loaded {
group = &adapter.TailscaleUserGroup{
UserID: userID,
}
if profile, hasProfile := status.User[peer.UserID]; hasProfile {
group.LoginName = profile.LoginName
group.DisplayName = profile.DisplayName
group.ProfilePicURL = profile.ProfilePicURL
}
groupIndex[userID] = group
result.UserGroups = append(result.UserGroups, group)
}
group.Peers = append(group.Peers, convertTailscalePeer(peer))
}
result.Peers = make([]*adapter.TailscalePeer, 0, len(status.Peer))
for _, peer := range status.Peer {
result.Peers = append(result.Peers, convertTailscalePeer(peer))
for _, group := range result.UserGroups {
slices.SortStableFunc(group.Peers, func(a, b *adapter.TailscalePeer) int {
if a.Online != b.Online {
if a.Online {
return -1
}
return 1
}
return 0
})
}
return result
}
@ -81,12 +103,3 @@ func convertTailscalePeer(peer *ipnstate.PeerStatus) *adapter.TailscalePeer {
KeyExpiry: keyExpiry,
}
}
func convertTailscaleUser(id tailcfg.UserID, profile tailcfg.UserProfile) *adapter.TailscaleUser {
return &adapter.TailscaleUser{
ID: int64(id),
LoginName: profile.LoginName,
DisplayName: profile.DisplayName,
ProfilePicURL: profile.ProfilePicURL,
}
}