[management] expose peer MAC addresses and make peers searchable by MAC (#6553)

This commit is contained in:
Allan ELKAIM
2026-10-02 15:00:26 +02:00
committed by GitHub
parent 0712a5a5b9
commit e2678d4e05
21 changed files with 265 additions and 46 deletions
@@ -127,7 +127,7 @@ func (h *handler) validateNetworkRange(ctx context.Context, accountID, userID st
}
func (h *handler) validateCapacity(ctx context.Context, accountID, userID string, prefix netip.Prefix) error {
peers, err := h.accountManager.GetPeers(ctx, accountID, userID, "", "")
peers, err := h.accountManager.GetPeers(ctx, accountID, userID, "", "", "")
if err != nil {
return status.Errorf(status.Internal, "get peer count: %v", err)
}
@@ -58,7 +58,7 @@ func (h *handler) getAllGroups(w http.ResponseWriter, r *http.Request) {
return
}
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
if err != nil {
util.WriteError(r.Context(), err, w)
return
@@ -77,7 +77,7 @@ func (h *handler) getAllGroups(w http.ResponseWriter, r *http.Request) {
return
}
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
if err != nil {
util.WriteError(r.Context(), err, w)
return
@@ -169,7 +169,7 @@ func (h *handler) updateGroup(w http.ResponseWriter, r *http.Request) {
return
}
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
if err != nil {
util.WriteError(r.Context(), err, w)
return
@@ -226,7 +226,7 @@ func (h *handler) createGroup(w http.ResponseWriter, r *http.Request) {
return
}
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
if err != nil {
util.WriteError(r.Context(), err, w)
return
@@ -287,7 +287,7 @@ func (h *handler) getGroup(w http.ResponseWriter, r *http.Request) {
return
}
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "", "")
if err != nil {
util.WriteError(r.Context(), err, w)
return
@@ -78,7 +78,7 @@ func initGroupTestData(initGroups ...*types.Group) *handler {
return nil, status.Errorf(status.NotFound, "unknown group name")
},
GetPeersFunc: func(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) {
GetPeersFunc: func(ctx context.Context, accountID, userID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error) {
return maps.Values(TestPeers), nil
},
DeleteGroupFunc: func(_ context.Context, accountID, userId, groupID string) error {
@@ -317,10 +317,11 @@ func (h *Handler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
nameFilter := r.URL.Query().Get("name")
ipFilter := r.URL.Query().Get("ip")
macFilter := r.URL.Query().Get("mac")
accountID, userID := userAuth.AccountId, userAuth.UserId
peers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, nameFilter, ipFilter)
peers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, nameFilter, ipFilter, macFilter)
if err != nil {
util.WriteError(r.Context(), err, w)
return
@@ -571,6 +572,17 @@ func peerToAccessiblePeer(peer *nbpeer.Peer, dnsDomain string) api.AccessiblePee
}
}
func toNetworkAddresses(addrs []nbpeer.NetworkAddress) *[]api.NetworkAddress {
if len(addrs) == 0 {
return nil
}
out := make([]api.NetworkAddress, 0, len(addrs))
for _, a := range addrs {
out = append(out, api.NetworkAddress{NetIp: a.NetIP.String(), Mac: a.Mac})
}
return &out
}
func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, approved bool, reason string) *api.Peer {
osVersion := peer.Meta.OSVersion
if osVersion == "" {
@@ -583,6 +595,7 @@ func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsD
Name: peer.Name,
Ip: peer.IP.String(),
Ipv6: peerIPv6String(peer),
NetworkAddresses: toNetworkAddresses(peer.Meta.NetworkAddresses),
ConnectionIp: peer.Location.ConnectionIP.String(),
Connected: peer.Status.Connected,
LastSeen: peer.Status.LastSeen,
@@ -639,6 +652,7 @@ func toPeerListItemResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dn
Name: peer.Name,
Ip: peer.IP.String(),
Ipv6: peerIPv6String(peer),
NetworkAddresses: toNetworkAddresses(peer.Meta.NetworkAddresses),
ConnectionIp: peer.Location.ConnectionIP.String(),
Connected: peer.Status.Connected,
LastSeen: peer.Status.LastSeen,
@@ -173,7 +173,7 @@ func initTestMetaData(t *testing.T, peers ...*nbpeer.Peer) *Handler {
return nil, fmt.Errorf("user not found")
}
},
GetPeersFunc: func(_ context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) {
GetPeersFunc: func(_ context.Context, accountID, userID, nameFilter, ipFilter, macFilter string) ([]*nbpeer.Peer, error) {
return peers, nil
},
GetPeerGroupsFunc: func(ctx context.Context, accountID, peerID string) ([]*types.Group, error) {
@@ -364,6 +364,50 @@ func TestGetPeers(t *testing.T) {
}
}
func TestPeerResponseNetworkAddresses(t *testing.T) {
tests := []struct {
name string
addresses []nbpeer.NetworkAddress
wantJSON string
}{
{name: "not reported"},
{name: "empty", addresses: []nbpeer.NetworkAddress{}},
{
name: "multiple interfaces",
addresses: []nbpeer.NetworkAddress{
{NetIP: netip.MustParsePrefix("192.168.0.11/24"), Mac: "00:93:37:bd:83:0f"},
{NetIP: netip.MustParsePrefix("2001:db8::123/64"), Mac: "00:93:37:bd:83:10"},
},
wantJSON: `[{"net_ip":"192.168.0.11/24","mac":"00:93:37:bd:83:0f"},{"net_ip":"2001:db8::123/64","mac":"00:93:37:bd:83:10"}]`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
peer := &nbpeer.Peer{
Status: &nbpeer.PeerStatus{},
Meta: nbpeer.PeerSystemMeta{NetworkAddresses: tt.addresses},
}
responses := map[string]any{
"single peer": toSinglePeerResponse(peer, nil, "example.com", true, ""),
"peer list": toPeerListItemResponse(peer, nil, "example.com", 0),
}
for name, response := range responses {
t.Run(name, func(t *testing.T) {
body, err := json.Marshal(response)
require.NoError(t, err)
var fields map[string]json.RawMessage
require.NoError(t, json.Unmarshal(body, &fields))
if tt.wantJSON == "" {
assert.NotContains(t, fields, "network_addresses", "unreported interfaces should be omitted")
return
}
assert.JSONEq(t, tt.wantJSON, string(fields["network_addresses"]), "response should preserve interface addresses and MACs")
})
}
})
}
}
func TestGetAccessiblePeers(t *testing.T) {
peer1 := &nbpeer.Peer{
ID: "peer1",