void nm_dhcp_client_stop (NMDHCPClient *self, gboolean release) { NMDHCPClientPrivate *priv; g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_DHCP_CLIENT (self)); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); /* Kill the DHCP client */ if (!priv->dead) { NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release); priv->dead = TRUE; nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction, DHCP client pid %d", priv->iface, priv->pid); } /* And clean stuff up */ priv->pid = -1; dhcp_client_set_state (self, DHC_END, FALSE, TRUE); g_hash_table_remove_all (priv->options); timeout_cleanup (self); watch_cleanup (self); }
gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self, NMSettingIP6Config *s_ip6, guint8 *dhcp_anycast_addr, const char *hostname, gboolean info_only) { NMDHCPClientPrivate *priv; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->pid == -1, FALSE); g_return_val_if_fail (priv->ipv6 == TRUE, FALSE); g_return_val_if_fail (priv->uuid != NULL, FALSE); priv->info_only = info_only; nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)", priv->iface, priv->timeout); priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr, hostname, info_only); if (priv->pid > 0) start_monitor (self); return priv->pid ? TRUE : FALSE; }
gboolean nm_dhcp_client_get_ipv6 (NMDHCPClient *self) { g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); return NM_DHCP_CLIENT_GET_PRIVATE (self)->ipv6; }
const char * nm_dhcp_client_get_iface (NMDHCPClient *self) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); return NM_DHCP_CLIENT_GET_PRIVATE (self)->iface; }
GPid nm_dhcp_client_get_pid (NMDHCPClient *self) { g_return_val_if_fail (self != NULL, -1); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), -1); return NM_DHCP_CLIENT_GET_PRIVATE (self)->pid; }
void nm_dhcp_client_new_options (NMDHCPClient *self, GHashTable *options, const char *reason) { NMDHCPClientPrivate *priv; guint32 old_state; guint32 new_state; g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_DHCP_CLIENT (self)); g_return_if_fail (options != NULL); g_return_if_fail (reason != NULL); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); old_state = priv->state; new_state = string_to_state (reason); /* Clear old and save new DHCP options */ g_hash_table_remove_all (priv->options); g_hash_table_foreach (options, copy_option, priv->options); if (old_state == new_state) { /* dhclient will stay in the same state (or, really, provide the same * reason) for operations like RENEW and REBIND. We need to ensure * that triggers various DHCP lease change code, so we need to pass * along same-state transitions for these states. */ if ( new_state != DHC_BOUND4 && new_state != DHC_RENEW4 && new_state != DHC_REBIND4 && new_state != DHC_BOUND6 && new_state != DHC_RENEW6 && new_state != DHC_REBIND6) return; } /* Handle changed device state */ if (state_is_bound (new_state)) { /* Cancel the timeout if the DHCP client is now bound */ timeout_cleanup (self); } if (priv->ipv6) { nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 state changed %s -> %s", priv->iface, state_to_string (old_state), state_to_string (new_state)); } else { nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 state changed %s -> %s", priv->iface, state_to_string (old_state), state_to_string (new_state)); } dhcp_client_set_state (self, new_state, TRUE, FALSE); }
gboolean nm_dhcp_client_foreach_option (NMDHCPClient *self, GHFunc func, gpointer user_data) { NMDHCPClientPrivate *priv; GHashTableIter iter; gpointer iterkey, itervalue; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); g_return_val_if_fail (func != NULL, FALSE); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); if (!state_is_bound (priv->state)) { if (priv->ipv6) { nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface); } else { nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 client didn't bind to a lease.", priv->iface); } } g_hash_table_iter_init (&iter, priv->options); while (g_hash_table_iter_next (&iter, &iterkey, &itervalue)) { const char *key = iterkey, *value = itervalue; const char **p; static const char *filter_options[] = { "interface", "pid", "reason", "dhcp_message_type", NULL }; gboolean ignore = FALSE; /* Filter out stuff that's not actually new DHCP options */ for (p = filter_options; *p; p++) { if (!strcmp (*p, key) || !strncmp (key, OLD_TAG, strlen (OLD_TAG))) { ignore = TRUE; break; } } if (!ignore) { const char *tmp_key = key; /* Remove the "new_" prefix that dhclient passes back */ if (!strncmp (key, NEW_TAG, strlen (NEW_TAG))) tmp_key = key + strlen (NEW_TAG); func ((gpointer) tmp_key, (gpointer) value, user_data); } } return TRUE; }
gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self, NMSettingIP6Config *s_ip6, guint8 *dhcp_anycast_addr, const char *hostname, gboolean info_only) { NMDHCPClientPrivate *priv; char *escaped; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->pid == -1, FALSE); g_return_val_if_fail (priv->ipv6 == TRUE, FALSE); g_return_val_if_fail (priv->uuid != NULL, FALSE); /* If we don't have one yet, read the default DUID for this DHCPv6 client * from the client-specific persistent configuration. */ if (!priv->duid) priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); if (nm_logging_level_enabled (LOGL_DEBUG)) { escaped = escape_duid (priv->duid); nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, escaped); g_free (escaped); } priv->info_only = info_only; nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)", priv->iface, priv->timeout); priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr, hostname, info_only, priv->duid); if (priv->pid > 0) start_monitor (self); return priv->pid ? TRUE : FALSE; }
void nm_dhcp_client_new_options (NMDHCPClient *self, GHashTable *options, const char *reason) { NMDHCPClientPrivate *priv; guint32 old_state; guint32 new_state; g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_DHCP_CLIENT (self)); g_return_if_fail (options != NULL); g_return_if_fail (reason != NULL); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); old_state = priv->state; new_state = string_to_state (reason); /* Clear old and save new DHCP options */ g_hash_table_remove_all (priv->options); g_hash_table_foreach (options, copy_option, priv->options); if (old_state == new_state) return; /* Handle changed device state */ if (state_is_bound (new_state)) { /* Cancel the timeout if the DHCP client is now bound */ timeout_cleanup (self); } if (priv->ipv6) { nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 state changed %s -> %s", priv->iface, state_to_string (old_state), state_to_string (new_state)); } else { nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 state changed %s -> %s", priv->iface, state_to_string (old_state), state_to_string (new_state)); } dhcp_client_set_state (self, new_state, TRUE, FALSE); }
static void stop (NMDHCPClient *self, gboolean release) { NMDHCPClientPrivate *priv; g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_DHCP_CLIENT (self)); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_if_fail (priv->pid > 0); /* Clean up the watch handler since we're explicitly killing the daemon */ watch_cleanup (self); nm_dhcp_client_stop_pid (priv->pid, priv->iface, 0); priv->info_only = FALSE; }
NMIP6Config * nm_dhcp_client_get_ip6_config (NMDHCPClient *self, gboolean test) { NMDHCPClientPrivate *priv; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); if (test && !state_is_bound (priv->state)) { nm_log_warn (LOGD_DHCP6, "(%s): DHCPv6 client didn't bind to a lease.", priv->iface); return NULL; } if (!g_hash_table_size (priv->options)) { /* We never got a response from the DHCP client */ return NULL; } return ip6_options_to_config (self); }
/* Given a table of DHCP options from the client, convert into an IP4Config */ static NMIP4Config * ip4_options_to_config (NMDHCPClient *self) { NMDHCPClientPrivate *priv; NMIP4Config *ip4_config = NULL; struct in_addr tmp_addr; NMIP4Address *addr = NULL; char *str = NULL; guint32 gwaddr = 0, prefix = 0; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->options != NULL, NULL); ip4_config = nm_ip4_config_new (); if (!ip4_config) { nm_log_warn (LOGD_DHCP4, "(%s): couldn't allocate memory for an IP4Config!", priv->iface); return NULL; } addr = nm_ip4_address_new (); if (!addr) { nm_log_warn (LOGD_DHCP4, "(%s): couldn't allocate memory for an IP4 Address!", priv->iface); goto error; } str = g_hash_table_lookup (priv->options, "new_ip_address"); if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { nm_ip4_address_set_address (addr, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " address %s", str); } else goto error; str = g_hash_table_lookup (priv->options, "new_subnet_mask"); if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { prefix = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " prefix %d (%s)", prefix, str); } else { /* Get default netmask for the IP according to appropriate class. */ prefix = nm_utils_ip4_get_default_prefix (nm_ip4_address_get_address (addr)); nm_log_info (LOGD_DHCP4, " prefix %d (default)", prefix); } nm_ip4_address_set_prefix (addr, prefix); /* Routes: if the server returns classless static routes, we MUST ignore * the 'static_routes' option. */ if (!ip4_process_classless_routes (priv->options, ip4_config, &gwaddr)) process_classful_routes (priv->options, ip4_config); if (gwaddr) { char buf[INET_ADDRSTRLEN + 1]; inet_ntop (AF_INET, &gwaddr, buf, sizeof (buf)); nm_log_info (LOGD_DHCP4, " gateway %s", buf); nm_ip4_address_set_gateway (addr, gwaddr); } else { /* If the gateway wasn't provided as a classless static route with a * subnet length of 0, try to find it using the old-style 'routers' option. */ str = g_hash_table_lookup (priv->options, "new_routers"); if (str) { char **routers = g_strsplit (str, " ", 0); char **s; for (s = routers; *s; s++) { /* FIXME: how to handle multiple routers? */ if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { nm_ip4_address_set_gateway (addr, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " gateway %s", *s); break; } else nm_log_warn (LOGD_DHCP4, "ignoring invalid gateway '%s'", *s); } g_strfreev (routers); } } nm_ip4_config_take_address (ip4_config, addr); addr = NULL; str = g_hash_table_lookup (priv->options, "new_host_name"); if (str) nm_log_info (LOGD_DHCP4, " hostname '%s'", str); str = g_hash_table_lookup (priv->options, "new_domain_name_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { nm_ip4_config_add_nameserver (ip4_config, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " nameserver '%s'", *s); } else nm_log_warn (LOGD_DHCP4, "ignoring invalid nameserver '%s'", *s); } g_strfreev (searches); } str = g_hash_table_lookup (priv->options, "new_domain_name"); if (str) { char **domains = g_strsplit (str, " ", 0); char **s; for (s = domains; *s; s++) { nm_log_info (LOGD_DHCP4, " domain name '%s'", *s); nm_ip4_config_add_domain (ip4_config, *s); } g_strfreev (domains); } str = g_hash_table_lookup (priv->options, "new_domain_search"); if (str) process_domain_search (str, ip4_add_domain_search, ip4_config); str = g_hash_table_lookup (priv->options, "new_netbios_name_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { nm_ip4_config_add_wins (ip4_config, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " wins '%s'", *s); } else nm_log_warn (LOGD_DHCP4, "ignoring invalid WINS server '%s'", *s); } g_strfreev (searches); } str = g_hash_table_lookup (priv->options, "new_interface_mtu"); if (str) { int int_mtu; errno = 0; int_mtu = strtol (str, NULL, 10); if ((errno == EINVAL) || (errno == ERANGE)) goto error; if (int_mtu > 576) nm_ip4_config_set_mtu (ip4_config, int_mtu); } str = g_hash_table_lookup (priv->options, "new_nis_domain"); if (str) { nm_log_info (LOGD_DHCP4, " NIS domain '%s'", str); nm_ip4_config_set_nis_domain (ip4_config, str); } str = g_hash_table_lookup (priv->options, "new_nis_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET, *s, &tmp_addr) > 0) { nm_ip4_config_add_nis_server (ip4_config, tmp_addr.s_addr); nm_log_info (LOGD_DHCP4, " nis '%s'", *s); } else nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s); } g_strfreev (searches); } return ip4_config; error: if (addr) nm_ip4_address_unref (addr); g_object_unref (ip4_config); return NULL; }
/* Given a table of DHCP options from the client, convert into an IP6Config */ static NMIP6Config * ip6_options_to_config (NMDHCPClient *self) { NMDHCPClientPrivate *priv; NMIP6Config *ip6_config = NULL; struct in6_addr tmp_addr; NMIP6Address *addr = NULL; char *str = NULL; GHashTableIter iter; gpointer key, value; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->options != NULL, NULL); g_hash_table_iter_init (&iter, priv->options); while (g_hash_table_iter_next (&iter, &key, &value)) { nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'", priv->iface, (const char *) key, (const char *) value); } ip6_config = nm_ip6_config_new (); if (!ip6_config) { nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6Config!", priv->iface); return NULL; } str = g_hash_table_lookup (priv->options, "new_ip6_address"); if (str) { if (!inet_pton (AF_INET6, str, &tmp_addr)) { nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'", priv->iface, str); goto error; } addr = nm_ip6_address_new (); g_assert (addr); nm_ip6_address_set_address (addr, &tmp_addr); /* DHCPv6 IA_NA assignments are single address only */ nm_ip6_address_set_prefix (addr, 128); nm_log_info (LOGD_DHCP6, " address %s/128", str); nm_ip6_config_take_address (ip6_config, addr); } else if (priv->info_only == FALSE) { /* No address in Managed mode is a hard error */ goto error; } str = g_hash_table_lookup (priv->options, "new_host_name"); if (str) nm_log_info (LOGD_DHCP6, " hostname '%s'", str); str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) { nm_ip6_config_add_nameserver (ip6_config, &tmp_addr); nm_log_info (LOGD_DHCP6, " nameserver '%s'", *s); } else nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s); } g_strfreev (searches); } str = g_hash_table_lookup (priv->options, "new_dhcp6_domain_search"); if (str) process_domain_search (str, ip6_add_domain_search, ip6_config); return ip6_config; error: g_object_unref (ip6_config); return NULL; }
/* Given a table of DHCP options from the client, convert into an IP6Config */ static NMIP6Config * ip6_options_to_config (NMDHCPClient *self) { NMDHCPClientPrivate *priv; NMIP6Config *ip6_config = NULL; struct in6_addr tmp_addr; NMIP6Address *addr = NULL; char *str = NULL; GHashTableIter iter; gpointer key, value; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); g_return_val_if_fail (priv->options != NULL, NULL); g_hash_table_iter_init (&iter, priv->options); while (g_hash_table_iter_next (&iter, &key, &value)) { nm_log_dbg (LOGD_DHCP6, "(%s): option '%s'=>'%s'", priv->iface, (const char *) key, (const char *) value); } ip6_config = nm_ip6_config_new (); if (!ip6_config) { nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6Config!", priv->iface); return NULL; } addr = nm_ip6_address_new (); if (!addr) { nm_log_warn (LOGD_DHCP6, "(%s): couldn't allocate memory for an IP6 Address!", priv->iface); goto error; } str = g_hash_table_lookup (priv->options, "new_ip6_address"); if (str) { if (!inet_pton (AF_INET6, str, &tmp_addr)) { nm_log_warn (LOGD_DHCP6, "(%s): DHCP returned invalid address '%s'", priv->iface); goto error; } nm_ip6_address_set_address (addr, &tmp_addr); nm_log_info (LOGD_DHCP6, " address %s", str); } else { /* No address in managed mode is a hard error */ if (priv->info_only == FALSE) goto error; /* But "info-only" setups don't necessarily need an address */ nm_ip6_address_unref (addr); addr = NULL; } /* Only care about prefix if we got an address */ if (addr) { str = g_hash_table_lookup (priv->options, "new_ip6_prefixlen"); if (str) { long unsigned int prefix; errno = 0; prefix = strtoul (str, NULL, 10); if (errno != 0 || prefix > 128) goto error; nm_ip6_address_set_prefix (addr, (guint32) prefix); nm_log_info (LOGD_DHCP6, " prefix %lu", prefix); } nm_ip6_config_take_address (ip6_config, addr); addr = NULL; } str = g_hash_table_lookup (priv->options, "new_host_name"); if (str) nm_log_info (LOGD_DHCP6, " hostname '%s'", str); str = g_hash_table_lookup (priv->options, "new_dhcp6_name_servers"); if (str) { char **searches = g_strsplit (str, " ", 0); char **s; for (s = searches; *s; s++) { if (inet_pton (AF_INET6, *s, &tmp_addr) > 0) { nm_ip6_config_add_nameserver (ip6_config, &tmp_addr); nm_log_info (LOGD_DHCP6, " nameserver '%s'", *s); } else nm_log_warn (LOGD_DHCP6, "ignoring invalid nameserver '%s'", *s); } g_strfreev (searches); } str = g_hash_table_lookup (priv->options, "new_dhcp6_domain_search"); if (str) process_domain_search (str, ip6_add_domain_search, ip6_config); return ip6_config; error: if (addr) nm_ip6_address_unref (addr); g_object_unref (ip6_config); return NULL; }