dhcp: merge branch 'th/dhcp-cleanup-1'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1219
This commit is contained in:
Thomas Haller 2022-05-16 21:45:09 +02:00
commit 76a0696882
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
9 changed files with 365 additions and 352 deletions

View file

@ -109,53 +109,53 @@ find NetworkManager bind mounted at $BASEDIR_NM
run \`nm-env-prepare.sh setup --idx 1\` to setup test interfaces
Configure NetworkManager with
\$ ./configure \
--enable-address-sanitizer=no \
--enable-compile-warnings=yes \
--enable-concheck \
--enable-config-plugin-ibft=yes \
--enable-gtk-doc \
--enable-ifcfg-rh=yes \
--enable-ifcfg-suse \
--enable-ifnet \
--enable-ifupdown=yes \
--enable-introspection \
--enable-json-validation=yes \
--enable-maintainer-mode \
--enable-more-logging \
--enable-more-warnings=error \
--enable-ovs=yes \
--enable-polkit=yes \
--enable-teamdctl=yes \
--enable-undefined-sanitizer=no \
--enable-vala=yes \
--enable-wimax \
--localstatedir=/var \
--prefix=/opt/test \
--sysconfdir=/etc \
--with-config-dhcp-default=internal \
--with-config-dns-rc-manager-default=auto \
--with-consolekit=yes \
--with-consolekit=yes \
--with-crypto=nss \
--with-dhclient=yes \
--with-dhcpcanon=yes \
--with-dhcpcd=yes \
--with-iwd=yes \
--with-libnm-glib=yes \
--with-modem-manager-1 \
--with-netconfig=/bin/nowhere/netconfig \
--with-nm-cloud-setup=yes \
--with-nmcli=yes \
--with-nmtui=yes \
--with-ofono=yes \
--with-resolvconf=/bin/nowhere/resolvconf \
--with-session-tracking=systemd \
--with-suspend-resume=systemd \
--with-systemd-logind=yes \
--with-valgrind=yes \
--enable-tests="\${NM_BUILD_TESTS:-yes}" \
--with-more-asserts="\${NM_BUILD_MORE_ASSERTS:-1000}" \
\$ ./configure \\
--enable-address-sanitizer=no \\
--enable-compile-warnings=yes \\
--enable-concheck \\
--enable-config-plugin-ibft=yes \\
--enable-gtk-doc \\
--enable-ifcfg-rh=yes \\
--enable-ifcfg-suse \\
--enable-ifnet \\
--enable-ifupdown=yes \\
--enable-introspection \\
--enable-json-validation=yes \\
--enable-maintainer-mode \\
--enable-more-logging \\
--enable-more-warnings=error \\
--enable-ovs=yes \\
--enable-polkit=yes \\
--enable-teamdctl=yes \\
--enable-undefined-sanitizer=no \\
--enable-vala=yes \\
--enable-wimax \\
--localstatedir=/var \\
--prefix=/opt/test \\
--sysconfdir=/etc \\
--with-config-dhcp-default=internal \\
--with-config-dns-rc-manager-default=auto \\
--with-consolekit=yes \\
--with-consolekit=yes \\
--with-crypto=nss \\
--with-dhclient=yes \\
--with-dhcpcanon=yes \\
--with-dhcpcd=yes \\
--with-iwd=yes \\
--with-libnm-glib=yes \\
--with-modem-manager-1 \\
--with-netconfig=/bin/nowhere/netconfig \\
--with-nm-cloud-setup=yes \\
--with-nmcli=yes \\
--with-nmtui=yes \\
--with-ofono=yes \\
--with-resolvconf=/bin/nowhere/resolvconf \\
--with-session-tracking=systemd \\
--with-suspend-resume=systemd \\
--with-systemd-logind=yes \\
--with-valgrind=yes \\
--enable-tests="\${NM_BUILD_TESTS:-yes}" \\
--with-more-asserts="\${NM_BUILD_MORE_ASSERTS:-1000}" \\
"\${NM_CONFIGURE_OTPS[@]}"
Test with:
\$ systemctl stop NetworkManager; /opt/test/sbin/NetworkManager --debug 2>&1 | tee -a /tmp/nm-log.txt
@ -327,8 +327,7 @@ RUN dnf install -y \\
RUN dnf debuginfo-install --skip-broken \$(ldd /usr/sbin/NetworkManager | sed -n 's/.* => \\(.*\\) (0x[0-9A-Fa-f]*)$/\1/p' | xargs -n1 readlink -f) -y
RUN pip3 install --user \\
behave_html_formatter
RUN pip3 install --user behave_html_formatter || true
RUN systemctl enable NetworkManager
@ -412,7 +411,7 @@ do_run() {
podman run --privileged \
--name "$CONTAINER_NAME_NAME" \
-d \
-v "$BASEDIR_NM:$BASEDIR_NM:Z" \
-v "$BASEDIR_NM:$BASEDIR_NM" \
"${BIND_NM_CI[@]}" \
"${BIND_FILES[@]}" \
"$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG"

View file

@ -32,7 +32,10 @@
/*****************************************************************************/
enum { SIGNAL_NOTIFY, LAST_SIGNAL };
enum {
SIGNAL_NOTIFY,
LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = {0};
@ -43,10 +46,9 @@ typedef struct _NMDhcpClientPrivate {
const NML3ConfigData *l3cd;
GSource *no_lease_timeout_source;
GSource *ipv6_lladdr_timeout_source;
GSource *watch_source;
GBytes *effective_client_id;
pid_t pid;
guint watch_id;
NMDhcpState state;
bool iaid_explicit : 1;
bool is_stopped : 1;
struct {
@ -72,6 +74,97 @@ G_STATIC_ASSERT(!(((pid_t) -1) > 0));
/*****************************************************************************/
NM_UTILS_LOOKUP_STR_DEFINE(nm_dhcp_client_event_type_to_string,
NMDhcpClientEventType,
NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(NULL),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_CLIENT_EVENT_TYPE_BOUND, "bound"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_CLIENT_EVENT_TYPE_EXPIRE, "expire"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED, "extended"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_CLIENT_EVENT_TYPE_FAIL, "fail"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_CLIENT_EVENT_TYPE_TERMINATED,
"terminated"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT, "timeout"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_CLIENT_EVENT_TYPE_UNSPECIFIED,
"unspecified"), );
/*****************************************************************************/
int
nm_dhcp_client_get_addr_family(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return priv->config.addr_family;
}
const char *
nm_dhcp_client_get_iface(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return priv->config.iface;
}
NMDedupMultiIndex *
nm_dhcp_client_get_multi_idx(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return nm_l3cfg_get_multi_idx(priv->config.l3cfg);
}
int
nm_dhcp_client_get_ifindex(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return nm_l3cfg_get_ifindex(priv->config.l3cfg);
}
const NMDhcpClientConfig *
nm_dhcp_client_get_config(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return &priv->config;
}
GBytes *
nm_dhcp_client_get_effective_client_id(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return priv->effective_client_id;
}
/*****************************************************************************/
void
nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
gs_free char *tmp_str = NULL;
g_return_if_fail(NM_IS_DHCP_CLIENT(self));
g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2);
priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
if (nm_g_bytes_equal0(priv->effective_client_id, client_id))
return;
g_bytes_unref(priv->effective_client_id);
priv->effective_client_id = nm_g_bytes_ref(client_id);
_LOGT("%s: set %s",
priv->config.addr_family == AF_INET6 ? "duid" : "client-id",
priv->effective_client_id
? (tmp_str = nm_dhcp_utils_duid_to_string(priv->effective_client_id))
: "default");
}
/*****************************************************************************/
static void
_emit_notify(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data)
{
@ -101,6 +194,8 @@ connect_l3cfg_notify(NMDhcpClient *self)
}
}
/*****************************************************************************/
pid_t
nm_dhcp_client_get_pid(NMDhcpClient *self)
{
@ -109,86 +204,12 @@ nm_dhcp_client_get_pid(NMDhcpClient *self)
return NM_DHCP_CLIENT_GET_PRIVATE(self)->pid;
}
void
nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
g_return_if_fail(NM_IS_DHCP_CLIENT(self));
g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2);
priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
if (nm_g_bytes_equal0(priv->effective_client_id, client_id))
return;
g_bytes_unref(priv->effective_client_id);
priv->effective_client_id = client_id;
if (client_id)
g_bytes_ref(client_id);
{
gs_free char *s = NULL;
_LOGT("%s: set %s",
priv->config.addr_family == AF_INET6 ? "duid" : "client-id",
priv->effective_client_id
? (s = nm_dhcp_utils_duid_to_string(priv->effective_client_id))
: "default");
}
}
/*****************************************************************************/
NM_UTILS_LOOKUP_STR_DEFINE(nm_dhcp_state_to_string,
NMDhcpState,
NM_UTILS_LOOKUP_DEFAULT(NULL),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_BOUND, "bound"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_DONE, "done"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_EXPIRE, "expire"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_EXTENDED, "extended"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_FAIL, "fail"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_NOOP, "noop"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_TERMINATED, "terminated"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_TIMEOUT, "timeout"),
NM_UTILS_LOOKUP_STR_ITEM(NM_DHCP_STATE_UNKNOWN, "unknown"), );
static NMDhcpState
reason_to_state(NMDhcpClient *self, const char *iface, const char *reason)
{
if (g_ascii_strcasecmp(reason, "bound") == 0 || g_ascii_strcasecmp(reason, "bound6") == 0
|| g_ascii_strcasecmp(reason, "static") == 0)
return NM_DHCP_STATE_BOUND;
else if (g_ascii_strcasecmp(reason, "renew") == 0 || g_ascii_strcasecmp(reason, "renew6") == 0
|| g_ascii_strcasecmp(reason, "reboot") == 0
|| g_ascii_strcasecmp(reason, "rebind") == 0
|| g_ascii_strcasecmp(reason, "rebind6") == 0)
return NM_DHCP_STATE_EXTENDED;
else if (g_ascii_strcasecmp(reason, "timeout") == 0)
return NM_DHCP_STATE_TIMEOUT;
else if (g_ascii_strcasecmp(reason, "nak") == 0 || g_ascii_strcasecmp(reason, "expire") == 0
|| g_ascii_strcasecmp(reason, "expire6") == 0)
return NM_DHCP_STATE_EXPIRE;
else if (g_ascii_strcasecmp(reason, "end") == 0 || g_ascii_strcasecmp(reason, "stop") == 0
|| g_ascii_strcasecmp(reason, "stopped") == 0)
return NM_DHCP_STATE_DONE;
else if (g_ascii_strcasecmp(reason, "fail") == 0 || g_ascii_strcasecmp(reason, "abend") == 0)
return NM_DHCP_STATE_FAIL;
else if (g_ascii_strcasecmp(reason, "preinit") == 0)
return NM_DHCP_STATE_NOOP;
_LOGD("unmapped DHCP state '%s'", reason);
return NM_DHCP_STATE_UNKNOWN;
}
/*****************************************************************************/
static void
watch_cleanup(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
nm_clear_g_source(&priv->watch_id);
nm_clear_g_source_inst(&priv->watch_source);
}
void
@ -225,6 +246,8 @@ stop(NMDhcpClient *self, gboolean release)
priv->pid = -1;
}
/*****************************************************************************/
static gboolean
_no_lease_timeout(gpointer user_data)
{
@ -237,19 +260,12 @@ _no_lease_timeout(gpointer user_data)
&((NMDhcpClientNotifyData){
.notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT,
}));
return G_SOURCE_CONTINUE;
}
const NMDhcpClientConfig *
nm_dhcp_client_get_config(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return &priv->config;
}
static void
schedule_no_lease_timeout(NMDhcpClient *self)
_no_lease_timeout_schedule(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
@ -267,29 +283,55 @@ schedule_no_lease_timeout(NMDhcpClient *self)
}
}
/*****************************************************************************/
void
nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3ConfigData *l3cd)
_nm_dhcp_client_notify(NMDhcpClient *self,
NMDhcpClientEventType client_event_type,
const NML3ConfigData *l3cd)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
GHashTable *options;
const int IS_IPv4 = NM_IS_IPv4(priv->config.addr_family);
nm_auto_unref_l3cd const NML3ConfigData *l3cd_merged = NULL;
char sbuf1[NM_HASH_OBFUSCATE_PTR_STR_BUF_SIZE];
g_return_if_fail(NM_IS_DHCP_CLIENT(self));
nm_assert(NM_IN_SET(client_event_type,
NM_DHCP_CLIENT_EVENT_TYPE_UNSPECIFIED,
NM_DHCP_CLIENT_EVENT_TYPE_BOUND,
NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED,
NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT,
NM_DHCP_CLIENT_EVENT_TYPE_EXPIRE,
NM_DHCP_CLIENT_EVENT_TYPE_FAIL,
NM_DHCP_CLIENT_EVENT_TYPE_TERMINATED));
nm_assert((client_event_type >= NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT)
== NM_IN_SET(client_event_type,
NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT,
NM_DHCP_CLIENT_EVENT_TYPE_EXPIRE,
NM_DHCP_CLIENT_EVENT_TYPE_FAIL,
NM_DHCP_CLIENT_EVENT_TYPE_TERMINATED));
nm_assert((!!l3cd)
== NM_IN_SET(client_event_type,
NM_DHCP_CLIENT_EVENT_TYPE_BOUND,
NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED));
if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) {
g_return_if_fail(NM_IS_L3_CONFIG_DATA(l3cd));
g_return_if_fail(nm_l3_config_data_get_dhcp_lease(l3cd, priv->config.addr_family));
} else
g_return_if_fail(!l3cd);
nm_assert(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd));
nm_assert(!l3cd || nm_l3_config_data_get_dhcp_lease(l3cd, priv->config.addr_family));
_LOGT("notify: event=%s%s%s",
nm_dhcp_client_event_type_to_string(client_event_type),
NM_PRINT_FMT_QUOTED2(l3cd, "", ", l3cd=", NM_HASH_OBFUSCATE_PTR_STR(l3cd, sbuf1)));
if (l3cd)
nm_l3_config_data_seal(l3cd);
if (new_state >= NM_DHCP_STATE_TIMEOUT)
if (client_event_type >= NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT)
watch_cleanup(self);
if (!IS_IPv4 && l3cd) {
/* nm_dhcp_utils_merge_new_dhcp6_lease() relies on "life_starts" option
* for merging, which is only set by dhclient. Internal client never sets that,
* but it supports multiple IP addresses per lease. */
if (nm_dhcp_utils_merge_new_dhcp6_lease(priv->l3cd, l3cd, &l3cd_merged)) {
_LOGD("lease merged with existing one");
l3cd = nm_l3_config_data_seal(l3cd_merged);
@ -303,11 +345,11 @@ nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3Co
nm_clear_g_source_inst(&priv->no_lease_timeout_source);
} else {
if (priv->l3cd)
schedule_no_lease_timeout(self);
_no_lease_timeout_schedule(self);
}
/* FIXME(l3cfg:dhcp): the API of NMDhcpClient is changing to expose a simpler API.
* The internals like NMDhcpState should not be exposed (or possibly dropped in large
* The internals like the state should not be exposed (or possibly dropped in large
* parts). */
nm_l3_config_data_reset(&priv->l3cd, l3cd);
@ -360,7 +402,8 @@ nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3Co
* as a static address (bypassing ACD), then NML3Cfg is aware of that and signals
* immediate success. */
if (nm_dhcp_client_can_accept(self) && new_state == NM_DHCP_STATE_BOUND && priv->l3cd
if (nm_dhcp_client_can_accept(self) && client_event_type == NM_DHCP_CLIENT_EVENT_TYPE_BOUND
&& priv->l3cd
&& nm_l3_config_data_get_num_addresses(priv->l3cd, priv->config.addr_family) > 0) {
priv->l3cfg_notify.wait_dhcp_commit = TRUE;
} else {
@ -389,14 +432,15 @@ daemon_watch_cb(GPid pid, int status, gpointer user_data)
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
gs_free char *desc = NULL;
g_return_if_fail(priv->watch_id);
priv->watch_id = 0;
g_return_if_fail(priv->watch_source);
priv->watch_source = NULL;
_LOGI("client pid %d %s", pid, (desc = nm_utils_get_process_exit_status_desc(status)));
priv->pid = -1;
nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL);
_nm_dhcp_client_notify(self, NM_DHCP_CLIENT_EVENT_TYPE_TERMINATED, NULL);
}
void
@ -407,8 +451,8 @@ nm_dhcp_client_watch_child(NMDhcpClient *self, pid_t pid)
g_return_if_fail(priv->pid == -1);
priv->pid = pid;
g_return_if_fail(priv->watch_id == 0);
priv->watch_id = g_child_watch_add(pid, daemon_watch_cb, self);
g_return_if_fail(!priv->watch_source);
priv->watch_source = nm_g_child_watch_add_source(pid, daemon_watch_cb, self);
}
void
@ -422,23 +466,6 @@ nm_dhcp_client_stop_watch_child(NMDhcpClient *self, pid_t pid)
watch_cleanup(self);
}
gboolean
nm_dhcp_client_start_ip4(NMDhcpClient *self, GError **error)
{
NMDhcpClientPrivate *priv;
g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
g_return_val_if_fail(priv->pid == -1, FALSE);
g_return_val_if_fail(priv->config.addr_family == AF_INET, FALSE);
g_return_val_if_fail(priv->config.uuid, FALSE);
schedule_no_lease_timeout(self);
return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, error);
}
gboolean
nm_dhcp_client_accept(NMDhcpClient *self, GError **error)
{
@ -560,7 +587,7 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
connect_l3cfg_notify(self);
nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source);
schedule_no_lease_timeout(self);
_no_lease_timeout_schedule(self);
if (!NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, &error)) {
_emit_notify(self,
@ -642,40 +669,50 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
}
gboolean
nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error)
nm_dhcp_client_start(NMDhcpClient *self, GError **error)
{
NMDhcpClientPrivate *priv;
gs_unref_bytes GBytes *own_client_id = NULL;
const NMPlatformIP6Address *addr;
const NMPlatformIP6Address *addr = NULL;
int IS_IPv4;
g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
g_return_val_if_fail(priv->pid == -1, FALSE);
g_return_val_if_fail(priv->config.addr_family == AF_INET6, FALSE);
g_return_val_if_fail(priv->config.uuid, FALSE);
g_return_val_if_fail(!priv->effective_client_id, FALSE);
nm_assert(!priv->effective_client_id);
if (!priv->config.v6.enforce_duid)
own_client_id = NM_DHCP_CLIENT_GET_CLASS(self)->get_duid(self);
IS_IPv4 = NM_IS_IPv4(priv->config.addr_family);
nm_dhcp_client_set_effective_client_id(self, own_client_id ?: priv->config.client_id);
if (!IS_IPv4) {
if (!priv->config.v6.enforce_duid)
own_client_id = NM_DHCP_CLIENT_GET_CLASS(self)->get_duid(self);
addr = ipv6_lladdr_find(self);
if (!addr) {
_LOGD("waiting for IPv6LL address");
priv->l3cfg_notify.wait_ll_address = TRUE;
connect_l3cfg_notify(self);
priv->ipv6_lladdr_timeout_source =
nm_g_timeout_add_seconds_source(10, ipv6_lladdr_timeout, self);
return TRUE;
nm_dhcp_client_set_effective_client_id(self, own_client_id ?: priv->config.client_id);
addr = ipv6_lladdr_find(self);
if (!addr) {
_LOGD("waiting for IPv6LL address");
priv->l3cfg_notify.wait_ll_address = TRUE;
connect_l3cfg_notify(self);
priv->ipv6_lladdr_timeout_source =
nm_g_timeout_add_seconds_source(10, ipv6_lladdr_timeout, self);
return TRUE;
}
}
schedule_no_lease_timeout(self);
_no_lease_timeout_schedule(self);
if (IS_IPv4)
return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, error);
return NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, error);
}
/*****************************************************************************/
void
nm_dhcp_client_stop_existing(const char *pid_file, const char *binary_name)
{
@ -763,7 +800,7 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release)
_LOGI("canceled DHCP transaction");
nm_assert(priv->pid == -1);
nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL);
_nm_dhcp_client_notify(self, NM_DHCP_CLIENT_EVENT_TYPE_TERMINATED, NULL);
}
/*****************************************************************************/
@ -772,37 +809,31 @@ static char *
bytearray_variant_to_string(NMDhcpClient *self, GVariant *value, const char *key)
{
const guint8 *array;
char *str;
gsize length;
GString *str;
int i;
unsigned char c;
char *converted = NULL;
gsize i;
g_return_val_if_fail(value != NULL, NULL);
nm_assert(value);
array = g_variant_get_fixed_array(value, &length, 1);
/* Since the DHCP options come through environment variables, they should
* already be UTF-8 safe, but just make sure.
/* Since the DHCP options come originally came as environment variables, they
* have not guaranteed encoding. Let's only accept ASCII here.
*/
str = g_string_sized_new(length);
str = g_malloc(length + 1);
for (i = 0; i < length; i++) {
c = array[i];
guint8 c = array[i];
/* Convert NULLs to spaces and non-ASCII characters to ? */
if (c == '\0')
c = ' ';
str[i] = ' ';
else if (c > 127)
c = '?';
str = g_string_append_c(str, c);
str[i] = '?';
else
str[i] = (char) c;
}
str = g_string_append_c(str, '\0');
str[i] = '\0';
converted = str->str;
if (!g_utf8_validate(converted, -1, NULL))
_LOGW("option '%s' couldn't be converted to UTF-8", key);
g_string_free(str, FALSE);
return converted;
return str;
}
static int
@ -825,11 +856,13 @@ label_is_unknown_xyz(const char *label)
static void
maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant *value)
{
char *str_value = NULL;
char *str_value;
int priv_opt_num;
g_return_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING));
if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING))
return;
if (g_str_has_prefix(key, OLD_TAG))
if (NM_STR_HAS_PREFIX(key, OLD_TAG))
return;
/* Filter out stuff that's not actually new DHCP options */
@ -842,34 +875,33 @@ maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant
return;
str_value = bytearray_variant_to_string(self, value, key);
if (str_value) {
int priv_opt_num;
if (!str_value)
return;
g_hash_table_insert(hash, g_strdup(key), str_value);
g_hash_table_insert(hash, g_strdup(key), str_value);
/* dhclient has no special labels for private dhcp options: it uses "unknown_xyz"
/* dhclient has no special labels for private dhcp options: it uses "unknown_xyz"
* labels for that. We need to identify those to alias them to our "private_xyz"
* format unused in the internal dchp plugins.
*/
if ((priv_opt_num = label_is_unknown_xyz(key)) > 0) {
gs_free guint8 *check_val = NULL;
char *hex_str = NULL;
gsize len;
if ((priv_opt_num = label_is_unknown_xyz(key)) > 0) {
gs_free guint8 *check_val = NULL;
char *hex_str = NULL;
gsize len;
/* dhclient passes values from dhcp private options in its own "string" format:
/* dhclient passes values from dhcp private options in its own "string" format:
* if the raw values are printable as ascii strings, it will pass the string
* representation; if the values are not printable as an ascii string, it will
* pass a string displaying the hex values (hex string). Try to enforce passing
* always an hex string, converting string representation if needed.
*/
check_val = nm_utils_hexstr2bin_alloc(str_value, FALSE, TRUE, ":", 0, &len);
hex_str = nm_utils_bin2hexstr_full(check_val ?: (guint8 *) str_value,
check_val ? len : strlen(str_value),
':',
FALSE,
NULL);
g_hash_table_insert(hash, g_strdup_printf("private_%d", priv_opt_num), hex_str);
}
check_val = nm_utils_hexstr2bin_alloc(str_value, FALSE, TRUE, ":", 0, &len);
hex_str = nm_utils_bin2hexstr_full(check_val ?: (guint8 *) str_value,
check_val ? len : strlen(str_value),
':',
FALSE,
NULL);
g_hash_table_insert(hash, g_strdup_printf("private_%d", priv_opt_num), hex_str);
}
}
@ -896,8 +928,8 @@ nm_dhcp_client_handle_event(gpointer unused,
NMDhcpClient *self)
{
NMDhcpClientPrivate *priv;
guint32 new_state;
nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
NMDhcpClientEventType client_event_type;
NMPlatformIP6Address prefix = {
0,
};
@ -915,17 +947,29 @@ nm_dhcp_client_handle_event(gpointer unused,
if (priv->pid != pid)
return FALSE;
new_state = reason_to_state(self, priv->config.iface, reason);
if (new_state == NM_DHCP_STATE_NOOP)
_LOGD("DHCP event (reason: '%s')", reason);
if (NM_IN_STRSET_ASCII_CASE(reason, "preinit"))
return TRUE;
_LOGD("DHCP state '%s' -> '%s' (reason: '%s')",
nm_dhcp_state_to_string(priv->state),
nm_dhcp_state_to_string(new_state),
reason);
priv->state = new_state;
if (NM_IN_STRSET_ASCII_CASE(reason, "bound", "bound6", "static"))
client_event_type = NM_DHCP_CLIENT_EVENT_TYPE_BOUND;
else if (NM_IN_STRSET_ASCII_CASE(reason, "renew", "renew6", "reboot", "rebind", "rebind6"))
client_event_type = NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED;
else if (NM_IN_STRSET_ASCII_CASE(reason, "timeout"))
client_event_type = NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT;
else if (NM_IN_STRSET_ASCII_CASE(reason, "nak", "expire", "expire6"))
client_event_type = NM_DHCP_CLIENT_EVENT_TYPE_EXPIRE;
else if (NM_IN_STRSET_ASCII_CASE(reason, "end", "stop", "stopped"))
client_event_type = NM_DHCP_CLIENT_EVENT_TYPE_TERMINATED;
else if (NM_IN_STRSET_ASCII_CASE(reason, "fail", "abend"))
client_event_type = NM_DHCP_CLIENT_EVENT_TYPE_FAIL;
else
client_event_type = NM_DHCP_CLIENT_EVENT_TYPE_UNSPECIFIED;
if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) {
if (NM_IN_SET(client_event_type,
NM_DHCP_CLIENT_EVENT_TYPE_BOUND,
NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED)) {
gs_unref_hashtable GHashTable *str_options = NULL;
GVariantIter iter;
const char *name;
@ -956,8 +1000,7 @@ nm_dhcp_client_handle_event(gpointer unused,
str_options,
priv->config.v6.info_only);
}
} else
g_warn_if_reached();
}
if (l3cd) {
nm_l3_config_data_set_dhcp_lease_from_options(l3cd,
@ -974,13 +1017,16 @@ nm_dhcp_client_handle_event(gpointer unused,
return TRUE;
}
/* Fail if no valid IP config was received */
if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED) && !l3cd) {
if (NM_IN_SET(client_event_type,
NM_DHCP_CLIENT_EVENT_TYPE_BOUND,
NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED)
&& !l3cd) {
/* Fail if no valid IP config was received */
_LOGW("client bound but IP config not received");
new_state = NM_DHCP_STATE_FAIL;
client_event_type = NM_DHCP_CLIENT_EVENT_TYPE_FAIL;
}
nm_dhcp_client_set_state(self, new_state, l3cd);
_nm_dhcp_client_notify(self, client_event_type, l3cd);
return TRUE;
}
@ -994,43 +1040,47 @@ nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr)
/* IPv6 not implemented yet */
nm_assert(priv->config.addr_family == AF_INET);
if (!priv->config.reject_servers || !priv->config.reject_servers[0])
return FALSE;
if (priv->config.reject_servers) {
for (i = 0; priv->config.reject_servers[i]; i++) {
in_addr_t r_addr;
in_addr_t mask;
int r_prefix;
for (i = 0; priv->config.reject_servers[i]; i++) {
in_addr_t r_addr;
in_addr_t mask;
int r_prefix;
if (!nm_utils_parse_inaddr_prefix_bin(AF_INET,
priv->config.reject_servers[i],
NULL,
&r_addr,
&r_prefix))
nm_assert_not_reached();
if (!nm_utils_parse_inaddr_prefix_bin(AF_INET,
priv->config.reject_servers[i],
NULL,
&r_addr,
&r_prefix))
nm_assert_not_reached();
mask = _nm_utils_ip4_prefix_to_netmask(r_prefix < 0 ? 32 : r_prefix);
if ((addr4 & mask) == (r_addr & mask))
return TRUE;
mask = _nm_utils_ip4_prefix_to_netmask(r_prefix < 0 ? 32 : r_prefix);
if ((addr4 & mask) == (r_addr & mask))
return TRUE;
}
}
return FALSE;
}
/*****************************************************************************/
static void
config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src)
{
nm_assert(config);
nm_assert(src);
nm_assert(config != src);
*config = *src;
/* We must not return before un-aliasing all pointers in @config! */
g_object_ref(config->l3cfg);
if (config->hwaddr)
g_bytes_ref(config->hwaddr);
if (config->bcast_hwaddr)
g_bytes_ref(config->bcast_hwaddr);
if (config->vendor_class_identifier)
g_bytes_ref(config->vendor_class_identifier);
if (config->client_id)
g_bytes_ref(config->client_id);
nm_g_bytes_ref(config->hwaddr);
nm_g_bytes_ref(config->bcast_hwaddr);
nm_g_bytes_ref(config->vendor_class_identifier);
nm_g_bytes_ref(config->client_id);
config->iface = g_strdup(config->iface);
config->uuid = g_strdup(config->uuid);
@ -1038,16 +1088,14 @@ config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src)
config->hostname = g_strdup(config->hostname);
config->mud_url = g_strdup(config->mud_url);
config->reject_servers = (const char *const *) nm_strv_dup(config->reject_servers, -1, TRUE);
config->reject_servers = nm_strv_dup_packed(config->reject_servers, -1);
if (config->addr_family == AF_INET) {
if (NM_IS_IPv4(config->addr_family))
config->v4.last_address = g_strdup(config->v4.last_address);
} else if (config->addr_family == AF_INET6) {
else {
config->hwaddr = NULL;
config->bcast_hwaddr = NULL;
config->use_fqdn = TRUE;
} else {
nm_assert_not_reached();
}
if (!config->hostname && config->send_hostname) {
@ -1099,54 +1147,13 @@ config_clear(NMDhcpClientConfig *config)
nm_clear_g_free((gpointer *) &config->anycast_address);
nm_clear_g_free((gpointer *) &config->hostname);
nm_clear_g_free((gpointer *) &config->mud_url);
nm_clear_pointer((gpointer *) &config->reject_servers, g_strfreev);
nm_clear_g_free((gpointer *) &config->reject_servers);
if (config->addr_family == AF_INET) {
nm_clear_g_free((gpointer *) &config->v4.last_address);
}
}
int
nm_dhcp_client_get_addr_family(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return priv->config.addr_family;
}
const char *
nm_dhcp_client_get_iface(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return priv->config.iface;
}
NMDedupMultiIndex *
nm_dhcp_client_get_multi_idx(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return nm_l3cfg_get_multi_idx(priv->config.l3cfg);
}
int
nm_dhcp_client_get_ifindex(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return nm_l3cfg_get_ifindex(priv->config.l3cfg);
}
GBytes *
nm_dhcp_client_get_effective_client_id(NMDhcpClient *self)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
return priv->effective_client_id;
}
/*****************************************************************************/
static void
@ -1192,6 +1199,10 @@ dispose(GObject *object)
nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source);
nm_clear_pointer(&priv->effective_client_id, g_bytes_unref);
nm_assert(!priv->watch_source);
nm_assert(!priv->l3cd);
nm_assert(priv->l3cfg_notify.id == 0);
G_OBJECT_CLASS(nm_dhcp_client_parent_class)->dispose(object);
}

View file

@ -27,16 +27,16 @@
#define NM_DHCP_CLIENT_NOTIFY "dhcp-notify"
typedef enum {
NM_DHCP_STATE_UNKNOWN = 0,
NM_DHCP_STATE_BOUND, /* new lease */
NM_DHCP_STATE_EXTENDED, /* lease extended */
NM_DHCP_STATE_TIMEOUT, /* timed out contacting server */
NM_DHCP_STATE_DONE, /* client reported it's stopping */
NM_DHCP_STATE_EXPIRE, /* lease expired or NAKed */
NM_DHCP_STATE_FAIL, /* failed for some reason */
NM_DHCP_STATE_TERMINATED, /* client is no longer running */
NM_DHCP_STATE_NOOP, /* state is a non operation for NetworkManager */
} NMDhcpState;
NM_DHCP_CLIENT_EVENT_TYPE_UNSPECIFIED,
NM_DHCP_CLIENT_EVENT_TYPE_BOUND,
NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED,
NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT,
NM_DHCP_CLIENT_EVENT_TYPE_EXPIRE,
NM_DHCP_CLIENT_EVENT_TYPE_FAIL,
NM_DHCP_CLIENT_EVENT_TYPE_TERMINATED,
} NMDhcpClientEventType;
typedef enum _nm_packed {
NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
@ -82,7 +82,7 @@ typedef struct {
};
} NMDhcpClientNotifyData;
const char *nm_dhcp_state_to_string(NMDhcpState state);
const char *nm_dhcp_client_event_type_to_string(NMDhcpClientEventType client_event_type);
/* FIXME(l3cfg:dhcp:config): nm_dhcp_manager_start_ip[46]() has a gazillion of parameters,
* those get passed on as CONSTRUCT_ONLY properties to the NMDhcpClient. Drop
@ -230,8 +230,7 @@ typedef struct {
GType nm_dhcp_client_get_type(void);
gboolean nm_dhcp_client_start_ip4(NMDhcpClient *self, GError **error);
gboolean nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error);
gboolean nm_dhcp_client_start(NMDhcpClient *self, GError **error);
const NMDhcpClientConfig *nm_dhcp_client_get_config(NMDhcpClient *self);
@ -268,8 +267,9 @@ void nm_dhcp_client_watch_child(NMDhcpClient *self, pid_t pid);
void nm_dhcp_client_stop_watch_child(NMDhcpClient *self, pid_t pid);
void
nm_dhcp_client_set_state(NMDhcpClient *self, NMDhcpState new_state, const NML3ConfigData *l3cd);
void _nm_dhcp_client_notify(NMDhcpClient *self,
NMDhcpClientEventType client_event_type,
const NML3ConfigData *l3cd);
gboolean nm_dhcp_client_handle_event(gpointer unused,
const char *iface,

View file

@ -131,8 +131,7 @@ NMDhcpClient *
nm_dhcp_manager_start_client(NMDhcpManager *self, NMDhcpClientConfig *config, GError **error)
{
NMDhcpManagerPrivate *priv;
gs_unref_object NMDhcpClient *client = NULL;
gboolean success = FALSE;
gs_unref_object NMDhcpClient *client = NULL;
gsize hwaddr_len;
GType gtype;
@ -202,13 +201,7 @@ nm_dhcp_manager_start_client(NMDhcpManager *self, NMDhcpClientConfig *config, GE
* default outside of NetworkManager API.
*/
if (config->addr_family == AF_INET) {
success = nm_dhcp_client_start_ip4(client, error);
} else {
success = nm_dhcp_client_start_ip6(client, error);
}
if (!success)
if (!nm_dhcp_client_start(client, error))
return NULL;
return g_steal_pointer(&client);

View file

@ -882,15 +882,16 @@ bound4_handle(NMDhcpNettools *self, NDhcp4ClientLease *lease, gboolean extended)
if (!l3cd) {
_LOGW("failure to parse lease: %s", error->message);
g_clear_error(&error);
nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL);
return;
}
lease_save(self, lease, priv->lease_file);
nm_dhcp_client_set_state(NM_DHCP_CLIENT(self),
extended ? NM_DHCP_STATE_EXTENDED : NM_DHCP_STATE_BOUND,
l3cd);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self),
extended ? NM_DHCP_CLIENT_EVENT_TYPE_EXTENDED
: NM_DHCP_CLIENT_EVENT_TYPE_BOUND,
l3cd);
}
static void
@ -927,10 +928,10 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event)
break;
case N_DHCP4_CLIENT_EVENT_RETRACTED:
case N_DHCP4_CLIENT_EVENT_EXPIRED:
nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_EXPIRE, NULL);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_EXPIRE, NULL);
break;
case N_DHCP4_CLIENT_EVENT_CANCELLED:
nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL);
break;
case N_DHCP4_CLIENT_EVENT_GRANTED:
priv->lease = n_dhcp4_client_lease_ref(event->granted.lease);
@ -985,7 +986,7 @@ dhcp4_event_cb(int fd, GIOCondition condition, gpointer user_data)
*/
_LOGE("error %d dispatching events", r);
nm_clear_g_source_inst(&priv->event_source);
nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL);
return G_SOURCE_REMOVE;
}

View file

@ -205,7 +205,7 @@ bound6_handle(NMDhcpSystemd *self)
if (sd_dhcp6_client_get_lease(priv->client6, &lease) < 0 || !lease) {
_LOGW(" no lease!");
nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL);
return;
}
@ -221,11 +221,11 @@ bound6_handle(NMDhcpSystemd *self)
if (!l3cd) {
_LOGW("%s", error->message);
nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_FAIL, NULL);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL);
return;
}
nm_dhcp_client_set_state(NM_DHCP_CLIENT(self), NM_DHCP_STATE_BOUND, l3cd);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_BOUND, l3cd);
sd_dhcp6_lease_reset_pd_prefix_iter(lease);
while (!sd_dhcp6_lease_get_pd(lease,
@ -250,11 +250,11 @@ dhcp6_event_cb(sd_dhcp6_client *client, int event, gpointer user_data)
switch (event) {
case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_TIMEOUT, NULL);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(user_data), NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT, NULL);
break;
case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
case SD_DHCP6_CLIENT_EVENT_STOP:
nm_dhcp_client_set_state(NM_DHCP_CLIENT(user_data), NM_DHCP_STATE_FAIL, NULL);
_nm_dhcp_client_notify(NM_DHCP_CLIENT(user_data), NM_DHCP_CLIENT_EVENT_TYPE_FAIL, NULL);
break;
case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:

View file

@ -837,8 +837,7 @@ nm_dhcp_utils_merge_new_dhcp6_lease(const NML3ConfigData *l3cd_old,
const char *start;
const char *iaid;
nm_assert(out_l3cd_merged);
nm_assert(!*out_l3cd_merged);
nm_assert(out_l3cd_merged && !*out_l3cd_merged);
if (!l3cd_old)
return FALSE;

View file

@ -119,7 +119,7 @@ nm_ref_string_cmp(NMRefString *a, NMRefString *b)
NM_CMP_SELF(a, b);
/* It would be cheaper to first compare by length. But this
* way we get a nicer, ASCIIbethical sort order. */
* way we get a nicer, ASCIIbetical sort order. */
NM_CMP_DIRECT_MEMCMP(a->str, b->str, NM_MIN(a->len, b->len));
NM_CMP_DIRECT(a->len, b->len);
return nm_assert_unreachable_val(0);

View file

@ -623,6 +623,16 @@ nm_utils_is_separator(const char c)
/*****************************************************************************/
static inline GBytes *
nm_g_bytes_ref(GBytes *b)
{
if (b)
g_bytes_ref(b);
return b;
}
/*****************************************************************************/
GBytes *nm_g_bytes_get_empty(void);
GBytes *nm_g_bytes_new_from_str(const char *str);