network: support setting firewallBackend from network.conf

It still can have only one useful value ("iptables"), but once a 2nd
value is supported, it will be selectable by setting
"firewall_backend=nftables" in /etc/libvirt/network.conf.

If firewall_backend isn't set in network.conf, then libvirt will check
to see if FIREWALL_BACKEND_DEFAULT_1 is available and, if so, set
that. (Since FIREWALL_BACKEND_DEFAULT_1 is currently "iptables", this
means checking to see it the iptables binary is present on the
system).  If the default backend isn't available, that is considered a
fatal error (since no networks can be started anyway), so an error is
logged and startup of the network driver fails.

NB: network.conf is itself created from network.conf.in at build time,
and the advertised default setting of firewall_backend (in a commented
out line) is set from the meson_options.txt setting
"firewall_backend_default_1". This way the conf file will have correct
information no matter what ordering is chosen for default backend at
build time (as more backends are added, settings will be added for
"firewall_backend_default_n", and those will be settable in
meson_options.txt and on the meson commandline to change the ordering
of the auto-detection when no backend is set in network.conf).

virNetworkLoadDriverConfig() may look more complicated than necessary,
but as additional backends are added, it will be easier to add checks
for those backends (and to re-order the checks based on builders'
preferences).

Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Laine Stump 2024-04-19 22:19:42 -04:00
parent 45c4527f36
commit 64b966558c
13 changed files with 134 additions and 28 deletions

View File

@ -1638,6 +1638,10 @@ endif
if not get_option('driver_network').disabled() and conf.has('WITH_LIBVIRTD') if not get_option('driver_network').disabled() and conf.has('WITH_LIBVIRTD')
conf.set('WITH_NETWORK', 1) conf.set('WITH_NETWORK', 1)
firewall_backend_default_1 = get_option('firewall_backend_default_1')
firewall_backend_default_conf = firewall_backend_default_1
firewall_backend_default_1 = 'VIR_FIREWALL_BACKEND_' + firewall_backend_default_1.to_upper()
conf.set('FIREWALL_BACKEND_DEFAULT_1', firewall_backend_default_1)
elif get_option('driver_network').enabled() elif get_option('driver_network').enabled()
error('libvirtd must be enabled to build the network driver') error('libvirtd must be enabled to build the network driver')
endif endif

View File

@ -115,6 +115,7 @@ option('dtrace', type: 'feature', value: 'auto', description: 'use dtrace for st
option('firewalld', type: 'feature', value: 'auto', description: 'firewalld support') option('firewalld', type: 'feature', value: 'auto', description: 'firewalld support')
# dep:firewalld # dep:firewalld
option('firewalld_zone', type: 'feature', value: 'auto', description: 'whether to install firewalld libvirt zone') option('firewalld_zone', type: 'feature', value: 'auto', description: 'whether to install firewalld libvirt zone')
option('firewall_backend_default_1', type: 'string', value: 'iptables', description: 'first firewall backend to try when none is specified')
option('host_validate', type: 'feature', value: 'auto', description: 'build virt-host-validate') option('host_validate', type: 'feature', value: 'auto', description: 'build virt-host-validate')
option('init_script', type: 'combo', choices: ['systemd', 'openrc', 'check', 'none'], value: 'check', description: 'Style of init script to install') option('init_script', type: 'combo', choices: ['systemd', 'openrc', 'check', 'none'], value: 'check', description: 'Style of init script to install')
option('loader_nvram', type: 'string', value: '', description: 'Pass list of pairs of <loader>:<nvram> paths. Both pairs and list items are separated by a colon.') option('loader_nvram', type: 'string', value: '', description: 'Pass list of pairs of <loader>:<nvram> paths. Both pairs and list items are separated by a colon.')

View File

@ -1682,6 +1682,7 @@ static int
networkReloadFirewallRulesHelper(virNetworkObj *obj, networkReloadFirewallRulesHelper(virNetworkObj *obj,
void *opaque G_GNUC_UNUSED) void *opaque G_GNUC_UNUSED)
{ {
g_autoptr(virNetworkDriverConfig) cfg = virNetworkDriverGetConfig(networkGetDriver());
VIR_LOCK_GUARD lock = virObjectLockGuard(obj); VIR_LOCK_GUARD lock = virObjectLockGuard(obj);
virNetworkDef *def = virNetworkObjGetDef(obj); virNetworkDef *def = virNetworkObjGetDef(obj);
@ -1695,8 +1696,8 @@ networkReloadFirewallRulesHelper(virNetworkObj *obj,
* network type, forward='open', doesn't need this because it * network type, forward='open', doesn't need this because it
* has no iptables rules. * has no iptables rules.
*/ */
networkRemoveFirewallRules(def); networkRemoveFirewallRules(def, cfg->firewallBackend);
ignore_value(networkAddFirewallRules(def)); ignore_value(networkAddFirewallRules(def, cfg->firewallBackend));
break; break;
case VIR_NETWORK_FORWARD_OPEN: case VIR_NETWORK_FORWARD_OPEN:
@ -1948,7 +1949,7 @@ networkStartNetworkVirtual(virNetworkDriverState *driver,
/* Add "once per network" rules */ /* Add "once per network" rules */
if (def->forward.type != VIR_NETWORK_FORWARD_OPEN && if (def->forward.type != VIR_NETWORK_FORWARD_OPEN &&
networkAddFirewallRules(def) < 0) networkAddFirewallRules(def, cfg->firewallBackend) < 0)
goto error; goto error;
firewalRulesAdded = true; firewalRulesAdded = true;
@ -2066,7 +2067,7 @@ networkStartNetworkVirtual(virNetworkDriverState *driver,
if (firewalRulesAdded && if (firewalRulesAdded &&
def->forward.type != VIR_NETWORK_FORWARD_OPEN) def->forward.type != VIR_NETWORK_FORWARD_OPEN)
networkRemoveFirewallRules(def); networkRemoveFirewallRules(def, cfg->firewallBackend);
virNetworkObjUnrefMacMap(obj); virNetworkObjUnrefMacMap(obj);
@ -2078,7 +2079,8 @@ networkStartNetworkVirtual(virNetworkDriverState *driver,
static int static int
networkShutdownNetworkVirtual(virNetworkObj *obj) networkShutdownNetworkVirtual(virNetworkObj *obj,
virNetworkDriverConfig *cfg)
{ {
virNetworkDef *def = virNetworkObjGetDef(obj); virNetworkDef *def = virNetworkObjGetDef(obj);
pid_t dnsmasqPid; pid_t dnsmasqPid;
@ -2104,7 +2106,7 @@ networkShutdownNetworkVirtual(virNetworkObj *obj)
ignore_value(virNetDevSetOnline(def->bridge, false)); ignore_value(virNetDevSetOnline(def->bridge, false));
if (def->forward.type != VIR_NETWORK_FORWARD_OPEN) if (def->forward.type != VIR_NETWORK_FORWARD_OPEN)
networkRemoveFirewallRules(def); networkRemoveFirewallRules(def, cfg->firewallBackend);
ignore_value(virNetDevBridgeDelete(def->bridge)); ignore_value(virNetDevBridgeDelete(def->bridge));
@ -2408,7 +2410,7 @@ networkShutdownNetwork(virNetworkDriverState *driver,
case VIR_NETWORK_FORWARD_NAT: case VIR_NETWORK_FORWARD_NAT:
case VIR_NETWORK_FORWARD_ROUTE: case VIR_NETWORK_FORWARD_ROUTE:
case VIR_NETWORK_FORWARD_OPEN: case VIR_NETWORK_FORWARD_OPEN:
ret = networkShutdownNetworkVirtual(obj); ret = networkShutdownNetworkVirtual(obj, cfg);
break; break;
case VIR_NETWORK_FORWARD_BRIDGE: case VIR_NETWORK_FORWARD_BRIDGE:
@ -3259,7 +3261,7 @@ networkUpdate(virNetworkPtr net,
* old rules (and remember to load new ones after the * old rules (and remember to load new ones after the
* update). * update).
*/ */
networkRemoveFirewallRules(def); networkRemoveFirewallRules(def, cfg->firewallBackend);
needFirewallRefresh = true; needFirewallRefresh = true;
break; break;
default: default:
@ -3287,14 +3289,14 @@ networkUpdate(virNetworkPtr net,
parentIndex, xml, parentIndex, xml,
network_driver->xmlopt, flags) < 0) { network_driver->xmlopt, flags) < 0) {
if (needFirewallRefresh) if (needFirewallRefresh)
ignore_value(networkAddFirewallRules(def)); ignore_value(networkAddFirewallRules(def, cfg->firewallBackend));
goto cleanup; goto cleanup;
} }
/* @def is replaced */ /* @def is replaced */
def = virNetworkObjGetDef(obj); def = virNetworkObjGetDef(obj);
if (needFirewallRefresh && networkAddFirewallRules(def) < 0) if (needFirewallRefresh && networkAddFirewallRules(def, cfg->firewallBackend) < 0)
goto cleanup; goto cleanup;
if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) { if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) {

View File

@ -25,6 +25,7 @@
#include "datatypes.h" #include "datatypes.h"
#include "virlog.h" #include "virlog.h"
#include "virerror.h" #include "virerror.h"
#include "virfile.h"
#include "virutil.h" #include "virutil.h"
#include "bridge_driver_conf.h" #include "bridge_driver_conf.h"
@ -32,7 +33,6 @@
VIR_LOG_INIT("network.bridge_driver"); VIR_LOG_INIT("network.bridge_driver");
static virClass *virNetworkDriverConfigClass; static virClass *virNetworkDriverConfigClass;
static void virNetworkDriverConfigDispose(void *obj); static void virNetworkDriverConfigDispose(void *obj);
@ -62,10 +62,14 @@ virNetworkLoadDriverConfig(virNetworkDriverConfig *cfg G_GNUC_UNUSED,
const char *filename) const char *filename)
{ {
g_autoptr(virConf) conf = NULL; g_autoptr(virConf) conf = NULL;
g_autofree char *fwBackendStr = NULL;
bool fwBackendSelected = false;
size_t i;
int fwBackends[] = { FIREWALL_BACKEND_DEFAULT_1 };
G_STATIC_ASSERT(G_N_ELEMENTS(fwBackends) == VIR_FIREWALL_BACKEND_LAST);
int nFwBackends = G_N_ELEMENTS(fwBackends);
/* if file doesn't exist or is unreadable, ignore the "error" */ if (access(filename, R_OK) == 0) {
if (access(filename, R_OK) == -1)
return 0;
conf = virConfReadFile(filename, 0); conf = virConfReadFile(filename, 0);
if (!conf) if (!conf)
@ -73,7 +77,61 @@ virNetworkLoadDriverConfig(virNetworkDriverConfig *cfg G_GNUC_UNUSED,
/* use virConfGetValue*(conf, ...) functions to read any settings into cfg */ /* use virConfGetValue*(conf, ...) functions to read any settings into cfg */
if (virConfGetValueString(conf, "firewall_backend", &fwBackendStr) < 0)
return -1;
if (fwBackendStr) {
fwBackends[0] = virFirewallBackendTypeFromString(fwBackendStr);
nFwBackends = 1;
if (fwBackends[0] < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unrecognized 'firewall_backend = '%1$s' set in network driver config file %2$s"),
fwBackendStr, filename);
return -1;
}
VIR_INFO("firewall_backend setting requested from config file %s: '%s'",
virFirewallBackendTypeToString(fwBackends[0]), filename);
}
}
for (i = 0; i < nFwBackends && !fwBackendSelected; i++) {
switch ((virFirewallBackend)fwBackends[i]) {
case VIR_FIREWALL_BACKEND_IPTABLES: {
g_autofree char *iptablesInPath = virFindFileInPath(IPTABLES);
if (iptablesInPath)
fwBackendSelected = true;
break;
}
case VIR_FIREWALL_BACKEND_LAST:
virReportEnumRangeError(virFirewallBackend, fwBackends[i]);
return -1;
}
if (fwBackendSelected)
cfg->firewallBackend = fwBackends[i];
}
if (fwBackendSelected) {
VIR_INFO("using firewall_backend: '%s'",
virFirewallBackendTypeToString(cfg->firewallBackend));
return 0; return 0;
} else if (fwBackendStr) {
/* the explicitly requested backend wasn't found - this is a failure */
virReportError(VIR_ERR_INTERNAL_ERROR,
_("requested firewall_backend '%1$s' is not available"),
fwBackendStr);
return -1;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("could not find a usable firewall backend"));
return -1;
}
} }

View File

@ -26,6 +26,7 @@
#include "virdnsmasq.h" #include "virdnsmasq.h"
#include "virnetworkobj.h" #include "virnetworkobj.h"
#include "object_event.h" #include "object_event.h"
#include "virfirewall.h"
typedef struct _virNetworkDriverConfig virNetworkDriverConfig; typedef struct _virNetworkDriverConfig virNetworkDriverConfig;
struct _virNetworkDriverConfig { struct _virNetworkDriverConfig {
@ -37,6 +38,8 @@ struct _virNetworkDriverConfig {
char *stateDir; char *stateDir;
char *pidDir; char *pidDir;
char *dnsmasqStateDir; char *dnsmasqStateDir;
virFirewallBackend firewallBackend;
}; };
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNetworkDriverConfig, virObjectUnref); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNetworkDriverConfig, virObjectUnref);

View File

@ -303,7 +303,8 @@ int networkCheckRouteCollision(virNetworkDef *def)
int int
networkAddFirewallRules(virNetworkDef *def) networkAddFirewallRules(virNetworkDef *def,
virFirewallBackend firewallBackend G_GNUC_UNUSED)
{ {
if (virOnce(&createdOnce, networkSetupPrivateChains) < 0) if (virOnce(&createdOnce, networkSetupPrivateChains) < 0)
return -1; return -1;
@ -394,7 +395,8 @@ networkAddFirewallRules(virNetworkDef *def)
void void
networkRemoveFirewallRules(virNetworkDef *def) networkRemoveFirewallRules(virNetworkDef *def,
virFirewallBackend firewallBackend G_GNUC_UNUSED)
{ {
iptablesRemoveFirewallRules(def); iptablesRemoveFirewallRules(def);
} }

View File

@ -36,11 +36,13 @@ int networkCheckRouteCollision(virNetworkDef *def G_GNUC_UNUSED)
return 0; return 0;
} }
int networkAddFirewallRules(virNetworkDef *def G_GNUC_UNUSED) int networkAddFirewallRules(virNetworkDef *def G_GNUC_UNUSED,
virFirewallBackend firewallBackend G_GNUC_UNUSED)
{ {
return 0; return 0;
} }
void networkRemoveFirewallRules(virNetworkDef *def G_GNUC_UNUSED) void networkRemoveFirewallRules(virNetworkDef *def G_GNUC_UNUSED,
virFirewallBackend firewallBackend G_GNUC_UNUSED)
{ {
} }

View File

@ -32,6 +32,8 @@ void networkPostReloadFirewallRules(bool startup);
int networkCheckRouteCollision(virNetworkDef *def); int networkCheckRouteCollision(virNetworkDef *def);
int networkAddFirewallRules(virNetworkDef *def); int networkAddFirewallRules(virNetworkDef *def,
virFirewallBackend firewallBackend);
void networkRemoveFirewallRules(virNetworkDef *def); void networkRemoveFirewallRules(virNetworkDef *def,
virFirewallBackend firewallBackend);

View File

@ -22,11 +22,14 @@ module Libvirtd_network =
let int_entry (kw:string) = [ key kw . value_sep . int_val ] let int_entry (kw:string) = [ key kw . value_sep . int_val ]
let str_array_entry (kw:string) = [ key kw . value_sep . str_array_val ] let str_array_entry (kw:string) = [ key kw . value_sep . str_array_val ]
let firewall_backend_entry = str_entry "firewall_backend"
(* Each entry in the config is one of the following *) (* Each entry in the config is one of the following *)
let entry = firewall_backend_entry
let comment = [ label "#comment" . del /#[ \t]*/ "# " . store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ] let comment = [ label "#comment" . del /#[ \t]*/ "# " . store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ]
let empty = [ label "#empty" . eol ] let empty = [ label "#empty" . eol ]
let record = indent . eol let record = indent . entry . eol
let lns = ( record | comment | empty ) * let lns = ( record | comment | empty ) *

View File

@ -49,16 +49,34 @@ if conf.has('WITH_NETWORK')
], ],
} }
network_options_conf = configuration_data({
'FIREWALL_BACKEND': firewall_backend_default_conf,
})
network_conf = configure_file( network_conf = configure_file(
input: 'network.conf.in', input: 'network.conf.in',
output: 'network.conf', output: 'network.conf',
configuration: configmake_conf, configuration: network_options_conf,
) )
network_options_hack_conf = configuration_data({
'FIREWALL_BACKEND': firewall_backend_default_conf,
# This hack is necessary because the output file is going to be
# used as input for another configure_file() call later, which
# will take care of substituting @CONFIG@ with useful data
'CONFIG': '@CONFIG@',
})
test_libvirtd_network_aug_tmp = configure_file(
input: 'test_libvirtd_network.aug.in',
output: 'test_libvirtd_network.aug.tmp',
configuration: network_options_hack_conf,
)
virt_conf_files += network_conf virt_conf_files += network_conf
virt_aug_files += files('libvirtd_network.aug') virt_aug_files += files('libvirtd_network.aug')
virt_test_aug_files += { virt_test_aug_files += {
'name': 'test_libvirtd_network.aug', 'name': 'test_libvirtd_network.aug',
'aug': files('test_libvirtd_network.aug.in'), 'aug': test_libvirtd_network_aug_tmp,
'conf': network_conf, 'conf': network_conf,
'test_name': 'libvirtd_network', 'test_name': 'libvirtd_network',
'test_srcdir': meson.current_source_dir(), 'test_srcdir': meson.current_source_dir(),

View File

@ -1,3 +1,11 @@
# Master configuration file for the network driver. # Master configuration file for the network driver.
# All settings described here are optional - if omitted, sensible # All settings described here are optional - if omitted, sensible
# defaults are used. # defaults are used.
# firewall_backend:
#
# determines which subsystem to use to setup firewall packet
# filtering rules for virtual networks. Currently the only supported
# selection is "iptables".
#
#firewall_backend = "@FIREWALL_BACKEND@"

View File

@ -1,2 +1,5 @@
module Test_libvirtd_network = module Test_libvirtd_network =
@CONFIG@ @CONFIG@
test Libvirtd_network.lns get conf =
{ "firewall_backend" = "@FIREWALL_BACKEND@" }

View File

@ -98,7 +98,7 @@ static int testCompareXMLToArgvFiles(const char *xml,
if (!(def = virNetworkDefParse(NULL, xml, NULL, false))) if (!(def = virNetworkDefParse(NULL, xml, NULL, false)))
return -1; return -1;
if (networkAddFirewallRules(def) < 0) if (networkAddFirewallRules(def, VIR_FIREWALL_BACKEND_IPTABLES) < 0)
return -1; return -1;
actual = actualargv = virBufferContentAndReset(&buf); actual = actualargv = virBufferContentAndReset(&buf);