package opnsense import ( "github.com/go-kit/log" "github.com/go-kit/log/level" ) // gatewaysStatusResponse is the response from the OPNsense API that contains the gateways status details // The data is constucted in this script: // ---> https://github.com/opnsense/core/blob/master/src/opnsense/scripts/routes/gateway_status.php // Following the reverse engineering of the call: // ---> https://github.com/opnsense/core/blob/master/src/etc/inc/plugins.inc.d/dpinger.inc#L368 // From this file we know that Loss and Delay always have the same format of '%0.1f ms' type gatewaysStatusResponse struct { Items []struct { Name string `json:"name"` Address string `json:"address"` Status string `json:"status"` Loss string `json:"loss"` Delay string `json:"delay"` Stddev string `json:"stddev"` StatusTranslated string `json:"status_translated"` } `json:"items"` Status string `json:"status"` } // GatewayStatus is the custom type that represents the status of a gateway type GatewayStatus int const ( GatewayStatusOffline GatewayStatus = iota GatewayStatusOnline GatewayStatusUnknown ) type Gateway struct { Name string Address string Status GatewayStatus RTTMilliseconds float64 RTTDMilliseconds float64 LossPercentage float64 } type Gateways struct { Gateways []Gateway } // parseGatewayStatus parses a string status to a GatewayStatus type. func parseGatewayStatus(statusTranslated string, logger log.Logger, originalStatus string) GatewayStatus { switch statusTranslated { case "Online": return GatewayStatusOnline case "Offline": return GatewayStatusOffline default: level.Warn(logger). Log("msg", "unknown gateway status detected", "status", originalStatus) return GatewayStatusUnknown } } // FetchGateways fetches the gateways status details from the OPNsense API // and returns a safe wrapper Gateways struct. func (c *Client) FetchGateways() (Gateways, *APICallError) { var resp gatewaysStatusResponse var data Gateways url, ok := c.endpoints["gatewaysStatus"] if !ok { return data, &APICallError{ Endpoint: "gatewaysStatus", Message: "endpoint not found in client endpoints", StatusCode: 0, } } err := c.do("GET", url, nil, &resp) if err != nil { return data, err } for _, v := range resp.Items { data.Gateways = append(data.Gateways, Gateway{ Name: v.Name, Address: v.Address, Status: parseGatewayStatus(v.StatusTranslated, c.log, v.Status), RTTMilliseconds: parseStringToFloatWithReplace(v.Delay, c.gatewayRTTRegex, " ms", "rtt", c.log), RTTDMilliseconds: parseStringToFloatWithReplace(v.Stddev, c.gatewayRTTRegex, " ms", "rttd", c.log), LossPercentage: parseStringToFloatWithReplace(v.Loss, c.gatewayLossRegex, " %", "loss", c.log), }) } return data, nil }