From 698614ab55dec6770495170a0ddb3e2b37602e29 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 9 Sep 2026 16:27:23 +0200 Subject: [PATCH 1/2] kvm: fix security group conntrack NOTRACK optimization triggered by single-direction allow-all rule The raw-table NOTRACK rules for a VM IP in the cs_notrack/cs_notrack6 ipsets apply to that IP as both source and destination, disabling connection tracking for it in both directions at once. The check that populates these ipsets did not look at rule['ruletype'], so an allow-all rule in only one direction (e.g. egress all to 0.0.0.0/0) was enough to disable conntrack for the VM, breaking return traffic when the other direction (e.g. ingress) is restricted. Only skip conntrack for a family now when both an ingress allow-all and an egress allow-all rule exist for that family, matching the actual bidirectional effect of the NOTRACK rules. --- scripts/vm/network/security_group.py | 34 +++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/scripts/vm/network/security_group.py b/scripts/vm/network/security_group.py index 235e6342feea..8cdf09898e02 100755 --- a/scripts/vm/network/security_group.py +++ b/scripts/vm/network/security_group.py @@ -1136,23 +1136,25 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru ip4s, ip6s = split_ips_by_family(vm_ip, vm_ip6, sec_ips, str(ipv6_link_local_addr(vmMac))) rules = parse_network_rules(rules) - conntrack4_not_needed = False - conntrack6_not_needed = False + ingress4_allow_all = False + egress4_allow_all = False + ingress6_allow_all = False + egress6_allow_all = False for rule in rules: - """ - If any of the rules has an explicit allow all protocols from 0.0.0.0/0 (ipv4) - or ::/0 (ipv6), then that IP family doesn't need its connection tracked - Example contents of the rules list: - [ - {'ipv4': ['1.0.0.0/24', '0.0.0.0/0'], 'ipv6': ['::/0'], 'ruletype': 'I', 'start': 0, 'end': 0, 'protocol': 'all'}, - {'ipv4': ['1.1.1.1/32'], 'ipv6': [], 'ruletype': 'I', 'start': 1, 'end': 65535, 'protocol': 'tcp'}, - {'ipv4': [], 'ipv6': ['2001:db8::/32'], 'ruletype': 'I', 'start': 2000, 'end': 3000, 'protocol': 'tcp'} - ] - """ - if '0.0.0.0/0' in rule['ipv4'] and rule['protocol'].lower() == 'all': - conntrack4_not_needed = True - if '::/0' in rule['ipv6'] and rule['protocol'].lower() == 'all': - conntrack6_not_needed = True + if rule['protocol'].lower() == 'all': + if '0.0.0.0/0' in rule['ipv4']: + if rule['ruletype'] == 'E': + egress4_allow_all = True + else: + ingress4_allow_all = True + if '::/0' in rule['ipv6']: + if rule['ruletype'] == 'E': + egress6_allow_all = True + else: + ingress6_allow_all = True + + conntrack4_not_needed = ingress4_allow_all and egress4_allow_all + conntrack6_not_needed = ingress6_allow_all and egress6_allow_all if conntrack4_not_needed: add_to_ipset(NOTRACK_IPV4_IPSET, ip4s, "add") From 824f92ab0888165fc63bcb054e48a3b27ea43a49 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 15 Oct 2026 16:13:05 +0200 Subject: [PATCH 2/2] kvm: keep conntrack disabled for security groups with allow-all ingress and no egress rules Egress with no rules ends in an unconditional ACCEPT, so it is as open as an explicit allow-all egress rule. Treat it as open when deciding whether the VM IP can be added to the notrack ipset. --- scripts/vm/network/security_group.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/vm/network/security_group.py b/scripts/vm/network/security_group.py index 8cdf09898e02..df14674c22f1 100755 --- a/scripts/vm/network/security_group.py +++ b/scripts/vm/network/security_group.py @@ -1140,7 +1140,12 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru egress4_allow_all = False ingress6_allow_all = False egress6_allow_all = False + egress4_has_rules = False + egress6_has_rules = False for rule in rules: + if rule['ruletype'] == 'E': + egress4_has_rules = egress4_has_rules or bool(rule['ipv4']) + egress6_has_rules = egress6_has_rules or bool(rule['ipv6']) if rule['protocol'].lower() == 'all': if '0.0.0.0/0' in rule['ipv4']: if rule['ruletype'] == 'E': @@ -1153,8 +1158,11 @@ def add_network_rules(vm_name, vm_id, vm_ip, vm_ip6, signature, seqno, vmMac, ru else: ingress6_allow_all = True - conntrack4_not_needed = ingress4_allow_all and egress4_allow_all - conntrack6_not_needed = ingress6_allow_all and egress6_allow_all + # The NOTRACK rules apply to the IP in both directions, so conntrack can only be skipped when + # ingress allows all and egress is open as well: either an explicit allow-all egress rule or no + # egress rules at all (the egress chain then accepts everything). + conntrack4_not_needed = ingress4_allow_all and (egress4_allow_all or not egress4_has_rules) + conntrack6_not_needed = ingress6_allow_all and (egress6_allow_all or not egress6_has_rules) if conntrack4_not_needed: add_to_ipset(NOTRACK_IPV4_IPSET, ip4s, "add")