// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 - 2017 Red Hat, Inc. */ #include "nm-default.h" #include "nm-fake-platform.h" #include #include #include #include #include #include "nm-utils.h" #include "nm-core-utils.h" #include "nm-platform-utils.h" #include "nm-platform-private.h" #include "nmp-object.h" #include "nm-test-utils-core.h" /*****************************************************************************/ typedef struct { const NMPObject *obj; char *udi; struct in6_addr ip6_lladdr; } NMFakePlatformLink; typedef struct { GHashTable *options; GArray *links; } NMFakePlatformPrivate; struct _NMFakePlatform { NMPlatform parent; NMFakePlatformPrivate _priv; }; struct _NMFakePlatformClass { NMPlatformClass parent; }; G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM) #define NM_FAKE_PLATFORM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMFakePlatform, NM_IS_FAKE_PLATFORM, NMPlatform) /*****************************************************************************/ #define _NMLOG_PREFIX_NAME "platform-fake" #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__) #define _LOG(level, domain, self, ...) \ G_STMT_START { \ const NMLogLevel __level = (level); \ const NMLogDomain __domain = (domain); \ \ if (nm_logging_enabled (__level, __domain)) { \ char __prefix[32]; \ const char *__p_prefix = _NMLOG_PREFIX_NAME; \ NMPlatform *const __self = (self); \ \ if (__self && nm_platform_get_log_with_ptr (self)) { \ g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ __p_prefix = __prefix; \ } \ _nm_log (__level, __domain, 0, NULL, NULL, \ "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } \ } G_STMT_END /*****************************************************************************/ static void link_changed (NMPlatform *platform, NMFakePlatformLink *device, NMPCacheOpsType cache_op, const NMPObject *obj_old); static gboolean ipx_address_delete (NMPlatform *platform, int addr_family, int ifindex, gconstpointer addr, const guint8 *plen, gconstpointer peer_addr); static gboolean ipx_route_delete (NMPlatform *platform, int addr_family, int ifindex, const NMPObject *obj); static gboolean ip6_address_add (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen, struct in6_addr peer_addr, guint32 lifetime, guint32 preferred, guint flags); static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen); /*****************************************************************************/ #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ G_STMT_START { \ const char *const _pathid = (pathid); \ const int _dirfd = (dirfd); \ const char *const _path = (path); \ \ g_assert (_path && _path[0]); \ g_assert (!strstr (_path, "/../")); \ if (_dirfd < 0) { \ g_assert (!_pathid); \ g_assert (_path[0] == '/'); \ g_assert ( g_str_has_prefix (_path, "/proc/sys/") \ || g_str_has_prefix (_path, "/sys/")); \ } else { \ g_assert_not_reached (); \ } \ } G_STMT_END static gboolean sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); ASSERT_SYSCTL_ARGS (pathid, dirfd, path); g_hash_table_insert (priv->options, g_strdup (path), g_strdup (value)); return TRUE; } static char * sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *path) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); const char *v; ASSERT_SYSCTL_ARGS (pathid, dirfd, path); v = g_hash_table_lookup (priv->options, path); if (!v) { errno = ENOENT; return NULL; } return g_strdup (v); } static NMFakePlatformLink * link_get (NMPlatform *platform, int ifindex) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMFakePlatformLink *device; int idx; if (ifindex <= 0) g_return_val_if_reached (NULL); idx = ifindex - 1; if (idx >= priv->links->len) goto not_found; device = &g_array_index (priv->links, NMFakePlatformLink, idx); if (!device->obj) goto not_found; g_assert (ifindex == NMP_OBJECT_CAST_LINK (device->obj)->ifindex); g_assert (device->obj == nm_platform_link_get_obj (platform, ifindex, FALSE)); return device; not_found: _LOGD ("link not found: %d", ifindex); return NULL; } static void link_add_prepare (NMPlatform *platform, NMFakePlatformLink *device, NMPObject *obj_tmp) { gboolean connected; /* we must clear the driver, because platform cache wants to set it */ g_assert (obj_tmp->link.driver == g_intern_string (obj_tmp->link.driver)); obj_tmp->link.driver = NULL; if (NM_IN_SET (obj_tmp->link.type, NM_LINK_TYPE_BRIDGE, NM_LINK_TYPE_BOND)) { connected = FALSE; if (NM_FLAGS_HAS (obj_tmp->link.n_ifi_flags, IFF_UP)) { NMPLookup lookup; NMDedupMultiIter iter; const NMPObject *slave_candidate = NULL; nmp_cache_iter_for_each (&iter, nmp_cache_lookup (nm_platform_get_cache (platform), nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK)), &slave_candidate) { if (nmp_cache_link_connected_for_slave (obj_tmp->link.ifindex, slave_candidate)) { connected = TRUE; break; } } } } else connected = NM_FLAGS_HAS (obj_tmp->link.n_ifi_flags, IFF_UP); obj_tmp->link.n_ifi_flags = NM_FLAGS_ASSIGN (obj_tmp->link.n_ifi_flags, IFF_LOWER_UP, connected); obj_tmp->link.connected = connected; } static NMFakePlatformLink * link_add_pre (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMFakePlatformLink *device; int ifindex; NMPObject *o; NMPlatformLink *link; gs_free char *ip6_lladdr = NULL; g_assert (!name || strlen (name) < IFNAMSIZ); g_array_set_size (priv->links, priv->links->len + 1); device = &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1); ifindex = priv->links->len; memset (device, 0, sizeof (*device)); o = nmp_object_new_link (ifindex); link = NMP_OBJECT_CAST_LINK (o); ip6_lladdr = ifindex > 0 ? g_strdup_printf ("fe80::fa1e:%0x:%0x", ifindex / 256, ifindex % 256) : NULL; link->ifindex = name ? ifindex : 0; link->type = type; link->kind = g_intern_string (nm_link_type_to_string (type)); link->initialized = TRUE; if (name) strcpy (link->name, name); switch (link->type) { case NM_LINK_TYPE_DUMMY: link->n_ifi_flags = NM_FLAGS_SET (link->n_ifi_flags, IFF_NOARP); break; default: link->n_ifi_flags = NM_FLAGS_UNSET (link->n_ifi_flags, IFF_NOARP); break; } o->_link.netlink.is_in_netlink = TRUE; if (address) { g_assert (address_len > 0 && address_len <= sizeof (link->l_address.data)); memcpy (link->l_address.data, address, address_len); link->l_address.len = address_len; } else g_assert (address_len == 0); device->obj = o; device->udi = g_strdup_printf ("fake:%d", ifindex); device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); return device; } static int link_add (NMPlatform *platform, NMLinkType type, const char *name, int parent, const void *address, size_t address_len, gconstpointer extra_data, const NMPlatformLink **out_link) { NMFakePlatformLink *device; NMFakePlatformLink *device_veth = NULL; nm_auto_nmpobj const NMPObject *obj_old = NULL; nm_auto_nmpobj const NMPObject *obj_new = NULL; nm_auto_nmpobj const NMPObject *obj_old_veth = NULL; nm_auto_nmpobj const NMPObject *obj_new_veth = NULL; NMPCacheOpsType cache_op; NMPCacheOpsType cache_op_veth = NMP_CACHE_OPS_UNCHANGED; const char *veth_peer = NULL; NMPObject *dev_obj; NMPObject *dev_lnk = NULL; device = link_add_pre (platform, name, type, address, address_len); g_assert (device); dev_obj = (NMPObject *) device->obj; if (parent > 0) dev_obj->link.parent = parent; else g_assert (parent == 0); g_assert ((parent != 0) == NM_IN_SET (type, NM_LINK_TYPE_VLAN)); switch (type) { case NM_LINK_TYPE_VETH: veth_peer = extra_data; g_assert (veth_peer); device_veth = link_add_pre (platform, veth_peer, type, NULL, 0); break; case NM_LINK_TYPE_VLAN: { const NMPlatformLnkVlan *props = extra_data; g_assert (props); dev_lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, props); break; } case NM_LINK_TYPE_VXLAN: { const NMPlatformLnkVxlan *props = extra_data; g_assert (props); dev_lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, props); break; } default: g_assert (!extra_data); break; } if (dev_lnk) dev_obj->_link.netlink.lnk = dev_lnk; link_add_prepare (platform, device, (NMPObject *) device->obj); cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), (NMPObject *) device->obj, FALSE, &obj_old, &obj_new); g_assert (cache_op == NMP_CACHE_OPS_ADDED); nmp_object_unref (device->obj); device->obj = nmp_object_ref (obj_new); if (veth_peer) { link_add_prepare (platform, device_veth, (NMPObject *) device_veth->obj); cache_op_veth = nmp_cache_update_netlink (nm_platform_get_cache (platform), (NMPObject *) device_veth->obj, FALSE, &obj_old_veth, &obj_new_veth); g_assert (cache_op == NMP_CACHE_OPS_ADDED); nmp_object_unref (device->obj); device->obj = nmp_object_ref (obj_new); } if (out_link) *out_link = NMP_OBJECT_CAST_LINK (device->obj); link_changed (platform, device, cache_op, NULL); if (veth_peer) link_changed (platform, device_veth, cache_op_veth, NULL); return 0; } static NMFakePlatformLink * link_add_one (NMPlatform *platform, const char *name, NMLinkType link_type, void (*prepare_fcn) (NMPlatform *platform, NMFakePlatformLink *device, gconstpointer user_data), gconstpointer user_data, const NMPlatformLink **out_link) { NMFakePlatformLink *device; nm_auto_nmpobj const NMPObject *obj_old = NULL; nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCacheOpsType cache_op; int ifindex; device = link_add_pre (platform, name, NM_LINK_TYPE_VLAN, NULL, 0); ifindex = NMP_OBJECT_CAST_LINK (device->obj)->ifindex; if (prepare_fcn) prepare_fcn (platform, device, user_data); link_add_prepare (platform, device, (NMPObject *) device->obj); cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), (NMPObject *) device->obj, FALSE, &obj_old, &obj_new); g_assert (cache_op == NMP_CACHE_OPS_ADDED); nmp_object_unref (device->obj); device->obj = nmp_object_ref (obj_new); link_changed (platform, device, cache_op, obj_old); device = link_get (platform, ifindex); if (!device) g_assert_not_reached (); NM_SET_OUT (out_link, NMP_OBJECT_CAST_LINK (device->obj)); return device; } static gboolean link_delete (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); nm_auto_nmpobj const NMPObject *obj_old = NULL; nm_auto_nmpobj const NMPObject *obj_old2 = NULL; NMPCacheOpsType cache_op; if (!device) return FALSE; obj_old = g_steal_pointer (&device->obj); nm_clear_g_free (&device->udi); cache_op = nmp_cache_remove (nm_platform_get_cache (platform), obj_old, FALSE, FALSE, &obj_old2); g_assert (cache_op == NMP_CACHE_OPS_REMOVED); g_assert (obj_old2); g_assert (obj_old == obj_old2); /* Remove addresses and routes which belong to the deleted interface */ ipx_address_delete (platform, AF_INET, ifindex, NULL, NULL, NULL); ipx_address_delete (platform, AF_INET6, ifindex, NULL, NULL, NULL); ipx_route_delete (platform, AF_INET, ifindex, NULL); ipx_route_delete (platform, AF_INET6, ifindex, NULL); nm_platform_cache_update_emit_signal (platform, cache_op, obj_old2, NULL); return TRUE; } static void link_set_obj (NMPlatform *platform, NMFakePlatformLink *device, NMPObject *obj_tmp) { nm_auto_nmpobj const NMPObject *obj_new = NULL; nm_auto_nmpobj const NMPObject *obj_old = NULL; nm_auto_nmpobj NMPObject *obj_tmp_tmp = NULL; NMPCacheOpsType cache_op; g_assert (device); g_assert (NMP_OBJECT_GET_TYPE (device->obj) == NMP_OBJECT_TYPE_LINK); if (!obj_tmp) { obj_tmp_tmp = nmp_object_clone (device->obj, FALSE); obj_tmp = obj_tmp_tmp; } g_assert (NMP_OBJECT_GET_TYPE (obj_tmp) == NMP_OBJECT_TYPE_LINK); link_add_prepare (platform, device, obj_tmp); cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), obj_tmp, FALSE, &obj_old, &obj_new); g_assert (NM_IN_SET (cache_op, NMP_CACHE_OPS_UNCHANGED, NMP_CACHE_OPS_UPDATED)); g_assert (obj_old == device->obj); g_assert (obj_new); nmp_object_unref (device->obj); device->obj = nmp_object_ref (obj_new); link_changed (platform, device, cache_op, obj_old); } static void link_set_flags (NMPlatform *platform, NMFakePlatformLink *device, guint n_ifi_flags) { nm_auto_nmpobj NMPObject *obj_tmp = NULL; g_assert (device); g_assert (NMP_OBJECT_GET_TYPE (device->obj) == NMP_OBJECT_TYPE_LINK); obj_tmp = nmp_object_clone (device->obj, FALSE); obj_tmp->link.n_ifi_flags = n_ifi_flags; link_set_obj (platform, device, obj_tmp); } static void link_changed (NMPlatform *platform, NMFakePlatformLink *device, NMPCacheOpsType cache_op, const NMPObject *obj_old) { g_assert (device->obj); g_assert (!nmp_cache_link_connected_needs_toggle (nm_platform_get_cache (platform), device->obj, NULL, NULL)); nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, device->obj); if (!IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) { if (device->obj->link.connected) ip6_address_add (platform, device->obj->link.ifindex, device->ip6_lladdr, 64, in6addr_any, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); else ip6_address_delete (platform, device->obj->link.ifindex, device->ip6_lladdr, 64); } if (device->obj->link.master) { NMFakePlatformLink *master; master = link_get (platform, device->obj->link.master); link_set_obj (platform, master, NULL); } } static gboolean link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { NMFakePlatformLink *device = link_get (platform, ifindex); if (out_no_firmware) *out_no_firmware = FALSE; if (!device) { _LOGE ("failure changing link: netlink error (No such device)"); return FALSE; } link_set_flags (platform, device, NM_FLAGS_ASSIGN (device->obj->link.n_ifi_flags, IFF_UP, TRUE)); return TRUE; } static gboolean link_set_down (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); if (!device) { _LOGE ("failure changing link: netlink error (No such device)"); return FALSE; } link_set_flags (platform, device, NM_FLAGS_UNSET (device->obj->link.n_ifi_flags, IFF_UP)); return TRUE; } static gboolean link_set_arp (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); if (!device) { _LOGE ("failure changing link: netlink error (No such device)"); return FALSE; } link_set_flags (platform, device, NM_FLAGS_UNSET (device->obj->link.n_ifi_flags, IFF_NOARP)); return TRUE; } static gboolean link_set_noarp (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); if (!device) { _LOGE ("failure changing link: netlink error (No such device)"); return FALSE; } link_set_flags (platform, device, NM_FLAGS_SET (device->obj->link.n_ifi_flags, IFF_NOARP)); return TRUE; } static int link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t len) { NMFakePlatformLink *device = link_get (platform, ifindex); nm_auto_nmpobj NMPObject *obj_tmp = NULL; if ( len == 0 || len > NM_UTILS_HWADDR_LEN_MAX || !addr) g_return_val_if_reached (-NME_BUG); if (!device) return -NME_PL_EXISTS; obj_tmp = nmp_object_clone (device->obj, FALSE); obj_tmp->link.l_address.len = len; memset (obj_tmp->link.l_address.data, 0, sizeof (obj_tmp->link.l_address.data)); memcpy (obj_tmp->link.l_address.data, addr, len); link_set_obj (platform, device, obj_tmp); return 0; } static int link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { NMFakePlatformLink *device = link_get (platform, ifindex); nm_auto_nmpobj NMPObject *obj_tmp = NULL; if (!device) { _LOGE ("failure changing link: netlink error (No such device)"); return -NME_PL_EXISTS; } obj_tmp = nmp_object_clone (device->obj, FALSE); obj_tmp->link.mtu = mtu; link_set_obj (platform, device, obj_tmp); return 0; } static const char * link_get_udi (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); if (!device) return NULL; return device->udi; } static gboolean link_get_driver_info (NMPlatform *platform, int ifindex, char **out_driver_name, char **out_driver_version, char **out_fw_version) { if (out_driver_name) *out_driver_name = NULL; if (out_driver_version) *out_driver_version = NULL; if (out_fw_version) *out_fw_version = NULL; return TRUE; } static gboolean link_supports_carrier_detect (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); if (!device) return FALSE; switch (device->obj->link.type) { case NM_LINK_TYPE_DUMMY: return FALSE; default: return TRUE; } } static gboolean link_supports_vlans (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); if (!device) return FALSE; switch (device->obj->link.type) { case NM_LINK_TYPE_LOOPBACK: return FALSE; default: return TRUE; } } static gboolean link_supports_sriov (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); if (!device) return FALSE; switch (device->obj->link.type) { case NM_LINK_TYPE_LOOPBACK: return FALSE; default: return TRUE; } } static gboolean link_enslave (NMPlatform *platform, int master, int slave) { NMFakePlatformLink *device = link_get (platform, slave); NMFakePlatformLink *master_device = link_get (platform, master); g_return_val_if_fail (device, FALSE); g_return_val_if_fail (master_device, FALSE); if (device->obj->link.master != master) { nm_auto_nmpobj NMPObject *obj_tmp = NULL; obj_tmp = nmp_object_clone (device->obj, FALSE); obj_tmp->link.master = master; if (NM_IN_SET (master_device->obj->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) obj_tmp->link.n_ifi_flags = NM_FLAGS_SET (device->obj->link.n_ifi_flags, IFF_UP); link_set_obj (platform, device, obj_tmp); } return TRUE; } static gboolean link_release (NMPlatform *platform, int master_idx, int slave_idx) { NMFakePlatformLink *master = link_get (platform, master_idx); NMFakePlatformLink *slave = link_get (platform, slave_idx); nm_auto_nmpobj NMPObject *obj_tmp = NULL; g_return_val_if_fail (master, FALSE); g_return_val_if_fail (slave, FALSE); if (slave->obj->link.master != master->obj->link.ifindex) return FALSE; obj_tmp = nmp_object_clone (slave->obj, FALSE); obj_tmp->link.master = 0; link_set_obj (platform, slave, obj_tmp); return TRUE; } static gboolean link_vlan_change (NMPlatform *platform, int ifindex, NMVlanFlags flags_mask, NMVlanFlags flags_set, gboolean ingress_reset_all, const NMVlanQosMapping *ingress_map, gsize n_ingress_map, gboolean egress_reset_all, const NMVlanQosMapping *egress_map, gsize n_egress_map) { return FALSE; } struct infiniband_add_data { int parent; int p_key; }; static void _infiniband_add_prepare (NMPlatform *platform, NMFakePlatformLink *device, gconstpointer user_data) { const struct infiniband_add_data *d = user_data; NMPObject *obj_tmp; NMPObject *lnk; obj_tmp = (NMPObject *) device->obj; lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); lnk->lnk_infiniband.p_key = d->p_key; lnk->lnk_infiniband.mode = "datagram"; obj_tmp->link.parent = d->parent; obj_tmp->_link.netlink.lnk = lnk; } static gboolean infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link) { NMFakePlatformLink *parent_device; char name[IFNAMSIZ]; const struct infiniband_add_data d = { .parent = parent, .p_key = p_key, }; parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); nm_utils_new_infiniband_name (name, parent_device->obj->link.name, p_key); link_add_one (platform, name, NM_LINK_TYPE_INFINIBAND, _infiniband_add_prepare, &d, out_link); return TRUE; } static gboolean infiniband_partition_delete (NMPlatform *platform, int parent, int p_key) { NMFakePlatformLink *parent_device; gs_free char *name = NULL; parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); nm_utils_new_infiniband_name (name, parent_device->obj->link.name, p_key); return link_delete (platform, nm_platform_link_get_ifindex (platform, name)); } static gboolean wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps) { NMFakePlatformLink *device = link_get (platform, ifindex); g_return_val_if_fail (device, FALSE); if (device->obj->link.type != NM_LINK_TYPE_WIFI) return FALSE; if (caps) { *caps = ( NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104 | NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN | NM_WIFI_DEVICE_CAP_AP | NM_WIFI_DEVICE_CAP_ADHOC); } return TRUE; } static gboolean wifi_get_bssid (NMPlatform *platform, int ifindex, guint8 *bssid) { return FALSE; } static guint32 wifi_get_frequency (NMPlatform *platform, int ifindex) { return 0; } static int wifi_get_quality (NMPlatform *platform, int ifindex) { return 0; } static guint32 wifi_get_rate (NMPlatform *platform, int ifindex) { return 0; } static NM80211Mode wifi_get_mode (NMPlatform *platform, int ifindex) { return NM_802_11_MODE_UNKNOWN; } static void wifi_set_mode (NMPlatform *platform, int ifindex, NM80211Mode mode) { ; } static guint32 wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs) { return freqs[0]; } static void wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean running) { } static guint32 mesh_get_channel (NMPlatform *platform, int ifindex) { return 0; } static gboolean mesh_set_channel (NMPlatform *platform, int ifindex, guint32 channel) { return FALSE; } static gboolean mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) { return FALSE; } /*****************************************************************************/ static gboolean ipx_address_add (NMPlatform *platform, int addr_family, const NMPlatformObject *address) { nm_auto_nmpobj NMPObject *obj = NULL; NMPCacheOpsType cache_op; nm_auto_nmpobj const NMPObject *obj_old = NULL; nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCache *cache = nm_platform_get_cache (platform); g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); obj = nmp_object_new (addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS, address); cache_op = nmp_cache_update_netlink (cache, obj, FALSE, &obj_old, &obj_new); nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); return TRUE; } static gboolean ip4_address_add (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_addr, in_addr_t broadcast_address, guint32 lifetime, guint32 preferred, guint32 flags, const char *label) { NMPlatformIP4Address address; address = (NMPlatformIP4Address) { .addr_source = NM_IP_CONFIG_SOURCE_KERNEL, .ifindex = ifindex, .address = addr, .plen = plen, .peer_address = peer_addr, .broadcast_address = broadcast_address, .use_ip4_broadcast_address = TRUE, .timestamp = nm_utils_get_monotonic_timestamp_sec (), .lifetime = lifetime, .preferred = preferred, .n_ifa_flags = flags, }; if (label) g_strlcpy (address.label, label, sizeof (address.label)); return ipx_address_add (platform, AF_INET, (const NMPlatformObject *) &address); } static gboolean ip6_address_add (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen, struct in6_addr peer_addr, guint32 lifetime, guint32 preferred, guint32 flags) { NMPlatformIP6Address address; memset (&address, 0, sizeof (address)); address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; address.ifindex = ifindex; address.address = addr; address.peer_address = (IN6_IS_ADDR_UNSPECIFIED (&peer_addr) || IN6_ARE_ADDR_EQUAL (&addr, &peer_addr)) ? in6addr_any : peer_addr; address.plen = plen; address.timestamp = nm_utils_get_monotonic_timestamp_sec (); address.lifetime = lifetime; address.preferred = preferred; address.n_ifa_flags = flags; return ipx_address_add (platform, AF_INET6, (const NMPlatformObject *) &address); } static gboolean ipx_address_delete (NMPlatform *platform, int addr_family, int ifindex, gconstpointer addr, const guint8 *plen, gconstpointer peer_addr) { gs_unref_ptrarray GPtrArray *objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); NMDedupMultiIter iter; const NMPObject *o = NULL; guint i; guint32 peer_addr_i; g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); peer_addr_i = peer_addr ? *((guint32 *) peer_addr) : 0; nmp_cache_iter_for_each (&iter, nm_platform_lookup_object (platform, addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS, 0), &o) { const NMPObject *obj_old = NULL; if (addr_family == AF_INET) { const NMPlatformIP4Address *address = NMP_OBJECT_CAST_IP4_ADDRESS (o); if ( address->ifindex != ifindex || (addr && address->address != *((guint32 *) addr)) || (plen && address->plen != *plen) || ( peer_addr && (((peer_addr_i ^ address->peer_address) & _nm_utils_ip4_prefix_to_netmask (address->plen)) != 0))) continue; } else { const NMPlatformIP6Address *address = NMP_OBJECT_CAST_IP6_ADDRESS (o); g_assert (!peer_addr); if ( address->ifindex != ifindex || (addr && !IN6_ARE_ADDR_EQUAL (&address->address, addr)) || (plen && address->plen != *plen)) continue; } if (nmp_cache_remove (nm_platform_get_cache (platform), o, TRUE, FALSE, &obj_old) != NMP_CACHE_OPS_REMOVED) g_assert_not_reached (); g_assert (obj_old); g_ptr_array_add (objs, (gpointer) obj_old); } for (i = 0; i < objs->len; i++) { nm_platform_cache_update_emit_signal (platform, NMP_CACHE_OPS_REMOVED, objs->pdata[i], NULL); } return TRUE; } static gboolean ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) { return ipx_address_delete (platform, AF_INET, ifindex, &addr, &plen, &peer_address); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) { return ipx_address_delete (platform, AF_INET6, ifindex, &addr, &plen, NULL); } /*****************************************************************************/ static gboolean ipx_route_delete (NMPlatform *platform, int addr_family, int ifindex, const NMPObject *obj) { gs_unref_ptrarray GPtrArray *objs = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); NMDedupMultiIter iter; const NMPObject *o = NULL; guint i; NMPObjectType obj_type; if (addr_family == AF_UNSPEC) { g_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); g_assert (ifindex == -1); ifindex = NMP_OBJECT_CAST_IP_ROUTE (obj)->ifindex; obj_type = NMP_OBJECT_GET_TYPE (obj); } else { g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); g_assert (!obj); g_assert (ifindex > 0); obj_type = addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE; } nmp_cache_iter_for_each (&iter, nm_platform_lookup_object (platform, obj_type, ifindex), &o) { const NMPObject *obj_old = NULL; if (obj) { if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) { const NMPlatformIP4Route *route = NMP_OBJECT_CAST_IP4_ROUTE (o); const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (obj); if ( route->network != r->network || route->plen != r->plen || route->metric != r->metric) continue; } else { const NMPlatformIP6Route *route = NMP_OBJECT_CAST_IP6_ROUTE (o); const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (obj); if ( !IN6_ARE_ADDR_EQUAL (&route->network, &r->network) || route->plen != r->plen || route->metric != r->metric) continue; } } if (nmp_cache_remove (nm_platform_get_cache (platform), o, TRUE, FALSE, &obj_old) != NMP_CACHE_OPS_REMOVED) g_assert_not_reached (); g_assert (obj_old); g_ptr_array_add (objs, (gpointer) obj_old); } for (i = 0; i < objs->len; i++) { nm_platform_cache_update_emit_signal (platform, NMP_CACHE_OPS_REMOVED, objs->pdata[i], NULL); } return TRUE; } static gboolean object_delete (NMPlatform *platform, const NMPObject *obj) { g_assert (NM_IS_FAKE_PLATFORM (platform)); g_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); return ipx_route_delete (platform, AF_UNSPEC, -1, obj); } static int ip_route_add (NMPlatform *platform, NMPNlmFlags flags, int addr_family, const NMPlatformIPRoute *route) { NMDedupMultiIter iter; nm_auto_nmpobj NMPObject *obj = NULL; NMPCacheOpsType cache_op; const NMPObject *o = NULL; nm_auto_nmpobj const NMPObject *obj_old = NULL; nm_auto_nmpobj const NMPObject *obj_new = NULL; nm_auto_nmpobj const NMPObject *obj_replace = NULL; NMPCache *cache = nm_platform_get_cache (platform); gboolean has_gateway = FALSE; NMPlatformIPRoute *r = NULL; NMPlatformIP4Route *r4 = NULL; NMPlatformIP6Route *r6 = NULL; gboolean has_same_weak_id; gboolean only_dirty; guint16 nlmsgflags; g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); flags = NM_FLAGS_UNSET (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE); /* currently, only replace is implemented. */ g_assert (flags == NMP_NLM_FLAG_REPLACE); obj = nmp_object_new (addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); r = NMP_OBJECT_CAST_IP_ROUTE (obj); nm_platform_ip_route_normalize (addr_family, r); switch (addr_family) { case AF_INET: r4 = NMP_OBJECT_CAST_IP4_ROUTE (obj); if (r4->gateway) has_gateway = TRUE; break; case AF_INET6: r6 = NMP_OBJECT_CAST_IP6_ROUTE (obj); if (!IN6_IS_ADDR_UNSPECIFIED (&r6->gateway)) has_gateway = TRUE; break; default: nm_assert_not_reached (); } if (has_gateway) { gboolean has_route_to_gw = FALSE; nmp_cache_iter_for_each (&iter, nm_platform_lookup_object (platform, NMP_OBJECT_GET_TYPE (obj), 0), &o) { if (addr_family == AF_INET) { const NMPlatformIP4Route *item = NMP_OBJECT_CAST_IP4_ROUTE (o); guint32 n = nm_utils_ip4_address_clear_host_address (item->network, item->plen); guint32 g = nm_utils_ip4_address_clear_host_address (r4->gateway, item->plen); if ( r->ifindex == item->ifindex && n == g) { has_route_to_gw = TRUE; break; } } else { const NMPlatformIP6Route *item = NMP_OBJECT_CAST_IP6_ROUTE (o); if ( r->ifindex == item->ifindex && nm_utils_ip6_address_same_prefix (&r6->gateway, &item->network, item->plen)) { has_route_to_gw = TRUE; break; } } } if (!has_route_to_gw) { char sbuf[NM_UTILS_INET_ADDRSTRLEN]; if (addr_family == AF_INET) { nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", r->ifindex, _nm_utils_inet4_ntop (r4->network, sbuf), r->plen, r->metric); } else { nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", r->ifindex, _nm_utils_inet6_ntop (&r6->network, sbuf), r->plen, r->metric); } return -NME_UNSPEC; } } has_same_weak_id = FALSE; nmp_cache_iter_for_each (&iter, nm_platform_lookup_all (platform, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj), &o) { if (addr_family == AF_INET) { if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (o), r4, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) continue; } else { if (nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (o), r6, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) continue; } has_same_weak_id = TRUE; } nlmsgflags = 0; if (has_same_weak_id) { switch (flags) { case NMP_NLM_FLAG_REPLACE: nlmsgflags = NLM_F_REPLACE; break; default: g_assert_not_reached (); break; } } /* we manipulate the cache the same was as NMLinuxPlatform does it. */ cache_op = nmp_cache_update_netlink_route (cache, obj, FALSE, nlmsgflags, &obj_old, &obj_new, &obj_replace, NULL); only_dirty = FALSE; if (cache_op != NMP_CACHE_OPS_UNCHANGED) { if (obj_replace) { const NMDedupMultiEntry *entry_replace; entry_replace = nmp_cache_lookup_entry (cache, obj_replace); nm_assert (entry_replace && entry_replace->obj == obj_replace); nm_dedup_multi_entry_set_dirty (entry_replace, TRUE); only_dirty = TRUE; } nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } if (obj_replace) { cache_op = nmp_cache_remove (cache, obj_replace, TRUE, only_dirty, NULL); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); nm_platform_cache_update_emit_signal (platform, cache_op, obj_replace, NULL); } } return 0; } /*****************************************************************************/ static void nm_fake_platform_init (NMFakePlatform *fake_platform) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (fake_platform); priv->options = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); priv->links = g_array_new (TRUE, TRUE, sizeof (NMFakePlatformLink)); } void nm_fake_platform_setup (void) { NMPlatform *platform; platform = g_object_new (NM_TYPE_FAKE_PLATFORM, NM_PLATFORM_LOG_WITH_PTR, FALSE, NULL); nm_platform_setup (platform); link_add (platform, NM_LINK_TYPE_LOOPBACK, "lo", 0, NULL, 0, NULL, NULL); link_add (platform, NM_LINK_TYPE_ETHERNET, "eth0", 0, NULL, 0, NULL, NULL); link_add (platform, NM_LINK_TYPE_ETHERNET, "eth1", 0, NULL, 0, NULL, NULL); link_add (platform, NM_LINK_TYPE_ETHERNET, "eth2", 0, NULL, 0, NULL, NULL); } static void finalize (GObject *object) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (object); int i; g_hash_table_unref (priv->options); for (i = 0; i < priv->links->len; i++) { NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); g_free (device->udi); nm_clear_pointer (&device->obj, nmp_object_unref); } g_array_unref (priv->links); G_OBJECT_CLASS (nm_fake_platform_parent_class)->finalize (object); } static void nm_fake_platform_class_init (NMFakePlatformClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMPlatformClass *platform_class = NM_PLATFORM_CLASS (klass); NMPlatformKernelSupportType kernel_support; for (kernel_support = 0; kernel_support < _NM_PLATFORM_KERNEL_SUPPORT_NUM; kernel_support++) _nm_platform_kernel_support_init (kernel_support, -1); object_class->finalize = finalize; platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; platform_class->link_add = link_add; platform_class->link_delete = link_delete; platform_class->link_get_udi = link_get_udi; platform_class->link_set_up = link_set_up; platform_class->link_set_down = link_set_down; platform_class->link_set_arp = link_set_arp; platform_class->link_set_noarp = link_set_noarp; platform_class->link_set_address = link_set_address; platform_class->link_set_mtu = link_set_mtu; platform_class->link_get_driver_info = link_get_driver_info; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; platform_class->link_supports_vlans = link_supports_vlans; platform_class->link_supports_sriov = link_supports_sriov; platform_class->link_enslave = link_enslave; platform_class->link_release = link_release; platform_class->link_vlan_change = link_vlan_change; platform_class->infiniband_partition_add = infiniband_partition_add; platform_class->infiniband_partition_delete = infiniband_partition_delete; platform_class->wifi_get_capabilities = wifi_get_capabilities; platform_class->wifi_get_bssid = wifi_get_bssid; platform_class->wifi_get_frequency = wifi_get_frequency; platform_class->wifi_get_quality = wifi_get_quality; platform_class->wifi_get_rate = wifi_get_rate; platform_class->wifi_get_mode = wifi_get_mode; platform_class->wifi_set_mode = wifi_set_mode; platform_class->wifi_find_frequency = wifi_find_frequency; platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running; platform_class->mesh_get_channel = mesh_get_channel; platform_class->mesh_set_channel = mesh_set_channel; platform_class->mesh_set_ssid = mesh_set_ssid; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; platform_class->ip_route_add = ip_route_add; platform_class->object_delete = object_delete; }