Skip to content

Commit

Permalink
Change maximum number of buckets in an OVS group add/insert_bucket me…
Browse files Browse the repository at this point in the history
…ssage

The current implementation limits the maximum number of buckets in an OVS group
add/insert_bucket message to 800. This constraint is based on the fact that each
bucket has 3 actions, such as `set_field:0xa0a0007->reg0`,
`set_field:0x50/0xffff->reg4`, and `resubmit(,EndpointDNAT)`. However, an update
in antrea-io#5205 introduced a new action, `set_field:0x4000000/0x4000000->reg4`, for
remote Endpoints, making it impossible to accommodate 800 buckets with 4 actions
in an OVS group add/insert_bucket message.

To overcome the limitation, we set the maximum number of buckets to 700, considering
the worst-case scenario where each bucket includes all available actions. For example,
a bucket with all available actions, which is for a remote non-hostNetwork IPv6 Service
Endpoint like this: `set_field:0xa0a0007->xxreg0`, `set_field:0x50/0xffff->reg4`,
`set_field:0x100000/0x100000->reg4`, and `resubmit(,EndpointDNAT)`. The size of such
bucket is 88 bytes, and the header size of an OVS group message is 24 bytes.
According to https://opennetworking.org/wp-content/uploads/2014/10/openflow-switch-v1.5.1.pdf,
the max size of an Openflow 1.5 message is 64000 bytes, as a result, a message can have
a maximum of 727 buckets with the largest size.

Signed-off-by: Hongliang Liu <[email protected]>
  • Loading branch information
hongliangl committed Feb 1, 2024
1 parent a2266ee commit 720a91c
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 1 deletion.
70 changes: 70 additions & 0 deletions pkg/agent/openflow/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"sync/atomic"
"testing"
"time"
"unsafe"

"antrea.io/libOpenflow/openflow15"
"antrea.io/libOpenflow/protocol"
Expand All @@ -47,6 +48,8 @@ import (

const bridgeName = "dummy-br"

const openflowMessageMaxSize = 64000

var (
bridgeMgmtAddr = binding.GetMgmtAddress(ovsconfig.DefaultOVSRunDir, bridgeName)

Expand Down Expand Up @@ -1123,6 +1126,73 @@ func Test_client_InstallServiceGroup(t *testing.T) {
}
}

func getServiceBucketMaxSize(t *testing.T, ctrl *gomock.Controller) uint16 {
fakeOfTable := ovsoftest.NewMockTable(ctrl)
fakeNodeIPChecker := nodeiptest.NewFakeNodeIPChecker("2001::2")
ServiceLBTable.ofTable = fakeOfTable
defer func() {
ServiceLBTable.ofTable = nil
}()
fs := &featureService{
groupCache: sync.Map{},
bridge: binding.NewOFBridge(bridgeName, ""),
nodeIPChecker: fakeNodeIPChecker,
}
// To create a single-bucket group with maximum size, use a remote non-hostNetwork IPv6 Endpoint that has all
// available actions.
endpoint := proxy.NewBaseEndpointInfo("2001::1", "node1", "", 80, false, true, false, false, nil)
fakeOfTable.EXPECT().GetID().Return(uint8(1)).Times(1)
group := fs.serviceEndpointGroup(binding.GroupIDType(100), true, endpoint)

messages, err := group.GetBundleMessages(binding.AddMessage)
require.NoError(t, err)
require.Equal(t, 1, len(messages))
groupMod := messages[0].GetMessage().(*openflow15.GroupMod)
require.Equal(t, 1, len(groupMod.Buckets))
// Since this is a single-bucket group, the `BucketArrayLen` is the size of the bucket.
t.Logf("The maxinum size of a Service group bucket is %d bytes", groupMod.BucketArrayLen)
return groupMod.BucketArrayLen
}

func getMulticastBucketMaxSize(t *testing.T, ctrl *gomock.Controller) uint16 {
fs := &featureMulticast{
groupCache: sync.Map{},
bridge: binding.NewOFBridge(bridgeName, ""),
}
fakeOfTable := ovsoftest.NewMockTable(ctrl)
MulticastOutputTable.ofTable = fakeOfTable
defer func() {
MulticastOutputTable.ofTable = nil
}()

// Create a single-bucket group with maximum size.
fakeOfTable.EXPECT().GetID().Return(uint8(1)).Times(1)
group := fs.multicastReceiversGroup(binding.GroupIDType(100), 0, nil, []net.IP{net.ParseIP("192.168.77.100")})

messages, err := group.GetBundleMessages(binding.AddMessage)
require.NoError(t, err)
require.Equal(t, 1, len(messages))
groupMod := messages[0].GetMessage().(*openflow15.GroupMod)
require.Equal(t, 1, len(groupMod.Buckets))
// Since this is a single-bucket group, the `BucketArrayLen` is the size of the bucket.
t.Logf("The maxinum size of a Multicast group bucket is %d bytes", groupMod.BucketArrayLen)
return groupMod.BucketArrayLen
}

func TestMaxBucketsPerMessage(t *testing.T) {
ctrl := gomock.NewController(t)

groupModHeaderSize := uint16(unsafe.Sizeof(openflow15.GroupMod{}) - unsafe.Sizeof(openflow15.GroupMod{}.Buckets) - unsafe.Sizeof(openflow15.GroupMod{}.Properties))
serviceMaxBucketsPerMessage := int((openflowMessageMaxSize - groupModHeaderSize) / getServiceBucketMaxSize(t, ctrl))
t.Logf("Maximum buckets in a Service group: %d\n", serviceMaxBucketsPerMessage)
multicastMaxBucketsPerMessage := int((openflowMessageMaxSize - groupModHeaderSize) / getMulticastBucketMaxSize(t, ctrl))
t.Logf("Maximum buckets in a Multicast group: %d\n", multicastMaxBucketsPerMessage)

// Ensure that binding.MaxBucketsPerMessage is less than or equal to every feature's maximum supported buckets.
require.LessOrEqual(t, binding.MaxBucketsPerMessage, serviceMaxBucketsPerMessage)
require.LessOrEqual(t, binding.MaxBucketsPerMessage, multicastMaxBucketsPerMessage)
}

func Test_client_InstallEndpointFlows(t *testing.T) {
ep1IPv4 := "10.10.0.100"
ep2IPv4 := "10.10.0.101"
Expand Down
2 changes: 1 addition & 1 deletion pkg/ovs/openflow/ofctrl_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
)

var (
MaxBucketsPerMessage = 800
MaxBucketsPerMessage = 700
)

type ofGroup struct {
Expand Down

0 comments on commit 720a91c

Please sign in to comment.