mirror of
https://github.com/systemd/systemd.git
synced 2026-08-09 17:38:42 +00:00
test: add a mock ModemManager for basic coverage of sd-networkd's integration
Just the minimal setup and test case required to cover https://github.com/systemd/systemd/issues/41389 for now, can be expanded in the future Boring boilerplate is bot-made, don't @ me Co-developed-by: Claude Opus 4.6 noreply@anthropic.com
This commit is contained in:
@@ -258,6 +258,12 @@ executables += [
|
||||
network_test_template + {
|
||||
'sources' : files('test-networkd-util.c'),
|
||||
},
|
||||
test_template + {
|
||||
'sources' : files('test-modem-manager-mock.c'),
|
||||
'conditions' : ['ENABLE_NETWORKD'],
|
||||
'link_with' : [libshared],
|
||||
'type' : 'manual',
|
||||
},
|
||||
network_fuzz_template + {
|
||||
'sources' : files('fuzz-netdev-parser.c'),
|
||||
},
|
||||
|
||||
486
src/network/test-modem-manager-mock.c
Normal file
486
src/network/test-modem-manager-mock.c
Normal file
@@ -0,0 +1,486 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
/*
|
||||
* Minimal mock of ModemManager's D-Bus interface for testing systemd-networkd
|
||||
* wwan/bearer support.
|
||||
*
|
||||
* Claims the org.freedesktop.ModemManager1 bus name and responds to:
|
||||
* - GetManagedObjects on /org/freedesktop/ModemManager1
|
||||
* - GetAll on /org/freedesktop/ModemManager1/Bearer/0
|
||||
* - Simple.Connect on /org/freedesktop/ModemManager1/Modem/0
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
#include "sd-daemon.h"
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "build.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static char *arg_ifname = NULL;
|
||||
static char *arg_ipv4_address = NULL;
|
||||
static char *arg_ipv4_gateway = NULL;
|
||||
static uint32_t arg_ipv4_prefix = 24;
|
||||
static char *arg_ipv6_address = NULL;
|
||||
static char *arg_ipv6_gateway = NULL;
|
||||
static uint32_t arg_ipv6_prefix = 64;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_ifname, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_ipv4_address, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_ipv4_gateway, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_ipv6_address, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_ipv6_gateway, freep);
|
||||
|
||||
/* ModemManager enum values */
|
||||
#define MM_BEARER_IP_METHOD_STATIC 2
|
||||
#define MM_MODEM_PORT_TYPE_NET 2
|
||||
#define MM_MODEM_STATE_CONNECTED 11
|
||||
|
||||
static int append_bearer_properties(sd_bus_message *reply) {
|
||||
int r;
|
||||
|
||||
/* a{sv} of bearer properties */
|
||||
r = sd_bus_message_open_container(reply, 'a', "{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Interface */
|
||||
r = sd_bus_message_append(reply, "{sv}", "Interface", "s", arg_ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Connected */
|
||||
r = sd_bus_message_append(reply, "{sv}", "Connected", "b", true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Ip4Config: a{sv} */
|
||||
if (arg_ipv4_address) {
|
||||
r = sd_bus_message_open_container(reply, 'e', "sv");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append_basic(reply, 's', "Ip4Config");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_open_container(reply, 'v', "a{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_open_container(reply, 'a', "{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "method", "u", (uint32_t) MM_BEARER_IP_METHOD_STATIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "address", "s", arg_ipv4_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "prefix", "u", arg_ipv4_prefix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "gateway", "s", arg_ipv4_gateway);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "mtu", "u", (uint32_t) 1500);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* a{sv} */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* v */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* e */
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Ip6Config: a{sv} */
|
||||
if (arg_ipv6_address) {
|
||||
r = sd_bus_message_open_container(reply, 'e', "sv");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append_basic(reply, 's', "Ip6Config");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_open_container(reply, 'v', "a{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_open_container(reply, 'a', "{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "method", "u", (uint32_t) MM_BEARER_IP_METHOD_STATIC);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "address", "s", arg_ipv6_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "prefix", "u", arg_ipv6_prefix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "gateway", "s", arg_ipv6_gateway);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "mtu", "u", (uint32_t) 1500);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* a{sv} */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* v */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* e */
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Properties: a{sv} with apn */
|
||||
r = sd_bus_message_open_container(reply, 'e', "sv");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append_basic(reply, 's', "Properties");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_open_container(reply, 'v', "a{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_open_container(reply, 'a', "{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "{sv}", "apn", "s", "internet.test");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* a{sv} */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* v */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* e */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(reply); /* outer a{sv} */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_get_managed_objects(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_new_method_return(msg, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* a{oa{sa{sv}}} */
|
||||
r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Modem object */
|
||||
r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append_basic(reply, 'o', "/org/freedesktop/ModemManager1/Modem/0");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Array of interfaces */
|
||||
r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* org.freedesktop.ModemManager1.Modem interface */
|
||||
r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append_basic(reply, 's', "org.freedesktop.ModemManager1.Modem");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Modem properties: a{sv} */
|
||||
r = sd_bus_message_open_container(reply, 'a', "{sv}");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Bearers: ao */
|
||||
r = sd_bus_message_append(reply, "{sv}", "Bearers", "ao", 1, "/org/freedesktop/ModemManager1/Bearer/0");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* State: i (CONNECTED) */
|
||||
r = sd_bus_message_append(reply, "{sv}", "State", "i", (int32_t) MM_MODEM_STATE_CONNECTED);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* StateFailedReason: u (NONE) */
|
||||
r = sd_bus_message_append(reply, "{sv}", "StateFailedReason", "u", (uint32_t) 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Manufacturer */
|
||||
r = sd_bus_message_append(reply, "{sv}", "Manufacturer", "s", "MockModem");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Model */
|
||||
r = sd_bus_message_append(reply, "{sv}", "Model", "s", "Virtual");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Ports: a(su) — array of structs with port name and type */
|
||||
r = sd_bus_message_open_container(reply, 'e', "sv");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append_basic(reply, 's', "Ports");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_open_container(reply, 'v', "a(su)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_open_container(reply, 'a', "(su)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_append(reply, "(su)", arg_ifname, (uint32_t) MM_MODEM_PORT_TYPE_NET);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* a(su) */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* v */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* e */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(reply); /* modem properties a{sv} */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* e sa{sv} */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* a{sa{sv}} */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* e oa{sa{sv}} */
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_bus_message_close_container(reply); /* a{oa{sa{sv}}} */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_send(NULL, reply, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1; /* handled */
|
||||
}
|
||||
|
||||
static int handle_get_all(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_new_method_return(msg, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* bearer_get_all_handler() in networkd expects a leading interface name string
|
||||
* before the a{sv} properties dict (it calls sd_bus_message_skip(message, "s")). */
|
||||
r = sd_bus_message_append_basic(reply, 's', "org.freedesktop.ModemManager1.Bearer");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = append_bearer_properties(reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_send(NULL, reply, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1; /* handled */
|
||||
}
|
||||
|
||||
static int handle_simple_connect(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
int r;
|
||||
|
||||
/* Return the bearer path */
|
||||
r = sd_bus_message_new_method_return(msg, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(reply, "o", "/org/freedesktop/ModemManager1/Bearer/0");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_send(NULL, reply, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1; /* handled */
|
||||
}
|
||||
|
||||
static int filter_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) {
|
||||
const char *path, *interface, *member;
|
||||
uint8_t type;
|
||||
|
||||
if (sd_bus_message_get_type(m, &type) < 0 || type != SD_BUS_MESSAGE_METHOD_CALL)
|
||||
return 0;
|
||||
|
||||
path = sd_bus_message_get_path(m);
|
||||
interface = sd_bus_message_get_interface(m);
|
||||
member = sd_bus_message_get_member(m);
|
||||
|
||||
if (!path || !interface || !member)
|
||||
return 0;
|
||||
|
||||
if (streq(path, "/org/freedesktop/ModemManager1") &&
|
||||
streq(interface, "org.freedesktop.DBus.ObjectManager") &&
|
||||
streq(member, "GetManagedObjects"))
|
||||
return handle_get_managed_objects(m, userdata, error);
|
||||
|
||||
if (startswith(path, "/org/freedesktop/ModemManager1/Bearer/") &&
|
||||
streq(interface, "org.freedesktop.DBus.Properties") &&
|
||||
streq(member, "GetAll"))
|
||||
return handle_get_all(m, userdata, error);
|
||||
|
||||
if (startswith(path, "/org/freedesktop/ModemManager1/Modem/") &&
|
||||
streq(interface, "org.freedesktop.ModemManager1.Modem.Simple") &&
|
||||
streq(member, "Connect"))
|
||||
return handle_simple_connect(m, userdata, error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_IFNAME = 0x100,
|
||||
ARG_IPV4_ADDRESS,
|
||||
ARG_IPV4_GATEWAY,
|
||||
ARG_IPV4_PREFIX,
|
||||
ARG_IPV6_ADDRESS,
|
||||
ARG_IPV6_GATEWAY,
|
||||
ARG_IPV6_PREFIX,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "ifname", required_argument, NULL, ARG_IFNAME },
|
||||
{ "ipv4-address", required_argument, NULL, ARG_IPV4_ADDRESS },
|
||||
{ "ipv4-gateway", required_argument, NULL, ARG_IPV4_GATEWAY },
|
||||
{ "ipv4-prefix", required_argument, NULL, ARG_IPV4_PREFIX },
|
||||
{ "ipv6-address", required_argument, NULL, ARG_IPV6_ADDRESS },
|
||||
{ "ipv6-gateway", required_argument, NULL, ARG_IPV6_GATEWAY },
|
||||
{ "ipv6-prefix", required_argument, NULL, ARG_IPV6_PREFIX },
|
||||
{ "version", no_argument, NULL, 'v' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "vh", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
case ARG_IFNAME:
|
||||
if (free_and_strdup(&arg_ifname, optarg) < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
case ARG_IPV4_ADDRESS:
|
||||
if (free_and_strdup(&arg_ipv4_address, optarg) < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
case ARG_IPV4_GATEWAY:
|
||||
if (free_and_strdup(&arg_ipv4_gateway, optarg) < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
case ARG_IPV4_PREFIX:
|
||||
r = safe_atou32(optarg, &arg_ipv4_prefix);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse IPv4 prefix length: %m");
|
||||
break;
|
||||
case ARG_IPV6_ADDRESS:
|
||||
if (free_and_strdup(&arg_ipv6_address, optarg) < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
case ARG_IPV6_GATEWAY:
|
||||
if (free_and_strdup(&arg_ipv6_gateway, optarg) < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
case ARG_IPV6_PREFIX:
|
||||
r = safe_atou32(optarg, &arg_ipv6_prefix);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse IPv6 prefix length: %m");
|
||||
break;
|
||||
case 'v':
|
||||
return version();
|
||||
case 'h':
|
||||
printf("Usage: %s [OPTIONS...]\n\n"
|
||||
"Mock ModemManager D-Bus service for testing.\n\n"
|
||||
" --ifname=NAME Interface name\n"
|
||||
" --ipv4-address=ADDR IPv4 address\n"
|
||||
" --ipv4-gateway=ADDR IPv4 gateway\n"
|
||||
" --ipv4-prefix=LEN IPv4 prefix length\n"
|
||||
" --ipv6-address=ADDR IPv6 address\n"
|
||||
" --ipv6-gateway=ADDR IPv6 gateway\n"
|
||||
" --ipv6-prefix=LEN IPv6 prefix length\n"
|
||||
" -h, --help Show this help\n"
|
||||
" -v, --version Show version\n",
|
||||
program_invocation_short_name);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!arg_ifname)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--ifname is required");
|
||||
|
||||
return 1; /* work to do */
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_new(&event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create event loop: %m");
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to connect to system bus: %m");
|
||||
|
||||
r = sd_bus_add_filter(bus, NULL, filter_handler, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add filter: %m");
|
||||
|
||||
r = sd_bus_request_name(bus, "org.freedesktop.ModemManager1", 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire bus name: %m");
|
||||
|
||||
r = sd_bus_attach_event(bus, event, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach bus to event loop: %m");
|
||||
|
||||
(void) sd_notify(0, "READY=1");
|
||||
|
||||
return sd_event_loop(event);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
@@ -23,6 +23,7 @@ foreach testcase : [
|
||||
'NetworkdIPv6PrefixTests',
|
||||
'NetworkdMTUTests',
|
||||
'NetworkdSysctlTest',
|
||||
'NetworkdWWANTests',
|
||||
]
|
||||
integration_tests += [
|
||||
integration_test_template + {
|
||||
|
||||
12
test/test-network/conf/25-wwan-ipv4v6.network
Normal file
12
test/test-network/conf/25-wwan-ipv4v6.network
Normal file
@@ -0,0 +1,12 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=dummy98
|
||||
|
||||
[Network]
|
||||
LLDP=no
|
||||
LinkLocalAddressing=no
|
||||
IPv6AcceptRA=no
|
||||
|
||||
[MobileNetwork]
|
||||
APN=internet.test
|
||||
IPFamily=both
|
||||
13
test/test-network/conf/mock-modem-manager.conf
Normal file
13
test/test-network/conf/mock-modem-manager.conf
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
<!-- Allow root to own and interact with the mock ModemManager service -->
|
||||
<policy user="root">
|
||||
<allow own="org.freedesktop.ModemManager1"/>
|
||||
<allow send_destination="org.freedesktop.ModemManager1"/>
|
||||
</policy>
|
||||
<policy context="default">
|
||||
<allow send_destination="org.freedesktop.ModemManager1"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
@@ -73,6 +73,7 @@ resolvectl_bin = shutil.which('resolvectl', path=which_paths)
|
||||
timedatectl_bin = shutil.which('timedatectl', path=which_paths)
|
||||
udevadm_bin = shutil.which('udevadm', path=which_paths)
|
||||
test_ndisc_send = None
|
||||
test_modem_manager_mock = None
|
||||
build_dir = None
|
||||
source_dir = None
|
||||
|
||||
@@ -973,6 +974,28 @@ def start_radvd(*additional_options, config_file):
|
||||
def stop_radvd():
|
||||
stop_by_pid_file(radvd_pid_file)
|
||||
|
||||
def start_modem_manager_mock(*additional_options):
|
||||
dbus_policy_src = os.path.join(networkd_ci_temp_dir, 'mock-modem-manager.conf')
|
||||
cp(dbus_policy_src, '/etc/dbus-1/system.d/mock-modem-manager.conf')
|
||||
check_output('systemctl reload dbus.service')
|
||||
|
||||
command = ' '.join([test_modem_manager_mock] + list(additional_options))
|
||||
with open('/run/systemd/system/test-modem-manager-mock.service', mode='w', encoding='utf-8') as f:
|
||||
f.write('[Unit]\n'
|
||||
'Description=Mock ModemManager for networkd testing\n'
|
||||
'[Service]\n'
|
||||
'Type=notify\n'
|
||||
'BusName=org.freedesktop.ModemManager1\n'
|
||||
f'ExecStart={command}\n')
|
||||
check_output('systemctl daemon-reload')
|
||||
check_output('systemctl start test-modem-manager-mock.service')
|
||||
|
||||
def stop_modem_manager_mock():
|
||||
call('systemctl stop test-modem-manager-mock.service')
|
||||
rm_f('/run/systemd/system/test-modem-manager-mock.service')
|
||||
call('systemctl daemon-reload')
|
||||
rm_f('/etc/dbus-1/system.d/mock-modem-manager.conf')
|
||||
|
||||
def radvd_check_config(config_file):
|
||||
if not shutil.which('radvd'):
|
||||
print('radvd is not installed, assuming the config check failed')
|
||||
@@ -1099,6 +1122,7 @@ def tear_down_common():
|
||||
stop_dnsmasq()
|
||||
stop_isc_dhcpd()
|
||||
stop_radvd()
|
||||
stop_modem_manager_mock()
|
||||
|
||||
# 2. remove modules
|
||||
call_quiet('rmmod netdevsim')
|
||||
@@ -9567,6 +9591,54 @@ class NetworkdSysctlTest(unittest.TestCase, Utilities):
|
||||
self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/max_addresses'", log)
|
||||
self.assertNotIn("Sysctl monitor BPF returned error", log)
|
||||
|
||||
class NetworkdWWANTests(unittest.TestCase, Utilities):
|
||||
|
||||
def setUp(self):
|
||||
setup_common()
|
||||
|
||||
def tearDown(self):
|
||||
tear_down_common()
|
||||
|
||||
def test_wwan_ipv4v6_static(self):
|
||||
"""Test WWAN bearer with both IPv4 and IPv6 static configuration.
|
||||
|
||||
Regression test for https://github.com/systemd/systemd/issues/41389
|
||||
"""
|
||||
if not os.path.exists(test_modem_manager_mock):
|
||||
self.skipTest(f'{test_modem_manager_mock} does not exist.')
|
||||
|
||||
copy_network_unit('12-dummy.netdev', '25-wwan-ipv4v6.network')
|
||||
try:
|
||||
start_modem_manager_mock(
|
||||
'--ifname', 'dummy98',
|
||||
'--ipv4-address', '100.120.244.160',
|
||||
'--ipv4-gateway', '100.120.244.161',
|
||||
'--ipv4-prefix', '26',
|
||||
'--ipv6-address', '2001:db8::1',
|
||||
'--ipv6-gateway', '2001:db8::2',
|
||||
'--ipv6-prefix', '64',
|
||||
)
|
||||
except (subprocess.CalledProcessError, PermissionError, OSError) as e:
|
||||
self.skipTest(f'Failed to start mock ModemManager: {e}')
|
||||
start_networkd()
|
||||
self.wait_online('dummy98:routable')
|
||||
|
||||
output = check_output('ip -4 address show dev dummy98')
|
||||
print(output)
|
||||
self.assertIn('100.120.244.160/26', output)
|
||||
|
||||
output = check_output('ip -6 address show dev dummy98')
|
||||
print(output)
|
||||
self.assertIn('2001:db8::1/64', output)
|
||||
|
||||
output = check_output('ip -4 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertIn('default via 100.120.244.161', output)
|
||||
|
||||
output = check_output('ip -6 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertIn('default via 2001:db8::2', output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
|
||||
@@ -9626,6 +9698,11 @@ if __name__ == '__main__':
|
||||
else:
|
||||
test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
|
||||
|
||||
if build_dir:
|
||||
test_modem_manager_mock = os.path.normpath(os.path.join(build_dir, 'test-modem-manager-mock'))
|
||||
else:
|
||||
test_modem_manager_mock = '/usr/lib/systemd/tests/unit-tests/manual/test-modem-manager-mock'
|
||||
|
||||
if asan_options:
|
||||
env.update({'ASAN_OPTIONS': asan_options})
|
||||
if lsan_options:
|
||||
|
||||
Reference in New Issue
Block a user