Add network routes distribution groups (#606)

Updated tests, API, and account manager methods

Sync routes to peers in the distribution groups

Added store upgrade by adding the All group to routes that don't have them
This commit is contained in:
Maycon Santos 2022-12-06 10:11:57 +01:00 committed by GitHub
parent d1b7c23b19
commit a387e3cfc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 386 additions and 101 deletions

View File

@ -31,7 +31,7 @@ jobs:
uses: actions/checkout@v2
- name: Install dependencies
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libappindicator3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
- name: Install modules
run: go mod tidy
@ -60,7 +60,7 @@ jobs:
uses: actions/checkout@v2
- name: Install dependencies
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libappindicator3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
- name: Install modules
run: go mod tidy

View File

@ -11,7 +11,7 @@ jobs:
with:
go-version: 1.19.x
- name: Install dependencies
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libappindicator3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:

View File

@ -104,7 +104,7 @@ jobs:
run: git --no-pager diff --exit-code
- name: Install dependencies
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libappindicator3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev gcc-mingw-w64-x86-64
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev gcc-mingw-w64-x86-64
- name: Install rsrc
run: go install github.com/akavel/rsrc@v0.10.2
- name: Generate windows rsrc

View File

@ -75,7 +75,7 @@ type AccountManager interface {
DeleteRule(accountId, ruleID string) error
ListRules(accountID, userID string) ([]*Rule, error)
GetRoute(accountID, routeID, userID string) (*route.Route, error)
CreateRoute(accountID string, prefix, peer, description, netID string, masquerade bool, metric int, enabled bool) (*route.Route, error)
CreateRoute(accountID string, prefix, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error)
SaveRoute(accountID string, route *route.Route) error
UpdateRoute(accountID string, routeID string, operations []RouteUpdateOperation) (*route.Route, error)
DeleteRoute(accountID, routeID string) error
@ -137,31 +137,41 @@ type UserInfo struct {
Status string `json:"-"`
}
// GetPeersRoutes returns all active routes of provided peers
func (a *Account) GetPeersRoutes(givenPeers []*Peer) []*route.Route {
//TODO Peer.ID migration: we will need to replace search by Peer.ID here
routes := make([]*route.Route, 0)
for _, peer := range givenPeers {
peerRoutes := a.GetPeerRoutes(peer.Key)
activeRoutes := make([]*route.Route, 0)
for _, pr := range peerRoutes {
if pr.Enabled {
activeRoutes = append(activeRoutes, pr)
}
}
if len(activeRoutes) > 0 {
routes = append(routes, activeRoutes...)
}
// getRoutesToSync returns the enabled routes for the peer ID and the routes
// from the ACL peers that have distribution groups associated with the peer ID
func (a *Account) getRoutesToSync(peerID string, aclPeers []*Peer) []*route.Route {
routes := a.getEnabledRoutesByPeer(peerID)
groupListMap := a.getPeerGroups(peerID)
for _, peer := range aclPeers {
activeRoutes := a.getEnabledRoutesByPeer(peer.Key)
filteredRoutes := a.filterRoutesByGroups(activeRoutes, groupListMap)
routes = append(routes, filteredRoutes...)
}
return routes
}
// GetPeerRoutes returns a list of routes of a given peer
func (a *Account) GetPeerRoutes(peerPubKey string) []*route.Route {
// filterRoutesByGroups returns a list with routes that have distribution groups in the group's map
func (a *Account) filterRoutesByGroups(routes []*route.Route, groupListMap lookupMap) []*route.Route {
var filteredRoutes []*route.Route
for _, r := range routes {
for _, groupID := range r.Groups {
_, found := groupListMap[groupID]
if found {
filteredRoutes = append(filteredRoutes, r)
break
}
}
}
return filteredRoutes
}
// getEnabledRoutesByPeer returns a list of routes of a given peer
func (a *Account) getEnabledRoutesByPeer(peerPubKey string) []*route.Route {
//TODO Peer.ID migration: we will need to replace search by Peer.ID here
var routes []*route.Route
for _, r := range a.Routes {
if r.Peer == peerPubKey {
if r.Peer == peerPubKey && r.Enabled {
routes = append(routes, r)
continue
}
@ -299,6 +309,19 @@ func (a *Account) getUserGroups(userID string) ([]string, error) {
return user.AutoGroups, nil
}
func (a *Account) getPeerGroups(peerID string) lookupMap {
groupList := make(lookupMap)
for groupID, group := range a.Groups {
for _, id := range group.Peers {
if id == peerID {
groupList[groupID] = struct{}{}
break
}
}
}
return groupList
}
func (a *Account) getSetupKeyGroups(setupKey string) ([]string, error) {
key, err := a.FindSetupKey(setupKey)
if err != nil {

View File

@ -1075,7 +1075,7 @@ func TestFileStore_GetRoutesByPrefix(t *testing.T) {
assert.Contains(t, routeIDs, "route-2")
}
func TestAccount_GetPeersRoutes(t *testing.T) {
func TestAccount_GetRoutesToSync(t *testing.T) {
_, prefix, err := route.ParseNetwork("192.168.64.0/24")
if err != nil {
t.Fatal(err)
@ -1084,6 +1084,7 @@ func TestAccount_GetPeersRoutes(t *testing.T) {
Peers: map[string]*Peer{
"peer-1": {Key: "peer-1"}, "peer-2": {Key: "peer-2"}, "peer-3": {Key: "peer-1"},
},
Groups: map[string]*Group{"group1": {ID: "group1", Peers: []string{"peer-1", "peer-2"}}},
Routes: map[string]*route.Route{
"route-1": {
ID: "route-1",
@ -1095,6 +1096,7 @@ func TestAccount_GetPeersRoutes(t *testing.T) {
Masquerade: false,
Metric: 999,
Enabled: true,
Groups: []string{"group1"},
},
"route-2": {
ID: "route-2",
@ -1106,11 +1108,12 @@ func TestAccount_GetPeersRoutes(t *testing.T) {
Masquerade: false,
Metric: 999,
Enabled: true,
Groups: []string{"group1"},
},
},
}
routes := account.GetPeersRoutes([]*Peer{{Key: "peer-1"}, {Key: "peer-2"}, {Key: "non-existing-peer"}})
routes := account.getRoutesToSync("peer-2", []*Peer{{Key: "peer-1"}, {Key: "peer-3"}})
assert.Len(t, routes, 2)
routeIDs := make(map[string]struct{}, 2)
@ -1120,6 +1123,9 @@ func TestAccount_GetPeersRoutes(t *testing.T) {
assert.Contains(t, routeIDs, "route-1")
assert.Contains(t, routeIDs, "route-2")
emptyRoutes := account.getRoutesToSync("peer-3", []*Peer{{Key: "peer-1"}, {Key: "peer-2"}})
assert.Len(t, emptyRoutes, 0)
}
func TestAccount_Copy(t *testing.T) {

View File

@ -78,15 +78,7 @@ func getPeersCustomZone(account *Account, dnsDomain string) nbdns.CustomZone {
}
func getPeerNSGroups(account *Account, peerID string) []*nbdns.NameServerGroup {
groupList := make(lookupMap)
for groupID, group := range account.Groups {
for _, id := range group.Peers {
if id == peerID {
groupList[groupID] = struct{}{}
break
}
}
}
groupList := account.getPeerGroups(peerID)
var peerNSGroups []*nbdns.NameServerGroup

View File

@ -105,6 +105,19 @@ func restore(file string) (*FileStore, error) {
if len(existingLabels) != len(account.Peers) {
addPeerLabelsToAccount(account, existingLabels)
}
allGroup, err := account.GetGroupAll()
if err != nil {
log.Errorf("unable to find the All group, this should happen only when migrate from a version that didn't support groups. Error: %v", err)
// if the All group didn't exist we probably don't have routes to update
continue
}
for _, route := range account.Routes {
if len(route.Groups) == 0 {
route.Groups = []string{allGroup.ID}
}
}
}
// we need this persist to apply changes we made to account.Peers (we set them to Disconnected)

View File

@ -379,6 +379,11 @@ components:
masquerade:
description: Indicate if peer should masquerade traffic to this route's prefix
type: boolean
groups:
description: Route group tag groups
type: array
items:
type: string
required:
- id
- description
@ -388,6 +393,7 @@ components:
- network
- metric
- masquerade
- groups
Route:
allOf:
- type: object
@ -410,7 +416,7 @@ components:
path:
description: Route field to update in form /<field>
type: string
enum: [ "network","network_id","description","enabled","peer","metric","masquerade" ]
enum: [ "network","network_id","description","enabled","peer","metric","masquerade", "groups" ]
required:
- path
Nameserver:

View File

@ -65,6 +65,7 @@ const (
const (
RoutePatchOperationPathDescription RoutePatchOperationPath = "description"
RoutePatchOperationPathEnabled RoutePatchOperationPath = "enabled"
RoutePatchOperationPathGroups RoutePatchOperationPath = "groups"
RoutePatchOperationPathMasquerade RoutePatchOperationPath = "masquerade"
RoutePatchOperationPathMetric RoutePatchOperationPath = "metric"
RoutePatchOperationPathNetwork RoutePatchOperationPath = "network"
@ -296,6 +297,9 @@ type Route struct {
// Enabled Route status
Enabled bool `json:"enabled"`
// Groups Route group tag groups
Groups []string `json:"groups"`
// Id Route Id
Id string `json:"id"`
@ -344,6 +348,9 @@ type RouteRequest struct {
// Enabled Route status
Enabled bool `json:"enabled"`
// Groups Route group tag groups
Groups []string `json:"groups"`
// Masquerade Indicate if peer should masquerade traffic to this route's prefix
Masquerade bool `json:"masquerade"`

View File

@ -89,7 +89,7 @@ func (h *Routes) CreateRouteHandler(w http.ResponseWriter, r *http.Request) {
return
}
newRoute, err := h.accountManager.CreateRoute(account.Id, newPrefix.String(), peerKey, req.Description, req.NetworkId, req.Masquerade, req.Metric, req.Enabled)
newRoute, err := h.accountManager.CreateRoute(account.Id, newPrefix.String(), peerKey, req.Description, req.NetworkId, req.Masquerade, req.Metric, req.Groups, req.Enabled)
if err != nil {
util.WriteError(err, w)
return
@ -162,6 +162,7 @@ func (h *Routes) UpdateRouteHandler(w http.ResponseWriter, r *http.Request) {
Metric: req.Metric,
Description: req.Description,
Enabled: req.Enabled,
Groups: req.Groups,
}
err = h.accountManager.SaveRoute(account.Id, newRoute)
@ -298,6 +299,16 @@ func (h *Routes) PatchRouteHandler(w http.ResponseWriter, r *http.Request) {
Type: server.UpdateRouteEnabled,
Values: patch.Value,
})
case api.RoutePatchOperationPathGroups:
if patch.Op != api.RoutePatchOperationOpReplace {
util.WriteError(status.Errorf(status.InvalidArgument,
"groups field only accepts replace operation, got %s", patch.Op), w)
return
}
operations = append(operations, server.RouteUpdateOperation{
Type: server.UpdateRouteGroups,
Values: patch.Value,
})
default:
util.WriteError(status.Errorf(status.InvalidArgument, "invalid patch path"), w)
return
@ -383,5 +394,6 @@ func toRouteResponse(account *server.Account, serverRoute *route.Route) *api.Rou
NetworkType: serverRoute.NetworkType.String(),
Masquerade: serverRoute.Masquerade,
Metric: serverRoute.Metric,
Groups: serverRoute.Groups,
}
}

View File

@ -28,6 +28,7 @@ const (
notFoundPeerID = "100.64.0.200"
existingPeerKey = "existingPeerKey"
testAccountID = "test_id"
existingGroupID = "testGroup"
)
var baseExistingRoute = &route.Route{
@ -39,6 +40,7 @@ var baseExistingRoute = &route.Route{
Metric: 9999,
Masquerade: false,
Enabled: true,
Groups: []string{existingGroupID},
}
var testingAccount = &server.Account{
@ -64,7 +66,7 @@ func initRoutesTestData() *Routes {
}
return nil, status.Errorf(status.NotFound, "route with ID %s not found", routeID)
},
CreateRouteFunc: func(accountID string, network, peer, description, netID string, masquerade bool, metric int, enabled bool) (*route.Route, error) {
CreateRouteFunc: func(accountID string, network, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error) {
networkType, p, _ := route.ParseNetwork(network)
return &route.Route{
ID: existingRouteID,
@ -75,6 +77,7 @@ func initRoutesTestData() *Routes {
Description: description,
Masquerade: masquerade,
Enabled: enabled,
Groups: groups,
}, nil
},
SaveRouteFunc: func(_ string, _ *route.Route) error {
@ -116,6 +119,8 @@ func initRoutesTestData() *Routes {
routeToUpdate.Masquerade, _ = strconv.ParseBool(operation.Values[0])
case server.UpdateRouteEnabled:
routeToUpdate.Enabled, _ = strconv.ParseBool(operation.Values[0])
case server.UpdateRouteGroups:
routeToUpdate.Groups = operation.Values
default:
return nil, fmt.Errorf("no operation")
}
@ -181,7 +186,7 @@ func TestRoutesHandlers(t *testing.T) {
requestType: http.MethodPost,
requestPath: "/api/routes",
requestBody: bytes.NewBuffer(
[]byte(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\"}", existingPeerID))),
[]byte(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", existingPeerID, existingGroupID))),
expectedStatus: http.StatusOK,
expectedBody: true,
expectedRoute: &api.Route{
@ -193,13 +198,14 @@ func TestRoutesHandlers(t *testing.T) {
NetworkType: route.IPv4NetworkString,
Masquerade: false,
Enabled: false,
Groups: []string{existingGroupID},
},
},
{
name: "POST Not Found Peer",
requestType: http.MethodPost,
requestPath: "/api/routes",
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\"}", notFoundPeerID)),
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", notFoundPeerID, existingGroupID)),
expectedStatus: http.StatusNotFound,
expectedBody: false,
},
@ -207,7 +213,7 @@ func TestRoutesHandlers(t *testing.T) {
name: "POST Invalid Network Identifier",
requestType: http.MethodPost,
requestPath: "/api/routes",
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"12345678901234567890qwertyuiopqwertyuiop1\",\"Peer\":\"%s\"}", existingPeerID)),
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"12345678901234567890qwertyuiopqwertyuiop1\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", existingPeerID, existingGroupID)),
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: false,
},
@ -215,7 +221,7 @@ func TestRoutesHandlers(t *testing.T) {
name: "POST Invalid Network",
requestType: http.MethodPost,
requestPath: "/api/routes",
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/34\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\"}", existingPeerID)),
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/34\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", existingPeerID, existingGroupID)),
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: false,
},
@ -223,7 +229,7 @@ func TestRoutesHandlers(t *testing.T) {
name: "PUT OK",
requestType: http.MethodPut,
requestPath: "/api/routes/" + existingRouteID,
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\"}", existingPeerID)),
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", existingPeerID, existingGroupID)),
expectedStatus: http.StatusOK,
expectedBody: true,
expectedRoute: &api.Route{
@ -235,13 +241,14 @@ func TestRoutesHandlers(t *testing.T) {
NetworkType: route.IPv4NetworkString,
Masquerade: false,
Enabled: false,
Groups: []string{existingGroupID},
},
},
{
name: "PUT Not Found Route",
requestType: http.MethodPut,
requestPath: "/api/routes/" + notFoundRouteID,
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\"}", existingPeerID)),
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", existingPeerID, existingGroupID)),
expectedStatus: http.StatusNotFound,
expectedBody: false,
},
@ -249,7 +256,7 @@ func TestRoutesHandlers(t *testing.T) {
name: "PUT Not Found Peer",
requestType: http.MethodPut,
requestPath: "/api/routes/" + existingRouteID,
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\"}", notFoundPeerID)),
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", notFoundPeerID, existingGroupID)),
expectedStatus: http.StatusNotFound,
expectedBody: false,
},
@ -257,7 +264,7 @@ func TestRoutesHandlers(t *testing.T) {
name: "PUT Invalid Network Identifier",
requestType: http.MethodPut,
requestPath: "/api/routes/" + existingRouteID,
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"12345678901234567890qwertyuiopqwertyuiop1\",\"Peer\":\"%s\"}", existingPeerID)),
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"12345678901234567890qwertyuiopqwertyuiop1\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", existingPeerID, existingGroupID)),
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: false,
},
@ -265,7 +272,7 @@ func TestRoutesHandlers(t *testing.T) {
name: "PUT Invalid Network",
requestType: http.MethodPut,
requestPath: "/api/routes/" + existingRouteID,
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/34\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\"}", existingPeerID)),
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/34\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", existingPeerID, existingGroupID)),
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: false,
},
@ -285,6 +292,7 @@ func TestRoutesHandlers(t *testing.T) {
Masquerade: baseExistingRoute.Masquerade,
Enabled: baseExistingRoute.Enabled,
Metric: baseExistingRoute.Metric,
Groups: baseExistingRoute.Groups,
},
},
{
@ -304,6 +312,7 @@ func TestRoutesHandlers(t *testing.T) {
Masquerade: baseExistingRoute.Masquerade,
Enabled: baseExistingRoute.Enabled,
Metric: baseExistingRoute.Metric,
Groups: baseExistingRoute.Groups,
},
},
{

View File

@ -43,7 +43,7 @@ type MockAccountManager struct {
UpdatePeerMetaFunc func(peerKey string, meta server.PeerSystemMeta) error
UpdatePeerSSHKeyFunc func(peerKey string, sshKey string) error
UpdatePeerFunc func(accountID string, peer *server.Peer) (*server.Peer, error)
CreateRouteFunc func(accountID string, prefix, peer, description, netID string, masquerade bool, metric int, enabled bool) (*route.Route, error)
CreateRouteFunc func(accountID string, prefix, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error)
GetRouteFunc func(accountID, routeID, userID string) (*route.Route, error)
SaveRouteFunc func(accountID string, route *route.Route) error
UpdateRouteFunc func(accountID string, routeID string, operations []server.RouteUpdateOperation) (*route.Route, error)
@ -325,9 +325,9 @@ func (am *MockAccountManager) UpdatePeer(accountID string, peer *server.Peer) (*
}
// CreateRoute mock implementation of CreateRoute from server.AccountManager interface
func (am *MockAccountManager) CreateRoute(accountID string, network, peer, description, netID string, masquerade bool, metric int, enabled bool) (*route.Route, error) {
func (am *MockAccountManager) CreateRoute(accountID string, network, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error) {
if am.CreateRouteFunc != nil {
return am.CreateRouteFunc(accountID, network, peer, description, netID, masquerade, metric, enabled)
return am.CreateRouteFunc(accountID, network, peer, description, netID, masquerade, metric, groups, enabled)
}
return nil, status.Errorf(codes.Unimplemented, "method CreateRoute is not implemented")
}

View File

@ -120,7 +120,7 @@ func (am *DefaultAccountManager) GetPeers(accountID, userID string) ([]*Peer, er
// fetch all the peers that have access to the user's peers
for _, peer := range peers {
aclPeers := am.getPeersByACL(account, peer.Key)
aclPeers := account.getPeersByACL(peer.Key)
for _, p := range aclPeers {
peersMap[p.Key] = p
}
@ -293,8 +293,8 @@ func (am *DefaultAccountManager) GetNetworkMap(peerPubKey string) (*NetworkMap,
return nil, err
}
aclPeers := am.getPeersByACL(account, peerPubKey)
routesUpdate := account.GetPeersRoutes(append(aclPeers, account.Peers[peerPubKey]))
aclPeers := account.getPeersByACL(peerPubKey)
routesUpdate := account.getRoutesToSync(peerPubKey, aclPeers)
var zones []nbdns.CustomZone
peersCustomZone := getPeersCustomZone(account, am.dnsDomain)
@ -515,9 +515,9 @@ func (am *DefaultAccountManager) UpdatePeerMeta(peerPubKey string, meta PeerSyst
}
// getPeersByACL returns all peers that given peer has access to.
func (am *DefaultAccountManager) getPeersByACL(account *Account, peerPubKey string) []*Peer {
func (a *Account) getPeersByACL(peerPubKey string) []*Peer {
var peers []*Peer
srcRules, dstRules := account.GetPeerRules(peerPubKey)
srcRules, dstRules := a.GetPeerRules(peerPubKey)
groups := map[string]*Group{}
for _, r := range srcRules {
@ -526,7 +526,7 @@ func (am *DefaultAccountManager) getPeersByACL(account *Account, peerPubKey stri
}
if r.Flow == TrafficFlowBidirect {
for _, gid := range r.Destination {
if group, ok := account.Groups[gid]; ok {
if group, ok := a.Groups[gid]; ok {
groups[gid] = group
}
}
@ -539,7 +539,7 @@ func (am *DefaultAccountManager) getPeersByACL(account *Account, peerPubKey stri
}
if r.Flow == TrafficFlowBidirect {
for _, gid := range r.Source {
if group, ok := account.Groups[gid]; ok {
if group, ok := a.Groups[gid]; ok {
groups[gid] = group
}
}
@ -549,13 +549,13 @@ func (am *DefaultAccountManager) getPeersByACL(account *Account, peerPubKey stri
peersSet := make(map[string]struct{})
for _, g := range groups {
for _, pid := range g.Peers {
peer, ok := account.Peers[pid]
peer, ok := a.Peers[pid]
if !ok {
log.Warnf(
"peer %s found in group %s but doesn't belong to account %s",
pid,
g.ID,
account.Id,
a.Id,
)
continue
}

View File

@ -26,6 +26,8 @@ const (
UpdateRouteEnabled
// UpdateRouteNetworkIdentifier indicates a route net ID update operation
UpdateRouteNetworkIdentifier
// UpdateRouteGroups indicates a group list update operation
UpdateRouteGroups
)
// RouteUpdateOperationType operation type
@ -47,6 +49,8 @@ func (t RouteUpdateOperationType) String() string {
return "UpdateRouteEnabled"
case UpdateRouteNetworkIdentifier:
return "UpdateRouteNetworkIdentifier"
case UpdateRouteGroups:
return "UpdateRouteGroups"
default:
return "InvalidOperation"
}
@ -114,7 +118,7 @@ func (am *DefaultAccountManager) checkPrefixPeerExists(accountID, peer string, p
}
// CreateRoute creates and saves a new route
func (am *DefaultAccountManager) CreateRoute(accountID string, network, peer, description, netID string, masquerade bool, metric int, enabled bool) (*route.Route, error) {
func (am *DefaultAccountManager) CreateRoute(accountID string, network, peer, description, netID string, masquerade bool, metric int, groups []string, enabled bool) (*route.Route, error) {
unlock := am.Store.AcquireAccountLock(accountID)
defer unlock()
@ -148,6 +152,11 @@ func (am *DefaultAccountManager) CreateRoute(accountID string, network, peer, de
return nil, status.Errorf(status.InvalidArgument, "identifier should be between 1 and %d", route.MaxNetIDChar)
}
err = validateGroups(groups, account.Groups)
if err != nil {
return nil, err
}
newRoute.Peer = peer
newRoute.ID = xid.New().String()
newRoute.Network = newPrefix
@ -157,6 +166,7 @@ func (am *DefaultAccountManager) CreateRoute(accountID string, network, peer, de
newRoute.Masquerade = masquerade
newRoute.Metric = metric
newRoute.Enabled = enabled
newRoute.Groups = groups
if account.Routes == nil {
account.Routes = make(map[string]*route.Route)
@ -210,6 +220,11 @@ func (am *DefaultAccountManager) SaveRoute(accountID string, routeToSave *route.
}
}
err = validateGroups(routeToSave.Groups, account.Groups)
if err != nil {
return err
}
account.Routes[routeToSave.ID] = routeToSave
account.Network.IncSerial()
@ -300,6 +315,12 @@ func (am *DefaultAccountManager) UpdateRoute(accountID, routeID string, operatio
return nil, status.Errorf(status.InvalidArgument, "failed to parse enabled %s, not boolean", operation.Values[0])
}
newRoute.Enabled = enabled
case UpdateRouteGroups:
err = validateGroups(operation.Values, account.Groups)
if err != nil {
return nil, err
}
newRoute.Groups = operation.Values
}
}

View File

@ -8,8 +8,13 @@ import (
"testing"
)
const peer1Key = "BhRPtynAAYRDy08+q4HTMsos8fs4plTP4NOSh7C1ry8="
const peer2Key = "/yF0+vCfv+mRR5k0dca0TrGdO/oiNeAI58gToZm5NyI="
const (
peer1Key = "BhRPtynAAYRDy08+q4HTMsos8fs4plTP4NOSh7C1ry8="
peer2Key = "/yF0+vCfv+mRR5k0dca0TrGdO/oiNeAI58gToZm5NyI="
routeGroup1 = "routeGroup1"
routeGroup2 = "routeGroup2"
routeInvalidGroup1 = "routeInvalidGroup1"
)
func TestCreateRoute(t *testing.T) {
@ -21,6 +26,7 @@ func TestCreateRoute(t *testing.T) {
masquerade bool
metric int
enabled bool
groups []string
}
testCases := []struct {
@ -40,6 +46,7 @@ func TestCreateRoute(t *testing.T) {
masquerade: false,
metric: 9999,
enabled: true,
groups: []string{routeGroup1},
},
errFunc: require.NoError,
shouldCreate: true,
@ -52,10 +59,11 @@ func TestCreateRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
},
{
name: "Bad Prefix",
name: "Bad Prefix Should Fail",
inputArgs: input{
network: "192.168.0.0/34",
netID: "happy",
@ -64,12 +72,13 @@ func TestCreateRoute(t *testing.T) {
masquerade: false,
metric: 9999,
enabled: true,
groups: []string{routeGroup1},
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Bad Peer",
name: "Bad Peer Should Fail",
inputArgs: input{
network: "192.168.0.0/16",
netID: "happy",
@ -78,12 +87,13 @@ func TestCreateRoute(t *testing.T) {
masquerade: false,
metric: 9999,
enabled: true,
groups: []string{routeGroup1},
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Empty Peer",
name: "Empty Peer Should Create",
inputArgs: input{
network: "192.168.0.0/16",
netID: "happy",
@ -92,6 +102,7 @@ func TestCreateRoute(t *testing.T) {
masquerade: false,
metric: 9999,
enabled: false,
groups: []string{routeGroup1},
},
errFunc: require.NoError,
shouldCreate: true,
@ -104,10 +115,11 @@ func TestCreateRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: false,
Groups: []string{routeGroup1},
},
},
{
name: "Large Metric",
name: "Large Metric Should Fail",
inputArgs: input{
network: "192.168.0.0/16",
peer: peer1Key,
@ -116,12 +128,13 @@ func TestCreateRoute(t *testing.T) {
masquerade: false,
metric: 99999,
enabled: true,
groups: []string{routeGroup1},
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Small Metric",
name: "Small Metric Should Fail",
inputArgs: input{
network: "192.168.0.0/16",
netID: "happy",
@ -130,12 +143,13 @@ func TestCreateRoute(t *testing.T) {
masquerade: false,
metric: 0,
enabled: true,
groups: []string{routeGroup1},
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Large NetID",
name: "Large NetID Should Fail",
inputArgs: input{
network: "192.168.0.0/16",
peer: peer1Key,
@ -144,12 +158,13 @@ func TestCreateRoute(t *testing.T) {
masquerade: false,
metric: 9999,
enabled: true,
groups: []string{routeGroup1},
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Small NetID",
name: "Small NetID Should Fail",
inputArgs: input{
network: "192.168.0.0/16",
netID: "",
@ -158,6 +173,52 @@ func TestCreateRoute(t *testing.T) {
masquerade: false,
metric: 9999,
enabled: true,
groups: []string{routeGroup1},
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Empty Group List Should Fail",
inputArgs: input{
network: "192.168.0.0/16",
netID: "NewId",
peer: peer1Key,
description: "",
masquerade: false,
metric: 9999,
enabled: true,
groups: []string{},
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Empty Group ID string Should Fail",
inputArgs: input{
network: "192.168.0.0/16",
netID: "NewId",
peer: peer1Key,
description: "",
masquerade: false,
metric: 9999,
enabled: true,
groups: []string{""},
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Invalid Group Should Fail",
inputArgs: input{
network: "192.168.0.0/16",
netID: "NewId",
peer: peer1Key,
description: "",
masquerade: false,
metric: 9999,
enabled: true,
groups: []string{routeInvalidGroup1},
},
errFunc: require.Error,
shouldCreate: false,
@ -183,6 +244,7 @@ func TestCreateRoute(t *testing.T) {
testCase.inputArgs.netID,
testCase.inputArgs.masquerade,
testCase.inputArgs.metric,
testCase.inputArgs.groups,
testCase.inputArgs.enabled,
)
@ -220,6 +282,7 @@ func TestSaveRoute(t *testing.T) {
newPeer *string
newMetric *int
newPrefix *netip.Prefix
newGroups []string
skipCopying bool
shouldCreate bool
errFunc require.ErrorAssertionFunc
@ -237,10 +300,12 @@ func TestSaveRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
newPeer: &validPeer,
newMetric: &validMetric,
newPrefix: &validPrefix,
newGroups: []string{routeGroup2},
errFunc: require.NoError,
shouldCreate: true,
expectedRoute: &route.Route{
@ -253,10 +318,11 @@ func TestSaveRoute(t *testing.T) {
Masquerade: false,
Metric: validMetric,
Enabled: true,
Groups: []string{routeGroup2},
},
},
{
name: "Bad Prefix",
name: "Bad Prefix Should Fail",
existingRoute: &route.Route{
ID: "testingRoute",
Network: netip.MustParsePrefix("192.168.0.0/16"),
@ -267,12 +333,13 @@ func TestSaveRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
newPrefix: &invalidPrefix,
errFunc: require.Error,
},
{
name: "Bad Peer",
name: "Bad Peer Should Fail",
existingRoute: &route.Route{
ID: "testingRoute",
Network: netip.MustParsePrefix("192.168.0.0/16"),
@ -283,12 +350,13 @@ func TestSaveRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
newPeer: &invalidPeer,
errFunc: require.Error,
},
{
name: "Invalid Metric",
name: "Invalid Metric Should Fail",
existingRoute: &route.Route{
ID: "testingRoute",
Network: netip.MustParsePrefix("192.168.0.0/16"),
@ -299,12 +367,13 @@ func TestSaveRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
newMetric: &invalidMetric,
errFunc: require.Error,
},
{
name: "Invalid NetID",
name: "Invalid NetID Should Fail",
existingRoute: &route.Route{
ID: "testingRoute",
Network: netip.MustParsePrefix("192.168.0.0/16"),
@ -315,12 +384,13 @@ func TestSaveRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
newMetric: &invalidMetric,
errFunc: require.Error,
},
{
name: "Nil Route",
name: "Nil Route Should Fail",
existingRoute: &route.Route{
ID: "testingRoute",
Network: netip.MustParsePrefix("192.168.0.0/16"),
@ -331,10 +401,62 @@ func TestSaveRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
skipCopying: true,
errFunc: require.Error,
},
{
name: "Empty Group List Should Fail",
existingRoute: &route.Route{
ID: "testingRoute",
Network: netip.MustParsePrefix("192.168.0.0/16"),
NetID: validNetID,
NetworkType: route.IPv4Network,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
newGroups: []string{},
errFunc: require.Error,
},
{
name: "Empty Group ID String Should Fail",
existingRoute: &route.Route{
ID: "testingRoute",
Network: netip.MustParsePrefix("192.168.0.0/16"),
NetID: validNetID,
NetworkType: route.IPv4Network,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
newGroups: []string{""},
errFunc: require.Error,
},
{
name: "Invalid Group Should Fail",
existingRoute: &route.Route{
ID: "testingRoute",
Network: netip.MustParsePrefix("192.168.0.0/16"),
NetID: validNetID,
NetworkType: route.IPv4Network,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
newGroups: []string{routeInvalidGroup1},
errFunc: require.Error,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
@ -370,6 +492,10 @@ func TestSaveRoute(t *testing.T) {
if testCase.newPrefix != nil {
routeToSave.Network = *testCase.newPrefix
}
if testCase.newGroups != nil {
routeToSave.Groups = testCase.newGroups
}
}
err = am.SaveRoute(account.Id, routeToSave)
@ -409,6 +535,7 @@ func TestUpdateRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
}
testCases := []struct {
@ -423,7 +550,7 @@ func TestUpdateRoute(t *testing.T) {
name: "Happy Path Single OPS",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRoutePeer,
Values: []string{peer2Key},
},
@ -440,40 +567,45 @@ func TestUpdateRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
},
{
name: "Happy Path Multiple OPS",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRouteDescription,
Values: []string{"great"},
},
RouteUpdateOperation{
{
Type: UpdateRouteNetwork,
Values: []string{"192.168.0.0/24"},
},
RouteUpdateOperation{
{
Type: UpdateRoutePeer,
Values: []string{peer2Key},
},
RouteUpdateOperation{
{
Type: UpdateRouteMetric,
Values: []string{"3030"},
},
RouteUpdateOperation{
{
Type: UpdateRouteMasquerade,
Values: []string{"true"},
},
RouteUpdateOperation{
{
Type: UpdateRouteEnabled,
Values: []string{"false"},
},
RouteUpdateOperation{
{
Type: UpdateRouteNetworkIdentifier,
Values: []string{"megaRoute"},
},
{
Type: UpdateRouteGroups,
Values: []string{routeGroup2},
},
},
errFunc: require.NoError,
shouldCreate: true,
@ -487,23 +619,24 @@ func TestUpdateRoute(t *testing.T) {
Masquerade: true,
Metric: 3030,
Enabled: false,
Groups: []string{routeGroup2},
},
},
{
name: "Empty Values",
name: "Empty Values Should Fail",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRoutePeer,
},
},
errFunc: require.Error,
},
{
name: "Multiple Values",
name: "Multiple Values Should Fail",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRoutePeer,
Values: []string{peer2Key, peer1Key},
},
@ -511,10 +644,10 @@ func TestUpdateRoute(t *testing.T) {
errFunc: require.Error,
},
{
name: "Bad Prefix",
name: "Bad Prefix Should Fail",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRouteNetwork,
Values: []string{"192.168.0.0/34"},
},
@ -522,10 +655,10 @@ func TestUpdateRoute(t *testing.T) {
errFunc: require.Error,
},
{
name: "Bad Peer",
name: "Bad Peer Should Fail",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRoutePeer,
Values: []string{"non existing Peer"},
},
@ -536,7 +669,7 @@ func TestUpdateRoute(t *testing.T) {
name: "Empty Peer",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRoutePeer,
Values: []string{""},
},
@ -553,13 +686,14 @@ func TestUpdateRoute(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
},
},
{
name: "Large Network ID",
name: "Large Network ID Should Fail",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRouteNetworkIdentifier,
Values: []string{"12345678901234567890qwertyuiopqwertyuiop1"},
},
@ -567,10 +701,10 @@ func TestUpdateRoute(t *testing.T) {
errFunc: require.Error,
},
{
name: "Empty Network ID",
name: "Empty Network ID Should Fail",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRouteNetworkIdentifier,
Values: []string{""},
},
@ -578,10 +712,10 @@ func TestUpdateRoute(t *testing.T) {
errFunc: require.Error,
},
{
name: "Invalid Metric",
name: "Invalid Metric Should Fail",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRouteMetric,
Values: []string{"999999"},
},
@ -589,16 +723,27 @@ func TestUpdateRoute(t *testing.T) {
errFunc: require.Error,
},
{
name: "Invalid Boolean",
name: "Invalid Boolean Should Fail",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
{
Type: UpdateRouteMasquerade,
Values: []string{"yes"},
},
},
errFunc: require.Error,
},
{
name: "Invalid Group Should Fail",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
{
Type: UpdateRouteGroups,
Values: []string{routeInvalidGroup1},
},
},
errFunc: require.Error,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
@ -697,6 +842,7 @@ func TestGetNetworkMap_RouteSync(t *testing.T) {
Masquerade: false,
Metric: 9999,
Enabled: true,
Groups: []string{routeGroup1},
}
am, err := createRouterManager(t)
@ -714,7 +860,7 @@ func TestGetNetworkMap_RouteSync(t *testing.T) {
require.Len(t, newAccountRoutes.Routes, 0, "new accounts should have no routes")
createdRoute, err := am.CreateRoute(account.Id, baseRoute.Network.String(), baseRoute.Peer,
baseRoute.Description, baseRoute.NetID, baseRoute.Masquerade, baseRoute.Metric, false)
baseRoute.Description, baseRoute.NetID, baseRoute.Masquerade, baseRoute.Metric, baseRoute.Groups, false)
require.NoError(t, err)
noDisabledRoutes, err := am.GetNetworkMap(peer1Key)
@ -734,7 +880,14 @@ func TestGetNetworkMap_RouteSync(t *testing.T) {
peer2Routes, err := am.GetNetworkMap(peer2Key)
require.NoError(t, err)
require.Len(t, peer2Routes.Routes, 1, "we should receive one route for peer2")
require.Len(t, peer2Routes.Routes, 0, "no routes for peers not in the distribution group")
err = am.GroupAddPeer(account.Id, routeGroup1, peer2Key)
require.NoError(t, err)
peer2Routes, err = am.GetNetworkMap(peer2Key)
require.NoError(t, err)
require.Len(t, peer2Routes.Routes, 1, "we should receive one route")
require.True(t, peer1Routes.Routes[0].IsEqual(peer2Routes.Routes[0]), "routes should be the same for peers in the same group")
newGroup := &Group{
@ -844,6 +997,26 @@ func initTestRouteAccount(t *testing.T, am *DefaultAccountManager) (*Account, er
if err != nil {
return nil, err
}
newGroup := &Group{
ID: routeGroup1,
Name: routeGroup1,
Peers: []string{peer1Key},
}
err = am.SaveGroup(accountID, newGroup)
if err != nil {
return nil, err
}
newGroup = &Group{
ID: routeGroup2,
Name: routeGroup2,
Peers: []string{peer1Key},
}
err = am.SaveGroup(accountID, newGroup)
if err != nil {
return nil, err
}
return am.Store.GetAccount(account.Id)
}

View File

@ -73,6 +73,7 @@ type Route struct {
Masquerade bool
Metric int
Enabled bool
Groups []string
}
// Copy copies a route object
@ -87,6 +88,7 @@ func (r *Route) Copy() *Route {
Metric: r.Metric,
Masquerade: r.Masquerade,
Enabled: r.Enabled,
Groups: r.Groups,
}
}
@ -100,7 +102,8 @@ func (r *Route) IsEqual(other *Route) bool {
other.Peer == r.Peer &&
other.Metric == r.Metric &&
other.Masquerade == r.Masquerade &&
other.Enabled == r.Enabled
other.Enabled == r.Enabled &&
compareGroupsList(r.Groups, other.Groups)
}
// ParseNetwork Parses a network prefix string and returns a netip.Prefix object and if is invalid, IPv4 or IPv6
@ -122,3 +125,23 @@ func ParseNetwork(networkString string) (NetworkType, netip.Prefix, error) {
return IPv4Network, masked, nil
}
func compareGroupsList(list, other []string) bool {
if len(list) != len(other) {
return false
}
for _, id := range list {
match := false
for _, otherID := range other {
if id == otherID {
match = true
break
}
}
if !match {
return false
}
}
return true
}