static void rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_data) { static NMIP6Config *rdisc_config = NULL; NMIP6Config *existing; static int system_support = -1; guint32 ifa_flags = 0x00; int i; if (system_support == -1) { /* * Check, whether kernel is 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_kernel_extended_ifa_flags (NM_PLATFORM_GET); } if (system_support) ifa_flags = IFA_F_NOPREFIXROUTE; if (global_opt.tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR || global_opt.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; } existing = nm_ip6_config_capture (ifindex, FALSE, global_opt.tempaddr); if (rdisc_config) nm_ip6_config_subtract (existing, rdisc_config); else rdisc_config = nm_ip6_config_new (ifindex); 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 (rdisc_config, &gateway->address); } else nm_ip6_config_set_gateway (rdisc_config, NULL); } if (changed & NM_RDISC_CONFIG_ADDRESSES) { /* Rebuild address list from router discovery cache. */ nm_ip6_config_reset_addresses (rdisc_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.n_ifa_flags = ifa_flags; nm_ip6_config_add_address (rdisc_config, &address); } } if (changed & NM_RDISC_CONFIG_ROUTES) { /* Rebuild route list from router discovery cache. */ nm_ip6_config_reset_routes (rdisc_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 && discovered_route->plen <= 128) { 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 = global_opt.priority_v6; nm_ip6_config_add_route (rdisc_config, &route); } } } if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) { /* Unsupported until systemd DHCPv6 is ready */ } if (changed & NM_RDISC_CONFIG_HOP_LIMIT) nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdisc->hop_limit); if (changed & NM_RDISC_CONFIG_MTU) { char val[16]; g_snprintf (val, sizeof (val), "%d", rdisc->mtu); nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "mtu"), val); } nm_ip6_config_merge (existing, rdisc_config, NM_IP_CONFIG_MERGE_DEFAULT); if (!nm_ip6_config_commit (existing, ifindex, TRUE)) nm_log_warn (LOGD_IP6, "(%s): failed to apply IPv6 config", global_opt.ifname); }
int main (int argc, char *argv[]) { char *bad_domains = NULL; GError *error = NULL; gboolean wrote_pidfile = FALSE; gs_free char *pidfile = NULL; 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; guint sd_id; nm_g_type_init (); setpgid (getpid (), getpid ()); do_early_setup (&argc, &argv); if (global_opt.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); } if (global_opt.show_version) { fprintf (stdout, NM_DIST_VERSION "\n"); exit (0); } nm_main_utils_ensure_root (); if (!global_opt.ifname || !global_opt.uuid) { fprintf (stderr, _("An interface name and UUID are required\n")); exit (1); } ifindex = if_nametoindex (global_opt.ifname); if (ifindex <= 0) { fprintf (stderr, _("Failed to find interface index for %s (%s)\n"), global_opt.ifname, strerror (errno)); exit (1); } pidfile = g_strdup_printf (NMIH_PID_FILE_FMT, ifindex); nm_main_utils_ensure_not_running_pidfile (pidfile); nm_main_utils_ensure_rundir (); if (!nm_logging_setup (global_opt.opt_log_level, global_opt.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); } if (global_opt.become_daemon && !global_opt.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 (); nm_logging_syslog_openlog (global_opt.logging_backend ? global_opt.logging_backend : (global_opt.debug ? "debug" : NULL)); nm_log_info (LOGD_CORE, "nm-iface-helper (version " NM_DIST_VERSION ") is starting..."); /* Set up platform interaction layer */ nm_linux_platform_setup (); tmp = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddr_len); if (tmp) { hwaddr = g_byte_array_sized_new (hwaddr_len); g_byte_array_append (hwaddr, tmp, hwaddr_len); } if (global_opt.iid_str) { GBytes *bytes; gsize ignored = 0; bytes = nm_utils_hexstr2bin (global_opt.iid_str); if (!bytes || g_bytes_get_size (bytes) != sizeof (*iid)) { fprintf (stderr, _("(%s): Invalid IID %s\n"), global_opt.ifname, global_opt.iid_str); exit (1); } iid = g_bytes_unref_to_data (bytes, &ignored); } if (global_opt.dhcp4_address) { nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip4_property_path (global_opt.ifname, "promote_secondaries"), "1"); dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), global_opt.ifname, ifindex, hwaddr, global_opt.uuid, global_opt.priority_v4, !!global_opt.dhcp4_hostname, global_opt.dhcp4_hostname, global_opt.dhcp4_fqdn, global_opt.dhcp4_clientid, 45, NULL, global_opt.dhcp4_address); g_assert (dhcp4_client); g_signal_connect (dhcp4_client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (dhcp4_state_changed), NULL); } if (global_opt.slaac) { nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, TRUE); rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, ifindex, global_opt.ifname, global_opt.uuid, global_opt.addr_gen_mode, NULL); g_assert (rdisc); if (iid) nm_rdisc_set_iid (rdisc, *iid); nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra"), "1"); nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_defrtr"), "0"); nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_pinfo"), "0"); nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_rtr_pref"), "0"); g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (ip6_address_changed), rdisc); 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); } sd_id = nm_sd_event_attach_default (); g_main_loop_run (main_loop); g_clear_pointer (&hwaddr, g_byte_array_unref); if (pidfile && wrote_pidfile) unlink (pidfile); nm_log_info (LOGD_CORE, "exiting"); nm_clear_g_source (&sd_id); exit (0); }
static inline gint32 ipv6_sysctl_get (const char *ifname, const char *property, gint32 defval) { return nm_platform_sysctl_get_int32 (nm_utils_ip6_property_path (ifname, property), defval); }