// SPDX-FileCopyrightText: 2022 Free Mobile // SPDX-License-Identifier: AGPL-3.0-only package bmp import ( "errors" "fmt" "net/netip" "strconv" "strings" "time" "github.com/osrg/gobgp/v4/pkg/packet/bgp" ) // Configuration describes the configuration for the BMP component. Only one peer is emulated. type Configuration struct { // Target specify the IP address and port to generate BMP routes to. Empty if this component is disabled. Target string `validate:"isdefault|hostname_port"` // Routes is the set of routes to announce to the collector using BMP. Routes []RouteConfiguration `validate:"dive"` // LocalASN is the local AS number LocalASN uint16 `validate:"required,min=1"` // PeerASN is the peer AS number PeerASN uint16 `validate:"required,min=1"` // LocalIP is the local IP address. LocalIP netip.Addr `validate:"required"` // PeerIP is the peer IP address. PeerIP netip.Addr `validate:"required"` // RetryAfter tells how much time to wait before retrying RetryAfter time.Duration `validate:"min=0s"` // StatsDelay tells how much time to wait between two BMP stats message (to check connection liveness) StatsDelay time.Duration `validate:"min=0s"` } // RouteConfiguration describes a route to be generated with BMP. type RouteConfiguration struct { // Prefix is the set of prefixes to announce. Prefixes []netip.Prefix `validate:"min=1"` // ASPath is the AS path to associate with the prefixes. ASPath []uint32 `validate:"min=1"` // Communities are the set of standard communities to associate with the prefixes. Communities []Community // LargeCommunities are the set of large communities to associate with the prefixes. LargeCommunities []LargeCommunity } // DefaultConfiguration represents the default configuration for the BMP component. func DefaultConfiguration() Configuration { return Configuration{ LocalASN: 64496, PeerASN: 64497, LocalIP: netip.MustParseAddr("2001:db8::1"), PeerIP: netip.MustParseAddr("2001:db8::2"), RetryAfter: time.Duration(5 * time.Second), StatsDelay: time.Duration(time.Minute), } } // Community is a standard community. type Community uint32 // UnmarshalText parses a standard community. func (comm *Community) UnmarshalText(input []byte) error { text := string(input) elems := strings.Split(text, ":") if len(elems) != 2 { return errors.New("community should be ASN:XX") } asn, err := strconv.ParseUint(elems[0], 10, 16) if err != nil { return errors.New("community should be ASN2:XX") } local, err := strconv.ParseUint(elems[1], 10, 16) if err != nil { return errors.New("community should be ASN:XX2") } *comm = Community((asn << 16) + local) return nil } // String turns a community to a string. func (comm Community) String() string { return fmt.Sprintf("%d:%d", comm>>16, comm&0xffff) } // LargeCommunity represents a large community. type LargeCommunity bgp.LargeCommunity // UnmarshalText parses a large community func (comm *LargeCommunity) UnmarshalText(input []byte) error { text := string(input) elems := strings.Split(text, ":") if len(elems) != 3 { return errors.New("community should be ASN:XX:YY") } asn, err := strconv.ParseUint(elems[0], 10, 32) if err != nil { return errors.New("community should be ASN4:XX:YY") } local1, err := strconv.ParseUint(elems[1], 10, 32) if err != nil { return errors.New("community should be ASN:XX4:YY") } local2, err := strconv.ParseUint(elems[2], 10, 32) if err != nil { return errors.New("community should be ASN:XX:YY4") } *comm = LargeCommunity{ ASN: uint32(asn), LocalData1: uint32(local1), LocalData2: uint32(local2), } return nil } // String turns a large community to a string. func (comm LargeCommunity) String() string { b := bgp.LargeCommunity(comm) return b.String() }