package peer import ( "encoding/json" "io" "log" "net/http" "net/netip" "net/url" "sync/atomic" "time" "vppn/m" ) var zeroAddrPort netip.AddrPort type routeInfo struct { Up bool Route route } type Router struct { conf m.PeerConfig routes [256]*atomic.Pointer[routeInfo] } func NewRouter(conf m.PeerConfig) *Router { rm := &Router{ conf: conf, } for i := range rm.routes { rm.routes[i] = &atomic.Pointer[routeInfo]{} rm.routes[i].Store(&routeInfo{}) } go rm.pollHub() return rm } func (rm *Router) GetRoute(ip byte) *route { if route := rm.routes[ip].Load(); route != nil && route.Up { return &route.Route } return nil } func (rm *Router) pollHub() { u, err := url.Parse(rm.conf.HubAddress) if err != nil { log.Fatalf("Failed to parse hub address %s: %v", rm.conf.HubAddress, err) } u.Path = "/peer/fetch-state/" client := &http.Client{Timeout: 8 * time.Second} req := &http.Request{ Method: http.MethodGet, URL: u, Header: http.Header{}, } req.SetBasicAuth("", rm.conf.APIKey) rm._pollHub(client, req) for range time.Tick(time.Minute) { rm._pollHub(client, req) } } func (rm *Router) _pollHub(client *http.Client, req *http.Request) { var state m.NetworkState log.Printf("Fetching peer state from %s...", rm.conf.HubAddress) resp, err := client.Do(req) if err != nil { log.Printf("Failed to fetch peer state: %v", err) return } body, err := io.ReadAll(resp.Body) _ = resp.Body.Close() if err != nil { log.Printf("Failed to read body: %v", err) return } if err := json.Unmarshal(body, &state); err != nil { log.Printf("Failed to unmarshal response from hub: %v", err) return } for i, peer := range state.Peers { if peer == nil { continue } route := rm.routes[i].Load() rm.routes[i].Store(rm.updateRoute(route, peer)) } } func (rm *Router) updateRoute(routePtr *routeInfo, peer *m.Peer) *routeInfo { if peer == nil { return &routeInfo{} } route := *routePtr addr, ok := netip.AddrFromSlice(peer.IP) if !ok { return &routeInfo{} } route.Up = true route.Route.Addr = netip.AddrPortFrom(addr, peer.Port) route.Route.Mediator = peer.Mediator route.Route.ViaIP = 0 if len(route.Route.SignPubKey) == 0 { route.Route.SignPubKey = peer.SignPubKey route.Route.EncSharedKey = computeSharedKey(peer.EncPubKey, rm.conf.EncPrivKey) } return &route }