static NMIP4Config * lease_to_ip4_config (const char *iface, int ifindex, sd_dhcp_lease *lease, GHashTable *options, guint32 default_priority, gboolean log_lease, GError **error) { NMIP4Config *ip4_config = NULL; struct in_addr tmp_addr; const struct in_addr *addr_list; char buf[INET_ADDRSTRLEN]; const char *str; guint32 lifetime = 0, i; NMPlatformIP4Address address; GString *l; gs_free sd_dhcp_route **routes = NULL; guint16 mtu; int r, num; guint64 end_time; const void *data; gsize data_len; gboolean metered = FALSE; gboolean static_default_gateway = FALSE; g_return_val_if_fail (lease != NULL, NULL); ip4_config = nm_ip4_config_new (ifindex); /* Address */ sd_dhcp_lease_get_address (lease, &tmp_addr); memset (&address, 0, sizeof (address)); address.address = tmp_addr.s_addr; address.peer_address = tmp_addr.s_addr; str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); LOG_LEASE (LOGD_DHCP4, " address %s", str); add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str); /* Prefix/netmask */ sd_dhcp_lease_get_netmask (lease, &tmp_addr); address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); LOG_LEASE (LOGD_DHCP4, " plen %d", address.plen); add_option (options, dhcp4_requests, SD_DHCP_OPTION_SUBNET_MASK, nm_utils_inet4_ntop (tmp_addr.s_addr, NULL)); /* Lease time */ sd_dhcp_lease_get_lifetime (lease, &lifetime); address.timestamp = nm_utils_get_monotonic_timestamp_s (); address.lifetime = address.preferred = lifetime; end_time = (guint64) time (NULL) + lifetime; LOG_LEASE (LOGD_DHCP4, " expires in %" G_GUINT32_FORMAT " seconds", lifetime); add_option_u64 (options, dhcp4_requests, SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, end_time); address.addr_source = NM_IP_CONFIG_SOURCE_DHCP; nm_ip4_config_add_address (ip4_config, &address); /* DNS Servers */ num = sd_dhcp_lease_get_dns (lease, &addr_list); if (num > 0) { l = g_string_sized_new (30); for (i = 0; i < num; i++) { if (addr_list[i].s_addr) { nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr); str = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL); LOG_LEASE (LOGD_DHCP4, " nameserver '%s'", str); g_string_append_printf (l, "%s%s", l->len ? " " : "", str); } } if (l->len) add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME_SERVER, l->str); g_string_free (l, TRUE); } /* Domain Name */ r = sd_dhcp_lease_get_domainname (lease, &str); if (r == 0) { /* Multiple domains sometimes stuffed into option 15 "Domain Name". * As systemd escapes such characters, split them at \\032. */ char **domains = g_strsplit (str, "\\032", 0); char **s; for (s = domains; *s; s++) { LOG_LEASE (LOGD_DHCP4, " domain name '%s'", *s); nm_ip4_config_add_domain (ip4_config, *s); } g_strfreev (domains); add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME, str); } /* Hostname */ r = sd_dhcp_lease_get_hostname (lease, &str); if (r == 0) { LOG_LEASE (LOGD_DHCP4, " hostname '%s'", str); add_option (options, dhcp4_requests, SD_DHCP_OPTION_HOST_NAME, str); } /* Routes */ num = sd_dhcp_lease_get_routes (lease, &routes); if (num > 0) { l = g_string_sized_new (30); for (i = 0; i < num; i++) { NMPlatformIP4Route route = { 0 }; const char *gw_str; guint8 plen; struct in_addr a; if (sd_dhcp_route_get_destination (routes[i], &a) < 0) continue; route.network = a.s_addr; if ( sd_dhcp_route_get_destination_prefix_length (routes[i], &plen) < 0 || plen > 32) continue; route.plen = plen; if (sd_dhcp_route_get_gateway (routes[i], &a) < 0) continue; route.gateway = a.s_addr; if (route.plen) { route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; route.metric = default_priority; nm_ip4_config_add_route (ip4_config, &route); str = nm_utils_inet4_ntop (route.network, buf); gw_str = nm_utils_inet4_ntop (route.gateway, NULL); LOG_LEASE (LOGD_DHCP4, " static route %s/%d gw %s", str, route.plen, gw_str); g_string_append_printf (l, "%s%s/%d %s", l->len ? " " : "", str, route.plen, gw_str); } else { if (!static_default_gateway) { static_default_gateway = TRUE; nm_ip4_config_set_gateway (ip4_config, route.gateway); str = nm_utils_inet4_ntop (route.gateway, NULL); LOG_LEASE (LOGD_DHCP4, " gateway %s", str); add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str); } } } if (l->len) add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, l->str); g_string_free (l, TRUE); } /* If the DHCP server returns both a Classless Static Routes option and a * Router option, the DHCP client MUST ignore the Router option [RFC 3442]. * Be more lenient and ignore the Router option only if Classless Static * Routes contain a default gateway (as other DHCP backends do). */ /* Gateway */ if (!static_default_gateway) { r = sd_dhcp_lease_get_router (lease, &tmp_addr); if (r == 0) { nm_ip4_config_set_gateway (ip4_config, tmp_addr.s_addr); str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); LOG_LEASE (LOGD_DHCP4, " gateway %s", str); add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str); } } /* MTU */ r = sd_dhcp_lease_get_mtu (lease, &mtu); if (r == 0 && mtu) { nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); add_option_u32 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu); LOG_LEASE (LOGD_DHCP4, " mtu %u", mtu); } /* NTP servers */ num = sd_dhcp_lease_get_ntp (lease, &addr_list); if (num > 0) { l = g_string_sized_new (30); for (i = 0; i < num; i++) { str = nm_utils_inet4_ntop (addr_list[i].s_addr, buf); LOG_LEASE (LOGD_DHCP4, " ntp server '%s'", str); g_string_append_printf (l, "%s%s", l->len ? " " : "", str); } add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, l->str); g_string_free (l, TRUE); } r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len); if (r >= 0) metered = !!memmem (data, data_len, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED")); nm_ip4_config_set_metered (ip4_config, metered); return ip4_config; }
static NMIP6Config * lease_to_ip6_config (const char *iface, int ifindex, sd_dhcp6_lease *lease, GHashTable *options, gboolean log_lease, gboolean info_only, GError **error) { struct in6_addr tmp_addr, *dns; uint32_t lft_pref, lft_valid; NMIP6Config *ip6_config; const char *addr_str; char **domains; GString *str; int num, i; gint32 ts; g_return_val_if_fail (lease, NULL); ip6_config = nm_ip6_config_new (ifindex); ts = nm_utils_get_monotonic_timestamp_s (); str = g_string_sized_new (30); /* Addresses */ sd_dhcp6_lease_reset_address_iter (lease); while (sd_dhcp6_lease_get_address (lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) { NMPlatformIP6Address address = { .plen = 128, .address = tmp_addr, .timestamp = ts, .lifetime = lft_valid, .preferred = lft_pref, .addr_source = NM_IP_CONFIG_SOURCE_DHCP, }; nm_ip6_config_add_address (ip6_config, &address); addr_str = nm_utils_inet6_ntop (&tmp_addr, NULL); g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); LOG_LEASE (LOGD_DHCP6, " address %s", nm_platform_ip6_address_to_string (&address, NULL, 0)); }; if (str->len) { add_option (options, dhcp6_requests, DHCP6_OPTION_IP_ADDRESS, str->str); g_string_set_size (str , 0); } if (!info_only && nm_ip6_config_get_num_addresses (ip6_config) == 0) { g_string_free (str, TRUE); g_object_unref (ip6_config); g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, "no address received in managed mode"); return NULL; } /* DNS servers */ num = sd_dhcp6_lease_get_dns (lease, &dns); if (num > 0) { for (i = 0; i < num; i++) { nm_ip6_config_add_nameserver (ip6_config, &dns[i]); addr_str = nm_utils_inet6_ntop (&dns[i], NULL); g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); LOG_LEASE (LOGD_DHCP6, " nameserver %s", addr_str); } add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DNS_SERVERS, str->str); g_string_set_size (str, 0); } /* Search domains */ num = sd_dhcp6_lease_get_domains (lease, &domains); if (num > 0) { for (i = 0; i < num; i++) { nm_ip6_config_add_search (ip6_config, domains[i]); g_string_append_printf (str, "%s%s", str->len ? " " : "", domains[i]); LOG_LEASE (LOGD_DHCP6, " domain name '%s'", domains[i]); } add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DOMAIN_LIST, str->str); g_string_set_size (str, 0); } g_string_free (str, TRUE); return ip6_config; } static void bound6_handle (NMDhcpSystemd *self) { NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)); gs_unref_object NMIP6Config *ip6_config = NULL; gs_unref_hashtable GHashTable *options = NULL; gs_free_error GError *error = NULL; sd_dhcp6_lease *lease; int r; r = sd_dhcp6_client_get_lease (priv->client6, &lease); if (r < 0 || !lease) { _LOGW (" no lease!"); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); return; } _LOGD ("lease available"); options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); ip6_config = lease_to_ip6_config (iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, options, TRUE, priv->info_only, &error); if (ip6_config) { nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_BOUND, G_OBJECT (ip6_config), options); } else { _LOGW ("%s", error->message); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); } }
static NMIP4Config * lease_to_ip4_config (const char *iface, int ifindex, sd_dhcp_lease *lease, GHashTable *options, guint32 default_priority, gboolean log_lease, GError **error) { NMIP4Config *ip4_config = NULL; struct in_addr tmp_addr; const struct in_addr *addr_list; char buf[INET_ADDRSTRLEN]; const char *str; guint32 lifetime = 0, i; NMPlatformIP4Address address; GString *l; struct sd_dhcp_route *routes; guint16 mtu; int r, num; guint64 end_time; const void *data; gsize data_len; gboolean metered = FALSE; g_return_val_if_fail (lease != NULL, NULL); ip4_config = nm_ip4_config_new (ifindex); /* Address */ sd_dhcp_lease_get_address (lease, &tmp_addr); memset (&address, 0, sizeof (address)); address.address = tmp_addr.s_addr; address.peer_address = tmp_addr.s_addr; str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); LOG_LEASE (LOGD_DHCP4, " address %s", str); add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str); /* Prefix/netmask */ sd_dhcp_lease_get_netmask (lease, &tmp_addr); address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); LOG_LEASE (LOGD_DHCP4, " plen %d", address.plen); add_option (options, dhcp4_requests, DHCP_OPTION_SUBNET_MASK, nm_utils_inet4_ntop (tmp_addr.s_addr, NULL)); /* Lease time */ sd_dhcp_lease_get_lifetime (lease, &lifetime); address.timestamp = nm_utils_get_monotonic_timestamp_s (); address.lifetime = address.preferred = lifetime; end_time = (guint64) time (NULL) + lifetime; LOG_LEASE (LOGD_DHCP4, " expires in %" G_GUINT32_FORMAT " seconds", lifetime); add_option_u64 (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS_LEASE_TIME, end_time); address.source = NM_IP_CONFIG_SOURCE_DHCP; nm_ip4_config_add_address (ip4_config, &address); /* Gateway */ r = sd_dhcp_lease_get_router (lease, &tmp_addr); if (r == 0) { nm_ip4_config_set_gateway (ip4_config, tmp_addr.s_addr); str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); LOG_LEASE (LOGD_DHCP4, " gateway %s", str); add_option (options, dhcp4_requests, DHCP_OPTION_ROUTER, str); } /* DNS Servers */ num = sd_dhcp_lease_get_dns (lease, &addr_list); if (num > 0) { l = g_string_sized_new (30); for (i = 0; i < num; i++) { if (addr_list[i].s_addr) { nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr); str = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL); LOG_LEASE (LOGD_DHCP4, " nameserver '%s'", str); g_string_append_printf (l, "%s%s", l->len ? " " : "", str); } } if (l->len) add_option (options, dhcp4_requests, DHCP_OPTION_DOMAIN_NAME_SERVER, l->str); g_string_free (l, TRUE); } /* Domain Name */ r = sd_dhcp_lease_get_domainname (lease, &str); if (r == 0) { /* Multiple domains sometimes stuffed into the option */ char **domains = g_strsplit (str, " ", 0); char **s; for (s = domains; *s; s++) { LOG_LEASE (LOGD_DHCP4, " domain name '%s'", *s); nm_ip4_config_add_domain (ip4_config, *s); } g_strfreev (domains); add_option (options, dhcp4_requests, DHCP_OPTION_DOMAIN_NAME, str); } /* Hostname */ r = sd_dhcp_lease_get_hostname (lease, &str); if (r == 0) { LOG_LEASE (LOGD_DHCP4, " hostname '%s'", str); add_option (options, dhcp4_requests, DHCP_OPTION_HOST_NAME, str); } /* Routes */ num = sd_dhcp_lease_get_routes (lease, &routes); if (num > 0) { l = g_string_sized_new (30); for (i = 0; i < num; i++) { NMPlatformIP4Route route; const char *gw_str; memset (&route, 0, sizeof (route)); route.network = routes[i].dst_addr.s_addr; route.plen = routes[i].dst_prefixlen; route.gateway = routes[i].gw_addr.s_addr; route.source = NM_IP_CONFIG_SOURCE_DHCP; route.metric = default_priority; nm_ip4_config_add_route (ip4_config, &route); str = nm_utils_inet4_ntop (route.network, buf); gw_str = nm_utils_inet4_ntop (route.gateway, NULL); LOG_LEASE (LOGD_DHCP4, " static route %s/%d gw %s", str, route.plen, gw_str); g_string_append_printf (l, "%s%s/%d %s", l->len ? " " : "", str, route.plen, gw_str); } add_option (options, dhcp4_requests, DHCP_OPTION_RFC3442_ROUTES, l->str); g_string_free (l, TRUE); } /* MTU */ r = sd_dhcp_lease_get_mtu (lease, &mtu); if (r == 0 && mtu) { nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); add_option_u32 (options, dhcp4_requests, DHCP_OPTION_INTERFACE_MTU, mtu); LOG_LEASE (LOGD_DHCP4, " mtu %u", mtu); } /* NTP servers */ num = sd_dhcp_lease_get_ntp (lease, &addr_list); if (num > 0) { l = g_string_sized_new (30); for (i = 0; i < num; i++) { str = nm_utils_inet4_ntop (addr_list[i].s_addr, buf); LOG_LEASE (LOGD_DHCP4, " ntp server '%s'", str); g_string_append_printf (l, "%s%s", l->len ? " " : "", str); } add_option (options, dhcp4_requests, DHCP_OPTION_NTP_SERVER, l->str); g_string_free (l, TRUE); } r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len); if (r >= 0) metered = !!memmem (data, data_len, "ANDROID_METERED", STRLEN ("ANDROID_METERED")); nm_ip4_config_set_metered (ip4_config, metered); return ip4_config; }