|
|
|
@@ -220,21 +220,22 @@ func (d *Database) beginTx() (*loggingTx, error) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A live-route upsert is an UPDATE followed, only when no row matched, by an
|
|
|
|
|
// INSERT. The UPDATE's rows-affected count (1 for an existing key, 0 for a new
|
|
|
|
|
// one) is what lets the in-memory route counters stay exact without a COUNT(*).
|
|
|
|
|
// Callers hold the database write lock, so no other writer can insert the same
|
|
|
|
|
// key between the two statements. The id column is set only on INSERT, so an
|
|
|
|
|
// updated route keeps its original id, exactly as the previous ON CONFLICT
|
|
|
|
|
// upsert did.
|
|
|
|
|
// INSERT. A live route is keyed by prefix and peer, so an announcement replaces
|
|
|
|
|
// the peer's route for the prefix whatever its origin AS. The UPDATE's
|
|
|
|
|
// rows-affected count (1 for an existing key, 0 for a new one) is what lets the
|
|
|
|
|
// in-memory route counters stay exact without a COUNT(*). Callers hold the
|
|
|
|
|
// database write lock, so no other writer can insert the same key between the
|
|
|
|
|
// two statements. The id column is set only on INSERT, so an updated route
|
|
|
|
|
// keeps its original id, exactly as the previous ON CONFLICT upsert did.
|
|
|
|
|
const (
|
|
|
|
|
updateLiveRouteV4SQL = `UPDATE live_routes_v4 SET mask_length = ?, as_path = ?, next_hop = ?,
|
|
|
|
|
last_updated = ? WHERE prefix = ? AND origin_asn = ? AND peer_ip = ?`
|
|
|
|
|
updateLiveRouteV4SQL = `UPDATE live_routes_v4 SET mask_length = ?, origin_asn = ?, as_path = ?,
|
|
|
|
|
next_hop = ?, last_updated = ? WHERE prefix = ? AND peer_ip = ?`
|
|
|
|
|
|
|
|
|
|
insertLiveRouteV4SQL = `INSERT INTO live_routes_v4 (id, prefix, mask_length, origin_asn, peer_ip,
|
|
|
|
|
as_path, next_hop, last_updated) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
|
|
|
|
|
|
|
|
updateLiveRouteV6SQL = `UPDATE live_routes_v6 SET mask_length = ?, as_path = ?, next_hop = ?,
|
|
|
|
|
last_updated = ? WHERE prefix = ? AND origin_asn = ? AND peer_ip = ?`
|
|
|
|
|
updateLiveRouteV6SQL = `UPDATE live_routes_v6 SET mask_length = ?, origin_asn = ?, as_path = ?,
|
|
|
|
|
next_hop = ?, last_updated = ? WHERE prefix = ? AND peer_ip = ?`
|
|
|
|
|
|
|
|
|
|
insertLiveRouteV6SQL = `INSERT INTO live_routes_v6 (id, prefix, mask_length, origin_asn, peer_ip,
|
|
|
|
|
as_path, next_hop, last_updated) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
|
|
@@ -253,8 +254,8 @@ const (
|
|
|
|
|
// live route for its prefix.
|
|
|
|
|
func upsertRouteRowV4(upd, ins, has *sql.Stmt, route *LiveRoute, pathJSON string) (
|
|
|
|
|
inserted, newPrefix bool, err error) {
|
|
|
|
|
res, err := upd.Exec(route.MaskLength, pathJSON, route.NextHop, route.LastUpdated,
|
|
|
|
|
route.Prefix, route.OriginASN, route.PeerIP)
|
|
|
|
|
res, err := upd.Exec(route.MaskLength, route.OriginASN, pathJSON, route.NextHop, route.LastUpdated,
|
|
|
|
|
route.Prefix, route.PeerIP)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, false, err
|
|
|
|
|
}
|
|
|
|
@@ -285,8 +286,8 @@ func upsertRouteRowV4(upd, ins, has *sql.Stmt, route *LiveRoute, pathJSON string
|
|
|
|
|
// live route for its prefix.
|
|
|
|
|
func upsertRouteRowV6(upd, ins, has *sql.Stmt, route *LiveRoute, pathJSON string) (
|
|
|
|
|
inserted, newPrefix bool, err error) {
|
|
|
|
|
res, err := upd.Exec(route.MaskLength, pathJSON, route.NextHop, route.LastUpdated,
|
|
|
|
|
route.Prefix, route.OriginASN, route.PeerIP)
|
|
|
|
|
res, err := upd.Exec(route.MaskLength, route.OriginASN, pathJSON, route.NextHop, route.LastUpdated,
|
|
|
|
|
route.Prefix, route.PeerIP)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, false, err
|
|
|
|
|
}
|
|
|
|
@@ -433,32 +434,18 @@ func (d *Database) DeleteLiveRouteBatch(deletions []LiveRouteDeletion) error {
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// No longer need to separate deletions since we handle them in the loop below
|
|
|
|
|
|
|
|
|
|
// Prepare statements for both IPv4 and IPv6 tables
|
|
|
|
|
stmtV4WithOrigin, err := tx.Prepare(`DELETE FROM live_routes_v4 WHERE prefix = ? AND origin_asn = ? AND peer_ip = ?`)
|
|
|
|
|
deleteV4, err := tx.Prepare(`DELETE FROM live_routes_v4 WHERE prefix = ? AND peer_ip = ?`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to prepare IPv4 delete with origin statement: %w", err)
|
|
|
|
|
return fmt.Errorf("failed to prepare IPv4 delete statement: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer func() { _ = stmtV4WithOrigin.Close() }()
|
|
|
|
|
defer func() { _ = deleteV4.Close() }()
|
|
|
|
|
|
|
|
|
|
stmtV4WithoutOrigin, err := tx.Prepare(`DELETE FROM live_routes_v4 WHERE prefix = ? AND peer_ip = ?`)
|
|
|
|
|
deleteV6, err := tx.Prepare(`DELETE FROM live_routes_v6 WHERE prefix = ? AND peer_ip = ?`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to prepare IPv4 delete without origin statement: %w", err)
|
|
|
|
|
return fmt.Errorf("failed to prepare IPv6 delete statement: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer func() { _ = stmtV4WithoutOrigin.Close() }()
|
|
|
|
|
|
|
|
|
|
stmtV6WithOrigin, err := tx.Prepare(`DELETE FROM live_routes_v6 WHERE prefix = ? AND origin_asn = ? AND peer_ip = ?`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to prepare IPv6 delete with origin statement: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer func() { _ = stmtV6WithOrigin.Close() }()
|
|
|
|
|
|
|
|
|
|
stmtV6WithoutOrigin, err := tx.Prepare(`DELETE FROM live_routes_v6 WHERE prefix = ? AND peer_ip = ?`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to prepare IPv6 delete without origin statement: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer func() { _ = stmtV6WithoutOrigin.Close() }()
|
|
|
|
|
defer func() { _ = deleteV6.Close() }()
|
|
|
|
|
|
|
|
|
|
hasV4, err := tx.Prepare(prefixHasLiveRouteV4SQL)
|
|
|
|
|
if err != nil {
|
|
|
|
@@ -477,38 +464,18 @@ func (d *Database) DeleteLiveRouteBatch(deletions []LiveRouteDeletion) error {
|
|
|
|
|
// Mask lengths of the prefixes this batch leaves with no live route.
|
|
|
|
|
var gonePrefixMaskLengthsV4, gonePrefixMaskLengthsV6 []int
|
|
|
|
|
for _, del := range deletions {
|
|
|
|
|
var stmt *sql.Stmt
|
|
|
|
|
|
|
|
|
|
// Select appropriate statement based on IP version and whether we have origin ASN
|
|
|
|
|
//nolint:nestif // Clear logic for selecting the right statement
|
|
|
|
|
if del.IPVersion == ipVersionV4 {
|
|
|
|
|
if del.OriginASN == 0 {
|
|
|
|
|
stmt = stmtV4WithoutOrigin
|
|
|
|
|
} else {
|
|
|
|
|
stmt = stmtV4WithOrigin
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if del.OriginASN == 0 {
|
|
|
|
|
stmt = stmtV6WithoutOrigin
|
|
|
|
|
} else {
|
|
|
|
|
stmt = stmtV6WithOrigin
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Execute deletion
|
|
|
|
|
var res sql.Result
|
|
|
|
|
if del.OriginASN == 0 {
|
|
|
|
|
res, err = stmt.Exec(del.Prefix, del.PeerIP)
|
|
|
|
|
} else {
|
|
|
|
|
res, err = stmt.Exec(del.Prefix, del.OriginASN, del.PeerIP)
|
|
|
|
|
stmt := deleteV4
|
|
|
|
|
if del.IPVersion != ipVersionV4 {
|
|
|
|
|
stmt = deleteV6
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := stmt.Exec(del.Prefix, del.PeerIP)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to delete route %s: %w", del.Prefix, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A deletion with no origin ASN can remove several rows, so use the
|
|
|
|
|
// exact rows-affected count to keep the in-memory route counters right.
|
|
|
|
|
// The feed often withdraws a route that is not live, which deletes
|
|
|
|
|
// nothing, so the in-memory route counters use the rows-affected count.
|
|
|
|
|
affected, err := res.RowsAffected()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to count deleted route %s: %w", del.Prefix, err)
|
|
|
|
@@ -1274,9 +1241,8 @@ func (d *Database) UpsertLiveRoute(route *LiveRoute) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DeleteLiveRoute deletes a live route
|
|
|
|
|
// If originASN is 0, deletes all routes for the prefix/peer combination
|
|
|
|
|
func (d *Database) DeleteLiveRoute(prefix string, originASN int, peerIP string) error {
|
|
|
|
|
// DeleteLiveRoute deletes the live route the peer has for the prefix
|
|
|
|
|
func (d *Database) DeleteLiveRoute(prefix, peerIP string) error {
|
|
|
|
|
d.lock("DeleteLiveRoute")
|
|
|
|
|
defer d.unlock()
|
|
|
|
|
|
|
|
|
@@ -1301,22 +1267,12 @@ func (d *Database) DeleteLiveRoute(prefix string, originASN int, peerIP string)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// Literal per-table queries (rather than one formatted with the table name)
|
|
|
|
|
// so the delete carries no dynamically built SQL. A delete with no origin
|
|
|
|
|
// ASN can remove several rows.
|
|
|
|
|
// so the delete carries no dynamically built SQL.
|
|
|
|
|
var res sql.Result
|
|
|
|
|
switch {
|
|
|
|
|
case isV4 && originASN == 0:
|
|
|
|
|
if isV4 {
|
|
|
|
|
res, err = tx.Exec(`DELETE FROM live_routes_v4 WHERE prefix = ? AND peer_ip = ?`, prefix, peerIP)
|
|
|
|
|
case isV4:
|
|
|
|
|
res, err = tx.Exec(
|
|
|
|
|
`DELETE FROM live_routes_v4 WHERE prefix = ? AND origin_asn = ? AND peer_ip = ?`,
|
|
|
|
|
prefix, originASN, peerIP)
|
|
|
|
|
case originASN == 0:
|
|
|
|
|
} else {
|
|
|
|
|
res, err = tx.Exec(`DELETE FROM live_routes_v6 WHERE prefix = ? AND peer_ip = ?`, prefix, peerIP)
|
|
|
|
|
default:
|
|
|
|
|
res, err = tx.Exec(
|
|
|
|
|
`DELETE FROM live_routes_v6 WHERE prefix = ? AND origin_asn = ? AND peer_ip = ?`,
|
|
|
|
|
prefix, originASN, peerIP)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|