merge: add 'configure-and-quit' option (rh #1083683) (rh #863515)

When initial configuration is complete, spawns helpers to preserve
DHCP and RA addresses on interfaces and quits the main NM process.
This commit is contained in:
Dan Williams 2014-11-07 12:19:50 -06:00
commit 289f788158
45 changed files with 2352 additions and 878 deletions

1
.gitignore vendored
View file

@ -232,6 +232,7 @@ valgrind-*.log
/src/dhcp-manager/tests/test-dhcp-options
/src/dhcp-manager/tests/test-dhcp-utils
/src/dnsmasq-manager/tests/test-dnsmasq-utils
/src/nm-iface-helper
/src/settings/plugins/ibft/tests/test-ibft
/src/settings/plugins/ifcfg-rh/tests/network-scripts/*-Test_Write_*
/src/settings/plugins/ifcfg-rh/tests/network-scripts/Test_Write_*

View file

@ -2343,7 +2343,7 @@ nmc_property_set_byte_array (NMSetting *setting, const char *prop, const char *v
char *val_strip;
const char *delimiters = " \t,";
long int val_int;
char *bin;
GBytes *bytes;
GByteArray *array = NULL;
gboolean success = TRUE;
@ -2352,11 +2352,9 @@ nmc_property_set_byte_array (NMSetting *setting, const char *prop, const char *v
val_strip = g_strstrip (g_strdup (val));
/* First try hex string in the format of AAbbCCDd */
bin = nm_utils_hexstr2bin (val_strip, strlen (val_strip));
if (bin) {
array = g_byte_array_sized_new (strlen (val_strip)/2);
g_byte_array_append (array, (const guint8 *) bin, strlen (val_strip)/2);
g_free (bin);
bytes = nm_utils_hexstr2bin (val_strip);
if (bytes) {
array = g_bytes_unref_to_array (bytes);
goto done;
}

View file

@ -518,6 +518,7 @@ fi
%{_libexecdir}/nm-dhcp-helper
%{_libexecdir}/nm-avahi-autoipd.action
%{_libexecdir}/nm-dispatcher
%{_libexecdir}/nm-iface-helper
%dir %{_libdir}/NetworkManager
%{_libdir}/NetworkManager/libnm-settings-plugin*.so
%{_mandir}/man1/*

View file

@ -33,6 +33,7 @@
#include "nm-glib-compat.h"
#include "nm-setting-private.h"
#include "crypto.h"
#include "gsystem-local-alloc.h"
#include "nm-setting-bond.h"
#include "nm-setting-bridge.h"
@ -2100,7 +2101,7 @@ nm_utils_rsa_key_encrypt_helper (const char *cipher,
if (!in_password) {
if (!crypto_randomize (pw_buf, sizeof (pw_buf), error))
return NULL;
in_password = tmp_password = nm_utils_bin2hexstr ((const char *) pw_buf, sizeof (pw_buf), -1);
in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1);
}
if (g_strcmp0 (cipher, CIPHER_AES_CBC) == 0)
@ -2124,7 +2125,7 @@ nm_utils_rsa_key_encrypt_helper (const char *cipher,
g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n");
/* Convert the salt to a hex string */
tmp = nm_utils_bin2hexstr ((const char *) salt, salt_len, salt_len * 2);
tmp = nm_utils_bin2hexstr (salt, salt_len, salt_len * 2);
g_string_append_printf (pem, "DEK-Info: %s,%s\n\n", cipher, tmp);
g_free (tmp);
@ -2876,13 +2877,13 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value,
/**
* nm_utils_bin2hexstr:
* @bytes: an array of bytes
* @src: an array of bytes
* @len: the length of the @bytes array
* @final_len: an index where to cut off the returned string, or -1
*
* Converts a byte-array @bytes into a hexadecimal string.
* If @final_len is greater than -1, the returned string is terminated at
* that index (returned_string[final_len] == '\0'),
* Converts the byte array @src into a hexadecimal string. If @final_len is
* greater than -1, the returned string is terminated at that index
* (returned_string[final_len] == '\0'),
*
* Return value: (transfer full): the textual form of @bytes
*/
@ -2891,9 +2892,10 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value,
* copyright Red Hat, Inc. under terms of the LGPL.
*/
char *
nm_utils_bin2hexstr (const char *bytes, int len, int final_len)
nm_utils_bin2hexstr (gconstpointer src, gsize len, int final_len)
{
static char hex_digits[] = "0123456789abcdef";
const guint8 *bytes = src;
char *result;
int i;
gsize buflen = (len * 2) + 1;
@ -2918,64 +2920,61 @@ nm_utils_bin2hexstr (const char *bytes, int len, int final_len)
return result;
}
/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
/**
* nm_utils_hex2byte:
* @hex: a string representing a hex byte
*
* Converts a hex string (2 characters) into its byte representation.
*
* Return value: a byte, or -1 if @hex doesn't represent a hex byte
*/
int
nm_utils_hex2byte (const char *hex)
{
int a, b;
a = g_ascii_xdigit_value (*hex++);
if (a < 0)
return -1;
b = g_ascii_xdigit_value (*hex++);
if (b < 0)
return -1;
return (a << 4) | b;
}
/**
* nm_utils_hexstr2bin:
* @hex: an hex string
* @len: the length of the @hex string (it has to be even)
* @hex: a string of hexadecimal characters with optional ':' separators
*
* Converts a hexadecimal string @hex into a byte-array. The returned array
* length is @len/2.
* Converts a hexadecimal string @hex into an array of bytes. The optional
* separator ':' may be used between single or pairs of hexadecimal characters,
* eg "00:11" or "0:1". Any "0x" at the beginning of @hex is ignored. @hex
* may not start or end with ':'.
*
* Return value: (transfer full): a array of bytes, or %NULL on error
* Return value: (transfer full): the converted bytes, or %NULL on error
*/
char *
nm_utils_hexstr2bin (const char *hex, size_t len)
GBytes *
nm_utils_hexstr2bin (const char *hex)
{
size_t i;
int a;
const char * ipos = hex;
char * buf = NULL;
char * opos;
guint i = 0, x = 0;
gs_free guint8 *c = NULL;
int a, b;
gboolean found_colon = FALSE;
/* Length must be a multiple of 2 */
if ((len % 2) != 0)
return NULL;
g_return_val_if_fail (hex != NULL, NULL);
opos = buf = g_malloc0 ((len / 2) + 1);
for (i = 0; i < len; i += 2) {
a = nm_utils_hex2byte (ipos);
if (a < 0) {
g_free (buf);
if (strncasecmp (hex, "0x", 2) == 0)
hex += 2;
found_colon = !!strchr (hex, ':');
c = g_malloc (strlen (hex) / 2 + 1);
for (;;) {
a = g_ascii_xdigit_value (hex[i++]);
if (a < 0)
return NULL;
if (hex[i] && hex[i] != ':') {
b = g_ascii_xdigit_value (hex[i++]);
if (b < 0)
return NULL;
c[x++] = ((guint) a << 4) | ((guint) b);
} else
c[x++] = (guint) a;
if (!hex[i])
break;
if (hex[i] == ':') {
if (!hex[i + 1]) {
/* trailing ':' is invalid */
return NULL;
}
i++;
} else if (found_colon) {
/* If colons exist, they must delimit 1 or 2 hex chars */
return NULL;
}
*opos++ = a;
ipos += 2;
}
return buf;
return g_bytes_new (c, x);
}
/* End from hostap */
/**
* nm_utils_iface_valid_name:

View file

@ -167,9 +167,8 @@ gboolean nm_utils_hwaddr_matches (gconstpointer hwaddr1,
gconstpointer hwaddr2,
gssize hwaddr2_len);
char *nm_utils_bin2hexstr (const char *bytes, int len, int final_len);
int nm_utils_hex2byte (const char *hex);
char *nm_utils_hexstr2bin (const char *hex, size_t len);
char *nm_utils_bin2hexstr (gconstpointer src, gsize len, int final_len);
GBytes *nm_utils_hexstr2bin (const char *hex);
gboolean nm_utils_iface_valid_name(const char *name);

View file

@ -3550,6 +3550,41 @@ test_setting_ip6_gateway (void)
g_object_unref (conn);
}
typedef struct {
const char *str;
const guint8 expected[20];
const guint expected_len;
} HexItem;
static void
test_hexstr2bin (void)
{
static const HexItem items[] = {
{ "aaBBCCddDD10496a", { 0xaa, 0xbb, 0xcc, 0xdd, 0xdd, 0x10, 0x49, 0x6a }, 8 },
{ "aa:bb:cc:dd:10:49:6a", { 0xaa, 0xbb, 0xcc, 0xdd, 0x10, 0x49, 0x6a }, 7 },
{ "0xccddeeff", { 0xcc, 0xdd, 0xee, 0xff }, 4 },
{ "1:2:66:77:80", { 0x01, 0x02, 0x66, 0x77, 0x80 }, 5 },
{ "e", { 0x0e }, 1 },
{ "aabb1199:" },
{ ":aabb1199" },
{ "aabb$$dd" },
{ "aab:ccc:ddd" },
{ "aab::ccc:ddd" },
};
GBytes *b;
guint i;
for (i = 0; i < G_N_ELEMENTS (items); i++) {
b = nm_utils_hexstr2bin (items[i].str);
if (items[i].expected_len) {
g_assert (b);
g_assert_cmpint (g_bytes_get_size (b), ==, items[i].expected_len);
g_assert (memcmp (g_bytes_get_data (b, NULL), items[i].expected, g_bytes_get_size (b)) == 0);
} else
g_assert (b == NULL);
}
}
NMTST_DEFINE ();
int main (int argc, char **argv)
@ -3641,6 +3676,8 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/test_setting_ip4_gateway", test_setting_ip4_gateway);
g_test_add_func ("/core/general/test_setting_ip6_gateway", test_setting_ip6_gateway);
g_test_add_func ("/core/general/hexstr2bin", test_hexstr2bin);
return g_test_run ();
}

View file

@ -756,7 +756,6 @@ global:
nm_utils_deinit;
nm_utils_escape_ssid;
nm_utils_file_is_pkcs12;
nm_utils_hex2byte;
nm_utils_hexstr2bin;
nm_utils_hwaddr_atoba;
nm_utils_hwaddr_aton;

View file

@ -1249,6 +1249,19 @@ nm_device_get_available_connections (NMDevice *device)
return NM_DEVICE_GET_PRIVATE (device)->available_connections;
}
static inline guint8
hex2byte (const char *hex)
{
int a, b;
a = g_ascii_xdigit_value (*hex++);
if (a < 0)
return -1;
b = g_ascii_xdigit_value (*hex++);
if (b < 0)
return -1;
return (a << 4) | b;
}
static char *
get_decoded_property (GUdevDevice *device, const char *property)
{
@ -1264,7 +1277,7 @@ get_decoded_property (GUdevDevice *device, const char *property)
n = unescaped = g_malloc0 (len + 1);
while (*p) {
if ((len >= 4) && (*p == '\\') && (*(p+1) == 'x')) {
*n++ = (char) nm_utils_hex2byte (p + 2);
*n++ = (char) hex2byte (p + 2);
p += 4;
len -= 4;
} else {

View file

@ -3,7 +3,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
Copyright (C) 2010 - 2013 Red Hat, Inc.
Copyright 2010 - 2014 Red Hat, Inc.
-->
<refentry id="NetworkManager.conf">
@ -199,6 +199,28 @@ Copyright (C) 2010 - 2013 Red Hat, Inc.
</listitem>
</varlistentry>
<varlistentry>
<term><varname>configure-and-quit</varname></term>
<listitem>
<para>
When set to <literal>true</literal>, NetworkManager quits after
performing initial network configuration but spawns small helpers
to preserve DHCP leases and IPv6 addresses. This is useful in
environments where network setup is more or less static or it is
desirable to save process time but still handle some dynamic
configurations. When this option is <literal>true</literal>,
network configuration for WiFi, WWAN, Bluetooth, ADSL, and PPPoE
interfaces cannot be preserved due to their use of external
services, and these devices will be deconfigured when NetworkManager
quits even though other interface's configuration may be preserved.
The default value is <literal>false</literal>, meaning that
NetworkManager will continue running after initial network
configuration and continue responding to system and hardware events,
D-Bus requests, and user commands.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>dns</varname></term>
<listitem><para>Set the DNS (<filename>resolv.conf</filename>) processing mode.</para>

View file

@ -126,6 +126,7 @@ libnm/nm-vpn-plugin-old.c
policy/org.freedesktop.NetworkManager.policy.in.in
src/NetworkManagerUtils.c
src/main.c
src/main-utils.c
src/dhcp-manager/nm-dhcp-dhclient.c
src/dhcp-manager/nm-dhcp-dhclient-utils.c
src/dhcp-manager/nm-dhcp-manager.c
@ -146,6 +147,7 @@ src/devices/wifi/nm-wifi-ap-utils.c
src/devices/wimax/nm-device-wimax.c
src/devices/wwan/nm-modem-broadband.c
src/nm-config.c
src/nm-iface-helper.c
src/nm-logging.c
src/nm-manager.c
src/nm-sleep-monitor-systemd.c

View file

@ -48,7 +48,10 @@ AM_CPPFLAGS = \
# primarily for its side effect of removing duplicates.
AM_CPPFLAGS += $(foreach d,$(sort $(dir $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d)
noinst_LTLIBRARIES = libNetworkManager.la libsystemd-dhcp.la
noinst_LTLIBRARIES = \
libNetworkManager.la \
libnm-iface-helper.la \
libsystemd-dhcp.la
######################
# libsystemd-dhcp
@ -124,6 +127,9 @@ sbin_PROGRAMS = NetworkManager
NetworkManager_SOURCES = \
$(nm_device_sources) $(nm_device_headers) \
$(nm_dhcp_client_sources) $(nm_dhcp_client_headers) \
main-utils.c \
main-utils.h \
main.c
NetworkManager_LDADD = libNetworkManager.la
@ -153,8 +159,21 @@ nm_device_headers = \
devices/nm-device-vlan.h \
devices/nm-device-vxlan.h
nm_dhcp_client_sources = \
dhcp-manager/nm-dhcp-dhclient.c \
dhcp-manager/nm-dhcp-dhclient-utils.c \
dhcp-manager/nm-dhcp-dhcpcd.c \
dhcp-manager/nm-dhcp-systemd.c
nm_dhcp_client_headers = \
dhcp-manager/nm-dhcp-dhclient.h \
dhcp-manager/nm-dhcp-dhclient-utils.h \
dhcp-manager/nm-dhcp-dhcpcd.h \
dhcp-manager/nm-dhcp-systemd.h
nm_sources = \
$(nm_device_headers) \
$(nm_dhcp_client_headers) \
devices/nm-device.c \
devices/nm-device.h \
devices/nm-device-ethernet-utils.c \
@ -170,14 +189,8 @@ nm_sources = \
dhcp-manager/nm-dhcp-client.h \
dhcp-manager/nm-dhcp-utils.c \
dhcp-manager/nm-dhcp-utils.h \
dhcp-manager/nm-dhcp-dhclient.c \
dhcp-manager/nm-dhcp-dhclient.h \
dhcp-manager/nm-dhcp-dhclient-utils.c \
dhcp-manager/nm-dhcp-dhclient-utils.h \
dhcp-manager/nm-dhcp-dhcpcd.c \
dhcp-manager/nm-dhcp-dhcpcd.h \
dhcp-manager/nm-dhcp-systemd.h \
dhcp-manager/nm-dhcp-systemd.c \
dhcp-manager/nm-dhcp-listener.c \
dhcp-manager/nm-dhcp-listener.h \
dhcp-manager/nm-dhcp-manager.c \
dhcp-manager/nm-dhcp-manager.h \
\
@ -448,6 +461,86 @@ NetworkManager_LDFLAGS = -rdynamic
######################
libnm_iface_helper_la_SOURCES = \
dhcp-manager/nm-dhcp-client.c \
dhcp-manager/nm-dhcp-client.h \
dhcp-manager/nm-dhcp-utils.c \
dhcp-manager/nm-dhcp-utils.h \
dhcp-manager/nm-dhcp-manager.c \
dhcp-manager/nm-dhcp-manager.h \
\
platform/nm-linux-platform.c \
platform/nm-linux-platform.h \
platform/nm-platform.c \
platform/nm-platform.h \
platform/wifi/wifi-utils-nl80211.c \
platform/wifi/wifi-utils-nl80211.h \
platform/wifi/wifi-utils-private.h \
platform/wifi/wifi-utils.c \
platform/wifi/wifi-utils.h \
\
rdisc/nm-fake-rdisc.c \
rdisc/nm-fake-rdisc.h \
rdisc/nm-lndp-rdisc.c \
rdisc/nm-lndp-rdisc.h \
rdisc/nm-rdisc.c \
rdisc/nm-rdisc.h \
\
nm-ip4-config.c \
nm-ip4-config.h \
nm-ip6-config.c \
nm-ip6-config.h \
\
nm-enum-types.c \
nm-enum-types.h \
nm-logging.c \
nm-logging.h \
nm-posix-signals.c \
nm-posix-signals.h \
NetworkManagerUtils.c \
NetworkManagerUtils.h
if WITH_WEXT
libnm_iface_helper_la_SOURCES += \
platform/wifi/wifi-utils-wext.c \
platform/wifi/wifi-utils-wext.h
endif
libnm_iface_helper_la_LIBADD = \
$(top_builddir)/libnm-core/libnm-core.la \
libsystemd-dhcp.la \
$(DBUS_LIBS) \
$(GLIB_LIBS) \
$(GUDEV_LIBS) \
$(LIBNL_LIBS) \
$(LIBNDP_LIBS) \
$(LIBDL) \
$(LIBM)
libexec_PROGRAMS = nm-iface-helper
nm_iface_helper_SOURCES = \
dhcp-manager/nm-dhcp-systemd.h \
dhcp-manager/nm-dhcp-systemd.c \
nm-iface-helper.c \
main-utils.c \
main-utils.h
nm_iface_helper_LDADD = \
$(top_builddir)/libnm-core/libnm-core.la \
libsystemd-dhcp.la \
libnm-iface-helper.la \
$(DBUS_LIBS) \
$(GLIB_LIBS) \
$(GUDEV_LIBS) \
$(LIBNL_LIBS) \
$(LIBNDP_LIBS) \
$(LIBM)
nm_iface_helper_LDFLAGS = -rdynamic
######################
dbusservicedir = $(DBUS_SYS_DIR)
dbusservice_DATA = org.freedesktop.NetworkManager.conf

View file

@ -2156,6 +2156,32 @@ out:
}
#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
#define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/"
G_STATIC_ASSERT (sizeof (IPV4_PROPERTY_DIR) == sizeof (IPV6_PROPERTY_DIR));
static const char *
_get_property_path (const char *ifname,
const char *property,
gboolean ipv6)
{
static char path[sizeof (IPV6_PROPERTY_DIR) + IFNAMSIZ + 32];
int len;
ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
property = ASSERT_VALID_PATH_COMPONENT (property);
len = g_snprintf (path,
sizeof (path),
"%s%s/%s",
ipv6 ? IPV6_PROPERTY_DIR : IPV4_PROPERTY_DIR,
ifname,
property);
g_assert (len < sizeof (path) - 1);
return path;
}
/**
* nm_utils_ip6_property_path:
* @ifname: an interface name
@ -2167,18 +2193,21 @@ out:
const char *
nm_utils_ip6_property_path (const char *ifname, const char *property)
{
#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
static char path[sizeof (IPV6_PROPERTY_DIR) + IFNAMSIZ + 32];
int len;
return _get_property_path (ifname, property, TRUE);
}
ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
property = ASSERT_VALID_PATH_COMPONENT (property);
len = g_snprintf (path, sizeof (path), IPV6_PROPERTY_DIR "%s/%s",
ifname, property);
g_assert (len < sizeof (path) - 1);
return path;
/**
* nm_utils_ip4_property_path:
* @ifname: an interface name
* @property: a property name
*
* Returns the path to IPv4 property @property on @ifname. Note that
* this uses a static buffer.
*/
const char *
nm_utils_ip4_property_path (const char *ifname, const char *property)
{
return _get_property_path (ifname, property, FALSE);
}
const char *

View file

@ -168,6 +168,7 @@ gint32 nm_utils_get_monotonic_timestamp_s (void);
const char *ASSERT_VALID_PATH_COMPONENT (const char *name) G_GNUC_WARN_UNUSED_RESULT;
const char *nm_utils_ip6_property_path (const char *ifname, const char *property);
const char *nm_utils_ip4_property_path (const char *ifname, const char *property);
gboolean nm_utils_is_specific_hostname (const char *name);

View file

@ -2975,7 +2975,8 @@ dhcp4_start (NMDevice *self,
nm_setting_ip_config_get_dhcp_hostname (s_ip4),
nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)),
priv->dhcp_timeout,
priv->dhcp_anycast_address);
priv->dhcp_anycast_address,
NULL);
if (tmp)
g_byte_array_free (tmp, TRUE);
@ -6989,6 +6990,173 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
_cleanup_generic_post (self, TRUE);
}
static char *
bin2hexstr (const char *bytes, gsize len)
{
GString *str;
int i;
g_return_val_if_fail (bytes != NULL, NULL);
g_return_val_if_fail (len > 0, NULL);
str = g_string_sized_new (len * 2 + 1);
for (i = 0; i < len; i++) {
if (str->len)
g_string_append_c (str, ':');
g_string_append_printf (str, "%02x", (guint8) bytes[i]);
}
return g_string_free (str, FALSE);
}
static char *
find_dhcp4_address (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
guint i, n;
if (!priv->ip4_config)
return NULL;
n = nm_ip4_config_get_num_addresses (priv->ip4_config);
for (i = 0; i < n; i++) {
const NMPlatformIP4Address *a = nm_ip4_config_get_address (priv->ip4_config, i);
if (a->source == NM_IP_CONFIG_SOURCE_DHCP)
return g_strdup (nm_utils_inet4_ntop (a->address, NULL));
}
return NULL;
}
void
nm_device_spawn_iface_helper (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
gboolean priority_set = FALSE, configured = FALSE;
NMConnection *connection;
GError *error = NULL;
const char *method;
GPtrArray *argv;
gs_free char *dhcp4_address = NULL;
if (priv->state != NM_DEVICE_STATE_ACTIVATED)
return;
if (!nm_device_can_assume_connections (self))
return;
connection = nm_device_get_connection (self);
g_assert (connection);
argv = g_ptr_array_sized_new (10);
g_ptr_array_set_free_func (argv, g_free);
g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/nm-iface-helper"));
g_ptr_array_add (argv, g_strdup ("--ifname"));
g_ptr_array_add (argv, g_strdup (nm_device_get_ip_iface (self)));
g_ptr_array_add (argv, g_strdup ("--uuid"));
g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection)));
dhcp4_address = find_dhcp4_address (self);
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
if ( priv->ip4_config
&& priv->ip4_state == IP_DONE
&& g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0
&& priv->dhcp4_client
&& dhcp4_address) {
NMSettingIPConfig *s_ip4;
GBytes *client_id;
char *hex_client_id;
const char *hostname;
s_ip4 = nm_connection_get_setting_ip4_config (connection);
g_assert (s_ip4);
g_ptr_array_add (argv, g_strdup ("--priority"));
g_ptr_array_add (argv, g_strdup_printf ("%u", nm_dhcp_client_get_priority (priv->dhcp4_client)));
priority_set = TRUE;
g_ptr_array_add (argv, g_strdup ("--dhcp4"));
g_ptr_array_add (argv, g_strdup (dhcp4_address));
if (nm_setting_ip_config_get_may_fail (s_ip4) == FALSE)
g_ptr_array_add (argv, g_strdup ("--dhcp4-required"));
client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client);
if (client_id) {
g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid"));
hex_client_id = bin2hexstr (g_bytes_get_data (client_id, NULL),
g_bytes_get_size (client_id));
g_ptr_array_add (argv, hex_client_id);
}
hostname = nm_dhcp_client_get_hostname (priv->dhcp4_client);
if (client_id) {
g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
g_ptr_array_add (argv, g_strdup (hostname));
}
configured = TRUE;
}
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
if ( priv->ip6_config
&& priv->ip6_state == IP_DONE
&& g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
&& priv->rdisc
&& priv->ac_ip6_config) {
NMSettingIPConfig *s_ip6;
char *hex_iid;
NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT;
s_ip6 = nm_connection_get_setting_ip6_config (connection);
g_assert (s_ip6);
g_ptr_array_add (argv, g_strdup ("--slaac"));
if (nm_setting_ip_config_get_may_fail (s_ip6) == FALSE)
g_ptr_array_add (argv, g_strdup ("--slaac-required"));
g_ptr_array_add (argv, g_strdup ("--slaac-tempaddr"));
g_ptr_array_add (argv, g_strdup_printf ("%d", priv->rdisc_use_tempaddr));
if (nm_device_get_ip_iface_identifier (self, &iid)) {
g_ptr_array_add (argv, g_strdup ("--iid"));
hex_iid = bin2hexstr ((const char *) iid.id_u8, sizeof (NMUtilsIPv6IfaceId));
g_ptr_array_add (argv, hex_iid);
}
configured = TRUE;
}
if (configured) {
GPid pid;
if (!priority_set) {
g_ptr_array_add (argv, g_strdup ("--priority"));
g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_priority (self)));
}
g_ptr_array_add (argv, NULL);
if (nm_logging_enabled (LOGL_DEBUG, LOGD_DEVICE)) {
char *tmp;
tmp = g_strjoinv (" ", (char **) argv->pdata);
_LOGD (LOGD_DEVICE, "running '%s'", tmp);
g_free (tmp);
}
if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error)) {
_LOGI (LOGD_DEVICE, "spawned helper PID %u", (guint) pid);
} else {
_LOGW (LOGD_DEVICE, "failed to spawn helper: %s", error->message);
g_error_free (error);
}
}
g_ptr_array_unref (argv);
}
/***********************************************************/
static gboolean

View file

@ -365,6 +365,8 @@ NMConnection *nm_device_new_default_connection (NMDevice *self);
const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self);
const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self);
void nm_device_spawn_iface_helper (NMDevice *self);
G_END_DECLS
/* For testing only */

View file

@ -45,6 +45,8 @@ typedef struct {
guint32 priority;
guint32 timeout;
GByteArray * duid;
GBytes * client_id;
char * hostname;
NMDhcpState state;
pid_t pid;
@ -143,6 +145,37 @@ nm_dhcp_client_get_priority (NMDhcpClient *self)
return NM_DHCP_CLIENT_GET_PRIVATE (self)->priority;
}
GBytes *
nm_dhcp_client_get_client_id (NMDhcpClient *self)
{
g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
return NM_DHCP_CLIENT_GET_PRIVATE (self)->client_id;
}
void
nm_dhcp_client_set_client_id (NMDhcpClient *self, GBytes *client_id)
{
NMDhcpClientPrivate *priv;
g_return_if_fail (NM_IS_DHCP_CLIENT (self));
priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
if (priv->client_id && client_id && g_bytes_equal (priv->client_id, client_id))
return;
g_clear_pointer (&priv->client_id, g_bytes_unref);
priv->client_id = client_id ? g_bytes_ref (client_id) : NULL;
}
const char *
nm_dhcp_client_get_hostname (NMDhcpClient *self)
{
g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname;
}
/********************************************/
static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
@ -360,7 +393,8 @@ gboolean
nm_dhcp_client_start_ip4 (NMDhcpClient *self,
const char *dhcp_client_id,
const char *dhcp_anycast_addr,
const char *hostname)
const char *hostname,
const char *last_ip4_address)
{
NMDhcpClientPrivate *priv;
@ -374,7 +408,12 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self,
nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)",
priv->iface, priv->timeout);
return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_client_id, dhcp_anycast_addr, hostname);
nm_dhcp_client_set_client_id (self, dhcp_client_id ? nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id) : NULL);
g_clear_pointer (&priv->hostname, g_free);
priv->hostname = g_strdup (hostname);
return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address);
}
/* uuid_parse does not work for machine-id, so we use our own converter */
@ -518,6 +557,9 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
g_free (str);
}
g_clear_pointer (&priv->hostname, g_free);
priv->hostname = g_strdup (hostname);
priv->info_only = info_only;
nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)",
@ -525,7 +567,6 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
dhcp_anycast_addr,
hostname,
info_only,
privacy,
priv->duid);
@ -673,10 +714,13 @@ copy_option (const char * key,
g_hash_table_insert (hash, g_strdup (key), str_value);
}
void
nm_dhcp_client_new_options (NMDhcpClient *self,
GHashTable *options,
const char *reason)
gboolean
nm_dhcp_client_handle_event (gpointer unused,
const char *iface,
gint64 pid,
GHashTable *options,
const char *reason,
NMDhcpClient *self)
{
NMDhcpClientPrivate *priv;
guint32 old_state;
@ -684,11 +728,19 @@ nm_dhcp_client_new_options (NMDhcpClient *self,
GHashTable *str_options = NULL;
GObject *ip_config = NULL;
g_return_if_fail (NM_IS_DHCP_CLIENT (self));
g_return_if_fail (options != NULL);
g_return_if_fail (reason != NULL);
g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (pid > 0, FALSE);
g_return_val_if_fail (options != NULL, FALSE);
g_return_val_if_fail (reason != NULL, FALSE);
priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
if (g_strcmp0 (priv->iface, iface) != 0)
return FALSE;
if (priv->pid != pid)
return FALSE;
old_state = priv->state;
new_state = reason_to_state (priv->iface, reason);
@ -719,6 +771,8 @@ nm_dhcp_client_new_options (NMDhcpClient *self,
if (str_options)
g_hash_table_destroy (str_options);
g_clear_object (&ip_config);
return TRUE;
}
/********************************************/

View file

@ -64,13 +64,11 @@ typedef struct {
/* Methods */
gboolean (*ip4_start) (NMDhcpClient *self,
const char *dhcp_client_id,
const char *anycast_addr,
const char *hostname);
const char *last_ip4_address);
gboolean (*ip6_start) (NMDhcpClient *self,
const char *anycast_addr,
const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid);
@ -99,6 +97,17 @@ typedef struct {
GType nm_dhcp_client_get_type (void);
typedef const char *(*NMDhcpClientGetPathFunc) (void);
typedef GSList * (*NMDhcpClientGetLeaseConfigsFunc) (const char *iface,
const char *uuid,
gboolean ipv6);
void _nm_dhcp_client_register (GType gtype,
const char *name,
NMDhcpClientGetPathFunc get_path_func,
NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func);
pid_t nm_dhcp_client_get_pid (NMDhcpClient *self);
const char *nm_dhcp_client_get_iface (NMDhcpClient *self);
@ -115,10 +124,15 @@ const GByteArray *nm_dhcp_client_get_hw_addr (NMDhcpClient *self);
guint32 nm_dhcp_client_get_priority (NMDhcpClient *self);
GBytes *nm_dhcp_client_get_client_id (NMDhcpClient *self);
const char *nm_dhcp_client_get_hostname (NMDhcpClient *self);
gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self,
const char *dhcp_client_id,
const char *dhcp_anycast_addr,
const char *hostname);
const char *hostname,
const char *last_ip4_address);
gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self,
const char *dhcp_anycast_addr,
@ -128,10 +142,6 @@ gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self,
void nm_dhcp_client_stop (NMDhcpClient *self, gboolean release);
void nm_dhcp_client_new_options (NMDhcpClient *self,
GHashTable *options,
const char *reason);
/* Backend helpers for subclasses */
void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name);
@ -144,5 +154,14 @@ void nm_dhcp_client_set_state (NMDhcpClient *self,
GObject *ip_config, /* NMIP4Config or NMIP6Config */
GHashTable *options); /* str:str hash */
gboolean nm_dhcp_client_handle_event (gpointer unused,
const char *iface,
gint64 pid,
GHashTable *options,
const char *reason,
NMDhcpClient *self);
void nm_dhcp_client_set_client_id (NMDhcpClient *self, GBytes *client_id);
#endif /* __NETWORKMANAGER_DHCP_CLIENT_H__ */

View file

@ -26,14 +26,14 @@
#include <arpa/inet.h>
#include "nm-dhcp-dhclient-utils.h"
#include "nm-dhcp-utils.h"
#include "nm-ip4-config.h"
#include "nm-utils.h"
#include "nm-platform.h"
#include "NetworkManagerUtils.h"
#include "gsystem-local-alloc.h"
#define CLIENTID_TAG "send dhcp-client-identifier"
#define CLIENTID_FORMAT CLIENTID_TAG " \"%s\"; # added by NetworkManager"
#define CLIENTID_FORMAT_OCTETS CLIENTID_TAG " %s; # added by NetworkManager"
#define HOSTNAME4_TAG "send host-name"
#define HOSTNAME4_FORMAT HOSTNAME4_TAG " \"%s\"; # added by NetworkManager"
@ -73,40 +73,39 @@ add_hostname (GString *str, const char *format, const char *hostname)
}
static void
add_ip4_config (GString *str, const char *dhcp_client_id, const char *hostname)
add_ip4_config (GString *str, GBytes *client_id, const char *hostname)
{
if (dhcp_client_id && *dhcp_client_id) {
gboolean is_octets = TRUE;
int i = 0;
if (client_id) {
const char *p;
gsize l;
guint i;
while (dhcp_client_id[i]) {
if (!g_ascii_isxdigit (dhcp_client_id[i])) {
is_octets = FALSE;
p = g_bytes_get_data (client_id, &l);
g_assert (p);
/* Allow type 0 (non-hardware address) to be represented as a string
* as long as all the characters are printable.
*/
for (i = 1; (p[0] == 0) && i < l; i++) {
if (!g_ascii_isprint (p[i]))
break;
}
i++;
if (!dhcp_client_id[i])
break;
if (g_ascii_isxdigit (dhcp_client_id[i])) {
i++;
if (!dhcp_client_id[i])
break;
}
if (dhcp_client_id[i] != ':') {
is_octets = FALSE;
break;
}
i++;
}
/* If the client ID is just hex digits and : then don't use quotes,
* because dhclient expects either a quoted ASCII string, or a byte
* array formated as hex octets separated by :
*/
if (is_octets)
g_string_append_printf (str, CLIENTID_FORMAT_OCTETS "\n", dhcp_client_id);
else
g_string_append_printf (str, CLIENTID_FORMAT "\n", dhcp_client_id);
g_string_append (str, CLIENTID_TAG " ");
if (i < l) {
/* Unprintable; convert to a hex string */
for (i = 0; i < l; i++) {
if (i > 0)
g_string_append_c (str, ':');
g_string_append_printf (str, "%02x", (guint8) p[i]);
}
} else {
/* Printable; just add to the line minus the 'type' */
g_string_append_c (str, '"');
g_string_append_len (str, p + 1, l - 1);
g_string_append_c (str, '"');
}
g_string_append (str, "; # added by NetworkManager\n");
}
add_hostname (str, HOSTNAME4_FORMAT "\n", hostname);
@ -134,14 +133,67 @@ add_ip6_config (GString *str, const char *hostname)
"send fqdn.server-update on;\n");
}
static GBytes *
read_client_id (const char *str)
{
gs_free char *s = NULL;
char *p;
g_assert (!strncmp (str, CLIENTID_TAG, STRLEN (CLIENTID_TAG)));
str += STRLEN (CLIENTID_TAG);
while (g_ascii_isspace (*str))
str++;
if (*str == '"') {
s = g_strdup (str + 1);
p = strrchr (s, '"');
if (p)
*p = '\0';
else
return NULL;
} else
s = g_strdup (str);
g_strchomp (s);
if (s[strlen (s) - 1] == ';')
s[strlen (s) - 1] = '\0';
return nm_dhcp_utils_client_id_string_to_bytes (s);
}
GBytes *
nm_dhcp_dhclient_get_client_id_from_config_file (const char *path)
{
gs_free char *contents = NULL;
gs_strfreev char **lines = NULL;
char **line;
g_return_val_if_fail (path != NULL, NULL);
if (!g_file_test (path, G_FILE_TEST_EXISTS))
return NULL;
if (!g_file_get_contents (path, &contents, NULL, NULL))
return NULL;
lines = g_strsplit_set (contents, "\n\r", 0);
for (line = lines; lines && *line; line++) {
if (!strncmp (*line, CLIENTID_TAG, STRLEN (CLIENTID_TAG)))
return read_client_id (*line);
}
return NULL;
}
char *
nm_dhcp_dhclient_create_config (const char *interface,
gboolean is_ip6,
const char *dhcp_client_id,
GBytes *client_id,
const char *anycast_addr,
const char *hostname,
const char *orig_path,
const char *orig_contents)
const char *orig_contents,
GBytes **out_new_client_id)
{
GString *new_contents;
GPtrArray *alsoreq;
@ -165,11 +217,15 @@ nm_dhcp_dhclient_create_config (const char *interface,
if (!strlen (g_strstrip (p)))
continue;
/* Override config file "dhcp-client-id" and use one from the
* connection.
*/
if (dhcp_client_id && !strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG)))
continue;
if (!strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG))) {
/* Override config file "dhcp-client-id" and use one from the connection */
if (client_id)
continue;
/* Otherwise capture and return the existing client id */
if (out_new_client_id)
*out_new_client_id = read_client_id (p);
}
/* Override config file hostname and use one from the connection */
if (hostname) {
@ -238,7 +294,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
add_also_request (alsoreq, "dhcp6.domain-search");
add_also_request (alsoreq, "dhcp6.client-id");
} else {
add_ip4_config (new_contents, dhcp_client_id, hostname);
add_ip4_config (new_contents, client_id, hostname);
add_also_request (alsoreq, "rfc3442-classless-static-routes");
add_also_request (alsoreq, "ms-classless-static-routes");
add_also_request (alsoreq, "static-routes");

View file

@ -27,11 +27,12 @@
char *nm_dhcp_dhclient_create_config (const char *interface,
gboolean is_ip6,
const char *dhcp_client_id,
GBytes *client_id,
const char *anycast_addr,
const char *hostname,
const char *orig_path,
const char *orig_contents);
const char *orig_contents,
GBytes **out_new_client_id);
char *nm_dhcp_dhclient_escape_duid (const GByteArray *duid);
@ -48,5 +49,7 @@ GSList *nm_dhcp_dhclient_read_lease_ip_configs (const char *iface,
gboolean ipv6,
GDateTime *now);
GBytes *nm_dhcp_dhclient_get_client_id_from_config_file (const char *path);
#endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */

View file

@ -43,6 +43,8 @@
#include "nm-dhcp-manager.h"
#include "nm-posix-signals.h"
#include "NetworkManagerUtils.h"
#include "nm-dhcp-listener.h"
#include "gsystem-local-alloc.h"
G_DEFINE_TYPE (NMDhcpDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
@ -55,7 +57,7 @@ typedef struct {
char *pid_file;
} NMDhcpDhclientPrivate;
const char *
static const char *
nm_dhcp_dhclient_get_path (void)
{
const char *path = NULL;
@ -122,7 +124,7 @@ get_dhclient_leasefile (const char *iface,
return NULL;
}
GSList *
static GSList *
nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
const char *uuid,
gboolean ipv6)
@ -151,10 +153,11 @@ static gboolean
merge_dhclient_config (const char *iface,
const char *conf_file,
gboolean is_ip6,
const char *dhcp_client_id,
GBytes *client_id,
const char *anycast_addr,
const char *hostname,
const char *orig_path,
GBytes **out_new_client_id,
GError **error)
{
char *orig = NULL, *new;
@ -173,7 +176,7 @@ merge_dhclient_config (const char *iface,
}
}
new = nm_dhcp_dhclient_create_config (iface, is_ip6, dhcp_client_id, anycast_addr, hostname, orig_path, orig);
new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, orig_path, orig, out_new_client_id);
g_assert (new);
success = g_file_set_contents (conf_file, new, -1, error);
g_free (new);
@ -257,9 +260,10 @@ static char *
create_dhclient_config (const char *iface,
gboolean is_ip6,
const char *uuid,
const char *dhcp_client_id,
GBytes *client_id,
const char *dhcp_anycast_addr,
const char *hostname)
const char *hostname,
GBytes **out_new_client_id)
{
char *orig = NULL, *new = NULL;
GError *error = NULL;
@ -284,7 +288,7 @@ create_dhclient_config (const char *iface,
}
error = NULL;
success = merge_dhclient_config (iface, new, is_ip6, dhcp_client_id, dhcp_anycast_addr, hostname, orig, &error);
success = merge_dhclient_config (iface, new, is_ip6, client_id, dhcp_anycast_addr, hostname, orig, out_new_client_id, &error);
if (!success) {
nm_log_warn (LOGD_DHCP, "(%s): error creating dhclient%s configuration: %s",
iface, is_ip6 ? "6" : "", error->message);
@ -473,41 +477,45 @@ dhclient_start (NMDhcpClient *client,
}
static gboolean
ip4_start (NMDhcpClient *client,
const char *dhcp_client_id,
const char *dhcp_anycast_addr,
const char *hostname)
ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
{
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
const char *iface, *uuid;
GBytes *client_id;
gs_unref_bytes GBytes *new_client_id = NULL;
const char *iface, *uuid, *hostname;
gboolean success = FALSE;
iface = nm_dhcp_client_get_iface (client);
uuid = nm_dhcp_client_get_uuid (client);
client_id = nm_dhcp_client_get_client_id (client);
hostname = nm_dhcp_client_get_hostname (client);
priv->conf_file = create_dhclient_config (iface, FALSE, uuid, dhcp_client_id, dhcp_anycast_addr, hostname);
if (!priv->conf_file) {
priv->conf_file = create_dhclient_config (iface, FALSE, uuid, client_id, dhcp_anycast_addr, hostname, &new_client_id);
if (priv->conf_file) {
if (new_client_id)
nm_dhcp_client_set_client_id (client, new_client_id);
success = dhclient_start (client, NULL, NULL, FALSE, NULL);
} else
nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
return FALSE;
}
return dhclient_start (client, NULL, NULL, FALSE, NULL);
return success;
}
static gboolean
ip6_start (NMDhcpClient *client,
const char *dhcp_anycast_addr,
const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
{
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
const char *iface, *uuid;
const char *iface, *uuid, *hostname;
iface = nm_dhcp_client_get_iface (client);
uuid = nm_dhcp_client_get_uuid (client);
hostname = nm_dhcp_client_get_hostname (client);
priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname);
priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname, NULL);
if (!priv->conf_file) {
nm_log_warn (LOGD_DHCP6, "(%s): error creating dhclient6 configuration file.", iface);
return FALSE;
@ -544,6 +552,24 @@ stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
}
}
static void
state_changed (NMDhcpClient *client,
NMDhcpState state,
GObject *ip_config,
GHashTable *options)
{
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
gs_unref_bytes GBytes *client_id = NULL;
if (nm_dhcp_client_get_client_id (client))
return;
if (state != NM_DHCP_STATE_BOUND)
return;
client_id = nm_dhcp_dhclient_get_client_id_from_config_file (priv->conf_file);
nm_dhcp_client_set_client_id (client, client_id);
}
static GByteArray *
get_duid (NMDhcpClient *client)
{
@ -612,6 +638,11 @@ nm_dhcp_dhclient_init (NMDhcpDhclient *self)
/* Fallback option */
if (!priv->def_leasefile)
priv->def_leasefile = SYSCONFDIR "/dhclient6.leases";
g_signal_connect (nm_dhcp_listener_get (),
NM_DHCP_LISTENER_EVENT,
G_CALLBACK (nm_dhcp_client_handle_event),
self);
}
static void
@ -619,6 +650,10 @@ dispose (GObject *object)
{
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object);
g_signal_handlers_disconnect_by_func (nm_dhcp_listener_get (),
G_CALLBACK (nm_dhcp_client_handle_event),
NM_DHCP_DHCLIENT (object));
g_free (priv->pid_file);
g_free (priv->conf_file);
g_free (priv->lease_file);
@ -641,5 +676,16 @@ nm_dhcp_dhclient_class_init (NMDhcpDhclientClass *dhclient_class)
client_class->ip6_start = ip6_start;
client_class->stop = stop;
client_class->get_duid = get_duid;
client_class->state_changed = state_changed;
}
static void __attribute__((constructor))
register_dhcp_dhclient (void)
{
g_type_init ();
_nm_dhcp_client_register (NM_TYPE_DHCP_DHCLIENT,
"dhclient",
nm_dhcp_dhclient_get_path,
nm_dhcp_dhclient_get_lease_ip_configs);
}

View file

@ -41,11 +41,5 @@ typedef struct {
GType nm_dhcp_dhclient_get_type (void);
GSList *nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
const char *uuid,
gboolean ipv6);
const char *nm_dhcp_dhclient_get_path (void);
#endif /* __NETWORKMANAGER_DHCP_DHCLIENT_H__ */

View file

@ -38,6 +38,7 @@
#include "nm-logging.h"
#include "nm-posix-signals.h"
#include "NetworkManagerUtils.h"
#include "nm-dhcp-listener.h"
G_DEFINE_TYPE (NMDhcpDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_CLIENT)
@ -47,7 +48,7 @@ typedef struct {
char *pid_file;
} NMDhcpDhcpcdPrivate;
const char *
static const char *
nm_dhcp_dhcpcd_get_path (void)
{
const char *path = NULL;
@ -72,17 +73,14 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED)
}
static gboolean
ip4_start (NMDhcpClient *client,
const char *dhcp_client_id,
const char *dhcp_anycast_addr,
const char *hostname)
ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
{
NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
GPtrArray *argv = NULL;
pid_t pid = -1;
GError *error = NULL;
char *pid_contents = NULL, *binary_name, *cmd_str;
const char *iface, *dhcpcd_path = NULL;
const char *iface, *dhcpcd_path, *hostname;
g_return_val_if_fail (priv->pid_file == NULL, FALSE);
@ -129,7 +127,8 @@ ip4_start (NMDhcpClient *client,
g_ptr_array_add (argv, (gpointer) "-4");
#endif
if (hostname && strlen (hostname)) {
hostname = nm_dhcp_client_get_hostname (client);
if (hostname) {
g_ptr_array_add (argv, (gpointer) "-h"); /* Send hostname to DHCP server */
g_ptr_array_add (argv, (gpointer) hostname );
}
@ -160,7 +159,6 @@ ip4_start (NMDhcpClient *client,
static gboolean
ip6_start (NMDhcpClient *client,
const char *dhcp_anycast_addr,
const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
@ -190,6 +188,10 @@ stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
static void
nm_dhcp_dhcpcd_init (NMDhcpDhcpcd *self)
{
g_signal_connect (nm_dhcp_listener_get (),
NM_DHCP_LISTENER_EVENT,
G_CALLBACK (nm_dhcp_client_handle_event),
self);
}
static void
@ -197,6 +199,10 @@ dispose (GObject *object)
{
NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (object);
g_signal_handlers_disconnect_by_func (nm_dhcp_listener_get (),
G_CALLBACK (nm_dhcp_client_handle_event),
NM_DHCP_DHCPCD (object));
g_free (priv->pid_file);
G_OBJECT_CLASS (nm_dhcp_dhcpcd_parent_class)->dispose (object);
@ -218,3 +224,13 @@ nm_dhcp_dhcpcd_class_init (NMDhcpDhcpcdClass *dhcpcd_class)
client_class->stop = stop;
}
static void __attribute__((constructor))
register_dhcp_dhclient (void)
{
g_type_init ();
_nm_dhcp_client_register (NM_TYPE_DHCP_DHCPCD,
"dhcpcd",
nm_dhcp_dhcpcd_get_path,
NULL);
}

View file

@ -41,7 +41,5 @@ typedef struct {
GType nm_dhcp_dhcpcd_get_type (void);
const char *nm_dhcp_dhcpcd_get_path (void);
#endif /* __NETWORKMANAGER_DHCP_DHCPCD_H__ */

View file

@ -0,0 +1,289 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright 2014 Red Hat, Inc.
*
*/
#include <config.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <dbus/dbus.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include "nm-dhcp-listener.h"
#include "nm-logging.h"
#include "nm-dbus-manager.h"
#include "nm-dbus-glib-types.h"
#include "nm-glib-compat.h"
#include "NetworkManagerUtils.h"
#define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client"
#define PRIV_SOCK_PATH NMRUNDIR "/private-dhcp"
#define PRIV_SOCK_TAG "dhcp"
typedef struct {
NMDBusManager * dbus_mgr;
guint new_conn_id;
guint dis_conn_id;
GHashTable * proxies;
DBusGProxy * proxy;
} NMDhcpListenerPrivate;
#define NM_DHCP_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_LISTENER, NMDhcpListenerPrivate))
G_DEFINE_TYPE (NMDhcpListener, nm_dhcp_listener, G_TYPE_OBJECT)
enum {
EVENT,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
/***************************************************/
static char *
garray_to_string (GArray *array, const char *key)
{
GString *str;
int i;
unsigned char c;
char *converted = NULL;
g_return_val_if_fail (array != NULL, NULL);
/* Since the DHCP options come through environment variables, they should
* already be UTF-8 safe, but just make sure.
*/
str = g_string_sized_new (array->len);
for (i = 0; i < array->len; i++) {
c = array->data[i];
/* Convert NULLs to spaces and non-ASCII characters to ? */
if (c == '\0')
c = ' ';
else if (c > 127)
c = '?';
str = g_string_append_c (str, c);
}
str = g_string_append_c (str, '\0');
converted = str->str;
if (!g_utf8_validate (converted, -1, NULL))
nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
g_string_free (str, FALSE);
return converted;
}
static char *
get_option (GHashTable *hash, const char *key)
{
GValue *value;
value = g_hash_table_lookup (hash, key);
if (value == NULL)
return NULL;
if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
"DBUS_TYPE_G_UCHAR_ARRAY",
(char *) key);
return NULL;
}
return garray_to_string ((GArray *) g_value_get_boxed (value), key);
}
static void
handle_event (DBusGProxy *proxy,
GHashTable *options,
gpointer user_data)
{
NMDhcpListener *self = NM_DHCP_LISTENER (user_data);
char *iface = NULL;
char *pid_str = NULL;
char *reason = NULL;
gint32 pid;
gboolean handled = FALSE;
iface = get_option (options, "interface");
if (iface == NULL) {
nm_log_warn (LOGD_DHCP, "DHCP event: didn't have associated interface.");
goto out;
}
pid_str = get_option (options, "pid");
pid = (gint32) nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1);
if (pid == -1 || pid != (GPid) pid) {
nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
goto out;
}
reason = get_option (options, "reason");
if (reason == NULL) {
nm_log_warn (LOGD_DHCP, "(pid %d) DHCP event didn't have a reason", pid);
goto out;
}
g_signal_emit (self, signals[EVENT], 0, iface, pid, options, reason, &handled);
if (!handled) {
if (g_ascii_strcasecmp (reason, "RELEASE") == 0) {
/* Ignore event when the dhcp client gets killed and we receive its last message */
nm_log_dbg (LOGD_DHCP, "(pid %d) unhandled RELEASE DHCP event for interface %s", pid, iface);
} else
nm_log_warn (LOGD_DHCP, "(pid %d) unhandled DHCP event for interface %s", pid, iface);
}
out:
g_free (iface);
g_free (pid_str);
g_free (reason);
}
#if HAVE_DBUS_GLIB_100
static void
new_connection_cb (NMDBusManager *mgr,
DBusGConnection *connection,
NMDhcpListener *self)
{
DBusGProxy *proxy;
/* Create a new proxy for the client */
proxy = dbus_g_proxy_new_for_peer (connection, "/", NM_DHCP_CLIENT_DBUS_IFACE);
dbus_g_proxy_add_signal (proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (proxy, "Event", G_CALLBACK (handle_event), self, NULL);
g_hash_table_insert (NM_DHCP_LISTENER_GET_PRIVATE (self)->proxies, connection, proxy);
}
static void
dis_connection_cb (NMDBusManager *mgr,
DBusGConnection *connection,
NMDhcpListener *self)
{
NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (self);
DBusGProxy *proxy;
proxy = g_hash_table_lookup (priv->proxies, connection);
if (proxy) {
dbus_g_proxy_disconnect_signal (proxy, "Event", G_CALLBACK (handle_event), self);
g_hash_table_remove (priv->proxies, connection);
}
}
#endif
/***************************************************/
NMDhcpListener *
nm_dhcp_listener_get (void)
{
static NMDhcpListener *singleton = NULL;
if (G_UNLIKELY (singleton == NULL))
singleton = g_object_new (NM_TYPE_DHCP_LISTENER, NULL);
g_assert (singleton);
return singleton;
}
static void
nm_dhcp_listener_init (NMDhcpListener *self)
{
NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (self);
#if !HAVE_DBUS_GLIB_100
DBusGConnection *g_connection;
#endif
/* Maps DBusGConnection :: DBusGProxy */
priv->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
priv->dbus_mgr = nm_dbus_manager_get ();
#if HAVE_DBUS_GLIB_100
/* Register the socket our DHCP clients will return lease info on */
nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
priv->new_conn_id = g_signal_connect (priv->dbus_mgr,
NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
G_CALLBACK (new_connection_cb),
self);
priv->dis_conn_id = g_signal_connect (priv->dbus_mgr,
NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
G_CALLBACK (dis_connection_cb),
self);
#else
g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
priv->proxy = dbus_g_proxy_new_for_name (g_connection,
"org.freedesktop.nm_dhcp_client",
"/",
NM_DHCP_CLIENT_DBUS_IFACE);
g_assert (priv->proxy);
dbus_g_proxy_add_signal (priv->proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->proxy, "Event", G_CALLBACK (handle_event), self, NULL);
#endif
}
static void
dispose (GObject *object)
{
NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (object);
if (priv->new_conn_id) {
g_signal_handler_disconnect (priv->dbus_mgr, priv->new_conn_id);
priv->new_conn_id = 0;
}
if (priv->dis_conn_id) {
g_signal_handler_disconnect (priv->dbus_mgr, priv->dis_conn_id);
priv->dis_conn_id = 0;
}
priv->dbus_mgr = NULL;
if (priv->proxies) {
g_hash_table_destroy (priv->proxies);
priv->proxies = NULL;
}
g_clear_object (&priv->proxy);
G_OBJECT_CLASS (nm_dhcp_listener_parent_class)->dispose (object);
}
static void
nm_dhcp_listener_class_init (NMDhcpListenerClass *listener_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (listener_class);
g_type_class_add_private (listener_class, sizeof (NMDhcpListenerPrivate));
/* virtual methods */
object_class->dispose = dispose;
/* signals */
signals[EVENT] =
g_signal_new (NM_DHCP_LISTENER_EVENT,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST, 0,
g_signal_accumulator_true_handled,
NULL, NULL,
G_TYPE_BOOLEAN, /* listeners return TRUE if handled */
4,
G_TYPE_STRING, /* iface */
G_TYPE_INT64, /* pid */
G_TYPE_HASH_TABLE, /* options */
G_TYPE_STRING); /* reason */
}

View file

@ -0,0 +1,39 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright 2014 Red Hat, Inc.
*/
#ifndef __NETWORKMANAGER_DHCP_LISTENER_H__
#define __NETWORKMANAGER_DHCP_LISTENER_H__
#include <glib.h>
#include <glib-object.h>
#define NM_TYPE_DHCP_LISTENER (nm_dhcp_listener_get_type ())
#define NM_DHCP_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListener))
#define NM_IS_DHCP_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_LISTENER))
#define NM_DHCP_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListenerClass))
#define NM_DHCP_LISTENER_EVENT "event"
typedef GObject NMDhcpListener;
typedef GObjectClass NMDhcpListenerClass;
GType nm_dhcp_listener_get_type (void);
NMDhcpListener *nm_dhcp_listener_get (void);
#endif /* __NETWORKMANAGER_DHCP_LISTENER_H__ */

View file

@ -23,7 +23,6 @@
#include "config.h"
#include <glib.h>
#include <glib/gi18n.h>
#include <dbus/dbus.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
@ -39,19 +38,13 @@
#include "nm-dhcp-dhcpcd.h"
#include "nm-dhcp-systemd.h"
#include "nm-logging.h"
#include "nm-dbus-manager.h"
#include "nm-config.h"
#include "nm-dbus-glib-types.h"
#include "nm-glib-compat.h"
#include "NetworkManagerUtils.h"
#define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client"
#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */
#define PRIV_SOCK_PATH NMRUNDIR "/private-dhcp"
#define PRIV_SOCK_TAG "dhcp"
/* default to installed helper, but can be modified for testing */
const char *nm_dhcp_helper_path = LIBEXECDIR "/nm-dhcp-helper";
@ -59,78 +52,89 @@ typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid, gbo
typedef struct {
GType client_type;
GetLeaseConfigFunc get_lease_ip_configs_func;
NMDBusManager * dbus_mgr;
guint new_conn_id;
guint dis_conn_id;
GHashTable * proxies;
GHashTable * clients;
DBusGProxy * proxy;
char * default_hostname;
} NMDhcpManagerPrivate;
#define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDhcpManagerPrivate))
G_DEFINE_TYPE (NMDhcpManager, nm_dhcp_manager, G_TYPE_OBJECT)
static char *
garray_to_string (GArray *array, const char *key)
/***************************************************/
typedef struct {
GType gtype;
const char *name;
NMDhcpClientGetPathFunc get_path_func;
NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func;
} ClientDesc;
static GSList *client_descs = NULL;
void
_nm_dhcp_client_register (GType gtype,
const char *name,
NMDhcpClientGetPathFunc get_path_func,
NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func)
{
GString *str;
int i;
unsigned char c;
char *converted = NULL;
ClientDesc *desc;
GSList *iter;
g_return_val_if_fail (array != NULL, NULL);
g_return_if_fail (gtype != G_TYPE_INVALID);
g_return_if_fail (name != NULL);
/* Since the DHCP options come through environment variables, they should
* already be UTF-8 safe, but just make sure.
*/
str = g_string_sized_new (array->len);
for (i = 0; i < array->len; i++) {
c = array->data[i];
/* Convert NULLs to spaces and non-ASCII characters to ? */
if (c == '\0')
c = ' ';
else if (c > 127)
c = '?';
str = g_string_append_c (str, c);
for (iter = client_descs; iter; iter = iter->next) {
desc = iter->data;
g_return_if_fail (desc->gtype != gtype);
g_return_if_fail (strcmp (desc->name, name) != 0);
}
str = g_string_append_c (str, '\0');
converted = str->str;
if (!g_utf8_validate (converted, -1, NULL))
nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
g_string_free (str, FALSE);
return converted;
desc = g_slice_new0 (ClientDesc);
desc->gtype = gtype;
desc->name = name;
desc->get_path_func = get_path_func;
desc->get_lease_configs_func = get_lease_configs_func;
client_descs = g_slist_prepend (client_descs, desc);
nm_log_info (LOGD_DHCP, "Registered DHCP client '%s'", name);
}
static NMDhcpClient *
get_client_for_pid (NMDhcpManager *manager, GPid pid)
static ClientDesc *
find_client_desc (const char *name, GType gtype)
{
NMDhcpManagerPrivate *priv;
GHashTableIter iter;
gpointer value;
GSList *iter;
g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
g_return_val_if_fail (name || gtype, NULL);
priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
for (iter = client_descs; iter; iter = iter->next) {
ClientDesc *desc = iter->data;
g_hash_table_iter_init (&iter, priv->clients);
while (g_hash_table_iter_next (&iter, NULL, &value)) {
NMDhcpClient *candidate = NM_DHCP_CLIENT (value);
if (nm_dhcp_client_get_pid (candidate) == pid)
return candidate;
if (name && strcmp (desc->name, name) != 0)
continue;
if (gtype && desc->name != 0)
continue;
return desc;
}
return NULL;
}
static GType
is_client_enabled (const char *name, GError **error)
{
ClientDesc *desc;
desc = find_client_desc (name, G_TYPE_INVALID);
if (desc && (!desc->get_path_func || desc->get_path_func()))
return desc->gtype;
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
_("'%s' support not found or not enabled."),
name);
return G_TYPE_INVALID;
}
/***************************************************/
static NMDhcpClient *
get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
{
@ -155,172 +159,26 @@ get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
return NULL;
}
static char *
get_option (GHashTable *hash, const char *key)
{
GValue *value;
value = g_hash_table_lookup (hash, key);
if (value == NULL)
return NULL;
if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
"DBUS_TYPE_G_UCHAR_ARRAY",
(char *) key);
return NULL;
}
return garray_to_string ((GArray *) g_value_get_boxed (value), key);
}
static void
nm_dhcp_manager_handle_event (DBusGProxy *proxy,
GHashTable *options,
gpointer user_data)
{
NMDhcpManager *manager = NM_DHCP_MANAGER (user_data);
NMDhcpClient *client;
char *iface = NULL;
char *pid_str = NULL;
char *reason = NULL;
long pid;
iface = get_option (options, "interface");
if (iface == NULL) {
nm_log_warn (LOGD_DHCP, "DHCP event: didn't have associated interface.");
goto out;
}
pid_str = get_option (options, "pid");
pid = nm_utils_ascii_str_to_int64 (pid_str, 10, 0, LONG_MAX, -1);
if (pid == -1 || pid != (GPid)pid) {
nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
goto out;
}
reason = get_option (options, "reason");
client = get_client_for_pid (manager, (GPid) pid);
if (client == NULL) {
if (reason && g_ascii_strcasecmp (reason, "RELEASE") == 0) {
/* This happens regularly, when the dhcp client gets killed and we receive its last message.
* Don't log a warning in this case. */
nm_log_dbg (LOGD_DHCP, "(pid %ld) unhandled RELEASE DHCP event for interface %s", pid, iface);
} else
nm_log_warn (LOGD_DHCP, "(pid %ld) unhandled DHCP event for interface %s", pid, iface);
goto out;
}
if (strcmp (iface, nm_dhcp_client_get_iface (client))) {
nm_log_warn (LOGD_DHCP, "(pid %ld) received DHCP event from unexpected interface '%s' (expected '%s')",
pid, iface, nm_dhcp_client_get_iface (client));
goto out;
}
if (reason == NULL) {
nm_log_warn (LOGD_DHCP, "(pid %ld) DHCP event didn't have a reason", pid);
goto out;
}
nm_dhcp_client_new_options (client, options, reason);
out:
g_free (iface);
g_free (pid_str);
g_free (reason);
}
#if HAVE_DBUS_GLIB_100
static void
new_connection_cb (NMDBusManager *mgr,
DBusGConnection *connection,
NMDhcpManager *self)
{
NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
DBusGProxy *proxy;
/* Create a new proxy for the client */
proxy = dbus_g_proxy_new_for_peer (connection, "/", NM_DHCP_CLIENT_DBUS_IFACE);
dbus_g_proxy_add_signal (proxy,
"Event",
DBUS_TYPE_G_MAP_OF_VARIANT,
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (proxy,
"Event",
G_CALLBACK (nm_dhcp_manager_handle_event),
self,
NULL);
g_hash_table_insert (priv->proxies, connection, proxy);
}
static void
dis_connection_cb (NMDBusManager *mgr,
DBusGConnection *connection,
NMDhcpManager *self)
{
NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
DBusGProxy *proxy;
proxy = g_hash_table_lookup (priv->proxies, connection);
if (proxy) {
dbus_g_proxy_disconnect_signal (proxy,
"Event",
G_CALLBACK (nm_dhcp_manager_handle_event),
self);
g_hash_table_remove (priv->proxies, connection);
}
}
#endif
static GType
get_client_type (const char *client, GError **error)
{
gboolean use_dhclient, use_dhcpcd;
GType client_gtype;
/* If a client was disabled at build-time, these will return FALSE */
use_dhclient = !!nm_dhcp_dhclient_get_path ();
use_dhcpcd = !!nm_dhcp_dhcpcd_get_path ();
if (!client) {
if (use_dhclient)
return NM_TYPE_DHCP_DHCLIENT;
else if (use_dhcpcd)
return NM_TYPE_DHCP_DHCPCD;
else {
g_set_error_literal (error,
NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
_("no usable DHCP client could be found."));
return G_TYPE_INVALID;
if (client)
client_gtype = is_client_enabled (client, error);
else {
/* Fallbacks */
client_gtype = is_client_enabled ("dhclient", NULL);
if (client_gtype == G_TYPE_INVALID)
client_gtype = is_client_enabled ("dhcpcd", NULL);
if (client_gtype == G_TYPE_INVALID)
client_gtype = is_client_enabled ("internal", NULL);
if (client_gtype == G_TYPE_INVALID) {
g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
_("no usable DHCP client could be found."));
}
}
if (!strcmp (client, "dhclient")) {
if (!use_dhclient) {
g_set_error_literal (error,
NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
_("'dhclient' could not be found or was disabled."));
return G_TYPE_INVALID;
}
return NM_TYPE_DHCP_DHCLIENT;
}
if (!strcmp (client, "dhcpcd")) {
if (!use_dhcpcd) {
g_set_error_literal (error,
NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
_("'dhcpcd' could not be found or was disabled."));
return G_TYPE_INVALID;
}
return NM_TYPE_DHCP_DHCPCD;
}
if (!strcmp (client, "internal"))
return NM_TYPE_DHCP_SYSTEMD;
g_set_error (error,
NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
_("unsupported DHCP client '%s'"), client);
return G_TYPE_INVALID;
return client_gtype;
}
static void client_state_changed (NMDhcpClient *client,
@ -366,7 +224,8 @@ client_start (NMDhcpManager *self,
const char *dhcp_anycast_addr,
const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy)
NMSettingIP6ConfigPrivacy privacy,
const char *last_ip4_address)
{
NMDhcpManagerPrivate *priv;
NMDhcpClient *client;
@ -407,7 +266,7 @@ client_start (NMDhcpManager *self,
if (ipv6)
success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, hostname, info_only, privacy);
else
success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname);
success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, last_ip4_address);
if (!success) {
remove_client (self, client);
@ -438,7 +297,8 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
const char *dhcp_hostname,
const char *dhcp_client_id,
guint32 timeout,
const char *dhcp_anycast_addr)
const char *dhcp_anycast_addr,
const char *last_ip_address)
{
const char *hostname = NULL;
@ -448,7 +308,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
hostname = get_send_hostname (self, dhcp_hostname);
return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE,
dhcp_client_id, timeout, dhcp_anycast_addr, hostname,
FALSE, 0);
FALSE, 0, last_ip_address);
}
/* Caller owns a reference to the NMDhcpClient on return */
@ -474,7 +334,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
hostname = get_send_hostname (self, dhcp_hostname);
return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE,
NULL, timeout, dhcp_anycast_addr, hostname, info_only,
privacy);
privacy, NULL);
}
void
@ -497,16 +357,15 @@ nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
const char *uuid,
gboolean ipv6)
{
NMDhcpManagerPrivate *priv;
ClientDesc *desc;
g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
g_return_val_if_fail (iface != NULL, NULL);
g_return_val_if_fail (uuid != NULL, NULL);
priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
if (priv->get_lease_ip_configs_func)
return priv->get_lease_ip_configs_func (iface, uuid, ipv6);
desc = find_client_desc (NULL, NM_DHCP_MANAGER_GET_PRIVATE (self)->client_type);
if (desc && desc->get_lease_configs_func)
return desc->get_lease_configs_func (iface, uuid, ipv6);
return NULL;
}
@ -527,24 +386,20 @@ static void
nm_dhcp_manager_init (NMDhcpManager *self)
{
NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
NMConfig *config = nm_config_get ();
const char *client;
GError *error = NULL;
#if !HAVE_DBUS_GLIB_100
DBusGConnection *g_connection;
#endif
/* Maps DBusGConnection :: DBusGProxy */
priv->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
/* Client-specific setup */
client = nm_config_get_dhcp_client (nm_config_get ());
priv->client_type = get_client_type (client, &error);
client = nm_config_get_dhcp_client (config);
if (nm_config_get_configure_and_quit (config)) {
if (g_strcmp0 (client, "internal") != 0)
nm_log_warn (LOGD_DHCP, "Using internal DHCP client since configure-and-quit is set.");
client = "internal";
}
if (priv->client_type == NM_TYPE_DHCP_DHCLIENT)
priv->get_lease_ip_configs_func = nm_dhcp_dhclient_get_lease_ip_configs;
else if (priv->client_type == NM_TYPE_DHCP_SYSTEMD)
priv->get_lease_ip_configs_func = nm_dhcp_systemd_get_lease_ip_configs;
else if (priv->client_type == G_TYPE_INVALID) {
priv->client_type = get_client_type (client, &error);
if (priv->client_type == G_TYPE_INVALID) {
nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
error->message);
}
@ -554,33 +409,6 @@ nm_dhcp_manager_init (NMDhcpManager *self)
NULL,
(GDestroyNotify) g_object_unref);
g_assert (priv->clients);
priv->dbus_mgr = nm_dbus_manager_get ();
#if HAVE_DBUS_GLIB_100
/* Register the socket our DHCP clients will return lease info on */
nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
priv->new_conn_id = g_signal_connect (priv->dbus_mgr,
NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
(GCallback) new_connection_cb,
self);
priv->dis_conn_id = g_signal_connect (priv->dbus_mgr,
NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
(GCallback) dis_connection_cb,
self);
#else
g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
priv->proxy = dbus_g_proxy_new_for_name (g_connection,
"org.freedesktop.nm_dhcp_client",
"/",
NM_DHCP_CLIENT_DBUS_IFACE);
g_assert (priv->proxy);
dbus_g_proxy_add_signal (priv->proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->proxy, "Event",
G_CALLBACK (nm_dhcp_manager_handle_event),
self,
NULL);
#endif
}
static void
@ -596,23 +424,6 @@ dispose (GObject *object)
g_list_free (values);
}
if (priv->new_conn_id) {
g_signal_handler_disconnect (priv->dbus_mgr, priv->new_conn_id);
priv->new_conn_id = 0;
}
if (priv->dis_conn_id) {
g_signal_handler_disconnect (priv->dbus_mgr, priv->dis_conn_id);
priv->dis_conn_id = 0;
}
priv->dbus_mgr = NULL;
if (priv->proxies) {
g_hash_table_destroy (priv->proxies);
priv->proxies = NULL;
}
if (priv->proxy)
g_object_unref (priv->proxy);
G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object);
}

View file

@ -61,7 +61,8 @@ NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *manager,
const char *dhcp_hostname,
const char *dhcp_client_id,
guint32 timeout,
const char *dhcp_anycast_addr);
const char *dhcp_anycast_addr,
const char *last_ip_address);
NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager,
const char *iface,

View file

@ -374,7 +374,7 @@ get_leasefile_path (const char *iface, const char *uuid, gboolean ipv6)
iface);
}
GSList *
static GSList *
nm_dhcp_systemd_get_lease_ip_configs (const char *iface,
const char *uuid,
gboolean ipv6)
@ -401,6 +401,28 @@ nm_dhcp_systemd_get_lease_ip_configs (const char *iface,
/************************************************************/
static void
_save_client_id (NMDhcpSystemd *self,
uint8_t type,
const uint8_t *client_id,
size_t len)
{
gs_unref_bytes GBytes *b = NULL;
gs_free char *buf = NULL;
g_return_if_fail (self != NULL);
g_return_if_fail (client_id != NULL);
g_return_if_fail (len > 0);
if (!nm_dhcp_client_get_client_id (NM_DHCP_CLIENT (self))) {
buf = g_malloc (len + 1);
buf[0] = type;
memcpy (buf + 1, client_id, len);
b = g_bytes_new (buf, len + 1);
nm_dhcp_client_set_client_id (NM_DHCP_CLIENT (self), b);
}
}
static void
bound4_handle (NMDhcpSystemd *self)
{
@ -428,9 +450,17 @@ bound4_handle (NMDhcpSystemd *self)
TRUE,
&error);
if (ip4_config) {
const uint8_t *client_id = NULL;
size_t client_id_len = 0;
uint8_t type = 0;
add_requests_to_options (options, dhcp4_requests);
sd_dhcp_lease_save (lease, priv->lease_file);
client_id = sd_dhcp_client_get_client_id(priv->client4, &type, &client_id_len);
if (client_id)
_save_client_id (self, type, client_id, client_id_len);
nm_dhcp_client_set_state (NM_DHCP_CLIENT (self),
NM_DHCP_STATE_BOUND,
G_OBJECT (ip4_config),
@ -485,18 +515,17 @@ get_arp_type (const GByteArray *hwaddr)
}
static gboolean
ip4_start (NMDhcpClient *client,
const char *dhcp_client_id,
const char *dhcp_anycast_addr,
const char *hostname)
ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
{
NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
const char *iface = nm_dhcp_client_get_iface (client);
const GByteArray *hwaddr;
sd_dhcp_lease *lease = NULL;
GBytes *override_client_id;
const uint8_t *client_id = NULL;
size_t client_id_len = 0;
struct in_addr last_addr;
struct in_addr last_addr = { 0 };
const char *hostname;
int r, i;
g_assert (priv->client4 == NULL);
@ -549,36 +578,38 @@ ip4_start (NMDhcpClient *client,
sd_dhcp_lease_load (priv->lease_file, &lease);
if (lease) {
r = sd_dhcp_lease_get_address (lease, &last_addr);
if (r == 0) {
r = sd_dhcp_client_set_request_address (priv->client4, &last_addr);
if (r < 0) {
nm_log_warn (LOGD_DHCP4, "(%s): failed to set last IPv4 address (%d)", iface, r);
goto error;
}
if (last_ip4_address)
inet_pton (AF_INET, last_ip4_address, &last_addr);
else if (lease)
sd_dhcp_lease_get_address (lease, &last_addr);
if (last_addr.s_addr) {
r = sd_dhcp_client_set_request_address (priv->client4, &last_addr);
if (r < 0) {
nm_log_warn (LOGD_DHCP4, "(%s): failed to set last IPv4 address (%d)", iface, r);
goto error;
}
}
if (dhcp_client_id) {
gs_unref_bytes GBytes *b = NULL;
b = nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id);
if (b) {
client_id = (const guint8 *) g_bytes_get_data (b, &client_id_len);
g_assert (client_id && client_id_len);
sd_dhcp_client_set_client_id (priv->client4,
client_id[0],
client_id + 1,
client_id_len - 1);
}
} else {
override_client_id = nm_dhcp_client_get_client_id (client);
if (override_client_id) {
client_id = g_bytes_get_data (override_client_id, &client_id_len);
g_assert (client_id && client_id_len);
sd_dhcp_client_set_client_id (priv->client4,
client_id[0],
client_id + 1,
client_id_len - 1);
} else if (lease) {
r = sd_dhcp_lease_get_client_id (lease, &client_id, &client_id_len);
if (r == 0 && client_id_len) {
sd_dhcp_client_set_client_id (priv->client4,
client_id[0],
client_id + 1,
client_id_len - 1);
_save_client_id (NM_DHCP_SYSTEMD (client),
client_id[0],
client_id + 1,
client_id_len - 1);
}
}
@ -591,6 +622,7 @@ ip4_start (NMDhcpClient *client,
sd_dhcp_client_set_request_option (priv->client4, dhcp4_requests[i].num);
}
hostname = nm_dhcp_client_get_hostname (client);
if (hostname) {
r = sd_dhcp_client_set_hostname (priv->client4, hostname);
if (r < 0) {
@ -653,7 +685,6 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data)
static gboolean
ip6_start (NMDhcpClient *client,
const char *dhcp_anycast_addr,
const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
@ -808,3 +839,13 @@ nm_dhcp_systemd_class_init (NMDhcpSystemdClass *sdhcp_class)
client_class->stop = stop;
}
static void __attribute__((constructor))
register_dhcp_dhclient (void)
{
g_type_init ();
_nm_dhcp_client_register (NM_TYPE_DHCP_SYSTEMD,
"internal",
NULL,
nm_dhcp_systemd_get_lease_ip_configs);
}

View file

@ -41,9 +41,5 @@ typedef struct {
GType nm_dhcp_systemd_get_type (void);
GSList *nm_dhcp_systemd_get_lease_ip_configs (const char *iface,
const char *uuid,
gboolean ipv6);
#endif /* NM_DHCP_SYSTEMD_H */

View file

@ -709,46 +709,17 @@ GBytes *
nm_dhcp_utils_client_id_string_to_bytes (const char *client_id)
{
GBytes *bytes = NULL;
guint i = 0, x = 0;
guint len;
char *c;
int a;
g_return_val_if_fail (client_id && client_id[0], NULL);
/* Accept a binary client ID in hex digits with the ':' delimiter,
* otherwise treat it as a string.
*/
len = strlen (client_id);
c = g_malloc0 (len / 2 + 1);
while (client_id[i]) {
a = g_ascii_xdigit_value (client_id[i++]);
if (a >= 0) {
if (client_id[i] != ':') {
c[x] = ((guint8) a << 4);
a = g_ascii_xdigit_value (client_id[i++]);
}
if (a >= 0)
c[x++] |= (guint8) a;
}
if (client_id[i]) {
if (client_id[i] != ':' || !client_id[i + 1]) {
/* missing or trailing ':' is invalid for hex-format */
a = -1;
}
i++;
}
if (a < 0) {
g_clear_pointer (&c, g_free);
break;
}
}
if (c) {
g_assert (x > 0);
bytes = g_bytes_new_take (c, x);
} else {
/* Try as hex encoded */
if (strchr (client_id, ':'))
bytes = nm_utils_hexstr2bin (client_id);
if (!bytes) {
/* Fall back to string */
len = strlen (client_id);
c = g_malloc (len + 1);
c[0] = 0; /* type: non-hardware address per RFC 2132 section 9.14 */
memcpy (c + 1, client_id, len);

View file

@ -18,6 +18,10 @@ noinst_PROGRAMS = \
####### dhclient leases test #######
test_dhcp_dhclient_SOURCES = \
$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.h \
$(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c \
$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.h \
$(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c \
test-dhcp-dhclient.c
test_dhcp_dhclient_LDADD = \

View file

@ -23,30 +23,42 @@
#include <unistd.h>
#include <arpa/inet.h>
#include "gsystem-local-alloc.h"
#include "NetworkManagerUtils.h"
#include "nm-dhcp-dhclient-utils.h"
#include "nm-dhcp-utils.h"
#include "nm-utils.h"
#include "nm-ip4-config.h"
#include "nm-platform.h"
#define DEBUG 0
#define DEBUG 1
static void
test_config (const char *orig,
const char *expected,
const char *hostname,
const char *dhcp_client_id,
GBytes *expected_new_client_id,
const char *iface,
const char *anycast_addr)
{
char *new;
gs_free char *new = NULL;
gs_unref_bytes GBytes *client_id = NULL;
gs_unref_bytes GBytes *new_client_id = NULL;
if (dhcp_client_id) {
client_id = nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id);
g_assert (client_id);
}
new = nm_dhcp_dhclient_create_config (iface,
FALSE,
dhcp_client_id,
client_id,
anycast_addr,
hostname,
"/path/to/dhclient.conf",
orig);
orig,
&new_client_id);
g_assert (new != NULL);
#if DEBUG
@ -60,9 +72,13 @@ test_config (const char *orig,
new, expected);
}
#endif
g_assert (strlen (new) == strlen (expected));
g_assert (strcmp (new, expected) == 0);
g_free (new);
g_assert_cmpstr (new, ==, expected);
if (expected_new_client_id) {
g_assert (new_client_id);
g_assert (g_bytes_equal (new_client_id, expected_new_client_id));
} else
g_assert (new_client_id == NULL);
}
/*******************************************/
@ -84,11 +100,7 @@ static const char *orig_missing_expected = \
static void
test_orig_missing (void)
{
test_config (NULL, orig_missing_expected,
NULL,
NULL,
"eth0",
NULL);
test_config (NULL, orig_missing_expected, NULL, NULL, NULL, "eth0", NULL);
}
/*******************************************/
@ -119,6 +131,7 @@ test_override_client_id (void)
test_config (override_client_id_orig, override_client_id_expected,
NULL,
"11:22:33:44:55:66",
NULL,
"eth0",
NULL);
}
@ -147,6 +160,7 @@ test_quote_client_id (void)
test_config (NULL, quote_client_id_expected,
NULL,
"1234",
NULL,
"eth0",
NULL);
}
@ -175,6 +189,7 @@ test_ascii_client_id (void)
test_config (NULL, ascii_client_id_expected,
NULL,
"qb:cd:ef:12:34:56",
NULL,
"eth0",
NULL);
}
@ -184,7 +199,7 @@ test_ascii_client_id (void)
static const char *hex_single_client_id_expected = \
"# Created by NetworkManager\n"
"\n"
"send dhcp-client-identifier ab:cd:e:12:34:56; # added by NetworkManager\n"
"send dhcp-client-identifier ab:cd:0e:12:34:56; # added by NetworkManager\n"
"\n"
"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
@ -203,6 +218,84 @@ test_hex_single_client_id (void)
test_config (NULL, hex_single_client_id_expected,
NULL,
"ab:cd:e:12:34:56",
NULL,
"eth0",
NULL);
}
/*******************************************/
static const char *existing_hex_client_id_orig = \
"send dhcp-client-identifier 00:30:04:20:7A:08;\n";
static const char *existing_hex_client_id_expected = \
"# Created by NetworkManager\n"
"# Merged from /path/to/dhclient.conf\n"
"\n"
"send dhcp-client-identifier 00:30:04:20:7A:08;\n"
"\n"
"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
"option wpad code 252 = string;\n"
"\n"
"also request rfc3442-classless-static-routes;\n"
"also request ms-classless-static-routes;\n"
"also request static-routes;\n"
"also request wpad;\n"
"also request ntp-servers;\n"
"\n";
static void
test_existing_hex_client_id (void)
{
gs_unref_bytes GBytes *new_client_id = NULL;
const guint8 bytes[] = { 0x00, 0x30, 0x04,0x20, 0x7A, 0x08 };
new_client_id = g_bytes_new (bytes, sizeof (bytes));
test_config (existing_hex_client_id_orig, existing_hex_client_id_expected,
NULL,
NULL,
new_client_id,
"eth0",
NULL);
}
/*******************************************/
#define EACID "qb:cd:ef:12:34:56"
static const char *existing_ascii_client_id_orig = \
"send dhcp-client-identifier \"" EACID "\";\n";
static const char *existing_ascii_client_id_expected = \
"# Created by NetworkManager\n"
"# Merged from /path/to/dhclient.conf\n"
"\n"
"send dhcp-client-identifier \"" EACID "\";\n"
"\n"
"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
"option wpad code 252 = string;\n"
"\n"
"also request rfc3442-classless-static-routes;\n"
"also request ms-classless-static-routes;\n"
"also request static-routes;\n"
"also request wpad;\n"
"also request ntp-servers;\n"
"\n";
static void
test_existing_ascii_client_id (void)
{
gs_unref_bytes GBytes *new_client_id = NULL;
char buf[STRLEN (EACID) + 1] = { 0 };
memcpy (buf + 1, EACID, STRLEN (EACID));
new_client_id = g_bytes_new (buf, sizeof (buf));
test_config (existing_ascii_client_id_orig, existing_ascii_client_id_expected,
NULL,
NULL,
new_client_id,
"eth0",
NULL);
}
@ -235,6 +328,7 @@ test_override_hostname (void)
test_config (override_hostname_orig, override_hostname_expected,
"blahblah",
NULL,
NULL,
"eth0",
NULL);
}
@ -267,6 +361,7 @@ static void
test_existing_alsoreq (void)
{
test_config (existing_alsoreq_orig, existing_alsoreq_expected,
NULL,
NULL,
NULL,
"eth0",
@ -305,6 +400,7 @@ static void
test_existing_multiline_alsoreq (void)
{
test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected,
NULL,
NULL,
NULL,
"eth0",
@ -616,6 +712,8 @@ main (int argc, char **argv)
g_test_add_func ("/dhcp/dhclient/quote_client_id", test_quote_client_id);
g_test_add_func ("/dhcp/dhclient/ascii_client_id", test_ascii_client_id);
g_test_add_func ("/dhcp/dhclient/hex_single_client_id", test_hex_single_client_id);
g_test_add_func ("/dhcp/dhclient/existing-hex-client-id", test_existing_hex_client_id);
g_test_add_func ("/dhcp/dhclient/existing-ascii-client-id", test_existing_ascii_client_id);
g_test_add_func ("/dhcp/dhclient/override_hostname", test_override_hostname);
g_test_add_func ("/dhcp/dhclient/existing_alsoreq", test_existing_alsoreq);
g_test_add_func ("/dhcp/dhclient/existing_multiline_alsoreq", test_existing_multiline_alsoreq);

281
src/main-utils.c Normal file
View file

@ -0,0 +1,281 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2004 - 2012 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
#include <config.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <locale.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gmodule.h>
#include "main-utils.h"
#include "nm-posix-signals.h"
#include "nm-logging.h"
static sigset_t signal_set;
static gboolean *quit_early = NULL;
/*
* Thread function waiting for signals and processing them.
* Wait for signals in signal set. The semantics of sigwait() require that all
* threads (including the thread calling sigwait()) have the signal masked, for
* reliable operation. Otherwise, a signal that arrives while this thread is
* not blocked in sigwait() might be delivered to another thread.
*/
static void *
signal_handling_thread (void *arg)
{
GMainLoop *main_loop = arg;
int signo;
while (1) {
sigwait (&signal_set, &signo);
switch (signo) {
case SIGINT:
case SIGTERM:
nm_log_info (LOGD_CORE, "caught signal %d, shutting down normally.", signo);
*quit_early = TRUE; /* for quitting before entering the main loop */
g_main_loop_quit (main_loop);
break;
case SIGHUP:
/* Reread config stuff like system config files, VPN service files, etc */
nm_log_info (LOGD_CORE, "caught signal %d, not supported yet.", signo);
break;
case SIGPIPE:
/* silently ignore signal */
break;
default:
nm_log_err (LOGD_CORE, "caught unexpected signal %d", signo);
break;
}
}
return NULL;
}
/**
* nm_main_utils_setup_signals:
* @main_loop: the #GMainLoop to quit when SIGINT or SIGTERM is received
* @quit_early: location of a variable that will be set to TRUE when
* SIGINT or SIGTERM is received
*
* Mask the signals we are interested in and create a signal handling thread.
* Because all threads inherit the signal mask from their creator, all threads
* in the process will have the signals masked. That's why setup_signals() has
* to be called before creating other threads.
*
* Returns: %TRUE on success
*/
gboolean
nm_main_utils_setup_signals (GMainLoop *main_loop, gboolean *quit_early_ptr)
{
pthread_t signal_thread_id;
sigset_t old_sig_mask;
int status;
g_return_val_if_fail (main_loop != NULL, FALSE);
g_return_val_if_fail (quit_early_ptr != NULL, FALSE);
quit_early = quit_early_ptr;
sigemptyset (&signal_set);
sigaddset (&signal_set, SIGHUP);
sigaddset (&signal_set, SIGINT);
sigaddset (&signal_set, SIGTERM);
sigaddset (&signal_set, SIGPIPE);
/* Block all signals of interest. */
status = pthread_sigmask (SIG_BLOCK, &signal_set, &old_sig_mask);
if (status != 0) {
fprintf (stderr, _("Failed to set signal mask: %d"), status);
return FALSE;
}
/* Save original mask so that we could use it for child processes. */
nm_save_original_signal_mask (old_sig_mask);
/* Create the signal handling thread. */
status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, main_loop);
if (status != 0) {
fprintf (stderr, _("Failed to create signal handling thread: %d"), status);
return FALSE;
}
return TRUE;
}
gboolean
nm_main_utils_write_pidfile (const char *pidfile)
{
char pid[16];
int fd;
gboolean success = FALSE;
if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) {
fprintf (stderr, _("Opening %s failed: %s\n"), pidfile, strerror (errno));
return FALSE;
}
g_snprintf (pid, sizeof (pid), "%d", getpid ());
if (write (fd, pid, strlen (pid)) < 0)
fprintf (stderr, _("Writing to %s failed: %s\n"), pidfile, strerror (errno));
else
success = TRUE;
if (close (fd))
fprintf (stderr, _("Closing %s failed: %s\n"), pidfile, strerror (errno));
return success;
}
/**
* nm_main_utils_check_pidfile:
* @pidfile: the pid file
* @name: the process name
*
* Checks whether the pidfile already exists and contains PID of a running
* process.
*
* Returns: %TRUE if the specified pidfile already exists and contains the PID
* of a running process named @name, or %FALSE if not
*/
gboolean
nm_main_utils_check_pidfile (const char *pidfile, const char *name)
{
char *contents = NULL;
gsize len = 0;
glong pid;
char *proc_cmdline = NULL;
gboolean nm_running = FALSE;
const char *process_name;
/* Setup runtime directory */
if (g_mkdir_with_parents (NMRUNDIR, 0755) != 0) {
nm_log_err (LOGD_CORE, "Cannot create '%s': %s", NMRUNDIR, strerror (errno));
exit (1);
}
if (!g_file_get_contents (pidfile, &contents, &len, NULL))
return FALSE;
if (len <= 0)
goto done;
errno = 0;
pid = strtol (contents, NULL, 10);
if (pid <= 0 || pid > 65536 || errno)
goto done;
g_free (contents);
proc_cmdline = g_strdup_printf ("/proc/%ld/cmdline", pid);
if (!g_file_get_contents (proc_cmdline, &contents, &len, NULL))
goto done;
process_name = strrchr (contents, '/');
if (process_name)
process_name++;
else
process_name = contents;
if (strcmp (process_name, name) == 0) {
/* Check that the process exists */
if (kill (pid, 0) == 0) {
fprintf (stderr, _("%s is already running (pid %ld)\n"), name, pid);
nm_running = TRUE;
}
}
done:
g_free (proc_cmdline);
g_free (contents);
return nm_running;
}
gboolean
nm_main_utils_early_setup (const char *progname,
char **argv[],
int *argc,
GOptionEntry *options,
GOptionEntry *more_options,
const char *summary)
{
GOptionContext *opt_ctx = NULL;
GError *error = NULL;
gboolean success = FALSE;
int i;
/* Make GIO ignore the remote VFS service; otherwise it tries to use the
* session bus to contact the remote service, and NM shouldn't ever be
* talking on the session bus. See rh #588745
*/
setenv ("GIO_USE_VFS", "local", 1);
/*
* Set the umask to 0022, which results in 0666 & ~0022 = 0644.
* Otherwise, if root (or an su'ing user) has a wacky umask, we could
* write out an unreadable resolv.conf.
*/
umask (022);
/* Ensure gettext() gets the right environment (bgo #666516) */
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
if (getuid () != 0) {
fprintf (stderr, _("You must be root to run %s!\n"), progname);
exit (1);
}
for (i = 0; options[i].long_name; i++) {
if (!strcmp (options[i].long_name, "log-level"))
options[i].description = g_strdup_printf (options[i].description, nm_logging_all_levels_to_string ());
else if (!strcmp (options[i].long_name, "log-domains"))
options[i].description = g_strdup_printf (options[i].description, nm_logging_all_domains_to_string ());
}
/* Parse options */
opt_ctx = g_option_context_new (NULL);
g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE);
g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
g_option_context_set_help_enabled (opt_ctx, TRUE);
g_option_context_add_main_entries (opt_ctx, options, NULL);
if (more_options)
g_option_context_add_main_entries (opt_ctx, more_options, NULL);
g_option_context_set_summary (opt_ctx, summary);
success = g_option_context_parse (opt_ctx, argc, argv, &error);
if (!success) {
fprintf (stderr, _("%s. Please use --help to see a list of valid options.\n"),
error->message);
g_clear_error (&error);
}
g_option_context_free (opt_ctx);
return success;
}

39
src/main-utils.h Normal file
View file

@ -0,0 +1,39 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2014 Red Hat, Inc.
*/
#ifndef __MAIN_UTILS_H__
#define __MAIN_UTILS_H__
#include <glib.h>
gboolean nm_main_utils_setup_signals (GMainLoop *main_loop, gboolean *quit_early_ptr);
gboolean nm_main_utils_write_pidfile (const char *pidfile);
gboolean nm_main_utils_check_pidfile (const char *pidfile, const char *name);
gboolean nm_main_utils_early_setup (const char *progname,
char **argv[],
int *argc,
GOptionEntry *options,
GOptionEntry *more_options,
const char *summary);
#endif /* __MAIN_UTILS_H__ */

View file

@ -42,6 +42,7 @@
#include "gsystem-local-alloc.h"
#include "nm-dbus-interface.h"
#include "NetworkManagerUtils.h"
#include "main-utils.h"
#include "nm-manager.h"
#include "nm-linux-platform.h"
#include "nm-dns-manager.h"
@ -65,161 +66,7 @@
#define NM_DEFAULT_PID_FILE NMRUNDIR "/NetworkManager.pid"
#define NM_DEFAULT_SYSTEM_STATE_FILE NMSTATEDIR "/NetworkManager.state"
/*
* Globals
*/
static GMainLoop *main_loop = NULL;
static gboolean quit_early = FALSE;
static sigset_t signal_set;
void *signal_handling_thread (void *arg);
/*
* Thread function waiting for signals and processing them.
* Wait for signals in signal set. The semantics of sigwait() require that all
* threads (including the thread calling sigwait()) have the signal masked, for
* reliable operation. Otherwise, a signal that arrives while this thread is
* not blocked in sigwait() might be delivered to another thread.
*/
void *
signal_handling_thread (void *arg)
{
int signo;
while (1) {
sigwait (&signal_set, &signo);
switch (signo) {
case SIGINT:
case SIGTERM:
nm_log_info (LOGD_CORE, "caught signal %d, shutting down normally.", signo);
quit_early = TRUE; /* for quitting before entering the main loop */
g_main_loop_quit (main_loop);
break;
case SIGHUP:
/* Reread config stuff like system config files, VPN service files, etc */
nm_log_info (LOGD_CORE, "caught signal %d, not supported yet.", signo);
break;
case SIGPIPE:
/* silently ignore signal */
break;
default:
nm_log_err (LOGD_CORE, "caught unexpected signal %d", signo);
break;
}
}
return NULL;
}
/*
* Mask the signals we are interested in and create a signal handling thread.
* Because all threads inherit the signal mask from their creator, all threads
* in the process will have the signals masked. That's why setup_signals() has
* to be called before creating other threads.
*/
static gboolean
setup_signals (void)
{
pthread_t signal_thread_id;
sigset_t old_sig_mask;
int status;
sigemptyset (&signal_set);
sigaddset (&signal_set, SIGHUP);
sigaddset (&signal_set, SIGINT);
sigaddset (&signal_set, SIGTERM);
sigaddset (&signal_set, SIGPIPE);
/* Block all signals of interest. */
status = pthread_sigmask (SIG_BLOCK, &signal_set, &old_sig_mask);
if (status != 0) {
fprintf (stderr, _("Failed to set signal mask: %d"), status);
return FALSE;
}
/* Save original mask so that we could use it for child processes. */
nm_save_original_signal_mask (old_sig_mask);
/* Create the signal handling thread. */
status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, NULL);
if (status != 0) {
fprintf (stderr, _("Failed to create signal handling thread: %d"), status);
return FALSE;
}
return TRUE;
}
static gboolean
write_pidfile (const char *pidfile)
{
char pid[16];
int fd;
gboolean success = FALSE;
if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) {
fprintf (stderr, _("Opening %s failed: %s\n"), pidfile, strerror (errno));
return FALSE;
}
g_snprintf (pid, sizeof (pid), "%d", getpid ());
if (write (fd, pid, strlen (pid)) < 0)
fprintf (stderr, _("Writing to %s failed: %s\n"), pidfile, strerror (errno));
else
success = TRUE;
if (close (fd))
fprintf (stderr, _("Closing %s failed: %s\n"), pidfile, strerror (errno));
return success;
}
/* Check whether the pidfile already exists and contains PID of a running NetworkManager
* Returns: FALSE - specified pidfile doesn't exist or doesn't contain PID of a running NM process
* TRUE - specified pidfile already exists and contains PID of a running NM process
*/
static gboolean
check_pidfile (const char *pidfile)
{
char *contents = NULL;
gsize len = 0;
glong pid;
char *proc_cmdline = NULL;
gboolean nm_running = FALSE;
const char *process_name;
if (!g_file_get_contents (pidfile, &contents, &len, NULL))
return FALSE;
if (len <= 0)
goto done;
errno = 0;
pid = strtol (contents, NULL, 10);
if (pid <= 0 || pid > 65536 || errno)
goto done;
g_free (contents);
proc_cmdline = g_strdup_printf ("/proc/%ld/cmdline", pid);
if (!g_file_get_contents (proc_cmdline, &contents, &len, NULL))
goto done;
process_name = strrchr (contents, '/');
if (process_name)
process_name++;
else
process_name = contents;
if (strcmp (process_name, "NetworkManager") == 0) {
/* Check that the process exists */
if (kill (pid, 0) == 0) {
fprintf (stderr, _("NetworkManager is already running (pid %ld)\n"), pid);
nm_running = TRUE;
}
}
done:
g_free (proc_cmdline);
g_free (contents);
return nm_running;
}
static gboolean
parse_state_file (const char *filename,
@ -330,6 +177,13 @@ _init_nm_debug (const char *debug)
}
}
static void
manager_configure_quit (NMManager *manager, gpointer user_data)
{
nm_log_info (LOGD_CORE, "quitting now that startup is complete");
g_main_loop_quit (main_loop);
}
/*
* main
*
@ -337,7 +191,6 @@ _init_nm_debug (const char *debug)
int
main (int argc, char *argv[])
{
GOptionContext *opt_ctx = NULL;
char *opt_log_level = NULL;
char *opt_log_domains = NULL;
gboolean become_daemon = TRUE, run_from_build_dir = FALSE;
@ -347,7 +200,6 @@ main (int argc, char *argv[])
gs_free char *state_file = NULL;
gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE, wimax_enabled = TRUE;
gboolean success, show_version = FALSE;
int i;
NMManager *manager = NULL;
gs_unref_object NMVpnManager *vpn_manager = NULL;
gs_unref_object NMDnsManager *dns_mgr = NULL;
@ -361,6 +213,7 @@ main (int argc, char *argv[])
GError *error = NULL;
gboolean wrote_pidfile = FALSE;
char *bad_domains = NULL;
gboolean quit_early = FALSE;
GOptionEntry options[] = {
{ "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Print NetworkManager version and exit"), NULL },
@ -377,66 +230,15 @@ main (int argc, char *argv[])
{NULL}
};
/* Make GIO ignore the remote VFS service; otherwise it tries to use the
* session bus to contact the remote service, and NM shouldn't ever be
* talking on the session bus. See rh #588745
*/
setenv ("GIO_USE_VFS", "local", 1);
main_loop = g_main_loop_new (NULL, FALSE);
/*
* Set the umask to 0022, which results in 0666 & ~0022 = 0644.
* Otherwise, if root (or an su'ing user) has a wacky umask, we could
* write out an unreadable resolv.conf.
*/
umask (022);
/* Set locale to be able to use environment variables */
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
if (!g_module_supported ()) {
fprintf (stderr, _("GModules are not supported on your platform!\n"));
if (!nm_main_utils_early_setup ("NetworkManager",
&argv,
&argc,
options,
nm_config_get_options (),
_("NetworkManager monitors all network connections and automatically\nchooses the best connection to use. It also allows the user to\nspecify wireless access points which wireless cards in the computer\nshould associate with.")))
exit (1);
}
if (getuid () != 0) {
fprintf (stderr, _("You must be root to run NetworkManager!\n"));
exit (1);
}
for (i = 0; options[i].long_name; i++) {
if (!strcmp (options[i].long_name, "log-level")) {
options[i].description = g_strdup_printf (options[i].description,
nm_logging_all_levels_to_string ());
} else if (!strcmp (options[i].long_name, "log-domains")) {
options[i].description = g_strdup_printf (options[i].description,
nm_logging_all_domains_to_string ());
}
}
/* Parse options */
opt_ctx = g_option_context_new (NULL);
g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE);
g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
g_option_context_set_help_enabled (opt_ctx, TRUE);
g_option_context_add_main_entries (opt_ctx, options, NULL);
g_option_context_add_main_entries (opt_ctx, nm_config_get_options (), NULL);
g_option_context_set_summary (opt_ctx,
_("NetworkManager monitors all network connections and automatically\nchooses the best connection to use. It also allows the user to\nspecify wireless access points which wireless cards in the computer\nshould associate with."));
success = g_option_context_parse (opt_ctx, &argc, &argv, &error);
g_option_context_free (opt_ctx);
if (!success) {
fprintf (stderr, _("%s. Please use --help to see a list of valid options.\n"),
error->message);
g_clear_error (&error);
exit (1);
}
if (show_version) {
fprintf (stdout, NM_DIST_VERSION "\n");
@ -482,12 +284,6 @@ main (int argc, char *argv[])
g_free (path);
}
/* Setup runtime directory */
if (g_mkdir_with_parents (NMRUNDIR, 0755) != 0) {
nm_log_err (LOGD_CORE, "Cannot create '%s': %s", NMRUNDIR, strerror (errno));
exit (1);
}
/* Ensure state directory exists */
if (g_mkdir_with_parents (NMSTATEDIR, 0755) != 0) {
nm_log_err (LOGD_CORE, "Cannot create '%s': %s", NMSTATEDIR, strerror (errno));
@ -498,7 +294,7 @@ main (int argc, char *argv[])
state_file = state_file ? state_file : g_strdup (NM_DEFAULT_SYSTEM_STATE_FILE);
/* check pid file */
if (check_pidfile (pidfile))
if (nm_main_utils_check_pidfile (pidfile, "NetworkManager"))
exit (1);
/* Read the config file and CLI overrides */
@ -549,14 +345,13 @@ main (int argc, char *argv[])
saved_errno);
exit (1);
}
if (write_pidfile (pidfile))
wrote_pidfile = TRUE;
wrote_pidfile = nm_main_utils_write_pidfile (pidfile);
}
_init_nm_debug (nm_config_get_debug (config));
/* Set up unix signal handling - before creating threads, but after daemonizing! */
if (!setup_signals ())
if (!nm_main_utils_setup_signals (main_loop, &quit_early))
exit (1);
if (g_fatal_warnings) {
@ -592,8 +387,6 @@ main (int argc, char *argv[])
#endif
);
main_loop = g_main_loop_new (NULL, FALSE);
/* Set up platform interaction layer */
nm_linux_platform_setup ();
@ -662,6 +455,8 @@ main (int argc, char *argv[])
}
}
g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
nm_manager_start (manager);
/* Make sure the loopback interface is up. If interface is down, we bring
@ -680,10 +475,10 @@ main (int argc, char *argv[])
success = TRUE;
/* Told to quit before getting to the mainloop by the signal handler */
if (quit_early == TRUE)
goto done;
if (!quit_early)
g_main_loop_run (main_loop);
g_main_loop_run (main_loop);
nm_manager_stop (manager);
done:
g_clear_object (&manager);

View file

@ -61,6 +61,8 @@ typedef struct {
char **no_auto_default;
char **ignore_carrier;
gboolean configure_and_quit;
} NMConfigPrivate;
static NMConfig *singleton = NULL;
@ -72,41 +74,36 @@ G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT)
/************************************************************************/
static gboolean
_parse_bool_str (const char *str, gboolean *out_value)
_get_bool_value (GKeyFile *keyfile,
const char *section,
const char *key,
gboolean default_value)
{
gboolean value;
gsize len;
char *s = NULL;
gboolean value = default_value;
char *str;
g_return_val_if_fail (str, FALSE);
g_return_val_if_fail (keyfile != NULL, default_value);
g_return_val_if_fail (section != NULL, default_value);
g_return_val_if_fail (key != NULL, default_value);
while (g_ascii_isspace (*str))
str++;
str = g_key_file_get_value (keyfile, section, key, NULL);
if (!str)
return default_value;
if (!*str)
return FALSE;
len = strlen (str);
if (g_ascii_isspace (str[len-1])) {
str = s = g_strdup (str);
g_strchomp (s);
g_strstrip (str);
if (str[0]) {
if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
value = TRUE;
else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
value = FALSE;
else {
nm_log_warn (LOGD_CORE, "Unrecognized value for %s.%s: '%s'. Assuming '%s'",
section, key, str, default_value ? "true" : "false");
}
}
if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
value = TRUE;
else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
value = FALSE;
else {
g_free (s);
return FALSE;
}
if (out_value)
*out_value = value;
g_free (s);
return TRUE;
g_free (str);
return value;
}
/************************************************************************/
@ -218,6 +215,12 @@ nm_config_get_connectivity_response (NMConfig *config)
return NM_CONFIG_GET_PRIVATE (config)->connectivity_response;
}
gboolean
nm_config_get_configure_and_quit (NMConfig *config)
{
return NM_CONFIG_GET_PRIVATE (config)->configure_and_quit;
}
char *
nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error)
{
@ -513,7 +516,6 @@ nm_config_new (GError **error)
GFileInfo *info;
GPtrArray *confs;
const char *name;
char *value;
int i;
GString *config_description;
@ -583,23 +585,9 @@ nm_config_new (GError **error)
if (!priv->plugins && STRLEN (CONFIG_PLUGINS_DEFAULT) > 0)
priv->plugins = g_strsplit (CONFIG_PLUGINS_DEFAULT, ",", -1);
value = g_key_file_get_value (priv->keyfile, "main", "monitor-connection-files", NULL);
priv->monitor_connection_files = FALSE;
if (value) {
if (!_parse_bool_str (value, &priv->monitor_connection_files))
nm_log_warn (LOGD_CORE, "Unrecognized value for main.monitor-connection-files: %s. Assuming 'false'", value);
g_free (value);
}
priv->monitor_connection_files = _get_bool_value (priv->keyfile, "main", "monitor-connection-files", FALSE);
value = g_key_file_get_value (priv->keyfile, "main", "auth-polkit", NULL);
priv->auth_polkit = NM_CONFIG_DEFAULT_AUTH_POLKIT;
if (value) {
if (!_parse_bool_str (value, &priv->auth_polkit)) {
nm_log_warn (LOGD_CORE, "Unrecognized value for main.auth-polkit: %s. Assuming '%s'", value,
NM_CONFIG_DEFAULT_AUTH_POLKIT ? "true" : "false");
}
g_free (value);
}
priv->auth_polkit = _get_bool_value (priv->keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT);
priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL);
priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL);
@ -623,6 +611,8 @@ nm_config_new (GError **error)
priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL);
priv->configure_and_quit = _get_bool_value (priv->keyfile, "main", "configure-and-quit", FALSE);
return singleton;
}

View file

@ -61,6 +61,7 @@ const char *nm_config_get_debug (NMConfig *config);
const char *nm_config_get_connectivity_uri (NMConfig *config);
guint nm_config_get_connectivity_interval (NMConfig *config);
const char *nm_config_get_connectivity_response (NMConfig *config);
gboolean nm_config_get_configure_and_quit (NMConfig *config);
gboolean nm_config_get_ethernet_can_auto_default (NMConfig *config, NMDevice *device);
void nm_config_set_ethernet_no_auto_default (NMConfig *config, NMDevice *device);

529
src/nm-iface-helper.c Normal file
View file

@ -0,0 +1,529 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2014 Red Hat, Inc.
*/
#include <config.h>
#include <glib.h>
#include <glib-unix.h>
#include <getopt.h>
#include <locale.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <glib/gi18n.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <signal.h>
#include "gsystem-local-alloc.h"
#include "NetworkManagerUtils.h"
#include "nm-linux-platform.h"
#include "nm-dhcp-manager.h"
#include "nm-logging.h"
#include "main-utils.h"
#include "nm-rdisc.h"
#include "nm-lndp-rdisc.h"
#include "nm-utils.h"
#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
#endif
#define NMIH_PID_FILE_FMT NMRUNDIR "/nm-iface-helper-%d.pid"
static GMainLoop *main_loop = NULL;
static char *ifname = NULL;
static int ifindex = -1;
static gboolean slaac_required = FALSE;
static gboolean dhcp4_required = FALSE;
static int tempaddr = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
static int priority = -1;
static void
dhcp4_state_changed (NMDhcpClient *client,
NMDhcpState state,
NMIP4Config *ip4_config,
GHashTable *options,
gpointer user_data)
{
static NMIP4Config *last_config = NULL;
NMIP4Config *existing;
g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d", ifname, state);
switch (state) {
case NM_DHCP_STATE_BOUND:
g_assert (ip4_config);
existing = nm_ip4_config_capture (ifindex, FALSE);
if (last_config)
nm_ip4_config_subtract (existing, last_config);
nm_ip4_config_merge (existing, ip4_config);
if (!nm_ip4_config_commit (existing, ifindex))
nm_log_warn (LOGD_DHCP4, "(%s): failed to apply DHCPv4 config", ifname);
if (last_config) {
g_object_unref (last_config);
last_config = nm_ip4_config_new ();
nm_ip4_config_replace (last_config, ip4_config, NULL);
}
break;
case NM_DHCP_STATE_TIMEOUT:
case NM_DHCP_STATE_DONE:
case NM_DHCP_STATE_FAIL:
if (dhcp4_required) {
nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 timed out or failed, quitting...", ifname);
g_main_loop_quit (main_loop);
} else
nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 timed out or failed", ifname);
break;
default:
break;
}
}
static void
rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_data)
{
static NMIP6Config *last_config = NULL;
NMIP6Config *existing;
NMIP6Config *ip6_config;
static int system_support = -1;
guint ifa_flags = 0x00;
int i;
if (system_support == -1) {
/*
* Check, if both libnl and the kernel are recent enough,
* to help user space handling RA. If it's not supported,
* we have no ipv6-privacy and must add autoconf addresses
* as /128. The reason for the /128 is to prevent the kernel
* from adding a prefix route for this address.
**/
system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
nm_platform_check_support_kernel_extended_ifa_flags ();
}
if (system_support)
ifa_flags = IFA_F_NOPREFIXROUTE;
if (tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
|| tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
{
/* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */
ifa_flags |= IFA_F_MANAGETEMPADDR;
}
ip6_config = nm_ip6_config_new ();
if (changed & NM_RDISC_CONFIG_GATEWAYS) {
/* Use the first gateway as ordered in router discovery cache. */
if (rdisc->gateways->len) {
NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, 0);
nm_ip6_config_set_gateway (ip6_config, &gateway->address);
} else
nm_ip6_config_set_gateway (ip6_config, NULL);
}
if (changed & NM_RDISC_CONFIG_ADDRESSES) {
/* Rebuild address list from router discovery cache. */
nm_ip6_config_reset_addresses (ip6_config);
/* rdisc->addresses contains at most max_addresses entries.
* This is different from what the kernel does, which
* also counts static and temporary addresses when checking
* max_addresses.
**/
for (i = 0; i < rdisc->addresses->len; i++) {
NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
NMPlatformIP6Address address;
memset (&address, 0, sizeof (address));
address.address = discovered_address->address;
address.plen = system_support ? 64 : 128;
address.timestamp = discovered_address->timestamp;
address.lifetime = discovered_address->lifetime;
address.preferred = discovered_address->preferred;
if (address.preferred > address.lifetime)
address.preferred = address.lifetime;
address.source = NM_IP_CONFIG_SOURCE_RDISC;
address.flags = ifa_flags;
nm_ip6_config_add_address (ip6_config, &address);
}
}
if (changed & NM_RDISC_CONFIG_ROUTES) {
/* Rebuild route list from router discovery cache. */
nm_ip6_config_reset_routes (ip6_config);
for (i = 0; i < rdisc->routes->len; i++) {
NMRDiscRoute *discovered_route = &g_array_index (rdisc->routes, NMRDiscRoute, i);
NMPlatformIP6Route route;
/* Only accept non-default routes. The router has no idea what the
* local configuration or user preferences are, so sending routes
* with a prefix length of 0 is quite rude and thus ignored.
*/
if (discovered_route->plen > 0) {
memset (&route, 0, sizeof (route));
route.network = discovered_route->network;
route.plen = discovered_route->plen;
route.gateway = discovered_route->gateway;
route.source = NM_IP_CONFIG_SOURCE_RDISC;
route.metric = priority;
nm_ip6_config_add_route (ip6_config, &route);
}
}
}
if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) {
/* Unsupported until systemd DHCPv6 is ready */
}
/* hop_limit == 0 is a special value "unspecified", so do not touch
* in this case */
if (changed & NM_RDISC_CONFIG_HOP_LIMIT && rdisc->hop_limit > 0) {
char val[16];
g_snprintf (val, sizeof (val), "%d", rdisc->hop_limit);
nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "hop_limit"), val);
}
if (changed & NM_RDISC_CONFIG_MTU) {
char val[16];
g_snprintf (val, sizeof (val), "%d", rdisc->mtu);
nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "mtu"), val);
}
existing = nm_ip6_config_capture (ifindex, FALSE, tempaddr);
if (last_config)
nm_ip6_config_subtract (existing, last_config);
nm_ip6_config_merge (existing, ip6_config);
if (!nm_ip6_config_commit (existing, ifindex))
nm_log_warn (LOGD_IP6, "(%s): failed to apply IPv6 config", ifname);
if (last_config) {
g_object_unref (last_config);
last_config = nm_ip6_config_new ();
nm_ip6_config_replace (last_config, ip6_config, NULL);
}
}
static void
rdisc_ra_timeout (NMRDisc *rdisc, gpointer user_data)
{
if (slaac_required) {
nm_log_warn (LOGD_IP6, "(%s): IPv6 timed out or failed, quitting...", ifname);
g_main_loop_quit (main_loop);
} else
nm_log_warn (LOGD_IP6, "(%s): IPv6 timed out or failed", ifname);
}
static gboolean
quit_handler (gpointer user_data)
{
gboolean *quit_early_ptr = user_data;
*quit_early_ptr = TRUE;
g_main_loop_quit (main_loop);
return G_SOURCE_CONTINUE;
}
static void
setup_signals (gboolean *quit_early_ptr)
{
sigset_t sigmask;
sigemptyset (&sigmask);
pthread_sigmask (SIG_SETMASK, &sigmask, NULL);
signal (SIGPIPE, SIG_IGN);
g_unix_signal_add (SIGINT, quit_handler, quit_early_ptr);
g_unix_signal_add (SIGTERM, quit_handler, quit_early_ptr);
}
int
main (int argc, char *argv[])
{
char *opt_log_level = NULL;
char *opt_log_domains = NULL;
gboolean debug = FALSE, g_fatal_warnings = FALSE, become_daemon = FALSE;
gboolean show_version = FALSE, slaac = FALSE;
char *bad_domains = NULL, *dhcp4_hostname = NULL, *uuid = NULL;
char *iid_str = NULL, *dhcp4_clientid = NULL, *dhcp4_address = NULL;
gs_unref_object NMDhcpManager *dhcp_mgr = NULL;
GError *error = NULL;
gboolean wrote_pidfile = FALSE;
gs_free char *pidfile = NULL;
gboolean quit_early = FALSE;
gs_unref_object NMDhcpClient *dhcp4_client = NULL;
gs_unref_object NMRDisc *rdisc = NULL;
GByteArray *hwaddr = NULL;
size_t hwaddr_len = 0;
gconstpointer tmp;
gs_free NMUtilsIPv6IfaceId *iid = NULL;
GOptionEntry options[] = {
/* Interface/IP config */
{ "ifname", 'i', 0, G_OPTION_ARG_STRING, &ifname, N_("The interface to manage"), N_("eth0") },
{ "uuid", 'u', 0, G_OPTION_ARG_STRING, &uuid, N_("Connection UUID"), N_("661e8cd0-b618-46b8-9dc9-31a52baaa16b") },
{ "slaac", 's', 0, G_OPTION_ARG_NONE, &slaac, N_("Whether to manage IPv6 SLAAC"), NULL },
{ "slaac-required", '6', 0, G_OPTION_ARG_NONE, &slaac_required, N_("Whether SLAAC must be successful"), NULL },
{ "slaac-tempaddr", 't', 0, G_OPTION_ARG_INT, &tempaddr, N_("Use an IPv6 temporary privacy address"), NULL },
{ "dhcp4", 'd', 0, G_OPTION_ARG_STRING, &dhcp4_address, N_("Current DHCPv4 address"), NULL },
{ "dhcp4-required", '4', 0, G_OPTION_ARG_NONE, &dhcp4_required, N_("Whether DHCPv4 must be successful"), NULL },
{ "dhcp4-clientid", 'c', 0, G_OPTION_ARG_STRING, &dhcp4_clientid, N_("Hex-encoded DHCPv4 client ID"), NULL },
{ "dhcp4-hostname", 'h', 0, G_OPTION_ARG_STRING, &dhcp4_hostname, N_("Hostname to send to DHCP server"), N_("barbar") },
{ "priority", 'p', 0, G_OPTION_ARG_INT, &priority, N_("Route priority"), N_("10") },
{ "iid", 'e', 0, G_OPTION_ARG_STRING, &iid_str, N_("Hex-encoded Interface Identifier"), N_("") },
/* Logging/debugging */
{ "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Print NetworkManager version and exit"), NULL },
{ "no-daemon", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &become_daemon, N_("Don't become a daemon"), NULL },
{ "debug", 'b', 0, G_OPTION_ARG_NONE, &debug, N_("Don't become a daemon, and log to stderr"), NULL },
{ "log-level", 0, 0, G_OPTION_ARG_STRING, &opt_log_level, N_("Log level: one of [%s]"), "INFO" },
{ "log-domains", 0, 0, G_OPTION_ARG_STRING, &opt_log_domains,
N_("Log domains separated by ',': any combination of [%s]"),
"PLATFORM,RFKILL,WIFI" },
{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, N_("Make all warnings fatal"), NULL },
{NULL}
};
setpgid (getpid (), getpid ());
if (!nm_main_utils_early_setup ("nm-iface-helper",
&argv,
&argc,
options,
NULL,
_("nm-iface-helper is a small, standalone process that manages a single network interface.")))
exit (1);
if (show_version) {
fprintf (stdout, NM_DIST_VERSION "\n");
exit (0);
}
if (!ifname || !uuid) {
fprintf (stderr, _("An interface name and UUID are required\n"));
exit (1);
}
if (!nm_logging_setup (opt_log_level,
opt_log_domains,
&bad_domains,
&error)) {
fprintf (stderr,
_("%s. Please use --help to see a list of valid options.\n"),
error->message);
exit (1);
} else if (bad_domains) {
fprintf (stderr,
_("Ignoring unrecognized log domain(s) '%s' passed on command line.\n"),
bad_domains);
g_clear_pointer (&bad_domains, g_free);
}
pidfile = g_strdup_printf (NMIH_PID_FILE_FMT, ifindex);
g_assert (pidfile);
/* check pid file */
if (nm_main_utils_check_pidfile (pidfile, "nm-iface-helper"))
exit (1);
if (become_daemon && !debug) {
if (daemon (0, 0) < 0) {
int saved_errno;
saved_errno = errno;
fprintf (stderr, _("Could not daemonize: %s [error %u]\n"),
g_strerror (saved_errno),
saved_errno);
exit (1);
}
if (nm_main_utils_write_pidfile (pidfile))
wrote_pidfile = TRUE;
}
/* Set up unix signal handling - before creating threads, but after daemonizing! */
main_loop = g_main_loop_new (NULL, FALSE);
setup_signals (&quit_early);
if (g_fatal_warnings) {
GLogLevelFlags fatal_mask;
fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
g_log_set_always_fatal (fatal_mask);
}
nm_logging_syslog_openlog (debug);
#if !GLIB_CHECK_VERSION (2, 35, 0)
g_type_init ();
#endif
nm_log_info (LOGD_CORE, "nm-iface-helper (version " NM_DIST_VERSION ") is starting...");
/* Set up platform interaction layer */
nm_linux_platform_setup ();
ifindex = nm_platform_link_get_ifindex (ifname);
if (ifindex <= 0) {
fprintf (stderr, _("Failed to find interface index for %s\n"), ifname);
exit (1);
}
tmp = nm_platform_link_get_address (ifindex, &hwaddr_len);
if (tmp) {
hwaddr = g_byte_array_sized_new (hwaddr_len);
g_byte_array_append (hwaddr, tmp, hwaddr_len);
}
if (iid_str) {
GBytes *bytes;
gsize ignored = 0;
bytes = nm_utils_hexstr2bin (iid_str);
if (!bytes || g_bytes_get_size (bytes) != sizeof (*iid)) {
fprintf (stderr, _("(%s): Invalid IID %s\n"), ifname, iid_str);
exit (1);
}
iid = g_bytes_unref_to_data (bytes, &ignored);
}
priority = MAX (0, priority);
if (dhcp4_address) {
nm_platform_sysctl_set (nm_utils_ip4_property_path (ifname, "promote_secondaries"), "1");
/* Initialize DHCP manager */
dhcp_mgr = nm_dhcp_manager_get ();
g_assert (dhcp_mgr != NULL);
dhcp4_client = nm_dhcp_manager_start_ip4 (dhcp_mgr,
ifname,
ifindex,
hwaddr,
uuid,
priority,
!!dhcp4_hostname,
dhcp4_hostname,
dhcp4_clientid,
45,
NULL,
dhcp4_address);
g_assert (dhcp4_client);
g_signal_connect (dhcp4_client,
NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
G_CALLBACK (dhcp4_state_changed),
NULL);
}
if (slaac) {
nm_platform_link_set_user_ipv6ll_enabled (ifindex, TRUE);
rdisc = nm_lndp_rdisc_new (ifindex, ifname);
g_assert (rdisc);
if (iid)
nm_rdisc_set_iid (rdisc, *iid);
nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra"), "1");
nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_defrtr"), "0");
nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_pinfo"), "0");
nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_rtr_pref"), "0");
g_signal_connect (rdisc,
NM_RDISC_CONFIG_CHANGED,
G_CALLBACK (rdisc_config_changed),
NULL);
g_signal_connect (rdisc,
NM_RDISC_RA_TIMEOUT,
G_CALLBACK (rdisc_ra_timeout),
NULL);
nm_rdisc_start (rdisc);
}
if (!quit_early)
g_main_loop_run (main_loop);
g_clear_pointer (&hwaddr, g_byte_array_unref);
nm_logging_syslog_closelog ();
if (pidfile && wrote_pidfile)
unlink (pidfile);
nm_log_info (LOGD_CORE, "exiting");
exit (0);
}
/*******************************************************/
/* Stub functions */
gconstpointer nm_config_get (void);
const char *nm_config_get_dhcp_client (gpointer unused);
gboolean nm_config_get_configure_and_quit (gpointer unused);
gconstpointer nm_dbus_manager_get (void);
void nm_dbus_manager_register_exported_type (gpointer unused, GType gtype, gconstpointer unused2);
void nm_dbus_manager_register_object (gpointer unused, const char *path, gpointer object);
gconstpointer
nm_config_get (void)
{
return GUINT_TO_POINTER (1);
}
const char *
nm_config_get_dhcp_client (gpointer unused)
{
return "internal";
}
gboolean
nm_config_get_configure_and_quit (gpointer unused)
{
return TRUE;
}
gconstpointer
nm_dbus_manager_get (void)
{
return GUINT_TO_POINTER (1);
}
void
nm_dbus_manager_register_exported_type (gpointer unused, GType gtype, gconstpointer unused2)
{
}
void
nm_dbus_manager_register_object (gpointer unused, const char *path, gpointer object)
{
}

View file

@ -83,6 +83,7 @@ nm_ip4_config_new (void)
}
#ifndef NM_IFACE_HELPER
void
nm_ip4_config_export (NMIP4Config *config)
{
@ -94,6 +95,7 @@ nm_ip4_config_export (NMIP4Config *config)
nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, config);
}
}
#endif
const char *
nm_ip4_config_get_dbus_path (const NMIP4Config *config)
@ -1952,7 +1954,9 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
#ifndef NM_IFACE_HELPER
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (config_class),
&dbus_glib_nm_ip4_config_object_info);
#endif
}

View file

@ -73,6 +73,7 @@ nm_ip6_config_new (void)
return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG, NULL);
}
#ifndef NM_IFACE_HELPER
void
nm_ip6_config_export (NMIP6Config *config)
{
@ -84,6 +85,7 @@ nm_ip6_config_export (NMIP6Config *config)
nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, config);
}
}
#endif
const char *
nm_ip6_config_get_dbus_path (const NMIP6Config *config)
@ -1863,7 +1865,9 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
#ifndef NM_IFACE_HELPER
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (config_class),
&dbus_glib_nm_ip6_config_object_info);
#endif
}

View file

@ -59,6 +59,7 @@
#include "nm-session-monitor.h"
#include "nm-activation-request.h"
#include "nm-core-internal.h"
#include "nm-config.h"
#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
#define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd"
@ -214,6 +215,7 @@ enum {
USER_PERMISSIONS_CHANGED,
ACTIVE_CONNECTION_ADDED,
ACTIVE_CONNECTION_REMOVED,
CONFIGURE_QUIT,
LAST_SIGNAL
};
@ -706,6 +708,9 @@ check_if_startup_complete (NMManager *self)
g_signal_handlers_disconnect_by_func (dev, G_CALLBACK (device_has_pending_action_changed), self);
}
if (nm_config_get_configure_and_quit (nm_config_get ()))
g_signal_emit (self, signals[CONFIGURE_QUIT], 0);
}
static void
@ -745,6 +750,8 @@ remove_device (NMManager *manager,
nm_device_set_unmanaged_quitting (device);
else
nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
} else if (quitting && nm_config_get_configure_and_quit (nm_config_get ())) {
nm_device_spawn_iface_helper (device);
}
}
@ -4167,6 +4174,16 @@ nm_manager_start (NMManager *self)
check_if_startup_complete (self);
}
void
nm_manager_stop (NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
/* Remove all devices */
while (priv->devices)
remove_device (self, NM_DEVICE (priv->devices->data), TRUE, TRUE);
}
static gboolean
handle_firmware_changed (gpointer user_data)
{
@ -4990,9 +5007,7 @@ dispose (GObject *object)
G_CALLBACK (authority_changed_cb),
manager);
/* Remove all devices */
while (priv->devices)
remove_device (manager, NM_DEVICE (priv->devices->data), TRUE, TRUE);
g_assert (priv->devices == NULL);
if (priv->ac_cleanup_id) {
g_source_remove (priv->ac_cleanup_id);
@ -5258,6 +5273,13 @@ nm_manager_class_init (NMManagerClass *manager_class)
0, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
signals[CONFIGURE_QUIT] =
g_signal_new (NM_MANAGER_CONFIGURE_QUIT,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (manager_class),
&dbus_glib_nm_manager_object_info);

View file

@ -59,6 +59,7 @@
/* Internal signals */
#define NM_MANAGER_ACTIVE_CONNECTION_ADDED "active-connection-added"
#define NM_MANAGER_ACTIVE_CONNECTION_REMOVED "active-connection-removed"
#define NM_MANAGER_CONFIGURE_QUIT "configure-quit"
struct _NMManager {
@ -88,6 +89,7 @@ NMManager * nm_manager_new (NMSettings *settings,
NMManager * nm_manager_get (void);
void nm_manager_start (NMManager *manager);
void nm_manager_stop (NMManager *manager);
NMState nm_manager_get_state (NMManager *manager);
const GSList *nm_manager_get_active_connections (NMManager *manager);
GSList * nm_manager_get_activatable_connections (NMManager *manager);

View file

@ -3224,7 +3224,6 @@ make_wireless_setting (shvarFile *ifcfg,
GError **error)
{
NMSettingWireless *s_wireless;
GBytes *bytes = NULL;
char *value = NULL;
gint64 chan = 0;
@ -3256,19 +3255,19 @@ make_wireless_setting (shvarFile *ifcfg,
value = svGetValue (ifcfg, "ESSID", TRUE);
if (value) {
gsize ssid_len = 0, value_len = strlen (value);
char *p = value, *tmp;
char buf[33];
GBytes *bytes = NULL;
gsize ssid_len = 0;
gsize value_len = strlen (value);
ssid_len = value_len;
if ( (value_len >= 2)
&& (value[0] == '"')
&& (value[value_len - 1] == '"')) {
/* Strip the quotes and unescape */
p = value + 1;
char *p = value + 1;
value[value_len - 1] = '\0';
svUnescape (p);
ssid_len = strlen (p);
bytes = g_bytes_new (p, strlen (p));
} else if ((value_len > 2) && (strncmp (value, "0x", 2) == 0)) {
/* Hex representation */
if (value_len % 2) {
@ -3279,34 +3278,27 @@ make_wireless_setting (shvarFile *ifcfg,
goto error;
}
p = value + 2;
while (*p) {
if (!g_ascii_isxdigit (*p)) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid SSID '%s' character (looks like hex SSID but '%c' isn't a hex digit)",
value, *p);
g_free (value);
goto error;
}
p++;
bytes = nm_utils_hexstr2bin (value);
if (!bytes) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid SSID '%s' (looks like hex SSID but isn't)",
value);
g_free (value);
goto error;
}
} else
bytes = g_bytes_new (value, value_len);
tmp = nm_utils_hexstr2bin (value + 2, value_len - 2);
ssid_len = (value_len - 2) / 2;
memcpy (buf, tmp, ssid_len);
p = &buf[0];
g_free (tmp);
}
ssid_len = g_bytes_get_size (bytes);
if (ssid_len > 32 || ssid_len == 0) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)",
value, ssid_len);
g_bytes_unref (bytes);
g_free (value);
goto error;
}
bytes = g_bytes_new (p, ssid_len);
g_object_set (s_wireless, NM_SETTING_WIRELESS_SSID, bytes, NULL);
g_bytes_unref (bytes);
g_free (value);

View file

@ -43,8 +43,16 @@ connection_id_from_ifnet_name (const char *conn_name)
int name_len = strlen (conn_name);
/* Convert a hex-encoded conn_name (only used for wifi SSIDs) to human-readable one */
if ((name_len > 2) && (g_str_has_prefix (conn_name, "0x")))
return nm_utils_hexstr2bin (conn_name + 2, name_len - 2);
if ((name_len > 2) && (g_str_has_prefix (conn_name, "0x"))) {
GBytes *bytes = nm_utils_hexstr2bin (conn_name);
char *buf;
if (bytes) {
buf = g_strndup (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
g_bytes_unref (bytes);
return buf;
}
}
return g_strdup (conn_name);
}
@ -882,7 +890,6 @@ make_wireless_connection_setting (const char *conn_name,
NMSetting8021x **s_8021x,
GError **error)
{
GBytes *bytes;
const char *mac = NULL;
NMSettingWireless *wireless_setting = NULL;
gboolean adhoc = FALSE;
@ -913,9 +920,8 @@ make_wireless_connection_setting (const char *conn_name,
/* handle ssid (hex and ascii) */
if (conn_name) {
GBytes *bytes;
gsize ssid_len = 0, value_len = strlen (conn_name);
const char *p;
char *tmp, *converted = NULL;
ssid_len = value_len;
if ((value_len > 2) && (g_str_has_prefix (conn_name, "0x"))) {
@ -926,32 +932,27 @@ make_wireless_connection_setting (const char *conn_name,
conn_name);
goto error;
}
// ignore "0x"
p = conn_name + 2;
if (!is_hex (p)) {
bytes = nm_utils_hexstr2bin (conn_name);
if (!bytes) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid SSID '%s' character (looks like hex SSID but '%c' isn't a hex digit)",
conn_name, *p);
"Invalid SSID '%s' (looks like hex SSID but isn't)",
conn_name);
goto error;
}
tmp = nm_utils_hexstr2bin (p, value_len - 2);
ssid_len = (value_len - 2) / 2;
converted = g_malloc0 (ssid_len + 1);
memcpy (converted, tmp, ssid_len);
g_free (tmp);
}
} else
bytes = g_bytes_new (conn_name, value_len);
ssid_len = g_bytes_get_size (bytes);
if (ssid_len > 32 || ssid_len == 0) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)",
conn_name, ssid_len);
goto error;
}
bytes = g_bytes_new (converted ? converted : conn_name, ssid_len);
g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, bytes, NULL);
g_bytes_unref (bytes);
g_free (converted);
} else {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Missing SSID");
@ -1095,7 +1096,7 @@ add_one_wep_key (const char *ssid,
}
converted = nm_utils_bin2hexstr (tmp, strlen (tmp), strlen (tmp) * 2);
converted = nm_utils_bin2hexstr (tmp, strlen (tmp), -1);
g_free (tmp);
} else {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,

View file

@ -542,8 +542,8 @@ add_wep_key (NMSupplicantConfig *self,
const char *name,
NMWepKeyType wep_type)
{
char *value;
gboolean success;
GBytes *bytes;
gboolean success = FALSE;
size_t key_len = key ? strlen (key) : 0;
if (!key || !key_len)
@ -552,9 +552,15 @@ add_wep_key (NMSupplicantConfig *self,
if ( (wep_type == NM_WEP_KEY_TYPE_UNKNOWN)
|| (wep_type == NM_WEP_KEY_TYPE_KEY)) {
if ((key_len == 10) || (key_len == 26)) {
value = nm_utils_hexstr2bin (key, strlen (key));
success = nm_supplicant_config_add_option (self, name, value, key_len / 2, TRUE);
g_free (value);
bytes = nm_utils_hexstr2bin (key);
if (bytes) {
success = nm_supplicant_config_add_option (self,
name,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
TRUE);
g_bytes_unref (bytes);
}
if (!success) {
nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
return FALSE;
@ -590,8 +596,7 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
NMSetting8021x *setting_8021x,
const char *con_uuid)
{
char *value;
gboolean success;
gboolean success = FALSE;
const char *key_mgmt, *auth_alg;
const char *psk;
@ -612,10 +617,18 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
size_t psk_len = strlen (psk);
if (psk_len == 64) {
GBytes *bytes;
/* Hex PSK */
value = nm_utils_hexstr2bin (psk, psk_len);
success = nm_supplicant_config_add_option (self, "psk", value, psk_len / 2, TRUE);
g_free (value);
bytes = nm_utils_hexstr2bin (psk);
if (bytes) {
success = nm_supplicant_config_add_option (self,
"psk",
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
TRUE);
g_bytes_unref (bytes);
}
if (!success) {
nm_log_warn (LOGD_SUPPLICANT, "Error adding 'psk' to supplicant config.");
return FALSE;
@ -653,6 +666,7 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
const char *wep1 = nm_setting_wireless_security_get_wep_key (setting, 1);
const char *wep2 = nm_setting_wireless_security_get_wep_key (setting, 2);
const char *wep3 = nm_setting_wireless_security_get_wep_key (setting, 3);
char *value;
if (!add_wep_key (self, wep0, "wep_key0", wep_type))
return FALSE;