Skip to content

Commit d36afd0

Browse files
authored
Support dynamic ofport on OVS gateway/tunnel port (#3976)
Originally the OpenFlow entries are using static port numbers for gateway/tunnel/uplink which are used in the ofport_request fields in OVS port creation. This is possible to introduce traffic issues if the actual numbers assigned by OVS are different from the requests. With this change, the predefined port numbers are still used in the OVS port creation request, and save the actual port numbers in NodeConfig after the OVS ports are created. And for the OpenFlow entries, the actual port numbers are used. Signed-off-by: wenyingd <wenyingd@vmware.com>
1 parent 623b9c0 commit d36afd0

20 files changed

Lines changed: 236 additions & 111 deletions

cmd/antrea-agent/agent.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,9 @@ func run(o *Options) error {
348348
asyncRuleDeleteInterval,
349349
o.dnsServerOverride,
350350
v4Enabled,
351-
v6Enabled)
351+
v6Enabled,
352+
nodeConfig.GatewayConfig.OFPort,
353+
nodeConfig.TunnelOFPort)
352354
if err != nil {
353355
return fmt.Errorf("error creating new NetworkPolicy controller: %v", err)
354356
}

pkg/agent/agent.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -608,11 +608,17 @@ func (i *Initializer) setupGatewayInterface() error {
608608
}
609609
gwPortUUID, err := i.ovsBridgeClient.CreateInternalPort(i.hostGateway, config.HostGatewayOFPort, externalIDs)
610610
if err != nil {
611-
klog.Errorf("Failed to create gateway port %s on OVS bridge: %v", i.hostGateway, err)
611+
klog.ErrorS(err, "Failed to create gateway port on OVS bridge", "port", i.hostGateway)
612612
return err
613613
}
614+
gwPort, err := i.ovsBridgeClient.GetOFPort(i.hostGateway, false)
615+
if err != nil {
616+
klog.ErrorS(err, "Failed to get gateway ofport", "port", i.hostGateway)
617+
return err
618+
}
619+
klog.InfoS("Allocated OpenFlow port for gateway interface", "port", i.hostGateway, "ofPort", gwPort)
614620
gatewayIface = interfacestore.NewGatewayInterface(i.hostGateway)
615-
gatewayIface.OVSPortConfig = &interfacestore.OVSPortConfig{PortUUID: gwPortUUID, OFPort: config.HostGatewayOFPort}
621+
gatewayIface.OVSPortConfig = &interfacestore.OVSPortConfig{PortUUID: gwPortUUID, OFPort: gwPort}
616622
i.ifaceStore.AddInterface(gatewayIface)
617623
} else {
618624
klog.V(2).Infof("Gateway port %s already exists on OVS bridge", i.hostGateway)
@@ -657,7 +663,7 @@ func (i *Initializer) configureGatewayInterface(gatewayIface *interfacestore.Int
657663
return err
658664
}
659665

660-
i.nodeConfig.GatewayConfig = &config.GatewayConfig{Name: i.hostGateway, MAC: gwMAC}
666+
i.nodeConfig.GatewayConfig = &config.GatewayConfig{Name: i.hostGateway, MAC: gwMAC, OFPort: uint32(gatewayIface.OFPort)}
661667
gatewayIface.MAC = gwMAC
662668
gatewayIface.IPs = []net.IP{}
663669
if i.networkConfig.TrafficEncapMode.IsNetworkPolicyOnly() {
@@ -728,6 +734,7 @@ func (i *Initializer) setupDefaultTunnelInterface() error {
728734
}
729735
tunnelIface.TunnelInterfaceConfig.Csum = true
730736
}
737+
i.nodeConfig.TunnelOFPort = uint32(tunnelIface.OFPort)
731738
return nil
732739
}
733740

@@ -755,12 +762,19 @@ func (i *Initializer) setupDefaultTunnelInterface() error {
755762
}
756763
tunnelPortUUID, err := i.ovsBridgeClient.CreateTunnelPortExt(tunnelPortName, i.networkConfig.TunnelType, config.DefaultTunOFPort, shouldEnableCsum, localIPStr, "", "", "", nil, externalIDs)
757764
if err != nil {
758-
klog.Errorf("Failed to create tunnel port %s type %s on OVS bridge: %v", tunnelPortName, i.networkConfig.TunnelType, err)
765+
klog.ErrorS(err, "Failed to create tunnel port on OVS bridge", "port", tunnelPortName, "type", i.networkConfig.TunnelType)
759766
return err
760767
}
768+
tunPort, err := i.ovsBridgeClient.GetOFPort(tunnelPortName, false)
769+
if err != nil {
770+
klog.ErrorS(err, "Failed to get tunnel ofport on OVS bridge", "port", tunnelPortName, "type", i.networkConfig.TunnelType)
771+
return err
772+
}
773+
klog.InfoS("Allocated OpenFlow port for tunnel interface", "port", tunnelPortName, "ofPort", tunPort)
761774
tunnelIface = interfacestore.NewTunnelInterface(tunnelPortName, i.networkConfig.TunnelType, localIP, shouldEnableCsum)
762-
tunnelIface.OVSPortConfig = &interfacestore.OVSPortConfig{PortUUID: tunnelPortUUID, OFPort: config.DefaultTunOFPort}
775+
tunnelIface.OVSPortConfig = &interfacestore.OVSPortConfig{PortUUID: tunnelPortUUID, OFPort: tunPort}
763776
i.ifaceStore.AddInterface(tunnelIface)
777+
i.nodeConfig.TunnelOFPort = uint32(tunPort)
764778
}
765779
return nil
766780
}
@@ -1126,3 +1140,24 @@ func (i *Initializer) patchNodeAnnotations(nodeName, key string, value interface
11261140
func (i *Initializer) getNodeInterfaceFromIP(nodeIPs *utilip.DualStackIPs) (v4IPNet *net.IPNet, v6IPNet *net.IPNet, iface *net.Interface, err error) {
11271141
return getIPNetDeviceFromIP(nodeIPs, sets.NewString(i.hostGateway))
11281142
}
1143+
1144+
// getFreeOFPort returns an OpenFlow port number which is not used by any existing OVS port. Note that, the returned port
1145+
// is not saved in OVSDB yet before the real port is created, so it might introduce an issue for the same return value
1146+
// if it is called multiple times before OVS port creation.
1147+
func (i *Initializer) getFreeOFPort(startPort int) (int32, error) {
1148+
existingOFPorts := sets.NewInt32()
1149+
ports, err := i.ovsBridgeClient.GetPortList()
1150+
if err != nil {
1151+
return 0, err
1152+
}
1153+
for _, p := range ports {
1154+
existingOFPorts.Insert(p.OFPort)
1155+
}
1156+
port := int32(startPort)
1157+
for ; ; port++ {
1158+
if !existingOFPorts.Has(port) {
1159+
break
1160+
}
1161+
}
1162+
return port, nil
1163+
}

pkg/agent/agent_linux.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ func (i *Initializer) prepareOVSBridge() error {
9292
klog.V(2).Infof("Found ports from OVS bridge: %+v", ports)
9393
var uplinkPort *ovsconfig.OVSPortData
9494
for index := range ports {
95-
if ports[index].OFPort == config.UplinkOFPort {
95+
antreaIfaceType, _ := ports[index].ExternalIDs[interfacestore.AntreaInterfaceTypeKey]
96+
if antreaIfaceType == interfacestore.AntreaUplink {
9697
uplinkPort = &ports[index]
9798
break
9899
}
@@ -105,10 +106,11 @@ func (i *Initializer) prepareOVSBridge() error {
105106
return fmt.Errorf("cannot find uplink port %s: err=%w", uplinkPort.Name, err2)
106107
}
107108
klog.Infof("Found uplink device %s", adapter.Name)
109+
uplinkNetConfig.OFPort = uint32(uplinkPort.OFPort)
108110
uplinkNetConfig.Name = adapter.Name
109111
uplinkNetConfig.Index = adapter.Index
110112
uplinkInterface := interfacestore.NewUplinkInterface(uplinkPort.Name)
111-
uplinkInterface.OVSPortConfig = &interfacestore.OVSPortConfig{uplinkPort.UUID, config.UplinkOFPort} //nolint: govet
113+
uplinkInterface.OVSPortConfig = &interfacestore.OVSPortConfig{uplinkPort.UUID, uplinkPort.OFPort} //nolint: govet
112114
i.ifaceStore.AddInterface(uplinkInterface)
113115
}
114116
} else {
@@ -122,6 +124,17 @@ func (i *Initializer) prepareOVSBridge() error {
122124
}
123125
}
124126

127+
i.nodeConfig.HostInterfaceOFPort = config.BridgeOFPort
128+
if uplinkNetConfig.OFPort == 0 {
129+
freePort, err := i.getFreeOFPort(config.UplinkOFPort)
130+
if err != nil {
131+
klog.ErrorS(err, "Failed to find a free port on OVS")
132+
return err
133+
}
134+
uplinkNetConfig.OFPort = uint32(freePort)
135+
klog.InfoS("Set OpenFlow port in UplinkNetConfig", "ofport", freePort)
136+
}
137+
125138
return nil
126139
}
127140

@@ -197,18 +210,23 @@ func (i *Initializer) ConnectUplinkToOVSBridge() error {
197210

198211
// If uplink is already exists, return.
199212
uplink := uplinkNetConfig.Name
200-
if _, err := i.ovsBridgeClient.GetOFPort(uplink, false); err == nil {
201-
klog.Infof("Uplink %s already exists, skip the configuration", uplink)
213+
if uplinkOFPort, err := i.ovsBridgeClient.GetOFPort(uplink, false); err == nil {
214+
klog.InfoS("Uplink already exists, skip the configuration", "uplink", uplink, "port", uplinkOFPort)
202215
return nil
203216
}
204217
// Create uplink port.
205-
uplinkPortUUID, err := i.ovsBridgeClient.CreateUplinkPort(uplink, config.UplinkOFPort, map[string]interface{}{interfacestore.AntreaInterfaceTypeKey: interfacestore.AntreaUplink})
218+
uplinkPortUUID, err := i.ovsBridgeClient.CreateUplinkPort(uplink, int32(i.nodeConfig.UplinkNetConfig.OFPort), map[string]interface{}{interfacestore.AntreaInterfaceTypeKey: interfacestore.AntreaUplink})
206219
if err != nil {
207220
return fmt.Errorf("failed to add uplink port %s: err=%w", uplink, err)
208221
}
209222
// Add newly created uplinkInterface to interface cache. This will be overwritten by initInterfaceStore.
210223
uplinkInterface := interfacestore.NewUplinkInterface(uplink)
211-
uplinkInterface.OVSPortConfig = &interfacestore.OVSPortConfig{uplinkPortUUID, config.UplinkOFPort} //nolint: govet
224+
uplinkOFPort, err := i.ovsBridgeClient.GetOFPort(uplink, false)
225+
if err != nil {
226+
return fmt.Errorf("failed to get uplink ofport %s: err=%w", uplink, err)
227+
}
228+
klog.InfoS("Allocated OpenFlow port for uplink interface", "port", uplink, "ofPort", uplinkOFPort)
229+
uplinkInterface.OVSPortConfig = &interfacestore.OVSPortConfig{uplinkPortUUID, uplinkOFPort} //nolint: govet
212230
i.ifaceStore.AddInterface(uplinkInterface)
213231

214232
// Move network configuration of uplink interface to OVS bridge local interface.

pkg/agent/agent_windows.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,33 @@ func (i *Initializer) prepareOVSBridge() error {
158158
// If uplink is already exists, return.
159159
uplinkNetConfig := i.nodeConfig.UplinkNetConfig
160160
uplink := uplinkNetConfig.Name
161-
if _, err = i.ovsBridgeClient.GetOFPort(uplink, false); err == nil {
162-
klog.Infof("Uplink %s already exists, skip the configuration", uplink)
163-
return err
161+
if ofport, err := i.ovsBridgeClient.GetOFPort(uplink, false); err == nil {
162+
klog.InfoS("Uplink already exists, skip the configuration", "uplink", uplink, "port", ofport)
163+
i.nodeConfig.UplinkNetConfig.OFPort = uint32(ofport)
164+
i.nodeConfig.HostInterfaceOFPort = config.BridgeOFPort
165+
return nil
164166
}
165167
// Create uplink port.
168+
freePort, err := i.getFreeOFPort(config.UplinkOFPort)
169+
if err != nil {
170+
klog.ErrorS(err, "Failed to find a free port on OVS")
171+
return err
172+
}
166173
var uplinkPortUUID string
167-
uplinkPortUUID, err = i.ovsBridgeClient.CreateUplinkPort(uplink, config.UplinkOFPort, nil)
174+
uplinkPortUUID, err = i.ovsBridgeClient.CreateUplinkPort(uplink, freePort, nil)
168175
if err != nil {
169176
klog.Errorf("Failed to add uplink port %s: %v", uplink, err)
170177
return err
171178
}
179+
uplinkOFPort, err := i.ovsBridgeClient.GetOFPort(uplink, false)
180+
if err != nil {
181+
return fmt.Errorf("failed to get uplink ofport %s: err=%w", uplink, err)
182+
}
183+
klog.InfoS("Allocated OpenFlow port for uplink interface", "port", uplink, "ofPort", uplinkOFPort)
184+
i.nodeConfig.UplinkNetConfig.OFPort = uint32(uplinkOFPort)
185+
i.nodeConfig.HostInterfaceOFPort = config.BridgeOFPort
172186
uplinkInterface := interfacestore.NewUplinkInterface(uplink)
173-
uplinkInterface.OVSPortConfig = &interfacestore.OVSPortConfig{uplinkPortUUID, config.UplinkOFPort} //nolint: govet
187+
uplinkInterface.OVSPortConfig = &interfacestore.OVSPortConfig{uplinkPortUUID, uplinkOFPort} //nolint: govet
174188
i.ifaceStore.AddInterface(uplinkInterface)
175189
ovsCtlClient := ovsctl.NewClient(i.ovsBridge)
176190

@@ -184,7 +198,7 @@ func (i *Initializer) prepareOVSBridge() error {
184198
}
185199
// Set the uplink with "no-flood" config, so that the IP of local Pods and "antrea-gw0" will not be leaked to the
186200
// underlay network by the "normal" flow entry.
187-
if err = ovsCtlClient.SetPortNoFlood(config.UplinkOFPort); err != nil {
201+
if err = ovsCtlClient.SetPortNoFlood(int(uplinkOFPort)); err != nil {
188202
klog.Errorf("Failed to set the uplink port with no-flood config: %v", err)
189203
return err
190204
}

pkg/agent/config/node_config.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ type GatewayConfig struct {
7373
MAC net.HardwareAddr
7474
// LinkIndex is the link index of host gateway.
7575
LinkIndex int
76+
77+
// OFPort is the OpenFlow port number of host gateway allocated by OVS.
78+
OFPort uint32
7679
}
7780

7881
func (g *GatewayConfig) String() string {
@@ -87,6 +90,8 @@ type AdapterNetConfig struct {
8790
Gateway string
8891
DNSServers string
8992
Routes []interface{}
93+
// OFPort is the OpenFlow port number of the uplink interface allocated by OVS.
94+
OFPort uint32
9095
}
9196

9297
type WireGuardConfig struct {
@@ -136,6 +141,12 @@ type NodeConfig struct {
136141
// Auto discovery will use MTU value of the Node's primary interface.
137142
// For Encap and Hybrid mode, Node MTU will be adjusted to account for encap header.
138143
NodeMTU int
144+
// TunnelOFPort is the OpenFlow port number of tunnel interface allocated by OVS. With noEncap mode, the value is 0.
145+
TunnelOFPort uint32
146+
// HostInterfaceOFPort is the OpenFlow port number of the host interface allocated by OVS. The host interface is the
147+
// one which the IP/MAC of the uplink is moved to. If the host interface is the OVS bridge interface (br-int), the
148+
// value is config.BridgeOFPort.
149+
HostInterfaceOFPort uint32
139150
// The config of the gateway interface on the OVS bridge.
140151
GatewayConfig *GatewayConfig
141152
// The config of the OVS bridge uplink interface. Only for Windows Node.

pkg/agent/controller/networkpolicy/fqdn.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import (
3232
"k8s.io/client-go/util/workqueue"
3333
"k8s.io/klog/v2"
3434

35-
"antrea.io/antrea/pkg/agent/config"
3635
"antrea.io/antrea/pkg/agent/openflow"
3736
"antrea.io/antrea/pkg/agent/types"
3837
binding "antrea.io/antrea/pkg/ovs/openflow"
@@ -150,9 +149,10 @@ type fqdnController struct {
150149
selectorItemToRuleIDs map[fqdnSelectorItem]sets.String
151150
ipv4Enabled bool
152151
ipv6Enabled bool
152+
gwPort uint32
153153
}
154154

155-
func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServerOverride string, dirtyRuleHandler func(string), v4Enabled, v6Enabled bool) (*fqdnController, error) {
155+
func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServerOverride string, dirtyRuleHandler func(string), v4Enabled, v6Enabled bool, gwPort uint32) (*fqdnController, error) {
156156
controller := &fqdnController{
157157
ofClient: client,
158158
dirtyRuleHandler: dirtyRuleHandler,
@@ -166,6 +166,7 @@ func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServer
166166
selectorItemToRuleIDs: map[fqdnSelectorItem]sets.String{},
167167
ipv4Enabled: v4Enabled,
168168
ipv6Enabled: v6Enabled,
169+
gwPort: gwPort,
169170
}
170171
if controller.ofClient != nil {
171172
if err := controller.ofClient.NewDNSpacketInConjunction(dnsInterceptRuleID); err != nil {
@@ -809,6 +810,16 @@ func (f *fqdnController) sendDNSPacketout(pktIn *ofctrl.PacketIn) error {
809810
}
810811
}
811812
if prot == protocol.Type_UDP {
813+
inPort := f.gwPort
814+
if inPort == 0 {
815+
// Use the original in_port number in the packetIn message to avoid an invalid input port number. Note that,
816+
// this should not happen in container case as antrea-gw0 always exists. This check is for security.
817+
matches := pktIn.GetMatches()
818+
inPortField := matches.GetMatchByName("OXM_OF_IN_PORT")
819+
if inPortField != nil {
820+
inPort = inPortField.GetValue().(uint32)
821+
}
822+
}
812823
udpSrcPort, udpDstPort, err := binding.GetUDPHeaderData(pktIn.Data.Data)
813824
if err != nil {
814825
klog.ErrorS(err, "Failed to get UDP header data")
@@ -822,7 +833,7 @@ func (f *fqdnController) sendDNSPacketout(pktIn *ofctrl.PacketIn) error {
822833
pktIn.Data.HWDst.String(),
823834
srcIP,
824835
dstIP,
825-
uint32(config.HostGatewayOFPort),
836+
inPort,
826837
0,
827838
isIPv6,
828839
udpSrcPort,

pkg/agent/controller/networkpolicy/fqdn_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/stretchr/testify/require"
2525
"k8s.io/apimachinery/pkg/util/sets"
2626

27+
"antrea.io/antrea/pkg/agent/config"
2728
openflowtest "antrea.io/antrea/pkg/agent/openflow/testing"
2829
)
2930

@@ -42,6 +43,7 @@ func newMockFQDNController(t *testing.T, controller *gomock.Controller, dnsServe
4243
dirtyRuleHandler,
4344
true,
4445
false,
46+
config.HostGatewayOFPort,
4547
)
4648
require.NoError(t, err)
4749
return f, mockOFClient

pkg/agent/controller/networkpolicy/networkpolicy_controller.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ type Controller struct {
111111
ifaceStore interfacestore.InterfaceStore
112112
// denyConnStore is for storing deny connections for flow exporter.
113113
denyConnStore *connections.DenyConnectionStore
114+
gwPort uint32
115+
tunPort uint32
114116
}
115117

116118
// NewNetworkPolicyController returns a new *Controller.
@@ -129,7 +131,8 @@ func NewNetworkPolicyController(antreaClientGetter agent.AntreaClientProvider,
129131
asyncRuleDeleteInterval time.Duration,
130132
dnsServerOverride string,
131133
v4Enabled bool,
132-
v6Enabled bool) (*Controller, error) {
134+
v6Enabled bool,
135+
gwPort, tunPort uint32) (*Controller, error) {
133136
idAllocator := newIDAllocator(asyncRuleDeleteInterval, dnsInterceptRuleID)
134137
c := &Controller{
135138
antreaClientProvider: antreaClientGetter,
@@ -140,11 +143,13 @@ func NewNetworkPolicyController(antreaClientGetter agent.AntreaClientProvider,
140143
statusManagerEnabled: statusManagerEnabled,
141144
multicastEnabled: multicastEnabled,
142145
loggingEnabled: loggingEnabled,
146+
gwPort: gwPort,
147+
tunPort: tunPort,
143148
}
144149

145150
if antreaPolicyEnabled {
146151
var err error
147-
if c.fqdnController, err = newFQDNController(ofClient, idAllocator, dnsServerOverride, c.enqueueRule, v4Enabled, v6Enabled); err != nil {
152+
if c.fqdnController, err = newFQDNController(ofClient, idAllocator, dnsServerOverride, c.enqueueRule, v4Enabled, v6Enabled, gwPort); err != nil {
148153
return nil, err
149154
}
150155

pkg/agent/controller/networkpolicy/networkpolicy_controller_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
k8stesting "k8s.io/client-go/testing"
3333
"k8s.io/component-base/metrics/legacyregistry"
3434

35+
"antrea.io/antrea/pkg/agent/config"
3536
"antrea.io/antrea/pkg/agent/metrics"
3637
"antrea.io/antrea/pkg/agent/openflow"
3738
proxytypes "antrea.io/antrea/pkg/agent/proxy/types"
@@ -60,7 +61,7 @@ func newTestController() (*Controller, *fake.Clientset, *mockReconciler) {
6061
ch2 := make(chan string, 100)
6162
groupIDAllocator := openflow.NewGroupAllocator(false)
6263
groupCounters := []proxytypes.GroupCounter{proxytypes.NewGroupCounter(groupIDAllocator, ch2)}
63-
controller, _ := NewNetworkPolicyController(&antreaClientGetter{clientset}, nil, nil, "node1", podUpdateChannel, groupCounters, ch2, true, true, true, false, true, testAsyncDeleteInterval, "8.8.8.8:53", true, false)
64+
controller, _ := NewNetworkPolicyController(&antreaClientGetter{clientset}, nil, nil, "node1", podUpdateChannel, groupCounters, ch2, true, true, true, false, true, testAsyncDeleteInterval, "8.8.8.8:53", true, false, config.HostGatewayOFPort, config.DefaultTunOFPort)
6465
reconciler := newMockReconciler()
6566
controller.reconciler = reconciler
6667
controller.antreaPolicyLogger = nil

0 commit comments

Comments
 (0)