@@ -1201,3 +1201,124 @@ func ValidateMIGInstancesCreated(ctx context.Context, s *Scenario, migProfile st
12011201 require .NotContains (s .T , stdout , "No MIG-enabled devices found" , "no MIG devices were created.\n Output:\n %s" , stdout )
12021202 s .T .Logf ("MIG instances with profile %s are created" , migProfile )
12031203}
1204+
1205+ // ValidateIPTablesCompatibleWithCiliumEBPF validates that all iptables rules in each table match the provided patterns which are accounted for
1206+ // when eBPF host routing is enabled.
1207+ func ValidateIPTablesCompatibleWithCiliumEBPF (ctx context.Context , s * Scenario ) {
1208+ s .T .Helper ()
1209+ tablePatterns , globalPatterns := getIPTablesRulesCompatibleWithEBPFHostRouting ()
1210+ tables := []string {"filter" , "mangle" , "nat" , "raw" , "security" }
1211+ success := true
1212+
1213+ for _ , table := range tables {
1214+ s .T .Logf ("Validating iptables rules for table: %s" , table )
1215+
1216+ // Get the rules for this table
1217+ command := fmt .Sprintf ("sudo iptables -t %s -S" , table )
1218+ execResult := execScriptOnVMForScenarioValidateExitCode (ctx , s , command , 0 , fmt .Sprintf ("failed to get iptables rules for table %s" , table ))
1219+
1220+ stdout := execResult .stdout .String ()
1221+ rules := strings .Split (strings .TrimSpace (stdout ), "\n " )
1222+
1223+ // Get patterns for this table
1224+ patterns := tablePatterns [table ]
1225+ if patterns == nil {
1226+ patterns = []string {}
1227+ }
1228+
1229+ // Combine with global patterns
1230+ allPatterns := append ([]string {}, globalPatterns ... )
1231+ allPatterns = append (allPatterns , patterns ... )
1232+
1233+ // Check each rule
1234+ for _ , rule := range rules {
1235+ rule = strings .TrimSpace (rule )
1236+ if rule == "" {
1237+ continue
1238+ }
1239+
1240+ matched := false
1241+ for _ , pattern := range allPatterns {
1242+ pattern = strings .TrimSpace (pattern )
1243+ if pattern == "" {
1244+ continue
1245+ }
1246+
1247+ // Try regex match
1248+ matched , _ = regexp .MatchString (pattern , rule )
1249+ if matched {
1250+ break
1251+ }
1252+
1253+ // Also try exact match for non-regex patterns
1254+ if strings .Contains (rule , pattern ) {
1255+ matched = true
1256+ break
1257+ }
1258+ }
1259+
1260+ if ! matched {
1261+ s .T .Logf ("Rule in table %s did not match any pattern: %s" , table , rule )
1262+ success = false
1263+ }
1264+ }
1265+
1266+ s .T .Logf ("Checked rules in table %s against expected patterns" , table )
1267+ }
1268+
1269+ require .True (
1270+ s .T ,
1271+ success ,
1272+ "Rules found that do not match any of the given patterns. See previous log lines for details. " +
1273+ "This may indicate an unsupported iptables rule when eBPF host routing is enabled. " +
1274+ "Contact [email protected] for details." ,
1275+ )
1276+ }
1277+
1278+ // getIPTablesRulesCompatibleWithEBPFHostRouting returns the expected iptables patterns that are accounted for when EBPF host routing is enabled.
1279+ // If tests are failing due to unexpected iptables rules, it is because an iptables rule has been found, that was not accounted for in the implementation
1280+ // of the eBPF host routing feature in Cilium CNI. In eBPF host routing mode, iptables rules in the host network namespace are bypassed for pod
1281+ // traffic. So, any functionality that is built using iptables needs an equivalent non-iptables implementation that works in Cilium's eBPF host routing
1282+ // mode. For guidance on how this may be done, please contact [email protected] (Azure Container Networking Dataplane team). Once the feature 1283+ // is supported in eBPF host routing mode, or is blocked from being enabled alongside eBPF host routing mode, you can update this list.
1284+ func getIPTablesRulesCompatibleWithEBPFHostRouting () (map [string ][]string , []string ) {
1285+ tablePatterns := map [string ][]string {
1286+ "filter" : {
1287+ `-A FORWARD -d 168.63.129.16/32 -p tcp -m tcp --dport 32526 -j DROP` ,
1288+ `-A FORWARD -d 168.63.129.16/32 -p tcp -m tcp --dport 80 -j DROP` ,
1289+ },
1290+ "mangle" : {
1291+ `-A FORWARD -d 168\.63\.129\.16/32 -p tcp -m tcp --dport 80 -j DROP` ,
1292+ `-A FORWARD -d 168\.63\.129\.16/32 -p tcp -m tcp --dport 32526 -j DROP` ,
1293+ },
1294+ "nat" : {
1295+ `-A POSTROUTING -j SWIFT` ,
1296+ `-A SWIFT -s` ,
1297+ `-A POSTROUTING -j SWIFT-POSTROUTING` ,
1298+ `-A SWIFT-POSTROUTING -s` ,
1299+ },
1300+ "raw" : {
1301+ `^-A (PREROUTING|OUTPUT) -d 169\.254\.10\.(10|11)\/32 -p (tcp|udp) -m comment --comment "localdns: skip conntrack" -m (tcp|udp) --dport 53 -j NOTRACK$` ,
1302+ },
1303+ "security" : {
1304+ `-A OUTPUT -d 168\.63\.129\.16/32 -p tcp -m tcp --dport 53 -j ACCEPT` ,
1305+ `-A OUTPUT -d 168\.63\.129\.16/32 -p tcp -m owner --uid-owner 0 -j ACCEPT` ,
1306+ `-A OUTPUT -d 168\.63\.129\.16/32 -p tcp -m conntrack --ctstate INVALID,NEW -j DROP` ,
1307+ },
1308+ }
1309+
1310+ globalPatterns := []string {
1311+ `^-N .*` ,
1312+ `^-P .*` ,
1313+ `^-A (KUBE-SERVICES|KUBE-EXTERNAL-SERVICES|KUBE-NODEPORTS|KUBE-POSTROUTING|KUBE-MARK-MASQ|KUBE-FORWARD|KUBE-PROXY-FIREWALL|KUBE-PROXY-CANARY|KUBE-FIREWALL|KUBE-MARK-DROP) .*` ,
1314+ `^-A (KUBE-SEP|KUBE-SVC)` ,
1315+ `^-A .* -j (KUBE-SEP|KUBE-SVC|KUBE-SERVICES|KUBE-EXTERNAL-SERVICES|KUBE-NODEPORTS|KUBE-POSTROUTING|KUBE-MARK-MASQ|KUBE-FORWARD|KUBE-PROXY-FIREWALL|KUBE-PROXY-CANARY|KUBE-FIREWALL|KUBE-MARK-DROP)` ,
1316+ `^-A IP-MASQ-AGENT` ,
1317+ `^-A .* -j IP-MASQ-AGENT` ,
1318+ `^.*--comment.*cilium:` ,
1319+ `^.*--comment.*cilium-feeder:` ,
1320+ `-A FORWARD ! -s (?:\d{1,3}\.){3}\d{1,3}/32 -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -m comment --comment "AKS managed: added by AgentBaker ensureIMDSRestriction for IMDS restriction feature" -j DROP` ,
1321+ }
1322+
1323+ return tablePatterns , globalPatterns
1324+ }
0 commit comments