Skip to content

Commit 1b4f601

Browse files
committed
disconnect and reactivate Wi-Fi connection on startup and shutdown
iwd, when managed by NetworkManager, does not automatically disconnect from the network when we tell NetworkManager to unmanage the network, unlike wpa_supplicant. A workaround for this is to just tell NetworkManager to disconnect from the network, and it will tell iwd to do so. We also store the network configuration until (our) shutdown so it can be reapplied, as NetworkManager doesn't seem to autoconnect to the same network after you disconnect, even if it has stopped and started managing the network.
1 parent 8fedcce commit 1b4f601

File tree

3 files changed

+217
-63
lines changed

3 files changed

+217
-63
lines changed

pipe/linux/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ set_target_properties(wpa_client PROPERTIES
5757
add_dependencies(wpa_client wpa_client_build)
5858

5959
find_package(PkgConfig REQUIRED)
60-
pkg_check_modules(LIBSYSTEMD REQUIRED libsystemd>=229)
60+
pkg_check_modules(LIBSYSTEMD REQUIRED libsystemd>=221)
6161

6262
# Link our library with the client library
6363
target_link_libraries(vanilla-pipe PRIVATE

pipe/linux/wpa.c

Lines changed: 211 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <libgen.h>
66
#include <pthread.h>
77
#include <signal.h>
8+
#include <stdbool.h>
89
#include <stdio.h>
910
#include <stdlib.h>
1011
#include <string.h>
@@ -16,6 +17,7 @@
1617
#include <sys/wait.h>
1718
#include <systemd/sd-bus-protocol.h>
1819
#include <systemd/sd-bus.h>
20+
#include <systemd/sd-event.h>
1921
#include <unistd.h>
2022
#include <wpa_ctrl.h>
2123

@@ -29,6 +31,7 @@
2931
#define DBUS_PROP_IFACE "org.freedesktop.DBus.Properties"
3032
#define NM_BUS_NAME "org.freedesktop.NetworkManager"
3133
#define NM_BUS_DEVICE_IFACE NM_BUS_NAME ".Device"
34+
#define NM_BUS_PATH "/org/freedesktop/NetworkManager"
3235

3336
const char *wpa_ctrl_interface = "/var/run/wpa_supplicant_drc";
3437

@@ -345,25 +348,63 @@ void *read_stdin(void *)
345348
return NULL;
346349
}
347350

351+
static int get_networkmanager_device_path(sd_bus *bus, const char *wireless_interface, char **ret_path)
352+
{
353+
int r;
354+
355+
CLEANUP(sd_bus_message_unrefp) sd_bus_message *response = NULL;
356+
if ((r = sd_bus_call_method(bus, NM_BUS_NAME, NM_BUS_PATH, NM_BUS_NAME, "GetDeviceByIpIface", NULL, &response, "s", wireless_interface)) < 0)
357+
return r;
358+
359+
char *path;
360+
if ((r = sd_bus_message_read_basic(response, SD_BUS_TYPE_OBJECT_PATH, &path)) < 0)
361+
return r;
362+
363+
if ((*ret_path = strdup(path)) == NULL) {
364+
return -ENOMEM;
365+
}
366+
367+
return 0;
368+
}
369+
348370
void *wpa_setup_environment(void *data)
349371
{
350372
void *ret = THREADRESULT(VANILLA_ERROR);
351373

352374
struct sync_args *args = (struct sync_args *) data;
353375

354-
// Check status of interface with NetworkManager
355-
int is_managed = 0;
356-
if (is_networkmanager_managing_device(args->wireless_interface, &is_managed) != VANILLA_SUCCESS) {
357-
print_info("FAILED TO DETERMINE MANAGED STATE OF WIRELESS INTERFACE");
358-
//goto die;
359-
}
376+
CLEANUP(sd_event_unrefp) sd_event *bus_loop = NULL;
377+
if (sd_event_new(&bus_loop) < 0)
378+
print_info("FAILED TO CREATE EVENT LOOP");
360379

361-
// If NetworkManager is managing this device, temporarily stop it from doing so
362-
if (is_managed) {
363-
if (disable_networkmanager_on_device(args->wireless_interface) != VANILLA_SUCCESS) {
364-
print_info("FAILED TO SET %s TO UNMANAGED, RESULTS MAY BE UNPREDICTABLE");
380+
CLEANUP(sd_bus_unrefp) sd_bus *bus = NULL;
381+
if (bus_loop && sd_bus_default_system(&bus) < 0)
382+
print_info("FAILED TO CONNECT TO SYSTEM BUS, EXPECT THINGS TO NOT WORK");
383+
384+
if (bus && sd_bus_attach_event(bus, bus_loop, SD_EVENT_PRIORITY_NORMAL) < 0)
385+
print_info("FAILED TO ATTACH BUS TO EVENT LOOP");
386+
387+
CLEANUP(freep) char *connection = NULL;
388+
CLEANUP(freep) char *nm_device = NULL;
389+
int is_managed = 0;
390+
391+
if (bus) {
392+
if (get_networkmanager_device_path(bus, args->wireless_interface, &nm_device) < 0) {
393+
print_info("FAILED TO CONNECT TO NETWORKMANAGER, RESULTS MAY BE UNPREDICTABLE");
365394
} else {
366-
print_info("TEMPORARILY SET %s TO UNMANAGED", args->wireless_interface);
395+
// Check status of interface with NetworkManager
396+
if (is_networkmanager_managing_device(bus, nm_device, &is_managed) != VANILLA_SUCCESS) {
397+
print_info("FAILED TO DETERMINE MANAGED STATE OF WIRELESS INTERFACE");
398+
}
399+
400+
// If NetworkManager is managing this device, temporarily stop it from doing so
401+
if (is_managed) {
402+
if (disable_networkmanager_on_device(bus, bus_loop, nm_device, &connection) != VANILLA_SUCCESS) {
403+
print_info("FAILED TO SET %s TO UNMANAGED, RESULTS MAY BE UNPREDICTABLE");
404+
} else {
405+
print_info("TEMPORARILY SET %s TO UNMANAGED", args->wireless_interface);
406+
}
407+
}
367408
}
368409
}
369410

@@ -408,7 +449,7 @@ void *wpa_setup_environment(void *data)
408449
die_and_reenable_managed:
409450
if (is_managed) {
410451
print_info("SETTING %s BACK TO MANAGED", args->wireless_interface);
411-
enable_networkmanager_on_device(args->wireless_interface);
452+
enable_networkmanager_on_device(bus, bus_loop, nm_device, connection);
412453
}
413454

414455
die:
@@ -456,87 +497,198 @@ int call_dhcp(const char *network_interface, pid_t *dhclient_pid)
456497
}
457498
}
458499

459-
static int get_networkmanager_device_path(sd_bus *bus, const char *wireless_interface, char **ret_path, sd_bus_error *ret_error)
500+
int is_networkmanager_managing_device(sd_bus *bus, const char *nm_device, int *is_managed)
460501
{
461-
int r;
462-
463-
CLEANUP(sd_bus_message_unrefp) sd_bus_message *response = NULL;
464-
if ((r = sd_bus_call_method(bus, NM_BUS_NAME, "/org/freedesktop/NetworkManager", NM_BUS_NAME, "GetDeviceByIpIface", ret_error, &response, "s", wireless_interface)) < 0)
465-
return r;
502+
CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
466503

467-
char *path;
468-
r = sd_bus_message_read_basic(response, SD_BUS_TYPE_OBJECT_PATH, &path);
469-
if ((*ret_path = strdup(path)) == NULL) {
470-
print_info("FAILED TO ALLOCATE MEMORY");
471-
return sd_bus_error_set_errno(ret_error, ENOMEM);
504+
if (sd_bus_get_property_trivial(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "Managed", &err, SD_BUS_TYPE_BOOLEAN, is_managed) < 0) {
505+
print_info("FAILED TO DETERMINE WHETHER NETWORKMANAGER IS MANAGING %s: %s", nm_device, err.message);
506+
return VANILLA_ERROR;
472507
}
473-
return r;
508+
509+
return VANILLA_SUCCESS;
474510
}
475511

476-
int is_networkmanager_managing_device(const char *wireless_interface, int *is_managed)
512+
static int bus_get_property_opath(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *err, char **ret)
477513
{
478514
int r;
479-
CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
480515

481-
CLEANUP(sd_bus_unrefp) sd_bus *bus = NULL;
482-
if ((r = sd_bus_default_system(&bus)) < 0) {
483-
print_info("FAILED TO CONNECT TO SYSTEM BUS: %i", -r);
484-
return VANILLA_ERROR;
485-
}
516+
CLEANUP(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
517+
if ((r = sd_bus_get_property(bus, destination, path, interface, member, err, &reply, "o")) < 0)
518+
return r;
486519

487-
CLEANUP(freep) char *path = NULL;
488-
if ((r = get_networkmanager_device_path(bus, wireless_interface, &path, &err)) == -EHOSTUNREACH) {
489-
// NetworkManager doesn't seem to be running
490-
print_info("FAILED TO CONNECT TO NETWORKMANAGER, RESULTS MAY BE UNPREDICTABLE");
491-
*is_managed = 0;
492-
return VANILLA_SUCCESS;
493-
} else if (r < 0)
520+
const char *s;
521+
if ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_OBJECT_PATH, &s)) < 0)
494522
goto fail;
495523

496-
if (sd_bus_get_property_trivial(bus, NM_BUS_NAME, path, NM_BUS_DEVICE_IFACE, "Managed", &err, SD_BUS_TYPE_BOOLEAN, is_managed) < 0)
524+
if (!(*ret = strdup(s))) {
525+
r = errno;
497526
goto fail;
527+
}
498528

499-
return VANILLA_SUCCESS;
529+
return 0;
500530

501531
fail:
502-
print_info("FAILED TO DETERMINE WHETHER NETWORKMANAGER IS MANAGING %s: %s", wireless_interface, err.message);
503-
return VANILLA_ERROR;
532+
return sd_bus_error_set_errno(err, r);
504533
}
505534

506-
static int set_networkmanager_on_device(const char *wireless_interface, int on)
535+
#define NM_DEVICE_STATE_DISCONNECTED 30
536+
#define NM_DEVICE_STATE_DEACTIVATING 110
537+
538+
static int _nm_bus_state_signal_cb(sd_bus_message *msg, void *data, sd_bus_error *err, bool connecting)
507539
{
540+
bool *complete = data;
541+
uint32_t state;
508542
int r;
509-
CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
510-
CLEANUP(sd_bus_message_unrefp) sd_bus_message *call = NULL;
511543

512-
CLEANUP(sd_bus_unrefp) sd_bus *bus = NULL;
513-
if ((r = sd_bus_default_system(&bus)) < 0) {
514-
print_info("FAILED TO CONNECT TO SYSTEM BUS: %i", -r);
515-
return VANILLA_ERROR;
544+
r = sd_bus_message_read(msg, "u", &state);
545+
if (r < 0)
546+
return sd_bus_error_set_errno(err, r);
547+
548+
if (connecting && state == NM_DEVICE_STATE_DISCONNECTED) {
549+
print_info("remanaged!");
550+
*complete = true;
551+
return 0;
552+
} else if (connecting)
553+
goto fail;
554+
else if (state == NM_DEVICE_STATE_DISCONNECTED) {
555+
print_info("disconnected!");
556+
*complete = true;
557+
return 0;
558+
} else if (state == NM_DEVICE_STATE_DEACTIVATING)
559+
/* It's still disconnecting, let's wait */
560+
return 0;
561+
else
562+
goto fail;
563+
564+
fail:
565+
print_info("UNEXPECTED NM STATUS: %u", state);
566+
return sd_bus_error_set_errno(err, EINVAL);
567+
}
568+
569+
static int nm_bus_state_signal_disconn_cb(sd_bus_message *msg, void *data, sd_bus_error *err)
570+
{
571+
return _nm_bus_state_signal_cb(msg, data, err, false);
572+
}
573+
574+
static int nm_bus_state_signal_conn_cb(sd_bus_message *msg, void *data, sd_bus_error *err)
575+
{
576+
return _nm_bus_state_signal_cb(msg, data, err, true);
577+
}
578+
579+
static int nm_bus_register_state_signal(sd_bus *bus, const char *path, bool *complete, sd_bus_message_handler_t callback, sd_bus_error *err, sd_bus_slot **ret_slot)
580+
{
581+
int r;
582+
583+
r = sd_bus_match_signal(bus, ret_slot, NM_BUS_NAME, path, NM_BUS_DEVICE_IFACE, "StateChanged", callback, complete);
584+
if (r < 0)
585+
return sd_bus_error_set_errno(err, r);
586+
587+
return 0;
588+
}
589+
590+
static uint64_t get_time_µsecs()
591+
{
592+
struct timespec cur_time;
593+
clock_gettime(CLOCK_MONOTONIC_COARSE, &cur_time);
594+
return cur_time.tv_sec * 1000 * 1000 + cur_time.tv_nsec / 1000;
595+
}
596+
597+
static int nm_bus_await_signal(sd_bus *bus, sd_event *loop, bool *complete, sd_bus_slot *slot, sd_bus_error *err)
598+
{
599+
/* Give it ten seconds, should it be longer? */
600+
const uint64_t time_allowed = 10 * 1000 * 1000;
601+
602+
const uint64_t deadline = get_time_µsecs() + time_allowed;
603+
int r = 0;
604+
605+
while (!complete) {
606+
const uint64_t timeout = (deadline - get_time_µsecs());
607+
if (timeout > time_allowed)
608+
/* underflow occured, i.e. we timed out */
609+
return sd_bus_error_set_errno(err, ETIMEDOUT);
610+
611+
r = sd_event_run(loop, timeout);
612+
if (r < 0)
613+
return sd_bus_error_set_errno(err, r);
516614
}
517615

518-
CLEANUP(freep) char *path = NULL;
519-
if (get_networkmanager_device_path(bus, wireless_interface, &path, &err) < 0)
616+
return 0;
617+
}
618+
619+
int disable_networkmanager_on_device(sd_bus *bus, sd_event* loop, const char *nm_device, char **ret_connection)
620+
{
621+
CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
622+
CLEANUP(sd_bus_slot_unrefp) sd_bus_slot *bus_slot = NULL;
623+
CLEANUP(freep) char *active_conn = NULL;
624+
625+
/* Get the active connection object of the Wi-Fi adapter */
626+
if (bus_get_property_opath(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "ActiveConnection", &err, &active_conn) < 0)
627+
goto fail;
628+
629+
/* Get the *actual* connection object from the active connection object */
630+
if (bus_get_property_opath(bus, NM_BUS_NAME, active_conn, NM_BUS_NAME ".Connection.Active", "Connection", &err, ret_connection) < 0)
631+
goto fail;
632+
633+
/* We have to wait for NetworkManager to fully "unmanage" the device before
634+
* we tell it to disconnect the device, otherwise it will ignore the latter
635+
* request, so iwd won't disconnect on systems where it is used. */
636+
637+
bool disconnect_done = false;
638+
639+
/* Set up signal handler so we can know exactly when NetworkManager has
640+
* finished disconnecting the device. */
641+
if (nm_bus_register_state_signal(bus, nm_device, &disconnect_done, nm_bus_state_signal_disconn_cb, &err, &bus_slot) < 0)
520642
goto fail;
521643

522-
if (sd_bus_set_property(bus, NM_BUS_NAME, path, NM_BUS_DEVICE_IFACE, "Managed", &err, "b", on) < 0)
644+
/* Tell NM to disconnect the device */
645+
if (sd_bus_call_method(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "Disconnect", &err, NULL, "") < 0)
646+
goto fail;
647+
648+
/* Wait for NM to have disconnected the device */
649+
if (nm_bus_await_signal(bus, loop, &disconnect_done, bus_slot, &err) < 0)
650+
goto fail;
651+
652+
/* Tell NM to unmanage the device */
653+
if (sd_bus_set_property(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "Managed", &err, "b", 0) < 0)
523654
goto fail;
524655

525656
return VANILLA_SUCCESS;
526657

527658
fail:
528-
print_info("FAILED TO SET MANAGEMENT OVER NETWORKMANAGER INTERFACE %s: %s", wireless_interface, err.message);
659+
print_info("FAILED TO DISABLE NETWORKMANAGER ON INTERFACE %s: %s", nm_device, err.message);
529660
return VANILLA_ERROR;
530661
}
531662

532-
int disable_networkmanager_on_device(const char *wireless_interface)
663+
int enable_networkmanager_on_device(sd_bus *bus, sd_event* loop, const char *nm_device, const char *resume_connection)
533664
{
534-
return set_networkmanager_on_device(wireless_interface, 0);
535-
}
665+
CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
666+
CLEANUP(sd_bus_slot_unrefp) sd_bus_slot *bus_slot = NULL;
667+
CLEANUP(freep) char *active_conn = NULL;
668+
bool manage_done = false;
536669

537-
int enable_networkmanager_on_device(const char *wireless_interface)
538-
{
539-
return set_networkmanager_on_device(wireless_interface, 1);
670+
/* Set up signal handler so we can know exactly when NetworkManager has
671+
* finished managing the device. */
672+
if (nm_bus_register_state_signal(bus, nm_device, &manage_done, nm_bus_state_signal_conn_cb, &err, &bus_slot) < 0)
673+
goto fail;
674+
675+
/* Tell NM to manage the device */
676+
if (sd_bus_set_property(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "Managed", &err, "b", 1) < 0)
677+
goto fail;
678+
679+
/* Wait for NM to have started managing the device */
680+
if (nm_bus_await_signal(bus, loop, &manage_done, bus_slot, &err) < 0)
681+
goto fail;
682+
683+
/* Finally, reconnect the device if it was originally connected */
684+
if (strcmp(resume_connection, "/") && sd_bus_call_method(bus, NM_BUS_NAME, NM_BUS_PATH, NM_BUS_NAME, "ActivateConnection", &err, NULL, "ooo", resume_connection, nm_device, "/") < 0)
685+
goto fail;
686+
687+
return VANILLA_SUCCESS;
688+
689+
fail:
690+
print_info("FAILED TO ENABLE NETWORKMANAGER ON INTERFACE %s: %s", nm_device, err.message);
691+
return VANILLA_ERROR;
540692
}
541693

542694
void *do_relay(void *data)

pipe/linux/wpa.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <stdint.h>
55
#include <stdlib.h>
66
#include <sys/types.h>
7+
#include <systemd/sd-bus.h>
8+
#include <systemd/sd-event.h>
79

810
struct wpa_ctrl;
911
extern const char *wpa_ctrl_interface;
@@ -13,9 +15,9 @@ int start_wpa_supplicant(const char *wireless_interface, const char *config_file
1315

1416
int call_dhcp(const char *network_interface, pid_t *dhclient_pid);
1517

16-
int is_networkmanager_managing_device(const char *wireless_interface, int *is_managed);
17-
int disable_networkmanager_on_device(const char *wireless_interface);
18-
int enable_networkmanager_on_device(const char *wireless_interface);
18+
int is_networkmanager_managing_device(sd_bus *bus, const char *wireless_interface, int *is_managed);
19+
int disable_networkmanager_on_device(sd_bus *bus, sd_event* loop, const char *wireless_interface, char **ret_connection);
20+
int enable_networkmanager_on_device(sd_bus *bus, sd_event* loop, const char *wireless_interface, const char *resume_connection);
1921

2022
void pprint(const char *fmt, ...);
2123

0 commit comments

Comments
 (0)