Skip to content

Commit cdc2671

Browse files
authored
Merge pull request #5351 from kyrtapz/ct_inv_accept_bgp
Enable Layer2 route advertisements on LGW
2 parents 36ba17d + dcc403c commit cdc2671

File tree

6 files changed

+56
-58
lines changed

6 files changed

+56
-58
lines changed

dist/images/ovnkube.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,11 @@ local-nb-ovsdb() {
989989
wait_for_event attempts=3 process_ready ovnnb_db
990990
echo "=============== nb-ovsdb (unix sockets only) ========== RUNNING"
991991

992+
[[ "local" == "${OVN_GATEWAY_MODE}" && "true" == "${OVN_ROUTE_ADVERTISEMENTS_ENABLE}" ]] && {
993+
ovn-nbctl set NB_Global . options:use_ct_inv_match=false
994+
echo "=============== nb-ovsdb ========== reconfigured for route advertisements"
995+
}
996+
992997
# Let ovn-northd sleep and not use so much CPU
993998
ovn-nbctl set NB_Global . options:northd-backoff-interval-ms=${ovn_northd_backoff_interval}
994999
echo "=============== nb-ovsdb ========== reconfigured for northd backoff"

dist/templates/ovnkube-single-node-zone.yaml.j2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ spec:
7979
value: "{{ ovn_loglevel_nb }}"
8080
- name: OVN_NORTHD_BACKOFF_INTERVAL
8181
value: "{{ ovn_northd_backoff_interval }}"
82+
- name: OVN_GATEWAY_MODE
83+
value: "{{ ovn_gateway_mode }}"
84+
- name: OVN_ROUTE_ADVERTISEMENTS_ENABLE
85+
value: "{{ ovn_route_advertisements_enable }}"
8286
- name: K8S_APISERVER
8387
valueFrom:
8488
configMapKeyRef:

go-controller/pkg/clustermanager/routeadvertisements/controller.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,6 @@ func (c *Controller) generateFRRConfigurations(ra *ratypes.RouteAdvertisements)
373373
return nil, nil, fmt.Errorf("%w: selected network %q has unsupported topology %q", errConfig, networkName, network.TopologyType())
374374
}
375375

376-
if config.Gateway.Mode == config.GatewayModeLocal && network.TopologyType() == types.Layer2Topology {
377-
return nil, nil, fmt.Errorf("%w: BGP is currently not supported for Layer2 networks in local gateway mode, network: %s", errConfig, network.GetNetworkName())
378-
}
379-
380376
if advertisements.Has(ratypes.EgressIP) && network.TopologyType() == types.Layer2Topology {
381377
return nil, nil, fmt.Errorf("%w: EgressIP advertisement is currently not supported for Layer2 networks, network: %s", errConfig, network.GetNetworkName())
382378
}

go-controller/pkg/node/gateway_shared_intf.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -406,9 +406,9 @@ func (npw *nodePortWatcher) updateServiceFlowCache(service *corev1.Service, netI
406406

407407
// Add flows for default network services that are accessible from UDN networks
408408
if util.IsNetworkSegmentationSupportEnabled() {
409-
// The flow added below has a higher priority than the per UDN service flow:
410-
// priority=200, table=2, ip, ip_src=169.254.0.<UDN>, actions=set_field:<bridge-mac>->eth_dst,output:<UDN-patch-port>
411-
// This ordering ensures that traffic to UDN allowed default services goes to the the default patch port.
409+
// The flow added below has a higher priority than the per UDN service isolation flow:
410+
// priority=200, table=2, ip, ip_src=169.254.0.<UDN>, actions=drop
411+
// This ordering ensures that traffic to UDN allowed default services goes to the default patch port.
412412

413413
if util.IsUDNEnabledService(ktypes.NamespacedName{Namespace: service.Namespace, Name: service.Name}.String()) {
414414
key = strings.Join([]string{"UDNAllowedSVC", service.Namespace, service.Name}, "_")
@@ -1788,13 +1788,17 @@ func flowsForDefaultBridge(bridge *bridgeConfiguration, extraIPs []net.IP) ([]st
17881788
// Use the filtered subnets for the flow compute instead of the masqueradeIP
17891789
srcIPOrSubnet = matchingIPFamilySubnet.String()
17901790
}
1791+
1792+
// Drop traffic coming from the masquerade IP or the UDN subnet(for advertised UDNs) to ensure that
1793+
// isolation between networks is enforced. This handles the case where a pod on the UDN subnet is sending traffic to
1794+
// a service in another UDN.
17911795
dftFlows = append(dftFlows,
17921796
fmt.Sprintf("cookie=%s, priority=200, table=2, ip, ip_src=%s, "+
1793-
"actions=set_field:%s->eth_dst,output:%s",
1794-
defaultOpenFlowCookie, srcIPOrSubnet,
1795-
bridgeMacAddress, netConfig.ofPortPatch))
1797+
"actions=drop",
1798+
defaultOpenFlowCookie, srcIPOrSubnet))
1799+
17961800
dftFlows = append(dftFlows,
1797-
fmt.Sprintf("cookie=%s, priority=200, table=2, ip, pkt_mark=%s, "+
1801+
fmt.Sprintf("cookie=%s, priority=250, table=2, ip, pkt_mark=%s, "+
17981802
"actions=set_field:%s->eth_dst,output:%s",
17991803
defaultOpenFlowCookie, netConfig.pktMark,
18001804
bridgeMacAddress, netConfig.ofPortPatch))
@@ -1825,11 +1829,10 @@ func flowsForDefaultBridge(bridge *bridgeConfiguration, extraIPs []net.IP) ([]st
18251829
}
18261830
dftFlows = append(dftFlows,
18271831
fmt.Sprintf("cookie=%s, priority=200, table=2, ip6, ipv6_src=%s, "+
1828-
"actions=set_field:%s->eth_dst,output:%s",
1829-
defaultOpenFlowCookie, srcIPOrSubnet,
1830-
bridgeMacAddress, netConfig.ofPortPatch))
1832+
"actions=drop",
1833+
defaultOpenFlowCookie, srcIPOrSubnet))
18311834
dftFlows = append(dftFlows,
1832-
fmt.Sprintf("cookie=%s, priority=200, table=2, ip6, pkt_mark=%s, "+
1835+
fmt.Sprintf("cookie=%s, priority=250, table=2, ip6, pkt_mark=%s, "+
18331836
"actions=set_field:%s->eth_dst,output:%s",
18341837
defaultOpenFlowCookie, netConfig.pktMark,
18351838
bridgeMacAddress, netConfig.ofPortPatch))

go-controller/pkg/node/gateway_udn_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ func checkDefaultSvcIsolationOVSFlows(flows []string, defaultConfig *bridgeUDNCo
280280
Expect(nTable2Flows).To(Equal(1))
281281
}
282282

283-
func checkAdvertisedUDNSvcIsolationOVSFlows(flows []string, netConfig *bridgeUDNConfiguration, netName, bridgeMAC string, svcCIDR *net.IPNet, expectedNFlows int) {
283+
func checkAdvertisedUDNSvcIsolationOVSFlows(flows []string, netConfig *bridgeUDNConfiguration, netName string, svcCIDR *net.IPNet, expectedNFlows int) {
284284
By(fmt.Sprintf("Checking advertsised UDN %s service isolation flows for %s; expected %d flows",
285285
netName, svcCIDR.String(), expectedNFlows))
286286

@@ -303,8 +303,8 @@ func checkAdvertisedUDNSvcIsolationOVSFlows(flows []string, netConfig *bridgeUDN
303303

304304
var nFlows int
305305
for _, flow := range flows {
306-
if strings.Contains(flow, fmt.Sprintf("priority=200, table=2, %s, %s_src=%s, actions=set_field:%s->eth_dst,output:%s",
307-
protoPrefix, protoPrefix, matchingIPFamilySubnet, bridgeMAC, netConfig.ofPortPatch)) {
306+
if strings.Contains(flow, fmt.Sprintf("priority=200, table=2, %s, %s_src=%s, actions=drop",
307+
protoPrefix, protoPrefix, matchingIPFamilySubnet)) {
308308
nFlows++
309309
}
310310
if strings.Contains(flow, fmt.Sprintf("priority=550, in_port=LOCAL, %s, %s_src=%s, %s_dst=%s, actions=ct(commit,zone=64001,table=2)",
@@ -316,7 +316,7 @@ func checkAdvertisedUDNSvcIsolationOVSFlows(flows []string, netConfig *bridgeUDN
316316
Expect(nFlows).To(Equal(expectedNFlows))
317317
}
318318

319-
func checkUDNSvcIsolationOVSFlows(flows []string, netConfig *bridgeUDNConfiguration, netName, bridgeMAC string, svcCIDR *net.IPNet, expectedNFlows int) {
319+
func checkUDNSvcIsolationOVSFlows(flows []string, netConfig *bridgeUDNConfiguration, netName string, svcCIDR *net.IPNet, expectedNFlows int) {
320320
By(fmt.Sprintf("Checking UDN %s service isolation flows for %s; expected %d flows",
321321
netName, svcCIDR.String(), expectedNFlows))
322322

@@ -332,8 +332,8 @@ func checkUDNSvcIsolationOVSFlows(flows []string, netConfig *bridgeUDNConfigurat
332332

333333
var nFlows int
334334
for _, flow := range flows {
335-
if strings.Contains(flow, fmt.Sprintf("priority=200, table=2, %s, %s_src=%s, actions=set_field:%s->eth_dst,output:%s",
336-
protoPrefix, protoPrefix, mgmtMasqIP, bridgeMAC, netConfig.ofPortPatch)) {
335+
if strings.Contains(flow, fmt.Sprintf("priority=200, table=2, %s, %s_src=%s, actions=drop",
336+
protoPrefix, protoPrefix, mgmtMasqIP)) {
337337
nFlows++
338338
}
339339
}
@@ -797,7 +797,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
797797
checkDefaultSvcIsolationOVSFlows(flowMap["DEFAULT"], defaultUdnConfig, ofPortHost, bridgeMAC, svcCIDR)
798798

799799
// Expect exactly one flow per UDN for table 2 for service isolation.
800-
checkUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", bridgeMAC, svcCIDR, 1)
800+
checkUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", svcCIDR, 1)
801801
}
802802

803803
// The second call to checkPorts() will return no ofPort for the UDN - simulating a deletion that already was
@@ -827,7 +827,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
827827
checkDefaultSvcIsolationOVSFlows(flowMap["DEFAULT"], defaultUdnConfig, ofPortHost, bridgeMAC, svcCIDR)
828828

829829
// Expect no more flows per UDN for table 2 for service isolation.
830-
checkUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", bridgeMAC, svcCIDR, 0)
830+
checkUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", svcCIDR, 0)
831831
}
832832
return nil
833833
})
@@ -1028,7 +1028,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
10281028
checkDefaultSvcIsolationOVSFlows(flowMap["DEFAULT"], defaultUdnConfig, ofPortHost, bridgeMAC, svcCIDR)
10291029

10301030
// Expect exactly one flow per UDN for tables 0 and 2 for service isolation.
1031-
checkUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", bridgeMAC, svcCIDR, 1)
1031+
checkUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", svcCIDR, 1)
10321032
}
10331033

10341034
// The second call to checkPorts() will return no ofPort for the UDN - simulating a deletion that already was
@@ -1058,7 +1058,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
10581058
checkDefaultSvcIsolationOVSFlows(flowMap["DEFAULT"], defaultUdnConfig, ofPortHost, bridgeMAC, svcCIDR)
10591059

10601060
// Expect no more flows per UDN for tables 0 and 2 for service isolation.
1061-
checkUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", bridgeMAC, svcCIDR, 0)
1061+
checkUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", svcCIDR, 0)
10621062
}
10631063
return nil
10641064
})
@@ -1269,7 +1269,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
12691269
checkDefaultSvcIsolationOVSFlows(flowMap["DEFAULT"], defaultUdnConfig, ofPortHost, bridgeMAC, svcCIDR)
12701270

12711271
// Expect exactly one flow per advertised UDN for table 2 and table 0 for service isolation.
1272-
checkAdvertisedUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", bridgeMAC, svcCIDR, 2)
1272+
checkAdvertisedUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", svcCIDR, 2)
12731273
}
12741274

12751275
// The second call to checkPorts() will return no ofPort for the UDN - simulating a deletion that already was
@@ -1299,7 +1299,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
12991299
checkDefaultSvcIsolationOVSFlows(flowMap["DEFAULT"], defaultUdnConfig, ofPortHost, bridgeMAC, svcCIDR)
13001300

13011301
// Expect no more flows per UDN for table 2 and table0 for service isolation.
1302-
checkAdvertisedUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", bridgeMAC, svcCIDR, 0)
1302+
checkAdvertisedUDNSvcIsolationOVSFlows(flowMap["DEFAULT"], bridgeUdnConfig, "bluenet", svcCIDR, 0)
13031303
}
13041304
return nil
13051305
})

test/e2e/route_advertisements.go

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,6 @@ var _ = ginkgo.Describe("BGP: Pod to external server when CUDN network is advert
299299
Values: []string{f.Namespace.Name},
300300
}}}
301301

302-
if IsGatewayModeLocal(f.ClientSet) && cudnTemplate.Spec.Network.Topology == udnv1.NetworkTopologyLayer2 {
303-
e2eskipper.Skipf(
304-
"BGP for L2 networks on LGW is currently unsupported",
305-
)
306-
}
307302
// Create CUDN
308303
ginkgo.By("create ClusterUserDefinedNetwork")
309304
udnClient, err := udnclientset.NewForConfig(f.ClientConfig())
@@ -527,6 +522,10 @@ var _ = ginkgo.Describe("BGP: Pod to external server when CUDN network is advert
527522
var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks",
528523
func(cudnATemplate, cudnBTemplate *udnv1.ClusterUserDefinedNetwork) {
529524
const curlConnectionTimeoutCode = "28"
525+
const (
526+
ipFamilyV4 = iota
527+
ipFamilyV6
528+
)
530529

531530
f := wrappedTestFramework("bpp-network-isolation")
532531
f.SkipNamespaceCreation = true
@@ -542,9 +541,6 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks"
542541
var ra *rav1.RouteAdvertisements
543542
var hostNetworkPort int
544543
ginkgo.BeforeEach(func() {
545-
if cudnATemplate.Spec.Network.Topology == udnv1.NetworkTopologyLayer2 && isLocalGWModeEnabled() {
546-
e2eskipper.Skipf("Advertising Layer2 UDNs is not currently supported in LGW")
547-
}
548544
ginkgo.By("Configuring primary UDN namespaces")
549545
var err error
550546
udnNamespaceA, err = f.CreateNamespace(context.TODO(), f.BaseName, map[string]string{
@@ -719,9 +715,6 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks"
719715
})
720716

721717
ginkgo.AfterEach(func() {
722-
if cudnATemplate.Spec.Network.Topology == udnv1.NetworkTopologyLayer2 && isLocalGWModeEnabled() {
723-
return
724-
}
725718
gomega.Expect(f.ClientSet.CoreV1().Pods(udnNamespaceA.Name).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{})).To(gomega.Succeed())
726719
gomega.Expect(f.ClientSet.CoreV1().Pods(udnNamespaceB.Name).DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{})).To(gomega.Succeed())
727720

@@ -798,7 +791,7 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks"
798791
framework.Logf("Connectivity check successful:'%s' -> %s", client, targetAddress)
799792
return out, nil
800793
}
801-
clientName, clientNamespace, dst, expectedOutput, expectErr := connInfo(0)
794+
clientName, clientNamespace, dst, expectedOutput, expectErr := connInfo(ipFamilyV4)
802795

803796
asyncAssertion := gomega.Eventually
804797
timeout := time.Second * 30
@@ -819,7 +812,7 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks"
819812
}
820813
if isIPv6Supported(f.ClientSet) && isIPv4Supported(f.ClientSet) {
821814
// use ipFamilyIndex of 1 to pick the IPv6 addresses
822-
clientName, clientNamespace, dst, expectedOutput, expectErr := connInfo(1)
815+
clientName, clientNamespace, dst, expectedOutput, expectErr := connInfo(ipFamilyV6)
823816
out, err := checkConnectivity(clientName, clientNamespace, dst)
824817
if expectErr != (err != nil) {
825818
return fmt.Errorf("expected connectivity check to return error(%t), got %v, output %v", expectErr, err, out)
@@ -908,16 +901,7 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks"
908901
}),
909902
ginkgo.Entry("pod in the UDN should not be able to access a default network service",
910903
func(ipFamilyIndex int) (clientName string, clientNamespace string, dst string, expectedOutput string, expectErr bool) {
911-
err := true
912-
out := curlConnectionTimeoutCode
913-
if cudnATemplate.Spec.Network.Topology == udnv1.NetworkTopologyLayer2 {
914-
// FIXME: prevent looping of traffic in L2 UDNs
915-
// bad behaviour: packet is looping from management port -> breth0 -> GR -> management port -> breth0 and so on
916-
// which is a never ending loop
917-
// this causes curl timeout with code 7 host unreachable instead of code 28
918-
out = ""
919-
}
920-
return podsNetA[0].Name, podsNetA[0].Namespace, net.JoinHostPort(svcNetDefault.Spec.ClusterIPs[ipFamilyIndex], "8080") + "/clientip", out, err
904+
return podsNetA[0].Name, podsNetA[0].Namespace, net.JoinHostPort(svcNetDefault.Spec.ClusterIPs[ipFamilyIndex], "8080") + "/clientip", curlConnectionTimeoutCode, true
921905
}),
922906
ginkgo.Entry("pod in the UDN should be able to access kapi in default network service",
923907
func(ipFamilyIndex int) (clientName string, clientNamespace string, dst string, expectedOutput string, expectErr bool) {
@@ -982,14 +966,20 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks"
982966
errBool := false
983967
out := ""
984968
if cudnATemplate.Spec.Network.Topology == udnv1.NetworkTopologyLayer2 {
985-
// FIXME: fix assymmetry in L2 UDNs
986-
// bad behaviour: packet is coming from other node -> entering eth0 -> bretho and here kernel drops the packet since
987-
// rp_filter is set to 1 in breth0 and there is an iprule that sends the packet to mpX interface so kernel sees the packet
988-
// having return path different from the incoming interface.
989-
// The SNAT to nodeIP should fix this.
990-
// this causes curl timeout with code 28
991-
errBool = true
992-
out = curlConnectionTimeoutCode
969+
// FIXME: this should be removed once we add the SNAT for pod->node traffic
970+
// We now permit asymmetric traffic on LGW. This prevents the issue from occurring with IPv6.
971+
// However, for IPv4 LGW rp_filter is still blocking the replies.
972+
// The situation is different on SGW as we don't allow asymmetric traffic at all, which is why IPv6 traffic fails there too.
973+
if ipFamilyIndex == ipFamilyV4 || !isLocalGWModeEnabled() {
974+
// FIXME: fix assymmetry in L2 UDNs
975+
// bad behaviour: packet is coming from other node -> entering eth0 -> bretho and here kernel drops the packet since
976+
// rp_filter is set to 1 in breth0 and there is an iprule that sends the packet to mpX interface so kernel sees the packet
977+
// having return path different from the incoming interface.
978+
// The SNAT to nodeIP should fix this.
979+
// this causes curl timeout with code 28
980+
errBool = true
981+
out = curlConnectionTimeoutCode
982+
}
993983
}
994984
return clientPod.Name, clientPod.Namespace, net.JoinHostPort(nodeIP, fmt.Sprint(hostNetworkPort)) + "/hostname", out, errBool
995985
}),

0 commit comments

Comments
 (0)