static GBytes * read_client_id (const char *str) { gs_free char *s = NULL; char *p; g_assert (!strncmp (str, CLIENTID_TAG, NM_STRLEN (CLIENTID_TAG))); str += NM_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); }
gboolean nmp_utils_device_exists (const char *name) { #define SYS_CLASS_NET "/sys/class/net/" char sysdir[NM_STRLEN (SYS_CLASS_NET) + IFNAMSIZ]; if ( !name || strlen (name) >= IFNAMSIZ || !nm_utils_is_valid_path_component (name)) g_return_val_if_reached (FALSE); memcpy (sysdir, SYS_CLASS_NET, NM_STRLEN (SYS_CLASS_NET)); nm_utils_ifname_cpy (&sysdir[NM_STRLEN (SYS_CLASS_NET)], name); return g_file_test (sysdir, G_FILE_TEST_EXISTS); }
char * utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg) { gs_free char *base = NULL; char *ptr, *ifcfg = NULL; g_return_val_if_fail (path != NULL, NULL); if (utils_should_ignore_file (path, only_ifcfg)) return NULL; base = g_path_get_basename (path); if (strncmp (base, IFCFG_TAG, NM_STRLEN (IFCFG_TAG)) == 0) { if (base[NM_STRLEN (IFCFG_TAG)] == '\0') return NULL; if (utils_is_ifcfg_alias_file (base, NULL)) { ifcfg = g_strdup (path); ptr = strrchr (ifcfg, ':'); if (ptr && ptr > ifcfg) { *ptr = '\0'; if (g_file_test (ifcfg, G_FILE_TEST_EXISTS)) { /* the file has a colon, so it is probably an alias. * To be ~more~ certain that this is an alias file, * check whether a corresponding base file exists. */ if (only_ifcfg) { g_free (ifcfg); return NULL; } return ifcfg; } } g_free (ifcfg); } return g_strdup (path); } if (only_ifcfg) return NULL; return utils_get_ifcfg_path (path); }
static void take_option (GHashTable *options, const ReqOption *requests, guint option, char *value) { guint i; g_return_if_fail (value != NULL); for (i = 0; requests[i].name; i++) { if (requests[i].num == option) { g_hash_table_insert (options, (gpointer) (requests[i].name + NM_STRLEN (REQPREFIX)), value); break; } } /* Option should always be found */ g_assert (requests[i].name); }
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, NM_STRLEN (CLIENTID_TAG))) return read_client_id (*line); } return 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; 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; }
char * nm_dhcp_dhclient_create_config (const char *interface, gboolean is_ip6, GBytes *client_id, const char *anycast_addr, const char *hostname, const char *fqdn, const char *orig_path, const char *orig_contents, GBytes **out_new_client_id) { GString *new_contents; GPtrArray *fqdn_opts, *reqs; int i; g_return_val_if_fail (!anycast_addr || nm_utils_hwaddr_valid (anycast_addr, ETH_ALEN), NULL); new_contents = g_string_new (_("# Created by NetworkManager\n")); fqdn_opts = g_ptr_array_sized_new (5); reqs = g_ptr_array_new_full (5, g_free); if (orig_contents) { char **lines, **line; gboolean in_alsoreq = FALSE; gboolean in_req = FALSE; g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path); lines = g_strsplit_set (orig_contents, "\n\r", 0); for (line = lines; lines && *line; line++) { char *p = *line; if (!strlen (g_strstrip (p))) 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 || fqdn) { if (strncmp (p, HOSTNAME4_TAG, strlen (HOSTNAME4_TAG)) == 0) continue; if (strncmp (p, FQDN_TAG, strlen (FQDN_TAG)) == 0) continue; } /* To let user's FQDN options (except "fqdn.fqdn") override the * default ones set by NM, add them later */ if (!strncmp (p, FQDN_TAG_PREFIX, NM_STRLEN (FQDN_TAG_PREFIX))) { g_ptr_array_add (fqdn_opts, g_strdup (p + NM_STRLEN (FQDN_TAG_PREFIX))); continue; } /* Ignore 'script' since we pass our own */ if (g_str_has_prefix (p, "script ")) continue; /* Check for "request" */ if (!strncmp (p, REQ_TAG, strlen (REQ_TAG))) { in_req = TRUE; p += strlen (REQ_TAG); g_ptr_array_set_size (reqs, 0); } /* Save all request options for later use */ if (in_req) { in_req = !grab_request_options (reqs, p); continue; } /* Check for "also require" */ if (!strncmp (p, ALSOREQ_TAG, strlen (ALSOREQ_TAG))) { in_alsoreq = TRUE; p += strlen (ALSOREQ_TAG); } if (in_alsoreq) { in_alsoreq = !grab_request_options (reqs, p); continue; } /* Existing configuration line is OK, add it to new configuration */ g_string_append (new_contents, *line); g_string_append_c (new_contents, '\n'); } if (lines) g_strfreev (lines); } else g_string_append_c (new_contents, '\n'); if (is_ip6) { add_hostname6 (new_contents, hostname); add_request (reqs, "dhcp6.name-servers"); add_request (reqs, "dhcp6.domain-search"); add_request (reqs, "dhcp6.client-id"); } else { add_ip4_config (new_contents, client_id, hostname, fqdn); add_request (reqs, "rfc3442-classless-static-routes"); add_request (reqs, "ms-classless-static-routes"); add_request (reqs, "static-routes"); add_request (reqs, "wpad"); add_request (reqs, "ntp-servers"); } /* And add it to the dhclient configuration */ for (i = 0; i < reqs->len; i++) g_string_append_printf (new_contents, "also request %s;\n", (char *) reqs->pdata[i]); g_ptr_array_free (reqs, TRUE); for (i = 0; i < fqdn_opts->len; i++) { char *t = g_ptr_array_index (fqdn_opts, i); if (i == 0) g_string_append_printf (new_contents, "\n# FQDN options from %s\n", orig_path); g_string_append_printf (new_contents, FQDN_TAG_PREFIX "%s\n", t); g_free (t); } g_ptr_array_free (fqdn_opts, TRUE); g_string_append_c (new_contents, '\n'); if (anycast_addr) { g_string_append_printf (new_contents, "interface \"%s\" {\n" " initial-interval 1; \n" " anycast-mac ethernet %s;\n" "}\n", interface, anycast_addr); } return g_string_free (new_contents, FALSE); }