feat: enhance peer neighbor handling by introducing NormalizePeerNeighborString function. Update CreatePeer and UpdatePeer methods in Postgres and Memory stores to validate and normalize neighbor input, improving data integrity and error handling.
CI / changes (push) Successful in 5s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 26s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Has been skipped
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 15s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m4s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 1m26s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m29s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m27s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m23s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m19s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m29s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m22s
CI / changes (push) Successful in 5s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 26s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Has been skipped
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 15s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m4s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 1m26s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m29s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m27s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m23s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m19s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m29s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m22s
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -345,12 +344,8 @@ func renderPeersBirdFragment(st store.Backend, tenantID string, loc birdLocals)
|
|||||||
if p == nil || !p.Enabled {
|
if p == nil || !p.Enabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
neighbor := strings.TrimSpace(p.Neighbor)
|
addr, ok := store.ParsePeerNeighbor(p.Neighbor)
|
||||||
if neighbor == "" {
|
if !ok {
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr, err := netip.ParseAddr(neighbor)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !store.ValidASN(p.RemoteASN) {
|
if !store.ValidASN(p.RemoteASN) {
|
||||||
|
|||||||
@@ -315,6 +315,10 @@ func (p *Postgres) CreatePeer(tenantID string, in *store.BGPPeer) (*store.BGPPee
|
|||||||
if in == nil || in.RemoteASN == 0 {
|
if in == nil || in.RemoteASN == 0 {
|
||||||
return nil, store.ErrInvalidInput
|
return nil, store.ErrInvalidInput
|
||||||
}
|
}
|
||||||
|
neighbor, ok := store.NormalizePeerNeighborString(in.Neighbor)
|
||||||
|
if !ok {
|
||||||
|
return nil, store.ErrInvalidInput
|
||||||
|
}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
id := uuid.NewString()
|
id := uuid.NewString()
|
||||||
meta := map[string]any{"name": in.Name, "session_state": in.SessionState}
|
meta := map[string]any{"name": in.Name, "session_state": in.SessionState}
|
||||||
@@ -330,7 +334,7 @@ func (p *Postgres) CreatePeer(tenantID string, in *store.BGPPeer) (*store.BGPPee
|
|||||||
_, err := p.pool.Exec(ctx, `
|
_, err := p.pool.Exec(ctx, `
|
||||||
INSERT INTO bgp_peer (id, tenant_id, bgp_speaker_id, neighbor, remote_asn, enabled, policies_json, meta_json)
|
INSERT INTO bgp_peer (id, tenant_id, bgp_speaker_id, neighbor, remote_asn, enabled, policies_json, meta_json)
|
||||||
VALUES ($1,$2,$3,$4::inet, $5, $6, $7::jsonb, $8::jsonb)`,
|
VALUES ($1,$2,$3,$4::inet, $5, $6, $7::jsonb, $8::jsonb)`,
|
||||||
id, tenantID, sp, in.Neighbor, in.RemoteASN, in.Enabled, pol, string(mb))
|
id, tenantID, sp, neighbor, in.RemoteASN, in.Enabled, pol, string(mb))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -343,7 +347,11 @@ func (p *Postgres) UpdatePeer(tenantID, id string, patch *store.PeerPatch) (*sto
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if patch.Neighbor != nil {
|
if patch.Neighbor != nil {
|
||||||
cur.Neighbor = strings.TrimSpace(*patch.Neighbor)
|
n, ok := store.NormalizePeerNeighborString(*patch.Neighbor)
|
||||||
|
if !ok {
|
||||||
|
return nil, store.ErrInvalidInput
|
||||||
|
}
|
||||||
|
cur.Neighbor = n
|
||||||
}
|
}
|
||||||
if patch.RemoteASN != nil {
|
if patch.RemoteASN != nil {
|
||||||
cur.RemoteASN = *patch.RemoteASN
|
cur.RemoteASN = *patch.RemoteASN
|
||||||
|
|||||||
@@ -614,7 +614,11 @@ func (m *Memory) GetPeer(tenantID, id string) (*BGPPeer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Memory) CreatePeer(tenantID string, in *BGPPeer) (*BGPPeer, error) {
|
func (m *Memory) CreatePeer(tenantID string, in *BGPPeer) (*BGPPeer, error) {
|
||||||
if in == nil || strings.TrimSpace(in.Neighbor) == "" || in.RemoteASN == 0 {
|
if in == nil || in.RemoteASN == 0 {
|
||||||
|
return nil, ErrInvalidInput
|
||||||
|
}
|
||||||
|
neighbor, ok := NormalizePeerNeighborString(in.Neighbor)
|
||||||
|
if !ok {
|
||||||
return nil, ErrInvalidInput
|
return nil, ErrInvalidInput
|
||||||
}
|
}
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
@@ -625,7 +629,7 @@ func (m *Memory) CreatePeer(tenantID string, in *BGPPeer) (*BGPPeer, error) {
|
|||||||
id := uuid.NewString()
|
id := uuid.NewString()
|
||||||
p := &BGPPeer{
|
p := &BGPPeer{
|
||||||
ID: id, TenantID: tenantID, SpeakerID: in.SpeakerID, Name: in.Name,
|
ID: id, TenantID: tenantID, SpeakerID: in.SpeakerID, Name: in.Name,
|
||||||
Neighbor: strings.TrimSpace(in.Neighbor), RemoteASN: in.RemoteASN, Enabled: in.Enabled,
|
Neighbor: neighbor, RemoteASN: in.RemoteASN, Enabled: in.Enabled,
|
||||||
SessionState: in.SessionState, PoliciesJSON: in.PoliciesJSON,
|
SessionState: in.SessionState, PoliciesJSON: in.PoliciesJSON,
|
||||||
}
|
}
|
||||||
if !p.Enabled && p.SessionState == "" {
|
if !p.Enabled && p.SessionState == "" {
|
||||||
@@ -646,7 +650,11 @@ func (m *Memory) UpdatePeer(tenantID, id string, patch *PeerPatch) (*BGPPeer, er
|
|||||||
return nil, ErrNotFound
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
if patch.Neighbor != nil {
|
if patch.Neighbor != nil {
|
||||||
p.Neighbor = strings.TrimSpace(*patch.Neighbor)
|
n, ok := NormalizePeerNeighborString(*patch.Neighbor)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrInvalidInput
|
||||||
|
}
|
||||||
|
p.Neighbor = n
|
||||||
}
|
}
|
||||||
if patch.RemoteASN != nil {
|
if patch.RemoteASN != nil {
|
||||||
p.RemoteASN = *patch.RemoteASN
|
p.RemoteASN = *patch.RemoteASN
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParsePeerNeighbor parses a BGP neighbor value for BIRD output: a plain IPv4/IPv6
|
||||||
|
// address, or a host prefix (/32 or /128) which is a common input mistake.
|
||||||
|
func ParsePeerNeighbor(s string) (netip.Addr, bool) {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
return netip.Addr{}, false
|
||||||
|
}
|
||||||
|
if addr, err := netip.ParseAddr(s); err == nil {
|
||||||
|
return addr, true
|
||||||
|
}
|
||||||
|
pfx, err := netip.ParsePrefix(s)
|
||||||
|
if err != nil {
|
||||||
|
return netip.Addr{}, false
|
||||||
|
}
|
||||||
|
addr := pfx.Addr()
|
||||||
|
if addr.Is4() && pfx.Bits() == 32 {
|
||||||
|
return addr, true
|
||||||
|
}
|
||||||
|
if addr.Is6() && pfx.Bits() == 128 {
|
||||||
|
return addr, true
|
||||||
|
}
|
||||||
|
return netip.Addr{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizePeerNeighborString returns the canonical host address string for storage
|
||||||
|
// and BIRD, or false if s is not a usable neighbor.
|
||||||
|
func NormalizePeerNeighborString(s string) (string, bool) {
|
||||||
|
addr, ok := ParsePeerNeighbor(s)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return addr.String(), true
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestParsePeerNeighbor(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in string
|
||||||
|
want string
|
||||||
|
wantOK bool
|
||||||
|
}{
|
||||||
|
{"192.168.0.2", "192.168.0.2", true},
|
||||||
|
{"192.168.0.2/32", "192.168.0.2", true},
|
||||||
|
{" 192.168.0.2/32 ", "192.168.0.2", true},
|
||||||
|
{"2001:db8::1", "2001:db8::1", true},
|
||||||
|
{"2001:db8::1/128", "2001:db8::1", true},
|
||||||
|
{"192.168.0.0/24", "", false},
|
||||||
|
{"not-an-ip", "", false},
|
||||||
|
{"", "", false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
addr, ok := ParsePeerNeighbor(tt.in)
|
||||||
|
if ok != tt.wantOK {
|
||||||
|
t.Errorf("ParsePeerNeighbor(%q) ok=%v want %v", tt.in, ok, tt.wantOK)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !tt.wantOK {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if got := addr.String(); got != tt.want {
|
||||||
|
t.Errorf("ParsePeerNeighbor(%q) = %q want %q", tt.in, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user