static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { int r; sd_dhcp6_lease *lease; struct in6_addr ip6_addr; uint32_t lifetime_preferred, lifetime_valid; uint8_t prefixlen; r = sd_dhcp6_client_get_lease(client, &lease); if (r < 0) return r; sd_dhcp6_lease_reset_address_iter(lease); while (sd_dhcp6_lease_get_address(lease, &ip6_addr, &lifetime_preferred, &lifetime_valid) >= 0) { r = sd_icmp6_ra_get_prefixlen(link->icmp6_router_discovery, &ip6_addr, &prefixlen); if (r < 0 && r != -EADDRNOTAVAIL) { log_link_warning_errno(link, r, "Could not get prefix information: %m"); return r; } if (r == -EADDRNOTAVAIL) prefixlen = 128; r = dhcp6_address_change(link, &ip6_addr, prefixlen, lifetime_preferred, lifetime_valid); if (r < 0) return r; } return 0; }
static void test_client_solicit_cb(sd_dhcp6_client *client, int event, void *userdata) { sd_event *e = userdata; sd_dhcp6_lease *lease; struct in6_addr *addrs; char **domains; assert_se(e); assert_se(event == SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); assert_se(!strcmp("lab.intra", domains[0])); assert_se(domains[1] == NULL); assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); assert_se(!memcmp(addrs, &msg_advertise[124], 16)); assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); assert_se(!memcmp(addrs, &msg_advertise[159], 16)); assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY); if (verbose) printf(" got DHCPv6 event %d\n", event); sd_event_exit(e, 0); }
static int dhcp6_prefix_expired(Link *link) { int r; sd_dhcp6_lease *lease; struct in6_addr *expired_prefix, ip6_addr; uint8_t expired_prefixlen; uint32_t lifetime_preferred, lifetime_valid; r = sd_icmp6_ra_get_expired_prefix(link->icmp6_router_discovery, &expired_prefix, &expired_prefixlen); if (r < 0) return r; r = sd_dhcp6_client_get_lease(link->dhcp6_client, &lease); if (r < 0) return r; sd_dhcp6_lease_reset_address_iter(lease); while (sd_dhcp6_lease_get_address(lease, &ip6_addr, &lifetime_preferred, &lifetime_valid) >= 0) { r = sd_icmp6_prefix_match(expired_prefix, expired_prefixlen, &ip6_addr); if (r >= 0) { r = dhcp6_address_update(link, &ip6_addr, 128, lifetime_preferred, lifetime_valid); return r; } } return 0; }
static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { int r; Link *link = userdata; assert(link); assert(link->network); assert(link->manager); if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; switch(event) { case SD_DHCP6_CLIENT_EVENT_STOP: case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: if (sd_dhcp6_client_get_lease(client, NULL) >= 0) log_link_warning(link, "DHCPv6 lease lost"); link->dhcp6_configured = false; break; case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: r = dhcp6_lease_address_acquired(client, link); if (r < 0) { link_enter_failed(link); return; } /* fall through */ case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: r = dhcp6_lease_information_acquired(client, link); if (r < 0) { link_enter_failed(link); return; } link->dhcp6_configured = true; break; default: if (event < 0) log_link_warning_errno(link, event, "DHCPv6 error: %m"); else log_link_warning(link, "DHCPv6 unknown event: %d", event); return; } link_check_ready(link); }
static int dhcp6_prefix_expired(Link *link) { int r; sd_dhcp6_lease *lease; struct in6_addr *expired_prefix, ip6_addr; uint8_t expired_prefixlen; uint32_t lifetime_preferred, lifetime_valid; r = sd_icmp6_ra_get_expired_prefix(link->icmp6_router_discovery, &expired_prefix, &expired_prefixlen); if (r < 0) return r; r = sd_dhcp6_client_get_lease(link->dhcp6_client, &lease); if (r < 0) return r; log_link_struct(link, LOG_INFO, "MESSAGE=%-*s: IPv6 prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d expired", IFNAMSIZ, link->ifname, SD_ICMP6_ADDRESS_FORMAT_VAL(*expired_prefix), expired_prefixlen, NULL); sd_dhcp6_lease_reset_address_iter(lease); while (sd_dhcp6_lease_get_address(lease, &ip6_addr, &lifetime_preferred, &lifetime_valid) >= 0) { r = sd_icmp6_prefix_match(expired_prefix, expired_prefixlen, &ip6_addr); if (r < 0) continue; log_link_struct(link, LOG_INFO, "MESSAGE=%-*s: IPv6 prefix length updated "SD_ICMP6_ADDRESS_FORMAT_STR"/%d", IFNAMSIZ, link->ifname, SD_ICMP6_ADDRESS_FORMAT_VAL(ip6_addr), 128, NULL); dhcp6_address_update(link, &ip6_addr, 128, lifetime_preferred, lifetime_valid); } return 0; }
static void test_client_information_cb(sd_dhcp6_client *client, int event, void *userdata) { sd_event *e = userdata; sd_dhcp6_lease *lease; struct in6_addr *addrs; struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; char **domains; assert_se(e); assert_se(event == SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); assert_se(!strcmp("lab.intra", domains[0])); assert_se(domains[1] == NULL); assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); assert_se(!memcmp(addrs, &msg_advertise[124], 16)); assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); assert_se(!memcmp(addrs, &msg_advertise[159], 16)); if (verbose) printf(" got DHCPv6 event %d\n", event); assert_se(sd_dhcp6_client_set_information_request(client, false) == -EBUSY); assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0); assert_se(sd_dhcp6_client_stop(client) >= 0); assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); assert_se(sd_dhcp6_client_set_callback(client, test_client_solicit_cb, e) >= 0); assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); assert_se(sd_dhcp6_client_start(client) >= 0); }
static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { int r; sd_dhcp6_lease *lease; struct in6_addr ip6_addr; uint32_t lifetime_preferred, lifetime_valid; r = sd_dhcp6_client_get_lease(client, &lease); if (r < 0) return r; sd_dhcp6_lease_reset_address_iter(lease); while (sd_dhcp6_lease_get_address(lease, &ip6_addr, &lifetime_preferred, &lifetime_valid) >= 0) { r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid); if (r < 0) return r; } return 0; }
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); } }