Add live routing table with CIDR mask length tracking
- Added new live_routes table with mask_length column for tracking CIDR prefix lengths - Updated PrefixHandler to maintain live routing table with additions and deletions - Added route expiration functionality (5 minute timeout) to in-memory routing table - Added prefix distribution stats showing count of prefixes by mask length - Added IPv4/IPv6 prefix distribution cards to status page - Updated database interface with UpsertLiveRoute, DeleteLiveRoute, and GetPrefixDistribution - Set all handler queue depths to 50000 for consistency - Doubled DBHandler batch size to 32000 for better throughput - Fixed withdrawal handling to delete routes when origin ASN is available
This commit is contained in:
@@ -4,6 +4,7 @@ package database
|
||||
import (
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -387,5 +388,120 @@ func (d *Database) GetStats() (Stats, error) {
|
||||
stats.FileSizeBytes = fileInfo.Size()
|
||||
}
|
||||
|
||||
// Get live routes count
|
||||
err = d.db.QueryRow("SELECT COUNT(*) FROM live_routes").Scan(&stats.LiveRoutes)
|
||||
if err != nil {
|
||||
return stats, fmt.Errorf("failed to count live routes: %w", err)
|
||||
}
|
||||
|
||||
// Get prefix distribution
|
||||
stats.IPv4PrefixDistribution, stats.IPv6PrefixDistribution, err = d.GetPrefixDistribution()
|
||||
if err != nil {
|
||||
// Log but don't fail
|
||||
d.logger.Warn("Failed to get prefix distribution", "error", err)
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// UpsertLiveRoute inserts or updates a live route
|
||||
func (d *Database) UpsertLiveRoute(route *LiveRoute) error {
|
||||
query := `
|
||||
INSERT INTO live_routes (id, prefix, mask_length, ip_version, origin_asn, peer_ip, as_path, next_hop, last_updated)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(prefix, origin_asn, peer_ip) DO UPDATE SET
|
||||
mask_length = excluded.mask_length,
|
||||
ip_version = excluded.ip_version,
|
||||
as_path = excluded.as_path,
|
||||
next_hop = excluded.next_hop,
|
||||
last_updated = excluded.last_updated
|
||||
`
|
||||
|
||||
// Encode AS path as JSON
|
||||
pathJSON, err := json.Marshal(route.ASPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode AS path: %w", err)
|
||||
}
|
||||
|
||||
_, err = d.db.Exec(query,
|
||||
route.ID.String(),
|
||||
route.Prefix,
|
||||
route.MaskLength,
|
||||
route.IPVersion,
|
||||
route.OriginASN,
|
||||
route.PeerIP,
|
||||
string(pathJSON),
|
||||
route.NextHop,
|
||||
route.LastUpdated,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var query string
|
||||
var err error
|
||||
|
||||
if originASN == 0 {
|
||||
// Delete all routes for this prefix from this peer
|
||||
query = `DELETE FROM live_routes WHERE prefix = ? AND peer_ip = ?`
|
||||
_, err = d.db.Exec(query, prefix, peerIP)
|
||||
} else {
|
||||
// Delete specific route
|
||||
query = `DELETE FROM live_routes WHERE prefix = ? AND origin_asn = ? AND peer_ip = ?`
|
||||
_, err = d.db.Exec(query, prefix, originASN, peerIP)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetPrefixDistribution returns the distribution of prefixes by mask length
|
||||
func (d *Database) GetPrefixDistribution() (ipv4 []PrefixDistribution, ipv6 []PrefixDistribution, err error) {
|
||||
// IPv4 distribution
|
||||
query := `
|
||||
SELECT mask_length, COUNT(*) as count
|
||||
FROM live_routes
|
||||
WHERE ip_version = 4
|
||||
GROUP BY mask_length
|
||||
ORDER BY mask_length
|
||||
`
|
||||
rows, err := d.db.Query(query)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to query IPv4 distribution: %w", err)
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
for rows.Next() {
|
||||
var dist PrefixDistribution
|
||||
if err := rows.Scan(&dist.MaskLength, &dist.Count); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to scan IPv4 distribution: %w", err)
|
||||
}
|
||||
ipv4 = append(ipv4, dist)
|
||||
}
|
||||
|
||||
// IPv6 distribution
|
||||
query = `
|
||||
SELECT mask_length, COUNT(*) as count
|
||||
FROM live_routes
|
||||
WHERE ip_version = 6
|
||||
GROUP BY mask_length
|
||||
ORDER BY mask_length
|
||||
`
|
||||
rows, err = d.db.Query(query)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to query IPv6 distribution: %w", err)
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
for rows.Next() {
|
||||
var dist PrefixDistribution
|
||||
if err := rows.Scan(&dist.MaskLength, &dist.Count); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to scan IPv6 distribution: %w", err)
|
||||
}
|
||||
ipv6 = append(ipv6, dist)
|
||||
}
|
||||
|
||||
return ipv4, ipv6, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user