summaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/config/default.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/pkg/config/default.go')
-rw-r--r--internal/pkg/config/default.go524
1 files changed, 524 insertions, 0 deletions
diff --git a/internal/pkg/config/default.go b/internal/pkg/config/default.go
new file mode 100644
index 00000000..6ab1eddc
--- /dev/null
+++ b/internal/pkg/config/default.go
@@ -0,0 +1,524 @@
+package config
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math"
+ "net"
+ "reflect"
+ "strconv"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+ "github.com/osrg/gobgp/pkg/packet/bmp"
+ "github.com/osrg/gobgp/pkg/packet/rtr"
+ "github.com/spf13/viper"
+)
+
+const (
+ DEFAULT_HOLDTIME = 90
+ DEFAULT_IDLE_HOLDTIME_AFTER_RESET = 30
+ DEFAULT_CONNECT_RETRY = 120
+)
+
+var forcedOverwrittenConfig = []string{
+ "neighbor.config.peer-as",
+ "neighbor.timers.config.minimum-advertisement-interval",
+}
+
+var configuredFields map[string]interface{}
+
+func RegisterConfiguredFields(addr string, n interface{}) {
+ if configuredFields == nil {
+ configuredFields = make(map[string]interface{})
+ }
+ configuredFields[addr] = n
+}
+
+func defaultAfiSafi(typ AfiSafiType, enable bool) AfiSafi {
+ return AfiSafi{
+ Config: AfiSafiConfig{
+ AfiSafiName: typ,
+ Enabled: enable,
+ },
+ State: AfiSafiState{
+ AfiSafiName: typ,
+ Family: bgp.AddressFamilyValueMap[string(typ)],
+ },
+ }
+}
+
+func SetDefaultNeighborConfigValues(n *Neighbor, pg *PeerGroup, g *Global) error {
+ // Determines this function is called against the same Neighbor struct,
+ // and if already called, returns immediately.
+ if n.State.LocalAs != 0 {
+ return nil
+ }
+
+ return setDefaultNeighborConfigValuesWithViper(nil, n, g, pg)
+}
+
+func setDefaultNeighborConfigValuesWithViper(v *viper.Viper, n *Neighbor, g *Global, pg *PeerGroup) error {
+ if n == nil {
+ return fmt.Errorf("neighbor config is nil")
+ }
+ if g == nil {
+ return fmt.Errorf("global config is nil")
+ }
+
+ if v == nil {
+ v = viper.New()
+ }
+
+ if pg != nil {
+ if err := OverwriteNeighborConfigWithPeerGroup(n, pg); err != nil {
+ return err
+ }
+ }
+
+ if n.Config.LocalAs == 0 {
+ n.Config.LocalAs = g.Config.As
+ if !g.Confederation.Config.Enabled || n.IsConfederation(g) {
+ n.Config.LocalAs = g.Config.As
+ } else {
+ n.Config.LocalAs = g.Confederation.Config.Identifier
+ }
+ }
+ n.State.LocalAs = n.Config.LocalAs
+
+ if n.Config.PeerAs != n.Config.LocalAs {
+ n.Config.PeerType = PEER_TYPE_EXTERNAL
+ n.State.PeerType = PEER_TYPE_EXTERNAL
+ n.State.RemovePrivateAs = n.Config.RemovePrivateAs
+ n.AsPathOptions.State.ReplacePeerAs = n.AsPathOptions.Config.ReplacePeerAs
+ } else {
+ n.Config.PeerType = PEER_TYPE_INTERNAL
+ n.State.PeerType = PEER_TYPE_INTERNAL
+ if string(n.Config.RemovePrivateAs) != "" {
+ return fmt.Errorf("can't set remove-private-as for iBGP peer")
+ }
+ if n.AsPathOptions.Config.ReplacePeerAs {
+ return fmt.Errorf("can't set replace-peer-as for iBGP peer")
+ }
+ }
+
+ if n.State.NeighborAddress == "" {
+ n.State.NeighborAddress = n.Config.NeighborAddress
+ }
+
+ n.State.PeerAs = n.Config.PeerAs
+ n.AsPathOptions.State.AllowOwnAs = n.AsPathOptions.Config.AllowOwnAs
+
+ if !v.IsSet("neighbor.error-handling.config.treat-as-withdraw") {
+ n.ErrorHandling.Config.TreatAsWithdraw = true
+ }
+
+ if !v.IsSet("neighbor.timers.config.connect-retry") && n.Timers.Config.ConnectRetry == 0 {
+ n.Timers.Config.ConnectRetry = float64(DEFAULT_CONNECT_RETRY)
+ }
+ if !v.IsSet("neighbor.timers.config.hold-time") && n.Timers.Config.HoldTime == 0 {
+ n.Timers.Config.HoldTime = float64(DEFAULT_HOLDTIME)
+ }
+ if !v.IsSet("neighbor.timers.config.keepalive-interval") && n.Timers.Config.KeepaliveInterval == 0 {
+ n.Timers.Config.KeepaliveInterval = n.Timers.Config.HoldTime / 3
+ }
+ if !v.IsSet("neighbor.timers.config.idle-hold-time-after-reset") && n.Timers.Config.IdleHoldTimeAfterReset == 0 {
+ n.Timers.Config.IdleHoldTimeAfterReset = float64(DEFAULT_IDLE_HOLDTIME_AFTER_RESET)
+ }
+
+ if n.Config.NeighborInterface != "" {
+ if n.RouteServer.Config.RouteServerClient {
+ return fmt.Errorf("configuring route server client as unnumbered peer is not supported")
+ }
+ addr, err := GetIPv6LinkLocalNeighborAddress(n.Config.NeighborInterface)
+ if err != nil {
+ return err
+ }
+ n.State.NeighborAddress = addr
+ }
+
+ if n.Transport.Config.LocalAddress == "" {
+ if n.State.NeighborAddress == "" {
+ return fmt.Errorf("no neighbor address/interface specified")
+ }
+ ipAddr, err := net.ResolveIPAddr("ip", n.State.NeighborAddress)
+ if err != nil {
+ return err
+ }
+ localAddress := "0.0.0.0"
+ if ipAddr.IP.To4() == nil {
+ localAddress = "::"
+ if ipAddr.Zone != "" {
+ localAddress, err = getIPv6LinkLocalAddress(ipAddr.Zone)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ n.Transport.Config.LocalAddress = localAddress
+ }
+
+ if len(n.AfiSafis) == 0 {
+ if n.Config.NeighborInterface != "" {
+ n.AfiSafis = []AfiSafi{
+ defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true),
+ defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true),
+ }
+ } else if ipAddr, err := net.ResolveIPAddr("ip", n.State.NeighborAddress); err != nil {
+ return fmt.Errorf("invalid neighbor address: %s", n.State.NeighborAddress)
+ } else if ipAddr.IP.To4() != nil {
+ n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true)}
+ } else {
+ n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true)}
+ }
+ for i := range n.AfiSafis {
+ n.AfiSafis[i].AddPaths.Config.Receive = n.AddPaths.Config.Receive
+ n.AfiSafis[i].AddPaths.State.Receive = n.AddPaths.Config.Receive
+ n.AfiSafis[i].AddPaths.Config.SendMax = n.AddPaths.Config.SendMax
+ n.AfiSafis[i].AddPaths.State.SendMax = n.AddPaths.Config.SendMax
+ }
+ } else {
+ afs, err := extractArray(v.Get("neighbor.afi-safis"))
+ if err != nil {
+ return err
+ }
+ for i := range n.AfiSafis {
+ vv := viper.New()
+ if len(afs) > i {
+ vv.Set("afi-safi", afs[i])
+ }
+ rf, err := bgp.GetRouteFamily(string(n.AfiSafis[i].Config.AfiSafiName))
+ if err != nil {
+ return err
+ }
+ n.AfiSafis[i].State.Family = rf
+ n.AfiSafis[i].State.AfiSafiName = n.AfiSafis[i].Config.AfiSafiName
+ if !vv.IsSet("afi-safi.config.enabled") {
+ n.AfiSafis[i].Config.Enabled = true
+ }
+ n.AfiSafis[i].MpGracefulRestart.State.Enabled = n.AfiSafis[i].MpGracefulRestart.Config.Enabled
+ if !vv.IsSet("afi-safi.add-paths.config.receive") {
+ n.AfiSafis[i].AddPaths.Config.Receive = n.AddPaths.Config.Receive
+ }
+ n.AfiSafis[i].AddPaths.State.Receive = n.AfiSafis[i].AddPaths.Config.Receive
+ if !vv.IsSet("afi-safi.add-paths.config.send-max") {
+ n.AfiSafis[i].AddPaths.Config.SendMax = n.AddPaths.Config.SendMax
+ }
+ n.AfiSafis[i].AddPaths.State.SendMax = n.AfiSafis[i].AddPaths.Config.SendMax
+ }
+ }
+
+ n.State.Description = n.Config.Description
+ n.State.AdminDown = n.Config.AdminDown
+
+ if n.GracefulRestart.Config.Enabled {
+ if !v.IsSet("neighbor.graceful-restart.config.restart-time") && n.GracefulRestart.Config.RestartTime == 0 {
+ // RFC 4724 4. Operation
+ // A suggested default for the Restart Time is a value less than or
+ // equal to the HOLDTIME carried in the OPEN.
+ n.GracefulRestart.Config.RestartTime = uint16(n.Timers.Config.HoldTime)
+ }
+ if !v.IsSet("neighbor.graceful-restart.config.deferral-time") && n.GracefulRestart.Config.DeferralTime == 0 {
+ // RFC 4724 4.1. Procedures for the Restarting Speaker
+ // The value of this timer should be large
+ // enough, so as to provide all the peers of the Restarting Speaker with
+ // enough time to send all the routes to the Restarting Speaker
+ n.GracefulRestart.Config.DeferralTime = uint16(360)
+ }
+ }
+
+ if n.EbgpMultihop.Config.Enabled {
+ if n.TtlSecurity.Config.Enabled {
+ return fmt.Errorf("ebgp-multihop and ttl-security are mututally exclusive")
+ }
+ if n.EbgpMultihop.Config.MultihopTtl == 0 {
+ n.EbgpMultihop.Config.MultihopTtl = 255
+ }
+ } else if n.TtlSecurity.Config.Enabled {
+ if n.TtlSecurity.Config.TtlMin == 0 {
+ n.TtlSecurity.Config.TtlMin = 255
+ }
+ }
+
+ if n.RouteReflector.Config.RouteReflectorClient {
+ if n.RouteReflector.Config.RouteReflectorClusterId == "" {
+ n.RouteReflector.State.RouteReflectorClusterId = RrClusterIdType(g.Config.RouterId)
+ } else {
+ id := string(n.RouteReflector.Config.RouteReflectorClusterId)
+ if ip := net.ParseIP(id).To4(); ip != nil {
+ n.RouteReflector.State.RouteReflectorClusterId = n.RouteReflector.Config.RouteReflectorClusterId
+ } else if num, err := strconv.ParseUint(id, 10, 32); err == nil {
+ ip = make(net.IP, 4)
+ binary.BigEndian.PutUint32(ip, uint32(num))
+ n.RouteReflector.State.RouteReflectorClusterId = RrClusterIdType(ip.String())
+ } else {
+ return fmt.Errorf("route-reflector-cluster-id should be specified as IPv4 address or 32-bit unsigned integer")
+ }
+ }
+ }
+
+ return nil
+}
+
+func SetDefaultGlobalConfigValues(g *Global) error {
+ if len(g.AfiSafis) == 0 {
+ g.AfiSafis = []AfiSafi{}
+ for k := range AfiSafiTypeToIntMap {
+ g.AfiSafis = append(g.AfiSafis, defaultAfiSafi(k, true))
+ }
+ }
+
+ if g.Config.Port == 0 {
+ g.Config.Port = bgp.BGP_PORT
+ }
+
+ if len(g.Config.LocalAddressList) == 0 {
+ g.Config.LocalAddressList = []string{"0.0.0.0", "::"}
+ }
+ return nil
+}
+
+func setDefaultVrfConfigValues(v *Vrf) error {
+ if v == nil {
+ return fmt.Errorf("cannot set default values for nil vrf config")
+ }
+
+ if v.Config.Name == "" {
+ return fmt.Errorf("specify vrf name")
+ }
+
+ _, err := bgp.ParseRouteDistinguisher(v.Config.Rd)
+ if err != nil {
+ return fmt.Errorf("invalid rd for vrf %s: %s", v.Config.Name, v.Config.Rd)
+ }
+
+ if len(v.Config.ImportRtList) == 0 {
+ v.Config.ImportRtList = v.Config.BothRtList
+ }
+ for _, rtString := range v.Config.ImportRtList {
+ _, err := bgp.ParseRouteTarget(rtString)
+ if err != nil {
+ return fmt.Errorf("invalid import rt for vrf %s: %s", v.Config.Name, rtString)
+ }
+ }
+
+ if len(v.Config.ExportRtList) == 0 {
+ v.Config.ExportRtList = v.Config.BothRtList
+ }
+ for _, rtString := range v.Config.ExportRtList {
+ _, err := bgp.ParseRouteTarget(rtString)
+ if err != nil {
+ return fmt.Errorf("invalid export rt for vrf %s: %s", v.Config.Name, rtString)
+ }
+ }
+
+ return nil
+}
+
+func SetDefaultConfigValues(b *BgpConfigSet) error {
+ return setDefaultConfigValuesWithViper(nil, b)
+}
+
+func setDefaultPolicyConfigValuesWithViper(v *viper.Viper, p *PolicyDefinition) error {
+ stmts, err := extractArray(v.Get("policy.statements"))
+ if err != nil {
+ return err
+ }
+ for i := range p.Statements {
+ vv := viper.New()
+ if len(stmts) > i {
+ vv.Set("statement", stmts[i])
+ }
+ if !vv.IsSet("statement.actions.route-disposition") {
+ p.Statements[i].Actions.RouteDisposition = ROUTE_DISPOSITION_NONE
+ }
+ }
+ return nil
+}
+
+func setDefaultConfigValuesWithViper(v *viper.Viper, b *BgpConfigSet) error {
+ if v == nil {
+ v = viper.New()
+ }
+
+ if err := SetDefaultGlobalConfigValues(&b.Global); err != nil {
+ return err
+ }
+
+ for idx, server := range b.BmpServers {
+ if server.Config.Port == 0 {
+ server.Config.Port = bmp.BMP_DEFAULT_PORT
+ }
+ if server.Config.RouteMonitoringPolicy == "" {
+ server.Config.RouteMonitoringPolicy = BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY
+ }
+ // statistics-timeout is uint16 value and implicitly less than 65536
+ if server.Config.StatisticsTimeout != 0 && server.Config.StatisticsTimeout < 15 {
+ return fmt.Errorf("too small statistics-timeout value: %d", server.Config.StatisticsTimeout)
+ }
+ b.BmpServers[idx] = server
+ }
+
+ vrfNames := make(map[string]struct{})
+ vrfIDs := make(map[uint32]struct{})
+ for idx, vrf := range b.Vrfs {
+ if err := setDefaultVrfConfigValues(&vrf); err != nil {
+ return err
+ }
+
+ if _, ok := vrfNames[vrf.Config.Name]; ok {
+ return fmt.Errorf("duplicated vrf name: %s", vrf.Config.Name)
+ }
+ vrfNames[vrf.Config.Name] = struct{}{}
+
+ if vrf.Config.Id != 0 {
+ if _, ok := vrfIDs[vrf.Config.Id]; ok {
+ return fmt.Errorf("duplicated vrf id: %d", vrf.Config.Id)
+ }
+ vrfIDs[vrf.Config.Id] = struct{}{}
+ }
+
+ b.Vrfs[idx] = vrf
+ }
+ // Auto assign VRF identifier
+ for idx, vrf := range b.Vrfs {
+ if vrf.Config.Id == 0 {
+ for id := uint32(1); id < math.MaxUint32; id++ {
+ if _, ok := vrfIDs[id]; !ok {
+ vrf.Config.Id = id
+ vrfIDs[id] = struct{}{}
+ break
+ }
+ }
+ }
+ b.Vrfs[idx] = vrf
+ }
+
+ if b.Zebra.Config.Url == "" {
+ b.Zebra.Config.Url = "unix:/var/run/quagga/zserv.api"
+ }
+ if b.Zebra.Config.Version < 2 {
+ b.Zebra.Config.Version = 2
+ } else if b.Zebra.Config.Version > 4 {
+ b.Zebra.Config.Version = 4
+ }
+ if !v.IsSet("zebra.config.nexthop-trigger-enable") && !b.Zebra.Config.NexthopTriggerEnable && b.Zebra.Config.Version > 2 {
+ b.Zebra.Config.NexthopTriggerEnable = true
+ }
+ if b.Zebra.Config.NexthopTriggerDelay == 0 {
+ b.Zebra.Config.NexthopTriggerDelay = 5
+ }
+
+ list, err := extractArray(v.Get("neighbors"))
+ if err != nil {
+ return err
+ }
+
+ for idx, n := range b.Neighbors {
+ vv := viper.New()
+ if len(list) > idx {
+ vv.Set("neighbor", list[idx])
+ }
+
+ pg, err := b.getPeerGroup(n.Config.PeerGroup)
+ if err != nil {
+ return nil
+ }
+
+ if pg != nil {
+ identifier := vv.Get("neighbor.config.neighbor-address")
+ if identifier == nil {
+ identifier = vv.Get("neighbor.config.neighbor-interface")
+ }
+ RegisterConfiguredFields(identifier.(string), list[idx])
+ }
+
+ if err := setDefaultNeighborConfigValuesWithViper(vv, &n, &b.Global, pg); err != nil {
+ return err
+ }
+ b.Neighbors[idx] = n
+ }
+
+ for _, d := range b.DynamicNeighbors {
+ if err := d.validate(b); err != nil {
+ return err
+ }
+ }
+
+ for idx, r := range b.RpkiServers {
+ if r.Config.Port == 0 {
+ b.RpkiServers[idx].Config.Port = rtr.RPKI_DEFAULT_PORT
+ }
+ }
+
+ list, err = extractArray(v.Get("policy-definitions"))
+ if err != nil {
+ return err
+ }
+
+ for idx, p := range b.PolicyDefinitions {
+ vv := viper.New()
+ if len(list) > idx {
+ vv.Set("policy", list[idx])
+ }
+ if err := setDefaultPolicyConfigValuesWithViper(vv, &p); err != nil {
+ return err
+ }
+ b.PolicyDefinitions[idx] = p
+ }
+
+ return nil
+}
+
+func OverwriteNeighborConfigWithPeerGroup(c *Neighbor, pg *PeerGroup) error {
+ v := viper.New()
+
+ val, ok := configuredFields[c.Config.NeighborAddress]
+ if ok {
+ v.Set("neighbor", val)
+ } else {
+ v.Set("neighbor.config.peer-group", c.Config.PeerGroup)
+ }
+
+ overwriteConfig(&c.Config, &pg.Config, "neighbor.config", v)
+ overwriteConfig(&c.Timers.Config, &pg.Timers.Config, "neighbor.timers.config", v)
+ overwriteConfig(&c.Transport.Config, &pg.Transport.Config, "neighbor.transport.config", v)
+ overwriteConfig(&c.ErrorHandling.Config, &pg.ErrorHandling.Config, "neighbor.error-handling.config", v)
+ overwriteConfig(&c.LoggingOptions.Config, &pg.LoggingOptions.Config, "neighbor.logging-options.config", v)
+ overwriteConfig(&c.EbgpMultihop.Config, &pg.EbgpMultihop.Config, "neighbor.ebgp-multihop.config", v)
+ overwriteConfig(&c.RouteReflector.Config, &pg.RouteReflector.Config, "neighbor.route-reflector.config", v)
+ overwriteConfig(&c.AsPathOptions.Config, &pg.AsPathOptions.Config, "neighbor.as-path-options.config", v)
+ overwriteConfig(&c.AddPaths.Config, &pg.AddPaths.Config, "neighbor.add-paths.config", v)
+ overwriteConfig(&c.GracefulRestart.Config, &pg.GracefulRestart.Config, "neighbor.gradeful-restart.config", v)
+ overwriteConfig(&c.ApplyPolicy.Config, &pg.ApplyPolicy.Config, "neighbor.apply-policy.config", v)
+ overwriteConfig(&c.UseMultiplePaths.Config, &pg.UseMultiplePaths.Config, "neighbor.use-multiple-paths.config", v)
+ overwriteConfig(&c.RouteServer.Config, &pg.RouteServer.Config, "neighbor.route-server.config", v)
+ overwriteConfig(&c.TtlSecurity.Config, &pg.TtlSecurity.Config, "neighbor.ttl-security.config", v)
+
+ if !v.IsSet("neighbor.afi-safis") {
+ c.AfiSafis = append(c.AfiSafis, pg.AfiSafis...)
+ }
+
+ return nil
+}
+
+func overwriteConfig(c, pg interface{}, tagPrefix string, v *viper.Viper) {
+ nValue := reflect.Indirect(reflect.ValueOf(c))
+ nType := reflect.Indirect(nValue).Type()
+ pgValue := reflect.Indirect(reflect.ValueOf(pg))
+ pgType := reflect.Indirect(pgValue).Type()
+
+ for i := 0; i < pgType.NumField(); i++ {
+ field := pgType.Field(i).Name
+ tag := tagPrefix + "." + nType.Field(i).Tag.Get("mapstructure")
+ if func() bool {
+ for _, t := range forcedOverwrittenConfig {
+ if t == tag {
+ return true
+ }
+ }
+ return false
+ }() || !v.IsSet(tag) {
+ nValue.FieldByName(field).Set(pgValue.FieldByName(field))
+ }
+ }
+}