Skip to content
Merged

Pr3559 #3583

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
888 changes: 572 additions & 316 deletions api/gobgp.pb.go

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions pkg/config/oc/bgp_configs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/config/oc/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ func OverwriteNeighborConfigWithPeerGroup(c *Neighbor, pg *PeerGroup) error {
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.TcpAo.Config, &pg.TcpAo.Config, "neighbor.tcp-ao.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)
Expand Down
115 changes: 115 additions & 0 deletions pkg/config/oc/default_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (C) 2026 The GoBGP Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package oc

import (
"net/netip"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const testNeighborAddress = "192.0.2.1"

// registerConfiguredFields records the fields that the config file set for one
// neighbor. configuredFields is package state, so it is restored afterwards.
func registerConfiguredFields(t *testing.T, addr string, fields map[string]any) {
t.Helper()
saved := configuredFields
t.Cleanup(func() { configuredFields = saved })
configuredFields = map[string]any{addr: fields}
}

func newNeighborForTcpAoInheritanceTest() *Neighbor {
return &Neighbor{
Config: NeighborConfig{
NeighborAddress: netip.MustParseAddr(testNeighborAddress),
PeerGroup: "g",
},
}
}

func newPeerGroupForTcpAoInheritanceTest() *PeerGroup {
return &PeerGroup{
Config: PeerGroupConfig{
PeerGroupName: "g",
},
TcpAo: TcpAo{
Config: TcpAoConfig{Keychain: "group-chain", SendId: 1},
},
}
}

func TestOverwriteNeighborConfigWithPeerGroupTcpAo(t *testing.T) {
// A neighbor that says nothing about TCP-AO takes the whole
// configuration from its peer group.
t.Run("inherited_when_not_configured", func(t *testing.T) {
registerConfiguredFields(t, testNeighborAddress, map[string]any{
"config": map[string]any{
"neighbor-address": testNeighborAddress,
"peer-group": "g",
},
})

n := newNeighborForTcpAoInheritanceTest()
require.NoError(t, OverwriteNeighborConfigWithPeerGroup(n, newPeerGroupForTcpAoInheritanceTest()))
assert.Equal(t, KeychainRef("group-chain"), n.TcpAo.Config.Keychain)
assert.Equal(t, uint8(1), n.TcpAo.Config.SendId)
})

t.Run("kept_when_configured", func(t *testing.T) {
registerConfiguredFields(t, testNeighborAddress, map[string]any{
"config": map[string]any{
"neighbor-address": testNeighborAddress,
"peer-group": "g",
},
"tcp-ao": map[string]any{
"config": map[string]any{
"keychain": "peer-chain",
"send-id": 2,
},
},
})

n := newNeighborForTcpAoInheritanceTest()
n.TcpAo.Config = TcpAoConfig{Keychain: "peer-chain", SendId: 2}
require.NoError(t, OverwriteNeighborConfigWithPeerGroup(n, newPeerGroupForTcpAoInheritanceTest()))
assert.Equal(t, KeychainRef("peer-chain"), n.TcpAo.Config.Keychain)
assert.Equal(t, uint8(2), n.TcpAo.Config.SendId)
})

// The peer group is consulted per leaf, so a neighbor that names only a
// keychain still takes send-id from the group.
t.Run("send_id_inherited_when_only_keychain_is_configured", func(t *testing.T) {
registerConfiguredFields(t, testNeighborAddress, map[string]any{
"config": map[string]any{
"neighbor-address": testNeighborAddress,
"peer-group": "g",
},
"tcp-ao": map[string]any{
"config": map[string]any{
"keychain": "peer-chain",
},
},
})

n := newNeighborForTcpAoInheritanceTest()
n.TcpAo.Config = TcpAoConfig{Keychain: "peer-chain"}
require.NoError(t, OverwriteNeighborConfigWithPeerGroup(n, newPeerGroupForTcpAoInheritanceTest()))
assert.Equal(t, KeychainRef("peer-chain"), n.TcpAo.Config.Keychain)
assert.Equal(t, uint8(1), n.TcpAo.Config.SendId)
})
}
12 changes: 12 additions & 0 deletions pkg/config/oc/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,7 @@ func NewPeerFromConfigStruct(pconf *Neighbor) *api.Peer {
TcpMss: uint32(pconf.Transport.Config.TcpMss),
IpTos: uint32(pconf.Transport.Config.IpTos),
},
TcpAo: newTcpAoPeerConfigFromConfigStruct(&pconf.TcpAo.Config),
AfiSafis: afiSafis,
Bfd: &api.BfdPeerConfig{
Enabled: pconf.Bfd.Config.Enabled,
Expand Down Expand Up @@ -762,6 +763,16 @@ func readTcpAoMasterKey(secretKey string) ([]byte, error) {
return masterKey, nil
}

func newTcpAoPeerConfigFromConfigStruct(config *TcpAoConfig) *api.TcpAoPeerConfig {
if config.Keychain == "" {
return nil
}
return &api.TcpAoPeerConfig{
Keychain: string(config.Keychain),
SendId: uint32(config.SendId),
}
}

func NewPeerGroupFromConfigStruct(pconf *PeerGroup) *api.PeerGroup {
afiSafis := make([]*api.AfiSafi, 0, len(pconf.AfiSafis))
for _, f := range pconf.AfiSafis {
Expand Down Expand Up @@ -842,6 +853,7 @@ func NewPeerGroupFromConfigStruct(pconf *PeerGroup) *api.PeerGroup {
TcpMss: uint32(pconf.Transport.Config.TcpMss),
IpTos: uint32(pconf.Transport.Config.IpTos),
},
TcpAo: newTcpAoPeerConfigFromConfigStruct(&pconf.TcpAo.Config),
AfiSafis: afiSafis,
Bfd: &api.BfdPeerConfig{
Enabled: pconf.Bfd.Config.Enabled,
Expand Down
81 changes: 81 additions & 0 deletions pkg/config/oc/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,87 @@ func TestNewPeerGroupFromConfigStruct_BfdConfig(t *testing.T) {
})
}

func newPeerFromConfigForTCPAOTest(t *testing.T, tcpAo TcpAo) *api.Peer {
t.Helper()
n := &Neighbor{
Config: NeighborConfig{
NeighborAddress: netip.MustParseAddr("192.0.2.1"),
PeerAs: 65001,
},
TcpAo: tcpAo,
}
p := NewPeerFromConfigStruct(n)
require.NotNil(t, p)
return p
}

func newPeerGroupFromConfigForTCPAOTest(t *testing.T, tcpAo TcpAo) *api.PeerGroup {
t.Helper()
pg := &PeerGroup{
Config: PeerGroupConfig{
PeerGroupName: "pg-tcp-ao-test",
PeerAs: 65001,
},
TcpAo: tcpAo,
}
return NewPeerGroupFromConfigStruct(pg)
}

func TestNewPeerFromConfigStruct_TcpAoConfig(t *testing.T) {
t.Run("keychain_and_send_id", func(t *testing.T) {
p := newPeerFromConfigForTCPAOTest(t, TcpAo{
Config: TcpAoConfig{Keychain: "fabric", SendId: 3},
})
cfg := p.GetTcpAo()
require.NotNil(t, cfg)
assert.Equal(t, "fabric", cfg.Keychain)
assert.Equal(t, uint32(3), cfg.SendId)
})

// RFC 5925 section 3.1 allows any MKT ID from 0 to 255, so send-id 0
// selects the key with SendID 0. It does not mean "not set".
t.Run("send_id_zero_is_kept", func(t *testing.T) {
p := newPeerFromConfigForTCPAOTest(t, TcpAo{
Config: TcpAoConfig{Keychain: "fabric", SendId: 0},
})
cfg := p.GetTcpAo()
require.NotNil(t, cfg)
assert.Equal(t, "fabric", cfg.Keychain)
assert.Equal(t, uint32(0), cfg.SendId)
})

// An empty keychain is how the config model says that the peer does not
// use TCP-AO, so no API message is built even if send-id is set.
t.Run("no_keychain_disables_tcp_ao", func(t *testing.T) {
p := newPeerFromConfigForTCPAOTest(t, TcpAo{})
assert.Nil(t, p.GetTcpAo())
})

t.Run("send_id_without_keychain_is_dropped", func(t *testing.T) {
p := newPeerFromConfigForTCPAOTest(t, TcpAo{
Config: TcpAoConfig{SendId: 3},
})
assert.Nil(t, p.GetTcpAo())
})
}

func TestNewPeerGroupFromConfigStruct_TcpAoConfig(t *testing.T) {
t.Run("keychain_and_send_id", func(t *testing.T) {
pg := newPeerGroupFromConfigForTCPAOTest(t, TcpAo{
Config: TcpAoConfig{Keychain: "fabric", SendId: 7},
})
cfg := pg.GetTcpAo()
require.NotNil(t, cfg)
assert.Equal(t, "fabric", cfg.Keychain)
assert.Equal(t, uint32(7), cfg.SendId)
})

t.Run("no_keychain_disables_tcp_ao", func(t *testing.T) {
pg := newPeerGroupFromConfigForTCPAOTest(t, TcpAo{})
assert.Nil(t, pg.GetTcpAo())
})
}

func TestParseMaskLength(t *testing.T) {
assert := assert.New(t)
cases := []struct {
Expand Down
27 changes: 27 additions & 0 deletions pkg/server/grpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,19 @@ func newBfdConfigFromAPIStruct(a *api.BfdPeerConfig) (oc.BfdConfig, error) {
}, nil
}

func tcpAoConfigFromAPI(a *api.TcpAoPeerConfig) (oc.TcpAoConfig, error) {
if a == nil {
return oc.TcpAoConfig{}, nil
}
if a.SendId > 255 {
return oc.TcpAoConfig{}, status.Errorf(codes.InvalidArgument, "TCP-AO send ID %d is outside 0..255", a.SendId)
}
return oc.TcpAoConfig{
Keychain: oc.KeychainRef(a.Keychain),
SendId: uint8(a.SendId),
}, nil
}

func newNeighborFromAPIStruct(a *api.Peer) (*oc.Neighbor, error) {
pconf := &oc.Neighbor{}
if a.Conf != nil {
Expand Down Expand Up @@ -1099,6 +1112,13 @@ func newNeighborFromAPIStruct(a *api.Peer) (*oc.Neighbor, error) {
pconf.Transport.Config.TcpMss = uint16(a.Transport.TcpMss)
pconf.Transport.Config.IpTos = uint8(a.Transport.IpTos)
}
if a.TcpAo != nil {
tcpAo, err := tcpAoConfigFromAPI(a.TcpAo)
if err != nil {
return nil, err
}
pconf.TcpAo.Config = tcpAo
}
if a.EbgpMultihop != nil {
pconf.EbgpMultihop.Config.Enabled = a.EbgpMultihop.Enabled
pconf.EbgpMultihop.Config.MultihopTtl = uint8(a.EbgpMultihop.MultihopTtl)
Expand Down Expand Up @@ -1254,6 +1274,13 @@ func newPeerGroupFromAPIStruct(a *api.PeerGroup) (*oc.PeerGroup, error) {
pconf.Transport.Config.TcpMss = uint16(a.Transport.TcpMss)
pconf.Transport.Config.IpTos = uint8(a.Transport.IpTos)
}
if a.TcpAo != nil {
tcpAo, err := tcpAoConfigFromAPI(a.TcpAo)
if err != nil {
return nil, err
}
pconf.TcpAo.Config = tcpAo
}
if a.EbgpMultihop != nil {
pconf.EbgpMultihop.Config.Enabled = a.EbgpMultihop.Enabled
pconf.EbgpMultihop.Config.MultihopTtl = uint8(a.EbgpMultihop.MultihopTtl)
Expand Down
Loading