summary refs log tree commit diff
path: root/src/core/ppp/nm-ppp-mgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/ppp/nm-ppp-mgr.c')
-rw-r--r--src/core/ppp/nm-ppp-mgr.c620
1 files changed, 620 insertions, 0 deletions
diff --git a/src/core/ppp/nm-ppp-mgr.c b/src/core/ppp/nm-ppp-mgr.c
new file mode 100644
index 00000000..31c173a3
--- /dev/null
+++ b/src/core/ppp/nm-ppp-mgr.c
@@ -0,0 +1,620 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "src/core/nm-default-daemon.h"
+
+#include "nm-ppp-mgr.h"
+
+#include <net/if.h>
+
+#include "NetworkManagerUtils.h"
+#include "devices/nm-device-utils.h"
+#include "nm-act-request.h"
+#include "nm-netns.h"
+#include "nm-ppp-manager-call.h"
+#include "nm-ppp-status.h"
+
+/*****************************************************************************/
+
+struct _NMPppMgr {
+    NMPppMgrConfig config;
+    NMPPPManager  *ppp_manager;
+    GSource       *idle_start;
+    GSource       *connect_timeout_source;
+    union {
+        struct {
+            NMPppMgrIPData ip_data_6;
+            NMPppMgrIPData ip_data_4;
+        };
+        NMPppMgrIPData ip_data_x[2];
+    };
+    int                ifindex;
+    NMPppMgrStatsData  stats_data;
+    NMPppMgrState      state;
+    NMUtilsIPv6IfaceId ipv6_iid;
+    bool               ppp_started : 1;
+};
+
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN      LOGD_PPP
+#define _NMLOG_PREFIX_NAME "ppp-mgr"
+#define _NMLOG(level, ...) \
+    __NMLOG_DEFAULT_WITH_ADDR(level, _NMLOG_DOMAIN, _NMLOG_PREFIX_NAME, __VA_ARGS__)
+
+/*****************************************************************************/
+
+NM_UTILS_LOOKUP_STR_DEFINE(
+    nm_ppp_mgr_state_to_string,
+    NMPppMgrState,
+    NM_UTILS_LOOKUP_DEFAULT_WARN("???"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_STARTING, "starting"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX, "waiting-for-ifindex"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_HAVE_IFINDEX, "have-ifindex"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_HAVE_IP_CONFIG, "have-ip-config"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_FAILED_TO_START, "failed-to-start"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_FAILED_TO_IFINDEX, "failed-to-ifindex"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_STATE_FAILED, "failed"));
+
+NM_UTILS_LOOKUP_STR_DEFINE(nm_ppp_mgr_callback_type_to_string,
+                           NMPppMgrCallbackType,
+                           NM_UTILS_LOOKUP_DEFAULT_WARN("???"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED,
+                                                    "state-changed"),
+                           NM_UTILS_LOOKUP_STR_ITEM(NM_PPP_MGR_CALLBACK_TYPE_STATS_CHANGED,
+                                                    "stats-changed"));
+
+/*****************************************************************************/
+
+#define SELF_TO_USERDATA(self) (&(self)->idle_start)
+
+#define SELF_FROM_USERDATA(user_data) \
+    ((NMPppMgr *) (((const char *) (user_data)) - G_STRUCT_OFFSET(NMPppMgr, idle_start)))
+
+/*****************************************************************************/
+
+gboolean
+_nm_assert_is_ppp_mgr(const NMPppMgr *self)
+{
+    nm_assert(G_IS_OBJECT(self->ppp_manager));
+    nm_assert(NM_IS_NETNS(self->config.netns));
+    nm_assert(self->config.parent_iface);
+
+    return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+_callback_emit_with_data(NMPppMgr *self, const NMPppMgrCallbackData *callback_data)
+{
+    char sbuf_int[30];
+
+    switch (callback_data->callback_type) {
+    case NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED:
+        _LOGT("emit signal: %s, state=%s%s%s%s%s, reason=%s",
+              nm_ppp_mgr_callback_type_to_string(callback_data->callback_type),
+              nm_ppp_mgr_state_to_string(callback_data->data.state),
+              NM_PRINT_FMT_QUOTED2(callback_data->data.state != callback_data->data.old_state,
+                                   ", old-state=",
+                                   nm_ppp_mgr_state_to_string(callback_data->data.old_state),
+                                   ""),
+              NM_PRINT_FMT_QUOTED2(callback_data->data.ifindex > 0,
+                                   ", ifindex=",
+                                   nm_sprintf_buf(sbuf_int, "%d", callback_data->data.ifindex),
+                                   ""),
+              callback_data->data.reason_msg);
+        break;
+    case NM_PPP_MGR_CALLBACK_TYPE_STATS_CHANGED:
+        /* This signal might be emitted every 5 seconds. It's too verbose for logging.
+         * Be silent. */
+        break;
+    default:
+        nm_assert_not_reached();
+    }
+
+    self->config.callback(self, callback_data, self->config.user_data);
+}
+
+static void
+_callback_emit_state_change(NMPppMgr            *self,
+                            NMPppMgrCallbackType callback_type,
+                            NMPppMgrState        old_state,
+                            gboolean             ip_changed_4,
+                            gboolean             ip_changed_6,
+                            NMDeviceStateReason  reason,
+                            const char          *reason_msg)
+{
+    const NMPppMgrCallbackData callback_data = {
+        .callback_type = callback_type,
+        .data =
+            {
+                .old_state    = old_state,
+                .state        = self->state,
+                .ifindex      = self->ifindex,
+                .reason       = reason,
+                .reason_msg   = reason_msg,
+                .ip_data_4    = &self->ip_data_4,
+                .ip_data_6    = &self->ip_data_6,
+                .stats_data   = &self->stats_data,
+                .ip_changed_4 = ip_changed_4,
+                .ip_changed_6 = ip_changed_6,
+            },
+    };
+
+    _callback_emit_with_data(self, &callback_data);
+}
+
+/*****************************************************************************/
+
+NMPppMgrState
+nm_ppp_mgr_get_state(const NMPppMgr *self)
+{
+    g_return_val_if_fail(NM_IS_PPP_MGR(self), NM_PPP_MGR_STATE_FAILED);
+
+    return self->state;
+}
+
+int
+nm_ppp_mgr_get_ifindex(const NMPppMgr *self)
+{
+    g_return_val_if_fail(NM_IS_PPP_MGR(self), 0);
+
+    return self->ifindex;
+}
+
+const NMPppMgrIPData *
+nm_ppp_mgr_get_ip_data(const NMPppMgr *self, int addr_family)
+{
+    const int IS_IPv4 = NM_IS_IPv4(addr_family);
+
+    g_return_val_if_fail(NM_IS_PPP_MGR(self), NULL);
+
+    return &self->ip_data_x[IS_IPv4];
+}
+
+const NMPppMgrStatsData *
+nm_ppp_mgr_get_stats(const NMPppMgr *self)
+{
+    g_return_val_if_fail(NM_IS_PPP_MGR(self), NULL);
+
+    return &self->stats_data;
+}
+
+/*****************************************************************************/
+
+static void
+_set_state(NMPppMgr *self, NMPppMgrState state, NMPppMgrState *out_old_state)
+{
+    if (state >= NM_PPP_MGR_STATE_HAVE_IP_CONFIG)
+        nm_clear_g_source_inst(&self->connect_timeout_source);
+
+    NM_SET_OUT(out_old_state, self->state);
+    if (self->state != state) {
+        _LOGT("set state: %s (was %s)",
+              nm_ppp_mgr_state_to_string(state),
+              nm_ppp_mgr_state_to_string(self->state));
+        self->state = state;
+    }
+}
+
+static void
+_set_state_failed(NMPppMgr *self, NMPppMgrState state, NMPppMgrState *out_old_state)
+{
+    nm_assert(state >= _NM_PPP_MGR_STATE_FAILED_START);
+    nm_assert(self->state < _NM_PPP_MGR_STATE_FAILED_START);
+
+    _set_state(self, state, out_old_state);
+
+    self->ifindex = 0;
+    nm_clear_l3cd(&self->ip_data_4.l3cd);
+    nm_clear_l3cd(&self->ip_data_6.l3cd);
+    self->ip_data_4 = (NMPppMgrIPData){
+        .ip_received = FALSE,
+        .ip_enabled  = FALSE,
+    };
+    self->ip_data_6 = (NMPppMgrIPData){
+        .ip_received = FALSE,
+        .ip_enabled  = FALSE,
+    };
+
+    if (self->ppp_manager) {
+        g_signal_handlers_disconnect_by_data(self->ppp_manager, SELF_TO_USERDATA(self));
+        if (self->ppp_started) {
+            self->ppp_started = FALSE;
+            nm_ppp_manager_stop(self->ppp_manager, NULL, NULL, NULL);
+        }
+        g_object_unref(self->ppp_manager);
+    }
+}
+
+static gboolean
+_state_ready_for_have_ifindex(NMPppMgr *self)
+{
+    if (self->ip_data_4.ip_received) {
+        /* once we receive an IPv4 config, we consider this as ready.
+         *
+         * The problem is that we don't know when we can expect an IPv6 config
+         * too, so we cannot just keep waiting. I don't know how to solve this, but
+         * it means IPv4+IPv6 together doesn't work well (because we would not
+         * wait for IPv6, once IPv4 config is received. */
+        return TRUE;
+    }
+
+    return ((!self->ip_data_4.ip_enabled || self->ip_data_4.ip_received)
+            && (!self->ip_data_6.ip_enabled || self->ip_data_6.ip_received));
+}
+
+/*****************************************************************************/
+
+static void
+_ppp_signal_state_changed(NMPPPManager *ppp_manager, guint ppp_state_u, gpointer user_data)
+{
+    NMPppMgr           *self      = SELF_FROM_USERDATA(user_data);
+    NMPPPStatus         ppp_state = ppp_state_u;
+    NMPppMgrState       state;
+    NMPppMgrState       old_state;
+    NMDeviceStateReason reason;
+    const char         *reason_msg;
+
+    if ((guint) ppp_state != ppp_state_u)
+        ppp_state = NM_PPP_STATUS_UNKNOWN;
+
+    switch (ppp_state) {
+    case NM_PPP_STATUS_DISCONNECT:
+        state      = NM_PPP_MGR_STATE_FAILED;
+        reason     = NM_DEVICE_STATE_REASON_PPP_DISCONNECT;
+        reason_msg = "ppp signals disconnect";
+        break;
+    case NM_PPP_STATUS_DEAD:
+    case NM_PPP_STATUS_INTERN_DEAD:
+        state      = NM_PPP_MGR_STATE_FAILED;
+        reason     = NM_DEVICE_STATE_REASON_PPP_FAILED;
+        reason_msg = "ppp signals disconnect";
+        break;
+    default:
+        _LOGT("ppp signal about state changed: #%u signal (ignored)", ppp_state_u);
+        return;
+    }
+
+    _LOGT("ppp signal about state changed: #%u signal, new-state %s, state-reason=%s, %s",
+          ppp_state_u,
+          nm_ppp_mgr_state_to_string(state),
+          nm_device_state_reason_to_string(reason),
+          reason_msg);
+
+    _set_state_failed(self, state, &old_state);
+    _callback_emit_state_change(self,
+                                NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED,
+                                old_state,
+                                FALSE,
+                                FALSE,
+                                reason,
+                                reason_msg);
+}
+
+static void
+_ppp_signal_ifindex_set(NMPPPManager *ppp_manager,
+                        int           ifindex,
+                        const char   *ifname,
+                        gpointer      user_data)
+{
+    NMPppMgr     *self = SELF_FROM_USERDATA(user_data);
+    NMPppMgrState old_state;
+    NMPppMgrState new_state;
+    gboolean      ip_changed_4;
+    gboolean      ip_changed_6;
+
+    if (ifindex <= 0) {
+        nm_assert(self->state == NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX);
+        _LOGT("ifindex set: invalid ifindex received");
+        _set_state_failed(self, NM_PPP_MGR_STATE_FAILED_TO_IFINDEX, &old_state);
+        _callback_emit_state_change(self,
+                                    NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED,
+                                    old_state,
+                                    FALSE,
+                                    FALSE,
+                                    NM_DEVICE_STATE_REASON_PPP_FAILED,
+                                    "invalid ifindex provided by ppp plugin");
+        return;
+    }
+
+    if (self->ifindex > 0) {
+        nm_assert(self->state > NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX);
+        nm_assert(self->state < _NM_PPP_MGR_STATE_FAILED_START);
+
+        _LOGT("ifindex set: ignore ifindex %d, already set to %d", ifindex, self->ifindex);
+        return;
+    }
+
+    nm_assert(self->state == NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX);
+    self->ifindex = ifindex;
+
+    if (_state_ready_for_have_ifindex(self)) {
+        new_state    = NM_PPP_MGR_STATE_HAVE_IP_CONFIG;
+        ip_changed_4 = self->ip_data_4.ip_received;
+        ip_changed_6 = self->ip_data_6.ip_received;
+    } else {
+        new_state    = NM_PPP_MGR_STATE_HAVE_IFINDEX;
+        ip_changed_4 = FALSE;
+        ip_changed_6 = FALSE;
+    }
+
+    _LOGT("ifindex set: ifindex %d", ifindex);
+
+    _set_state(self, new_state, &old_state);
+    _callback_emit_state_change(self,
+                                NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED,
+                                old_state,
+                                ip_changed_4,
+                                ip_changed_6,
+                                NM_DEVICE_STATE_REASON_PPP_FAILED,
+                                "invalid ifindex provided by ppp plugin");
+}
+
+static void
+_ppp_signal_new_config(NMPPPManager             *ppp_manager,
+                       int                       addr_family,
+                       const NML3ConfigData     *l3cd,
+                       const NMUtilsIPv6IfaceId *iid,
+                       gpointer                  user_data)
+{
+    NMPppMgr     *self    = SELF_FROM_USERDATA(user_data);
+    const int     IS_IPv4 = NM_IS_IPv4(addr_family);
+    NMPppMgrState old_state;
+    gboolean      ip_changed_4;
+    gboolean      ip_changed_6;
+
+    nm_assert(self->state >= NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX);
+    nm_assert(self->state < _NM_PPP_MGR_STATE_FAILED_START);
+
+    if (nm_l3_config_data_equal(self->ip_data_x[IS_IPv4].l3cd, l3cd))
+        l3cd = self->ip_data_x[IS_IPv4].l3cd;
+
+    nm_l3_config_data_reset(&self->ip_data_x[IS_IPv4].l3cd, l3cd);
+    if (!IS_IPv4) {
+        if (iid)
+            self->ipv6_iid = *iid;
+        else
+            self->ipv6_iid = (NMUtilsIPv6IfaceId) NM_UTILS_IPV6_IFACE_ID_INIT;
+        self->ip_data_6.ipv6_iid =
+            nm_utils_memeqzero(&self->ipv6_iid, sizeof(self->ipv6_iid)) ? NULL : &self->ipv6_iid;
+    }
+    self->ip_data_x[IS_IPv4].ip_received = TRUE;
+
+    if (self->state == NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX) {
+        /* we still wait for the ifindex. We just cache the IP configuration,
+         * but leave the state unchanged. */
+        _LOGT("ip-config v%c received (still waiting for ifindex)",
+              nm_utils_addr_family_to_char(addr_family));
+        old_state = NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX;
+    } else {
+        NMPppMgrState new_state;
+
+        _LOGT("ip-config v%c received", nm_utils_addr_family_to_char(addr_family));
+        new_state = _state_ready_for_have_ifindex(self) ? NM_PPP_MGR_STATE_HAVE_IP_CONFIG
+                                                        : NM_PPP_MGR_STATE_HAVE_IFINDEX;
+        nm_assert((self->state == NM_PPP_MGR_STATE_HAVE_IFINDEX)
+                  || (self->state == NM_PPP_MGR_STATE_HAVE_IP_CONFIG
+                      && new_state == NM_PPP_MGR_STATE_HAVE_IP_CONFIG));
+        _set_state(self, new_state, &old_state);
+    }
+
+    ip_changed_4 = IS_IPv4;
+    ip_changed_6 = !IS_IPv4;
+
+    _callback_emit_state_change(self,
+                                NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED,
+                                old_state,
+                                ip_changed_4,
+                                ip_changed_6,
+                                NM_DEVICE_STATE_REASON_NONE,
+                                "ip config received");
+}
+
+static void
+_ppp_signal_stats(NMPPPManager *ppp_manager, guint in_bytes, guint out_bytes, gpointer user_data)
+{
+    NMPppMgr *self = SELF_FROM_USERDATA(user_data);
+
+    if (self->stats_data.in_bytes == in_bytes && self->stats_data.out_bytes == out_bytes)
+        return;
+
+    self->stats_data.in_bytes  = in_bytes;
+    self->stats_data.out_bytes = out_bytes;
+
+    _callback_emit_state_change(self,
+                                NM_PPP_MGR_CALLBACK_TYPE_STATS_CHANGED,
+                                self->state,
+                                FALSE,
+                                FALSE,
+                                NM_DEVICE_STATE_REASON_NONE,
+                                "stats update");
+}
+
+/*****************************************************************************/
+
+static gboolean
+_ifindex_timeout_cb(gpointer user_data)
+{
+    NMPppMgr     *self = user_data;
+    NMPppMgrState old_state;
+
+    nm_clear_g_source_inst(&self->connect_timeout_source);
+
+    _set_state_failed(self, NM_PPP_MGR_STATE_FAILED_TO_IFINDEX, &old_state);
+    _callback_emit_state_change(self,
+                                NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED,
+                                old_state,
+                                FALSE,
+                                FALSE,
+                                NM_DEVICE_STATE_REASON_PPP_FAILED,
+                                "timeout connecting");
+    return G_SOURCE_CONTINUE;
+}
+
+/*****************************************************************************/
+
+static gboolean
+_idle_start_cb(gpointer user_data)
+{
+    NMPppMgr             *self  = user_data;
+    gs_free_error GError *error = NULL;
+    NMPppMgrState         old_state;
+    gboolean              ip4_enabled;
+    gboolean              ip6_enabled;
+    NMPlatform           *platform;
+    const NMPlatformLink *plink;
+
+    nm_clear_g_source_inst(&self->idle_start);
+
+    /* We only evaluate ipx_enabled here. That is because nm_ppp_manager_start()
+     * will evaluate it based on act_req's applied connection (like we do now),
+     * but as the applied connection can be reapplied, let's do it at the
+     * same time to be sure we agree.
+     *
+     * This should be nicer solved by NMPPPManager not accessing the NMConnection
+     * or make little/no use of NMActRequest. */
+    nm_utils_ppp_ip_methods_enabled(nm_act_request_get_applied_connection(self->config.act_req),
+                                    &ip4_enabled,
+                                    &ip6_enabled);
+    self->ip_data_4.ip_enabled = !!ip4_enabled;
+    self->ip_data_6.ip_enabled = !!ip6_enabled;
+
+    g_signal_connect(self->ppp_manager,
+                     NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
+                     G_CALLBACK(_ppp_signal_state_changed),
+                     SELF_TO_USERDATA(self));
+    g_signal_connect(self->ppp_manager,
+                     NM_PPP_MANAGER_SIGNAL_IFINDEX_SET,
+                     G_CALLBACK(_ppp_signal_ifindex_set),
+                     SELF_TO_USERDATA(self));
+    g_signal_connect(self->ppp_manager,
+                     NM_PPP_MANAGER_SIGNAL_NEW_CONFIG,
+                     G_CALLBACK(_ppp_signal_new_config),
+                     SELF_TO_USERDATA(self));
+    g_signal_connect(self->ppp_manager,
+                     NM_PPP_MANAGER_SIGNAL_STATS,
+                     G_CALLBACK(_ppp_signal_stats),
+                     SELF_TO_USERDATA(self));
+
+    platform = nm_netns_get_platform(self->config.netns);
+    plink    = nm_platform_link_get_by_ifname(platform, self->config.parent_iface);
+    if (plink && !NM_FLAGS_HAS(plink->n_ifi_flags, IFF_UP)) {
+        nm_platform_link_change_flags(platform, plink->ifindex, IFF_UP, TRUE);
+    }
+
+    self->ppp_started = TRUE;
+    if (!nm_ppp_manager_start(self->ppp_manager,
+                              self->config.act_req,
+                              self->config.ppp_username,
+                              0,
+                              self->config.baud_override,
+                              &error)) {
+        gs_free char *reason_msg = NULL;
+
+        _set_state_failed(self, NM_PPP_MGR_STATE_FAILED_TO_START, &old_state);
+        reason_msg = g_strdup_printf("failed to start: %s", error->message);
+        _callback_emit_state_change(self,
+                                    NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED,
+                                    old_state,
+                                    FALSE,
+                                    FALSE,
+                                    NM_DEVICE_STATE_REASON_PPP_START_FAILED,
+                                    reason_msg);
+        return G_SOURCE_CONTINUE;
+    }
+
+    nm_assert(self->state == NM_PPP_MGR_STATE_STARTING);
+    _set_state(self, NM_PPP_MGR_STATE_WAITING_FOR_IFINDEX, &old_state);
+
+    self->connect_timeout_source =
+        nm_g_timeout_add_seconds_source(self->config.timeout_secs, _ifindex_timeout_cb, self);
+
+    _callback_emit_state_change(self,
+                                NM_PPP_MGR_CALLBACK_TYPE_STATE_CHANGED,
+                                old_state,
+                                FALSE,
+                                FALSE,
+                                NM_DEVICE_STATE_REASON_NONE,
+                                "pppd is starting");
+
+    return G_SOURCE_CONTINUE;
+}
+
+/*****************************************************************************/
+
+NMPppMgr *
+nm_ppp_mgr_start(const NMPppMgrConfig *config, GError **error)
+{
+    NMPppMgr     *self;
+    NMPPPManager *ppp_manager;
+
+    g_return_val_if_fail(config, NULL);
+    g_return_val_if_fail(NM_IS_NETNS(config->netns), NULL);
+    g_return_val_if_fail(config->parent_iface, NULL);
+    g_return_val_if_fail(NM_IS_ACT_REQUEST(config->act_req), NULL);
+    g_return_val_if_fail(config->callback, NULL);
+    g_return_val_if_fail(!error || !*error, NULL);
+
+    ppp_manager = nm_ppp_manager_create(config->parent_iface, error);
+
+    if (!ppp_manager)
+        return NULL;
+
+    self = g_slice_new(NMPppMgr);
+
+    *self = (NMPppMgr){
+        .config      = *config,
+        .ppp_manager = ppp_manager,
+        .idle_start  = nm_g_idle_add_source(_idle_start_cb, self),
+        .state       = NM_PPP_MGR_STATE_STARTING,
+        .ip_data_4 =
+            {
+                .ip_received = FALSE,
+                .ip_enabled  = NM_OPTION_BOOL_DEFAULT,
+            },
+        .ip_data_6 =
+            {
+                .ip_received = FALSE,
+                .ip_enabled  = NM_OPTION_BOOL_DEFAULT,
+            },
+        .stats_data =
+            {
+                .in_bytes  = 0,
+                .out_bytes = 0,
+            },
+    };
+
+    g_object_ref(self->config.act_req);
+    g_object_ref(self->config.netns);
+    self->config.parent_iface = g_strdup(self->config.parent_iface);
+    self->config.ppp_username = g_strdup(self->config.ppp_username);
+
+    _LOGD("created");
+
+    return self;
+}
+
+void
+nm_ppp_mgr_destroy(NMPppMgr *self)
+{
+    if (!self)
+        return;
+
+    _LOGD("destroying");
+
+    if (self->state < _NM_PPP_MGR_STATE_FAILED_START)
+        _set_state_failed(self, NM_PPP_MGR_STATE_FAILED, NULL);
+
+    nm_clear_g_source_inst(&self->idle_start);
+    nm_clear_g_source_inst(&self->connect_timeout_source);
+
+    g_object_unref(self->config.act_req);
+
+    g_free((char *) self->config.parent_iface);
+    g_free((char *) self->config.ppp_username);
+
+    g_object_unref(self->config.netns);
+
+    nm_g_slice_free(self);
+}