44 "context"
55 "fmt"
66 "net"
7+ "strings"
78 "sync"
89 "time"
910
@@ -21,6 +22,7 @@ import (
2122 adminpolicybasedrouteclient "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/adminpolicybasedroute/v1/apis/clientset/versioned/fake"
2223 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/factory"
2324 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/kube/mocks"
25+ "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/managementport"
2426 nodenft "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/nftables"
2527 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/routemanager"
2628 ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing"
@@ -1238,4 +1240,325 @@ add element inet ovn-kubernetes no-pmtud-remote-node-ips-v6 { 2002:db8:1::4 }
12381240
12391241 })
12401242
1243+ Describe ("node ingress snat exclude subnets" , func () {
1244+
1245+ var (
1246+ testNS ns.NetNS
1247+ nc * DefaultNodeNetworkController
1248+ app * cli.App
1249+ )
1250+
1251+ const (
1252+ nodeName = "my-node"
1253+ )
1254+
1255+ BeforeEach (func () {
1256+ var err error
1257+ testNS , err = testutils .NewNS ()
1258+ Expect (err ).NotTo (HaveOccurred ())
1259+ Expect (config .PrepareTestConfig ()).To (Succeed ())
1260+
1261+ app = cli .NewApp ()
1262+ app .Name = "test"
1263+ app .Flags = config .Flags
1264+ })
1265+
1266+ AfterEach (func () {
1267+ util .ResetNetLinkOpMockInst () // other tests in this package rely directly on netlink (e.g. gateway_init_linux_test.go)
1268+ Expect (testNS .Close ()).To (Succeed ())
1269+ })
1270+
1271+ Context ("with a cluster in IPv4 mode" , func () {
1272+ const (
1273+ ethName string = "lo1337"
1274+ nodeIP string = "169.254.254.60"
1275+ ethCIDR string = nodeIP + "/24"
1276+ )
1277+ var link netlink.Link
1278+
1279+ BeforeEach (func () {
1280+ config .IPv4Mode = true
1281+ config .IPv6Mode = false
1282+ config .Gateway .Mode = config .GatewayModeShared
1283+
1284+ // Note we must do this in default netNS because
1285+ // nc.WatchNodes() will spawn goroutines which we cannot lock to the testNS
1286+ ovntest .AddLink (ethName )
1287+
1288+ var err error
1289+ link , err = netlink .LinkByName (ethName )
1290+ Expect (err ).NotTo (HaveOccurred ())
1291+ err = netlink .LinkSetUp (link )
1292+ Expect (err ).NotTo (HaveOccurred ())
1293+
1294+ // Add an IP address
1295+ addr , err := netlink .ParseAddr (ethCIDR )
1296+ Expect (err ).NotTo (HaveOccurred ())
1297+ addr .Scope = int (netlink .SCOPE_UNIVERSE )
1298+ err = netlink .AddrAdd (link , addr )
1299+ Expect (err ).NotTo (HaveOccurred ())
1300+
1301+ })
1302+
1303+ AfterEach (func () {
1304+ err := netlink .LinkDel (link )
1305+ Expect (err ).NotTo (HaveOccurred ())
1306+ })
1307+
1308+ ovntest .OnSupportedPlatformsIt ("empty annotation on startup" , func () {
1309+
1310+ app .Action = func (_ * cli.Context ) error {
1311+ node := corev1.Node {
1312+ ObjectMeta : metav1.ObjectMeta {
1313+ Name : nodeName ,
1314+ Annotations : map [string ]string {},
1315+ },
1316+ Status : corev1.NodeStatus {
1317+ Addresses : []corev1.NodeAddress {
1318+ {
1319+ Type : corev1 .NodeInternalIP ,
1320+ Address : nodeIP ,
1321+ },
1322+ },
1323+ },
1324+ }
1325+
1326+ nft := nodenft .SetFakeNFTablesHelper ()
1327+
1328+ kubeFakeClient := fake .NewSimpleClientset (& corev1.NodeList {
1329+ Items : []corev1.Node {node },
1330+ })
1331+ fakeClient := & util.OVNNodeClientset {
1332+ KubeClient : kubeFakeClient ,
1333+ AdminPolicyRouteClient : adminpolicybasedrouteclient .NewSimpleClientset (),
1334+ NetworkAttchDefClient : nadfake .NewSimpleClientset (),
1335+ }
1336+
1337+ stop := make (chan struct {})
1338+ wf , err := factory .NewNodeWatchFactory (fakeClient , nodeName )
1339+ Expect (err ).NotTo (HaveOccurred ())
1340+ wg := & sync.WaitGroup {}
1341+ defer func () {
1342+ close (stop )
1343+ wg .Wait ()
1344+ wf .Shutdown ()
1345+ }()
1346+
1347+ err = wf .Start ()
1348+ Expect (err ).NotTo (HaveOccurred ())
1349+ routeManager := routemanager .NewController ()
1350+ cnnci := NewCommonNodeNetworkControllerInfo (kubeFakeClient , fakeClient .AdminPolicyRouteClient , wf , nil , nodeName , routeManager )
1351+ nc = newDefaultNodeNetworkController (cnnci , stop , wg , routeManager , nil )
1352+ nc .initRetryFrameworkForNode ()
1353+ err = setupPMTUDNFTSets ()
1354+ Expect (err ).NotTo (HaveOccurred ())
1355+ err = setupPMTUDNFTChain ()
1356+ Expect (err ).NotTo (HaveOccurred ())
1357+ defaultNetConfig := & bridgeUDNConfiguration {
1358+ ofPortPatch : "patch-breth0_ov" ,
1359+ }
1360+ nc .Gateway = & gateway {
1361+ openflowManager : & openflowManager {
1362+ flowCache : map [string ][]string {},
1363+ defaultBridge : & bridgeConfiguration {
1364+ netConfig : map [string ]* bridgeUDNConfiguration {
1365+ types .DefaultNetworkName : defaultNetConfig ,
1366+ },
1367+ },
1368+ },
1369+ }
1370+
1371+ err = managementport .SetupManagementPortNFTSets ()
1372+ Expect (err ).NotTo (HaveOccurred ())
1373+
1374+ // must run route manager manually which is usually started with nc.Start()
1375+ wg .Add (1 )
1376+ go func () {
1377+ defer GinkgoRecover ()
1378+ defer wg .Done ()
1379+ nc .routeManager .Run (stop , 10 * time .Second )
1380+ Expect (err ).NotTo (HaveOccurred ())
1381+ }()
1382+ By ("no nftables elements should present at startup" )
1383+
1384+ err = nc .WatchNodes ()
1385+ Expect (err ).NotTo (HaveOccurred ())
1386+ Expect (nft .Dump ()).NotTo (ContainSubstring ("add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.168.1.0/24 }" ))
1387+ Expect (nft .Dump ()).NotTo (ContainSubstring ("add element inet ovn-kubernetes mgmtport-no-snat-subnets-v6 { fd00::/64 }" ))
1388+
1389+ By ("adding subnets to node annotation should update nftables elements" )
1390+ node .Annotations [util .OvnNodeDontSNATSubnets ] = `["192.167.1.0/24"]`
1391+
1392+ _ , err = kubeFakeClient .CoreV1 ().Nodes ().Update (context .TODO (), & node , metav1.UpdateOptions {})
1393+ Expect (err ).NotTo (HaveOccurred ())
1394+
1395+ Eventually (func () bool {
1396+ cleanDump := strings .ReplaceAll (nft .Dump (), "\r " , "" )
1397+ return strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.167.1.0/24 }" ) &&
1398+ ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.168.1.0/24 }" ) &&
1399+ ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v6 { fd00::/64 }" )
1400+ }).WithTimeout (2 * time .Second ).Should (BeTrue ())
1401+
1402+ By ("adding extra subnets to node annotation should update nftables elements" )
1403+
1404+ node .Annotations [util .OvnNodeDontSNATSubnets ] = `["192.167.1.0/24","fd00::/64","192.169.1.0/24","fd11::/64"]`
1405+
1406+ _ , err = kubeFakeClient .CoreV1 ().Nodes ().Update (context .TODO (), & node , metav1.UpdateOptions {})
1407+ Expect (err ).NotTo (HaveOccurred ())
1408+
1409+ Eventually (func () bool {
1410+ cleanDump := strings .ReplaceAll (nft .Dump (), "\r " , "" )
1411+ return strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.167.1.0/24 }" ) &&
1412+ strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.169.1.0/24 }" ) &&
1413+ strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v6 { fd00::/64 }" )
1414+ }).WithTimeout (2 * time .Second ).Should (BeTrue ())
1415+
1416+ By ("deleting node should remove nftables elements" )
1417+ err = kubeFakeClient .CoreV1 ().Nodes ().Delete (context .TODO (), nodeName , metav1.DeleteOptions {})
1418+ Expect (err ).NotTo (HaveOccurred ())
1419+
1420+ Eventually (func () bool {
1421+ cleanDump := strings .ReplaceAll (nft .Dump (), "\r " , "" )
1422+ return ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.167.1.0/24 }" ) &&
1423+ ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.169.1.0/24 }" ) &&
1424+ ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v6 { fd00::/64 }" )
1425+
1426+ }).WithTimeout (2 * time .Second ).Should (BeTrue ())
1427+ return nil
1428+ }
1429+
1430+ err := app .Run ([]string {app .Name })
1431+ Expect (err ).NotTo (HaveOccurred ())
1432+ })
1433+
1434+ ovntest .OnSupportedPlatformsIt ("non-empty annotation on startup" , func () {
1435+
1436+ app .Action = func (_ * cli.Context ) error {
1437+ node := corev1.Node {
1438+ ObjectMeta : metav1.ObjectMeta {
1439+ Name : nodeName ,
1440+ Annotations : map [string ]string {
1441+ util .OvnNodeDontSNATSubnets : `["192.168.1.0/24","fd00::/64"]` ,
1442+ },
1443+ },
1444+ Status : corev1.NodeStatus {
1445+ Addresses : []corev1.NodeAddress {
1446+ {
1447+ Type : corev1 .NodeInternalIP ,
1448+ Address : nodeIP ,
1449+ },
1450+ },
1451+ },
1452+ }
1453+
1454+ nft := nodenft .SetFakeNFTablesHelper ()
1455+
1456+ kubeFakeClient := fake .NewSimpleClientset (& corev1.NodeList {
1457+ Items : []corev1.Node {node },
1458+ })
1459+ fakeClient := & util.OVNNodeClientset {
1460+ KubeClient : kubeFakeClient ,
1461+ AdminPolicyRouteClient : adminpolicybasedrouteclient .NewSimpleClientset (),
1462+ NetworkAttchDefClient : nadfake .NewSimpleClientset (),
1463+ }
1464+
1465+ stop := make (chan struct {})
1466+ wf , err := factory .NewNodeWatchFactory (fakeClient , nodeName )
1467+ Expect (err ).NotTo (HaveOccurred ())
1468+ wg := & sync.WaitGroup {}
1469+ defer func () {
1470+ close (stop )
1471+ wg .Wait ()
1472+ wf .Shutdown ()
1473+ }()
1474+
1475+ err = wf .Start ()
1476+ Expect (err ).NotTo (HaveOccurred ())
1477+ routeManager := routemanager .NewController ()
1478+ cnnci := NewCommonNodeNetworkControllerInfo (kubeFakeClient , fakeClient .AdminPolicyRouteClient , wf , nil , nodeName , routeManager )
1479+ nc = newDefaultNodeNetworkController (cnnci , stop , wg , routeManager , nil )
1480+ nc .initRetryFrameworkForNode ()
1481+ err = setupPMTUDNFTSets ()
1482+ Expect (err ).NotTo (HaveOccurred ())
1483+ err = setupPMTUDNFTChain ()
1484+ Expect (err ).NotTo (HaveOccurred ())
1485+ defaultNetConfig := & bridgeUDNConfiguration {
1486+ ofPortPatch : "patch-breth0_ov" ,
1487+ }
1488+ nc .Gateway = & gateway {
1489+ openflowManager : & openflowManager {
1490+ flowCache : map [string ][]string {},
1491+ defaultBridge : & bridgeConfiguration {
1492+ netConfig : map [string ]* bridgeUDNConfiguration {
1493+ types .DefaultNetworkName : defaultNetConfig ,
1494+ },
1495+ },
1496+ },
1497+ }
1498+
1499+ err = managementport .SetupManagementPortNFTSets ()
1500+ Expect (err ).NotTo (HaveOccurred ())
1501+
1502+ // must run route manager manually which is usually started with nc.Start()
1503+ wg .Add (1 )
1504+ go func () {
1505+ defer GinkgoRecover ()
1506+ defer wg .Done ()
1507+ nc .routeManager .Run (stop , 10 * time .Second )
1508+ Expect (err ).NotTo (HaveOccurred ())
1509+ }()
1510+ By ("expected nftables elements should present at startup" )
1511+
1512+ err = nc .WatchNodes ()
1513+ Expect (err ).NotTo (HaveOccurred ())
1514+ Expect (nft .Dump ()).To (ContainSubstring ("add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.168.1.0/24 }" ))
1515+ Expect (nft .Dump ()).To (ContainSubstring ("add element inet ovn-kubernetes mgmtport-no-snat-subnets-v6 { fd00::/64 }" ))
1516+
1517+ By ("editing subnets on node annotation should update nftables elements" )
1518+ node .Annotations [util .OvnNodeDontSNATSubnets ] = `["192.167.1.0/24"]`
1519+
1520+ _ , err = kubeFakeClient .CoreV1 ().Nodes ().Update (context .TODO (), & node , metav1.UpdateOptions {})
1521+ Expect (err ).NotTo (HaveOccurred ())
1522+
1523+ Eventually (func () bool {
1524+ cleanDump := strings .ReplaceAll (nft .Dump (), "\r " , "" )
1525+ return strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.167.1.0/24 }" ) &&
1526+ ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.168.1.0/24 }" ) &&
1527+ ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v6 { fd00::/64 }" )
1528+ }).WithTimeout (2 * time .Second ).Should (BeTrue ())
1529+
1530+ By ("adding extra subnets to node annotation should update nftables elements" )
1531+
1532+ node .Annotations [util .OvnNodeDontSNATSubnets ] = `["192.167.1.0/24","fd00::/64","192.169.1.0/24","fd11::/64"]`
1533+
1534+ _ , err = kubeFakeClient .CoreV1 ().Nodes ().Update (context .TODO (), & node , metav1.UpdateOptions {})
1535+ Expect (err ).NotTo (HaveOccurred ())
1536+
1537+ Eventually (func () bool {
1538+ cleanDump := strings .ReplaceAll (nft .Dump (), "\r " , "" )
1539+ return strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.167.1.0/24 }" ) &&
1540+ strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.169.1.0/24 }" ) &&
1541+ strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v6 { fd00::/64 }" )
1542+ }).WithTimeout (2 * time .Second ).Should (BeTrue ())
1543+
1544+ By ("deleting node should remove nftables elements" )
1545+ err = kubeFakeClient .CoreV1 ().Nodes ().Delete (context .TODO (), nodeName , metav1.DeleteOptions {})
1546+ Expect (err ).NotTo (HaveOccurred ())
1547+
1548+ Eventually (func () bool {
1549+ cleanDump := strings .ReplaceAll (nft .Dump (), "\r " , "" )
1550+ return ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.167.1.0/24 }" ) &&
1551+ ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v4 { 192.169.1.0/24 }" ) &&
1552+ ! strings .Contains (cleanDump , "add element inet ovn-kubernetes mgmtport-no-snat-subnets-v6 { fd00::/64 }" )
1553+
1554+ }).WithTimeout (2 * time .Second ).Should (BeTrue ())
1555+ return nil
1556+ }
1557+
1558+ err := app .Run ([]string {app .Name })
1559+ Expect (err ).NotTo (HaveOccurred ())
1560+ })
1561+
1562+ })
1563+ })
12411564})
0 commit comments