summaryrefslogtreecommitdiff
path: root/src/platform/nm-linux-platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/nm-linux-platform.c')
-rw-r--r--src/platform/nm-linux-platform.c269
1 files changed, 169 insertions, 100 deletions
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 3eb7ff3f8d..4e41bc9b27 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -176,6 +176,11 @@
* Forward declarations and enums
******************************************************************/
+typedef enum {
+ INFINIBAND_ACTION_CREATE_CHILD,
+ INFINIBAND_ACTION_DELETE_CHILD,
+} InfinibandAction;
+
enum {
DELAYED_ACTION_IDX_REFRESH_ALL_LINKS,
DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES,
@@ -303,23 +308,6 @@ _support_user_ipv6ll_detect (struct nlattr **tb)
* Various utilities
******************************************************************/
-static void
-clear_host_address (int family, const void *network, guint8 plen, void *dst)
-{
- g_return_if_fail (network);
-
- switch (family) {
- case AF_INET:
- *((in_addr_t *) dst) = nm_utils_ip4_address_clear_host_address (*((in_addr_t *) network), plen);
- break;
- case AF_INET6:
- nm_utils_ip6_address_clear_host_address ((struct in6_addr *) dst, (const struct in6_addr *) network, plen);
- break;
- default:
- g_assert_not_reached ();
- }
-}
-
static int
_vlan_qos_mapping_cmp_from (gconstpointer a, gconstpointer b, gpointer user_data)
{
@@ -588,12 +576,17 @@ _lookup_cached_link (const NMPCache *cache, int ifindex, gboolean *completed_fro
#define DEVTYPE_PREFIX "DEVTYPE="
static char *
-_linktype_read_devtype (const char *sysfs_path)
+_linktype_read_devtype (const char *ifname)
{
- gs_free char *uevent = g_strdup_printf ("%s/uevent", sysfs_path);
+ char uevent[NM_STRLEN ("/sys/class/net/123456789012345/uevent\0") + 100 /*safety*/];
char *contents = NULL;
char *cont, *end;
+ nm_sprintf_buf (uevent,
+ "/sys/class/net/%s/uevent",
+ NM_ASSERT_VALID_PATH_COMPONENT (ifname));
+ nm_assert (strlen (uevent) < sizeof (uevent) - 1);
+
if (!g_file_get_contents (uevent, &contents, NULL, NULL))
return NULL;
for (cont = contents; cont; cont = end) {
@@ -690,9 +683,8 @@ _linktype_get_type (NMPlatform *platform,
return NM_LINK_TYPE_IP6TNL;
if (ifname) {
+ char anycast_mask[NM_STRLEN ("/sys/class/net/123456789012345/anycast_mask\0") + 100 /*safety*/];
gs_free char *driver = NULL;
- gs_free char *sysfs_path = NULL;
- gs_free char *anycast_mask = NULL;
gs_free char *devtype = NULL;
/* Fallback OVS detection for kernel <= 3.16 */
@@ -709,12 +701,15 @@ _linktype_get_type (NMPlatform *platform,
}
}
- sysfs_path = g_strdup_printf ("/sys/class/net/%s", ifname);
- anycast_mask = g_strdup_printf ("%s/anycast_mask", sysfs_path);
+ nm_sprintf_buf (anycast_mask,
+ "/sys/class/net/%s/anycast_mask",
+ NM_ASSERT_VALID_PATH_COMPONENT (ifname));
+ nm_assert (strlen (anycast_mask) < sizeof (anycast_mask) - 1);
+
if (g_file_test (anycast_mask, G_FILE_TEST_EXISTS))
return NM_LINK_TYPE_OLPC_MESH;
- devtype = _linktype_read_devtype (sysfs_path);
+ devtype = _linktype_read_devtype (ifname);
for (i = 0; devtype && i < G_N_ELEMENTS (linktypes); i++) {
if (g_strcmp0 (devtype, linktypes[i].devtype) == 0) {
if (linktypes[i].nm_type == NM_LINK_TYPE_BNEP) {
@@ -729,7 +724,7 @@ _linktype_get_type (NMPlatform *platform,
}
/* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */
- if (wifi_utils_is_wifi (ifname, sysfs_path))
+ if (wifi_utils_is_wifi (ifname))
return NM_LINK_TYPE_WIFI;
if (arptype == ARPHRD_ETHER) {
@@ -803,9 +798,10 @@ _nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len)
static gboolean
_parse_af_inet6 (NMPlatform *platform,
struct nlattr *attr,
- NMUtilsIPv6IfaceId *out_iid,
- guint8 *out_iid_is_valid,
- guint8 *out_addr_gen_mode_inv)
+ NMUtilsIPv6IfaceId *out_token,
+ gboolean *out_token_valid,
+ guint8 *out_addr_gen_mode_inv,
+ gboolean *out_addr_gen_mode_valid)
{
static struct nla_policy policy[IFLA_INET6_MAX+1] = {
[IFLA_INET6_FLAGS] = { .type = NLA_U32 },
@@ -819,7 +815,8 @@ _parse_af_inet6 (NMPlatform *platform,
struct nlattr *tb[IFLA_INET6_MAX+1];
int err;
struct in6_addr i6_token;
- gboolean iid_is_valid = FALSE;
+ gboolean token_valid = FALSE;
+ gboolean addr_gen_mode_valid = FALSE;
guint8 i6_addr_gen_mode_inv = 0;
gboolean success = FALSE;
@@ -836,8 +833,7 @@ _parse_af_inet6 (NMPlatform *platform,
if (_check_addr_or_errout (tb, IFLA_INET6_TOKEN, sizeof (struct in6_addr))) {
nla_memcpy (&i6_token, tb[IFLA_INET6_TOKEN], sizeof (struct in6_addr));
- if (!IN6_IS_ADDR_UNSPECIFIED (&i6_token))
- iid_is_valid = TRUE;
+ token_valid = TRUE;
}
/* Hack to detect support addrgenmode of the kernel. We only parse
@@ -852,21 +848,18 @@ _parse_af_inet6 (NMPlatform *platform,
* to signal "unset". */
goto errout;
}
+ addr_gen_mode_valid = TRUE;
}
success = TRUE;
- if (iid_is_valid) {
- out_iid->id_u8[7] = i6_token.s6_addr[15];
- out_iid->id_u8[6] = i6_token.s6_addr[14];
- out_iid->id_u8[5] = i6_token.s6_addr[13];
- out_iid->id_u8[4] = i6_token.s6_addr[12];
- out_iid->id_u8[3] = i6_token.s6_addr[11];
- out_iid->id_u8[2] = i6_token.s6_addr[10];
- out_iid->id_u8[1] = i6_token.s6_addr[9];
- out_iid->id_u8[0] = i6_token.s6_addr[8];
- *out_iid_is_valid = TRUE;
+ if (token_valid) {
+ *out_token_valid = token_valid;
+ nm_utils_ipv6_interface_identifier_get_from_addr (out_token, &i6_token);
+ }
+ if (addr_gen_mode_valid) {
+ *out_addr_gen_mode_valid = addr_gen_mode_valid;
+ *out_addr_gen_mode_inv = i6_addr_gen_mode_inv;
}
- *out_addr_gen_mode_inv = i6_addr_gen_mode_inv;
errout:
return success;
}
@@ -1441,6 +1434,8 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
NMPObject *lnk_data = NULL;
gboolean address_complete_from_cache = TRUE;
gboolean lnk_data_complete_from_cache = TRUE;
+ gboolean af_inet6_token_valid = FALSE;
+ gboolean af_inet6_addr_gen_mode_valid = FALSE;
if (!nlmsg_valid_hdr (nlh, sizeof (*ifi)))
return NULL;
@@ -1517,9 +1512,10 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
case AF_INET6:
_parse_af_inet6 (platform,
af_attr,
- &obj->link.inet6_token.iid,
- &obj->link.inet6_token.is_valid,
- &obj->link.inet6_addr_gen_mode_inv);
+ &obj->link.inet6_token,
+ &af_inet6_token_valid,
+ &obj->link.inet6_addr_gen_mode_inv,
+ &af_inet6_addr_gen_mode_valid);
break;
}
}
@@ -1561,7 +1557,9 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
if ( completed_from_cache
&& ( lnk_data_complete_from_cache
- || address_complete_from_cache)) {
+ || address_complete_from_cache
+ || !af_inet6_token_valid
+ || !af_inet6_addr_gen_mode_valid)) {
_lookup_cached_link (cache, obj->link.ifindex, completed_from_cache, &link_cached);
if (link_cached) {
if ( lnk_data_complete_from_cache
@@ -1580,6 +1578,10 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
}
if (address_complete_from_cache)
obj->link.addr = link_cached->link.addr;
+ if (!af_inet6_token_valid)
+ obj->link.inet6_token = link_cached->link.inet6_token;
+ if (!af_inet6_addr_gen_mode_valid)
+ obj->link.inet6_addr_gen_mode_inv = link_cached->link.inet6_addr_gen_mode_inv;
}
}
@@ -1666,7 +1668,7 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
}
}
- obj->ip_address.source = NM_IP_CONFIG_SOURCE_KERNEL;
+ obj->ip_address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL;
obj->ip_address.n_ifa_flags = tb[IFA_FLAGS]
? nla_get_u32 (tb[IFA_FLAGS])
@@ -1896,9 +1898,10 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
*
* This happens, because this route is not nmp_object_is_alive().
* */
- obj->ip_route.source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED;
- } else
- obj->ip_route.source = nmp_utils_ip_config_source_from_rtprot (rtm->rtm_protocol);
+ obj->ip_route.rt_cloned = TRUE;
+ }
+
+ obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot (rtm->rtm_protocol);
obj_result = obj;
obj = NULL;
@@ -1951,7 +1954,8 @@ nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_m
static gboolean
_nl_msg_new_link_set_afspec (struct nl_msg *msg,
- int addr_gen_mode)
+ int addr_gen_mode,
+ NMUtilsIPv6IfaceId *iid)
{
struct nlattr *af_spec;
struct nlattr *af_attr;
@@ -1961,11 +1965,19 @@ _nl_msg_new_link_set_afspec (struct nl_msg *msg,
if (!(af_spec = nla_nest_start (msg, IFLA_AF_SPEC)))
goto nla_put_failure;
- if (addr_gen_mode >= 0) {
+ if (addr_gen_mode >= 0 || iid) {
if (!(af_attr = nla_nest_start (msg, AF_INET6)))
goto nla_put_failure;
- NLA_PUT_U8 (msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode);
+ if (addr_gen_mode >= 0)
+ NLA_PUT_U8 (msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode);
+
+ if (iid) {
+ struct in6_addr i6_token = { .s6_addr = { 0, } };
+
+ nm_utils_ipv6_addr_set_interface_identifier (&i6_token, *iid);
+ NLA_PUT (msg, IFLA_INET6_TOKEN, sizeof (struct in6_addr), &i6_token);
+ }
nla_nest_end (msg, af_attr);
}
@@ -2258,7 +2270,7 @@ _nl_msg_new_route (int nlmsg_type,
.rtm_family = family,
.rtm_tos = 0,
.rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */
- .rtm_protocol = nmp_utils_ip_config_source_to_rtprot (source),
+ .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (source),
.rtm_scope = scope,
.rtm_type = RTN_UNICAST,
.rtm_flags = 0,
@@ -2282,7 +2294,7 @@ _nl_msg_new_route (int nlmsg_type,
addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr);
- clear_host_address (family, network, plen, &network_clean);
+ nm_utils_ipx_address_clear_host_address (family, &network_clean, network, plen);
NLA_PUT (msg, RTA_DST, addr_len, &network_clean);
NLA_PUT_U32 (msg, RTA_PRIORITY, metric);
@@ -2485,6 +2497,7 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value)
gsize len;
char *actual;
gs_free char *actual_free = NULL;
+ int errsv;
g_return_val_if_fail (path != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
@@ -2495,18 +2508,22 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value)
/* Don't write to suspicious locations */
g_assert (!strstr (path, "/../"));
- if (!nm_platform_netns_push (platform, &netns))
+ if (!nm_platform_netns_push (platform, &netns)) {
+ errno = ENETDOWN;
return FALSE;
+ }
fd = open (path, O_WRONLY | O_TRUNC);
if (fd == -1) {
- if (errno == ENOENT) {
+ errsv = errno;
+ if (errsv == ENOENT) {
_LOGD ("sysctl: failed to open '%s': (%d) %s",
- path, errno, strerror (errno));
+ path, errsv, strerror (errsv));
} else {
_LOGE ("sysctl: failed to open '%s': (%d) %s",
- path, errno, strerror (errno));
+ path, errsv, strerror (errsv));
}
+ errno = errsv;
return FALSE;
}
@@ -2527,26 +2544,43 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value)
actual[len] = '\0';
/* Try to write the entire value three times if a partial write occurs */
+ errsv = 0;
for (tries = 0, nwrote = 0; tries < 3 && nwrote != len; tries++) {
nwrote = write (fd, actual, len);
if (nwrote == -1) {
- if (errno == EINTR) {
+ errsv = errno;
+ if (errsv == EINTR) {
_LOGD ("sysctl: interrupted, will try again");
continue;
}
break;
}
}
- if (nwrote == -1 && errno != EEXIST) {
+ if (nwrote == -1 && errsv != EEXIST) {
_LOGE ("sysctl: failed to set '%s' to '%s': (%d) %s",
- path, value, errno, strerror (errno));
+ path, value, errsv, strerror (errsv));
} else if (nwrote < len) {
_LOGE ("sysctl: failed to set '%s' to '%s' after three attempts",
path, value);
}
- close (fd);
- return (nwrote == len);
+ if (nwrote != len) {
+ if (close (fd) != 0) {
+ if (errsv != 0)
+ errno = errsv;
+ } else if (errsv != 0)
+ errno = errsv;
+ else
+ errno = EIO;
+ return FALSE;
+ }
+ if (close (fd) != 0) {
+ /* errno is already properly set. */
+ return FALSE;
+ }
+
+ /* success. errno is undefined (no need to set). */
+ return TRUE;
}
static GSList *sysctl_clear_cache_list;
@@ -3724,6 +3758,16 @@ cache_lookup_link (NMPlatform *platform, int ifindex)
return obj_cache;
}
+const NMPlatformObject *const*
+nm_linux_platform_lookup (NMPlatform *platform, const NMPCacheId *cache_id, guint *out_len)
+{
+ g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), NULL);
+ g_return_val_if_fail (cache_id, NULL);
+
+ return nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
+ cache_id, out_len);
+}
+
static GArray *
link_get_all (NMPlatform *platform)
{
@@ -4294,8 +4338,22 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable
0,
0);
if ( !nlmsg
- || !_nl_msg_new_link_set_afspec (nlmsg,
- mode))
+ || !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL))
+ g_return_val_if_reached (FALSE);
+
+ return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS;
+}
+
+static gboolean
+link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid)
+{
+ nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+
+ _LOGD ("link: change %d: token: set IPv6 address generation token to %s",
+ ifindex, nm_utils_inet6_interface_identifier_to_token (iid, NULL));
+
+ nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL, 0, 0);
+ if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid))
g_return_val_if_reached (FALSE);
return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS;
@@ -5095,57 +5153,67 @@ link_release (NMPlatform *platform, int master, int slave)
/******************************************************************/
static gboolean
-_infiniband_partition_action (NMPlatform *platform, int parent, int p_key, const char *action, char **ifname)
+_infiniband_partition_action (NMPlatform *platform,
+ InfinibandAction action,
+ int parent,
+ int p_key,
+ const NMPlatformLink **out_link)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
const NMPObject *obj_parent;
- gs_free char *path = NULL;
- gs_free char *id = NULL;
+ const NMPObject *obj;
+ char path[NM_STRLEN ("/sys/class/net/%s/%s") + IFNAMSIZ + 100];
+ char id[20];
+ char name[IFNAMSIZ];
+ gboolean success;
+
+ nm_assert (NM_IN_SET (action, INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD));
+ nm_assert (p_key > 0 && p_key <= 0xffff && p_key != 0x8000);
obj_parent = nmp_cache_lookup_link (priv->cache, parent);
- if (!obj_parent || !obj_parent->link.name[0])
- g_return_val_if_reached (FALSE);
+ if (!obj_parent || !obj_parent->link.name[0]) {
+ errno = ENOENT;
+ return FALSE;
+ }
- *ifname = g_strdup_printf ("%s.%04x", obj_parent->link.name, p_key);
+ nm_sprintf_buf (path,
+ "/sys/class/net/%s/%s",
+ NM_ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name),
+ (action == INFINIBAND_ACTION_CREATE_CHILD
+ ? "create_child"
+ : "delete_child"));
+ nm_sprintf_buf (id, "0x%04x", p_key);
+ success = nm_platform_sysctl_set (platform, path, id);
+ if (!success) {
+ if ( action == INFINIBAND_ACTION_DELETE_CHILD
+ && errno == ENODEV)
+ return TRUE;
+ return FALSE;
+ }
- path = g_strdup_printf ("/sys/class/net/%s/%s",
- NM_ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name),
- action);
- id = g_strdup_printf ("0x%04x", p_key);
+ nm_utils_new_infiniband_name (name, obj_parent->link.name, p_key);
+ do_request_link (platform, 0, name);
- return nm_platform_sysctl_set (platform, path, id);
-}
+ if (action == INFINIBAND_ACTION_DELETE_CHILD)
+ return TRUE;
+ obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE,
+ NM_LINK_TYPE_INFINIBAND, NULL, NULL);
+ if (out_link)
+ *out_link = obj ? &obj->link : NULL;
+ return !!obj;
+}
static gboolean
infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link)
{
- const NMPObject *obj;
- gs_free char *ifname = NULL;
-
- if (!_infiniband_partition_action (platform, parent, p_key, "create_child", &ifname))
- return FALSE;
-
- do_request_link (platform, 0, ifname);
-
- obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
- 0, ifname, FALSE, NM_LINK_TYPE_INFINIBAND, NULL, NULL);
- if (out_link)
- *out_link = obj ? &obj->link : NULL;
- return !!obj;
+ return _infiniband_partition_action (platform, INFINIBAND_ACTION_CREATE_CHILD, parent, p_key, out_link);
}
static gboolean
infiniband_partition_delete (NMPlatform *platform, int parent, int p_key)
{
- gs_free char *ifname = NULL;
-
- if (!_infiniband_partition_action (platform, parent, p_key, "delete_child", &ifname)) {
- if (errno != ENODEV)
- return FALSE;
- }
-
- return TRUE;
+ return _infiniband_partition_action (platform, INFINIBAND_ACTION_DELETE_CHILD, parent, p_key, NULL);
}
/******************************************************************/
@@ -5570,7 +5638,7 @@ ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NM
nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (routes[i])) == klass);
if ( with_rtprot_kernel
- || routes[i]->source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL)
+ || routes[i]->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL)
g_array_append_vals (array, routes[i], 1);
}
return array;
@@ -6397,6 +6465,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
platform_class->link_get_udev_device = link_get_udev_device;
platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled;
+ platform_class->link_set_token = link_set_token;
platform_class->link_set_address = link_set_address;
platform_class->link_get_permanent_address = link_get_permanent_address;