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);
}
Example #3
0
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);
}