Skip to content

Allow exclusion of destination addresses from usage on KVM shared networks - #14607

Open
MitchDrage wants to merge 2 commits into
apache:mainfrom
MitchDrage:usage-exempt-shared
Open

MitchDrage wants to merge 2 commits into
apache:mainfrom
MitchDrage:usage-exempt-shared

Conversation

@MitchDrage

Copy link
Copy Markdown
Contributor

Description

This PR applies network.usage.exempt.cidrs to VMs on shared networks on KVM. It builds on #14583, which adds the setting for virtual routers, so this branch includes that commit until #14583 merges.

On shared networks the host reports usage from libvirt, which reads each VM NIC's tap counters. Those count every byte, so the agent counts exempt traffic separately on the same tap and reports the libvirt total minus that.

How it works:

  1. The management server sends each KVM host its zone's exempt list in SetHostParamsCommand, on connect and whenever the setting is updated or reset.
  2. The agent keeps an nftables netdev table with the exempt addresses in two sets (IPv4 and IPv6). Each user VM's tap gets two chains: the ingress hook counts what the VM sends to exempt addresses, and the egress hook counts what it receives from them. A sync every 10 seconds adds chains for new taps (VM start, NIC plug, migration in) and repairs the table if it's flushed.
  3. When usage is collected, the agent reads those counters and takes them off the libvirt totals for each NIC.

A few details keep the numbers safe to bill. These are in UsageExemptBilling:

  • The ingress hook doesn't see the 14 byte Ethernet header, which the tap counters include, so 14 bytes per packet are added back to what the VM sends.
  • The management server treats a lower number as a counter reset and bills the previous value again. So the reported numbers for a tap never go down. The record lives in /run, so it survives an agent restart and a reboot clears it along with the tap counters.
  • If a tap's chains are wiped, the bytes they counted are kept. A tap with a new ifindex starts from zero, the same as libvirt.

Why nftables: hooking the tap counts both directions whatever bridge or switch it's plugged into, Linux bridge or OVS. iptables only sees bridged traffic with br_netfilter, which changes filtering for every VM. ebtables only covers Linux bridges. tc would sit alongside the qdiscs libvirt already uses for rate limiting. This adds nftables to the agent's package dependencies.

The egress hook needs Linux 5.16 or later, or a backport:

Host OS Kernel Works
EL9 (AlmaLinux 9.8 tested) 5.14 with backport Yes
Ubuntu 24.04, Debian 12 6.8 / 6.1 Yes (expected)
Ubuntu 22.04 (GA kernel) 5.15 No (tested). The HWE kernel works
Ubuntu 20.04, Debian 11, EL8 5.4 / 5.10 / 4.18 No (expected)

On kernels without the hook, or without nft, the agent logs one warning and reports usage as before.

Other changes and limits:

  • configureHostParams only refreshes the reconcile command logs when the reconcile setting is sent, which only happens on connect. Before this, any setting pushed to a running agent (for example migrate.wait) marked commands still in progress as interrupted.
  • Only user VMs' taps get counters. User VMs on isolated and VPC networks still get counters nobody reads, as the host can't tell which kind of network a NIC is on. That costs a minuscule amount of CPU per packet on those VMs. Fixing it needs the server to send extra NIC details on VM start, NIC plug and migration, so I've decided to leave that out of this PR and accept the trade-off.
  • OVS-DPDK NICs aren't covered. They have no tap, and from reading the code, usage isn't recorded for them today anyway.
  • If a VM adds its own VLAN tags to its traffic, exempt traffic is billed a little more: 4 bytes per packet with one tag, and the whole packet with two tags (Q-in-Q).
  • The sync runs every 10 seconds because the agent has no reliable hook for new taps, especially on a migration target. That's shorter than a VM takes to boot and send traffic, and a migrating VM's tap usually gets its counters during the memory copy. An idle sync is two short nft command.
  • After a migration the VM gets a new tap on the new host and its counters start from zero, the same as libvirt's today.
  • server: keep collecting VM network stats after a non DirectAttached NIC #14142 fixes skipped NICs in the same stats code. The two don't conflict.

Fixes: #14578

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Screenshots (if appropriate):

How Has This Been Tested?

  • Unit tests: UsageExemptBillingTest (the arithmetic), UsageExemptCountersTest (the nftables side against a fake nft) and AgentManagerImplTest (sending the list to one zone or all zones).
  • AlmaLinux 9.8 (kernel 5.14), real nftables and a tap driven by a test program: exempt traffic came off the totals to the byte in both directions.
  • Ubuntu 22.04 (kernel 5.15): the missing egress hook is detected, one warning is logged and usage is reported unchanged. The same happens without nft installed.
  • On a real KVM host (CloudStack 4.22.1.1, AlmaLinux 9.8): backported this PR and Allow exclusion of chosen addresses from virtual router usage counters #14583 to 4.22.1.1, replaced the KVM plugin and engine-api jars on one host, and left the management servers stock. With a test VM on a shared network, 200 exempt pings (208,400 bytes each way) came off the usage the management server stored for that VM, to the byte. After rolling back, the stored figures kept rising with no reset. Sending the list from the server is covered by unit tests.

How did you try to break this feature and the system with this change?

  • Ran nft flush ruleset between stats collections. The sync rebuilt the table and the exempt bytes already counted stayed off.
  • Restarted the agent before the server sent the list. It kept using the existing table, so the reported figures didn't jump up and then drop.
  • Removed a tap and created a new one with the same name. The new tap started from zero and the old chains were removed.
  • Fed the agent unreadable nft output. That collection kept the previous exempt counts and the stats request still succeeded.
  • Cleared the setting. Traffic already exempted stayed exempt and nothing new was counted.
  • With the setting unused, the agent doesn't run nft at all.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature - Allow excluding destinations from network usage

1 participant