package mikrotik import ( "encoding/json" "fmt" "io" "net/http" "git.faercol.me/faercol/topology-map/internal/config" "git.faercol.me/faercol/topology-map/internal/models" ) const arpRoute = "/rest/ip/arp" func buildQueryURL(targetIP, route string) string { return "http://" + targetIP + route } type MikrotikClient struct { httpClt *http.Client targets []config.MikrotikConfig } func NewMikrotikClient(targets []config.MikrotikConfig) *MikrotikClient { return &MikrotikClient{ httpClt: http.DefaultClient, targets: targets, } } func (c *MikrotikClient) doQuery(target config.MikrotikConfig, route string) ([]byte, error) { query, err := http.NewRequest(http.MethodGet, buildQueryURL(target.IPAddress, route), nil) if err != nil { return nil, err } query.SetBasicAuth(target.User, target.Password) resp, err := c.httpClt.Do(query) if err != nil { return nil, err } return io.ReadAll(resp.Body) } func (c *MikrotikClient) getARPCacheForTarget(target config.MikrotikConfig) ([]models.ARPRecord, error) { data, err := c.doQuery(target, arpRoute) if err != nil { return nil, err } var resp []models.ARPRecord if err := json.Unmarshal(data, &resp); err != nil { return nil, err } return resp, nil } func (c *MikrotikClient) GetARPRecords() ([]*models.ARPRecord, error) { arpCache := map[string]models.ARPRecord{} for _, t := range c.targets { records, err := c.getARPCacheForTarget(t) if err != nil { return nil, fmt.Errorf("failed to get ARP data for device %s: %w", t.IPAddress, err) } for _, r := range records { arpCache[r.MacAddress] = r } } var res []*models.ARPRecord for _, r := range arpCache { res = append(res, &r) } return res, nil }