diff options
| author | Michael Biebl <biebl@debian.org> | 2015-05-05 17:48:57 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2015-05-05 17:48:57 +0200 |
| commit | f408e27bccfacf347605a8d98649975a68f38a17 (patch) | |
| tree | 654fd6695c31511baf919b1c0870d119a352ed75 /src/platform | |
| parent | 2c032d8f1c6292c1338a615e6ec40252889ba85c (diff) | |
Imported Upstream version 1.0.2 upstream/1.0.2
Diffstat (limited to 'src/platform')
| -rw-r--r-- | src/platform/Makefile.in | 24 | ||||
| -rw-r--r-- | src/platform/nm-fake-platform.c | 18 | ||||
| -rw-r--r-- | src/platform/nm-linux-platform.c | 150 | ||||
| -rw-r--r-- | src/platform/nm-platform.c | 166 | ||||
| -rw-r--r-- | src/platform/nm-platform.h | 30 | ||||
| -rw-r--r-- | src/platform/tests/Makefile.am | 2 | ||||
| -rw-r--r-- | src/platform/tests/Makefile.in | 32 | ||||
| -rw-r--r-- | src/platform/tests/test-address.c | 6 | ||||
| -rw-r--r-- | src/platform/tests/test-cleanup.c | 6 | ||||
| -rw-r--r-- | src/platform/tests/test-common.c | 115 | ||||
| -rw-r--r-- | src/platform/tests/test-common.h | 8 | ||||
| -rw-r--r-- | src/platform/tests/test-link.c | 8 | ||||
| -rw-r--r-- | src/platform/tests/test-route.c | 97 | ||||
| -rw-r--r-- | src/platform/wifi/wifi-utils-nl80211.c | 11 | ||||
| -rw-r--r-- | src/platform/wifi/wifi-utils-wext.c | 15 | ||||
| -rw-r--r-- | src/platform/wifi/wifi-utils.h | 3 |
16 files changed, 632 insertions, 59 deletions
diff --git a/src/platform/Makefile.in b/src/platform/Makefile.in index d4c8ade7..a41de1ae 100644 --- a/src/platform/Makefile.in +++ b/src/platform/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.13.4 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -14,7 +14,17 @@ @SET_MAKE@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -78,7 +88,6 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/platform -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ $(top_srcdir)/m4/compiler_warnings.m4 \ @@ -95,6 +104,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ $(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -154,6 +164,7 @@ am__define_uniq_tagged_files = \ ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -205,7 +216,6 @@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ -DATADIRNAME = @DATADIRNAME@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ @@ -240,6 +250,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ @@ -456,7 +467,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/platform/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/platform/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -751,6 +761,8 @@ uninstall-am: mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am +.PRECIOUS: Makefile + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index a5eb7d4e..7b0d2d64 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -456,6 +456,15 @@ link_get_physical_port_id (NMPlatform *platform, int ifindex) return NULL; } +static guint +link_get_dev_id (NMPlatform *platform, int ifindex) +{ + /* We call link_get just to cause an error to be set if @ifindex is bad. */ + link_get (platform, ifindex); + + return 0; +} + static gboolean link_get_wake_on_lan (NMPlatform *platform, int ifindex) { @@ -1076,6 +1085,8 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, continue; if (item->plen != route.plen) continue; + if (item->metric != metric) + continue; memcpy (item, &route, sizeof (route)); g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); @@ -1097,6 +1108,8 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, NMPlatformIP6Route route; guint i; + metric = nm_utils_ip6_route_metric_normalize (metric); + memset (&route, 0, sizeof (route)); route.source = NM_IP_CONFIG_SOURCE_KERNEL; route.ifindex = ifindex; @@ -1116,6 +1129,8 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, continue; if (item->plen != route.plen) continue; + if (item->metric != metric) + continue; memcpy (item, &route, sizeof (route)); g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); @@ -1153,6 +1168,8 @@ ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int p NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; + metric = nm_utils_ip6_route_metric_normalize (metric); + for (i = 0; i < priv->ip6_routes->len; i++) { NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); @@ -1301,6 +1318,7 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_set_mtu = link_set_mtu; platform_class->link_get_physical_port_id = link_get_physical_port_id; + platform_class->link_get_dev_id = link_get_dev_id; platform_class->link_get_wake_on_lan = link_get_wake_on_lan; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 5d1522ce..c337ee9c 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -127,7 +127,7 @@ _nl_get_vtable (void) if (G_UNLIKELY (!vtable.f_nl_has_capability)) { void *handle; - handle = dlopen ("libnl-3.so", RTLD_LAZY | RTLD_NOLOAD); + handle = dlopen ("libnl-3.so.200", RTLD_LAZY | RTLD_NOLOAD); if (handle) { vtable.handle = handle; vtable.f_nl_has_capability = dlsym (handle, "nl_has_capability"); @@ -872,16 +872,15 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, const char if (!ifname) return_type (NM_LINK_TYPE_UNKNOWN, type); + driver = ethtool_get_driver (ifname); if (arptype == 256) { /* Some s390 CTC-type devices report 256 for the encapsulation type - * for some reason, but we need to call them Ethernet. FIXME: use - * something other than interface name to detect CTC here. + * for some reason, but we need to call them Ethernet. */ - if (g_str_has_prefix (ifname, "ctc")) + if (!g_strcmp0 (driver, "ctcm")) return_type (NM_LINK_TYPE_ETHERNET, "ethernet"); } - driver = ethtool_get_driver (ifname); if (!g_strcmp0 (driver, "openvswitch")) return_type (NM_LINK_TYPE_OPENVSWITCH, "openvswitch"); @@ -1008,7 +1007,7 @@ init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllin if (udev_device) { info->driver = udev_get_driver (platform, udev_device, info->ifindex); if (!info->driver) - info->driver = rtnl_link_get_type (rtnllink); + info->driver = g_intern_string (rtnl_link_get_type (rtnllink)); if (!info->driver) info->driver = ethtool_get_driver (info->name); if (!info->driver) @@ -1392,7 +1391,6 @@ static char to_string_buffer[255]; #define SET_AND_RETURN_STRING_BUFFER(...) \ G_STMT_START { \ g_snprintf (to_string_buffer, sizeof (to_string_buffer), ## __VA_ARGS__); \ - g_return_val_if_reached (to_string_buffer); \ return to_string_buffer; \ } G_STMT_END @@ -1668,6 +1666,9 @@ announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatfor { NMPlatformIP4Route route; + if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL) + return; + if (!_route_match ((struct rtnl_route *) object, AF_INET, 0, FALSE)) { nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP4 route %s", to_string_ip4_route ((struct rtnl_route *) object)); return; @@ -1680,6 +1681,9 @@ announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatfor { NMPlatformIP6Route route; + if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL) + return; + if (!_route_match ((struct rtnl_route *) object, AF_INET6, 0, FALSE)) { nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP6 route %s", to_string_ip6_route ((struct rtnl_route *) object)); return; @@ -1719,9 +1723,15 @@ refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, reason); } } else { + ObjectType type; + if (!kernel_object) return FALSE; + /* Unsupported object types should never have reached the caches */ + type = object_type_from_nl_object (kernel_object); + g_assert (type != OBJECT_TYPE_UNKNOWN); + hack_empty_master_iff_lower_up (platform, kernel_object); if (cached_object) @@ -1735,7 +1745,7 @@ refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed announce_object (platform, kernel_object, cached_object ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, reason); /* Refresh the master device (even on enslave/release) */ - if (object_type_from_nl_object (kernel_object) == OBJECT_TYPE_LINK) { + if (type == OBJECT_TYPE_LINK) { int kernel_master = rtnl_link_get_master ((struct rtnl_link *) kernel_object); int cached_master = cached_object ? rtnl_link_get_master ((struct rtnl_link *) cached_object) : 0; struct nl_object *master_object; @@ -1763,10 +1773,6 @@ add_object (NMPlatform *platform, struct nl_object *obj) auto_nl_object struct nl_object *object = obj; NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int nle; - struct nl_dump_params dp = { - .dp_type = NL_DUMP_DETAILS, - .dp_fd = stderr, - }; g_return_val_if_fail (object, FALSE); @@ -1782,7 +1788,18 @@ add_object (NMPlatform *platform, struct nl_object *obj) break; default: error ("Netlink error adding %s: %s", to_string_object (platform, object), nl_geterror (nle)); - nl_object_dump (object, &dp); + if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { + char buf[256]; + struct nl_dump_params dp = { + .dp_type = NL_DUMP_DETAILS, + .dp_buf = buf, + .dp_buflen = sizeof (buf), + }; + + nl_object_dump (object, &dp); + buf[sizeof (buf) - 1] = '\0'; + debug ("netlink object:\n%s", buf); + } return FALSE; } @@ -1997,6 +2014,11 @@ event_notification (struct nl_msg *msg, gpointer user_data) */ if (!kernel_object) return NL_OK; + + /* Ignore unsupported object types (e.g. AF_PHONET family addresses) */ + if (type == OBJECT_TYPE_UNKNOWN) + return NL_OK; + /* Handle external addition */ if (!cached_object) { nle = nl_cache_add (cache, kernel_object); @@ -2690,6 +2712,30 @@ link_get_physical_port_id (NMPlatform *platform, int ifindex) return id; } +static guint +link_get_dev_id (NMPlatform *platform, int ifindex) +{ + const char *ifname; + gs_free char *path = NULL, *id = NULL; + gint64 int_val; + + ifname = nm_platform_link_get_name (ifindex); + if (!ifname) + return 0; + + ifname = ASSERT_VALID_PATH_COMPONENT (ifname); + + path = g_strdup_printf ("/sys/class/net/%s/dev_id", ifname); + id = sysctl_get (platform, path); + if (!id || !*id) + return 0; + + /* Value is reported as hex */ + int_val = nm_utils_ascii_str_to_int64 (id, 16, 0, G_MAXUINT16, 0); + + return errno ? 0 : (int) int_val; +} + static int vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags) { @@ -3507,7 +3553,7 @@ build_rtnl_addr (int family, guint flags, const char *label) { - auto_nl_addr struct rtnl_addr *rtnladdr = _nm_rtnl_addr_alloc (ifindex); + auto_nl_object struct rtnl_addr *rtnladdr = _nm_rtnl_addr_alloc (ifindex); struct rtnl_addr *rtnladdr_copy; int addrlen = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (family, addr, addrlen); @@ -3698,6 +3744,7 @@ _route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean in if (rtnl_route_get_type (rtnlroute) != RTN_UNICAST || rtnl_route_get_table (rtnlroute) != RT_TABLE_MAIN || + rtnl_route_get_tos (rtnlroute) != 0 || (!include_proto_kernel && rtnl_route_get_protocol (rtnlroute) == RTPROT_KERNEL) || rtnl_route_get_family (rtnlroute) != family || rtnl_route_get_nnexthops (rtnlroute) != 1 || @@ -3845,6 +3892,8 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, struct in6_addr network, int plen, struct in6_addr gateway, guint32 metric, guint32 mss) { + metric = nm_utils_ip6_route_metric_normalize (metric); + return add_object (platform, build_rtnl_route (AF_INET6, ifindex, source, &network, plen, &gateway, NULL, metric, mss)); } @@ -3863,7 +3912,7 @@ route_search_cache (struct nl_cache *cache, int family, int ifindex, const void if (!_route_match (rtnlroute, family, ifindex, FALSE)) continue; - if (metric && metric != rtnl_route_get_priority (rtnlroute)) + if (metric != rtnl_route_get_priority (rtnlroute)) continue; dst = rtnl_route_get_dst (rtnlroute); @@ -3872,7 +3921,14 @@ route_search_cache (struct nl_cache *cache, int family, int ifindex, const void || nl_addr_get_prefixlen (dst) != plen) continue; - clear_host_address (family, nl_addr_get_binary_addr (dst), plen, dst_clean); + /* plen = 0 means all host bits, so all bits should be cleared. + * Likewise if the binary address is not present or all zeros. + */ + if (plen == 0 || nl_addr_iszero (dst)) + memset (dst_clean, 0, sizeof (dst_clean)); + else + clear_host_address (family, nl_addr_get_binary_addr (dst), plen, dst_clean); + if (memcmp (dst_clean, network_clean, family == AF_INET ? sizeof (guint32) : sizeof (struct in6_addr)) != 0) continue; @@ -3884,7 +3940,7 @@ route_search_cache (struct nl_cache *cache, int family, int ifindex, const void } static gboolean -refresh_route (NMPlatform *platform, int family, int ifindex, const void *network, int plen, int metric) +refresh_route (NMPlatform *platform, int family, int ifindex, const void *network, int plen, guint32 metric) { struct nl_cache *cache; auto_nl_object struct rtnl_route *cached_object = NULL; @@ -3910,6 +3966,19 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen cache = choose_cache_by_type (platform, OBJECT_TYPE_IP4_ROUTE); + if (metric == 0) { + /* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route. + * If no route with metric 0 exists, it might delete another route to the same destination. + * For nm_platform_ip4_route_delete() we don't want this semantic. + * + * Instead, re-fetch the route from kernel, and if that fails, there is nothing to do. + * On success, there is still a race that we might end up deleting the wrong route. */ + if (!refresh_object (platform, (struct nl_object *) route, FALSE, _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)) { + rtnl_route_put ((struct rtnl_route *) route); + return TRUE; + } + } + /* when deleting an IPv4 route, several fields of the provided route must match. * Lookup in the cache so that we hopefully get the right values. */ cached_object = (struct rtnl_route *) nl_cache_search (cache, route); @@ -3943,8 +4012,8 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen } rtnl_route_set_scope ((struct rtnl_route *) route, scope); - if (cached_object) - rtnl_route_set_tos ((struct rtnl_route *) route, rtnl_route_get_tos (cached_object)); + /* we only support routes with TOS zero. As such, delete_route() is also only able to delete + * routes with tos==0. build_rtnl_route() already initializes tos properly. */ /* The following fields are also relevant when comparing the route, but the default values * are already as we want them: @@ -3962,6 +4031,8 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, in { struct in6_addr gateway = IN6ADDR_ANY_INIT; + metric = nm_utils_ip6_route_metric_normalize (metric); + return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN ,&network, plen, &gateway, NULL, metric, 0), FALSE) && refresh_route (platform, AF_INET6, ifindex, &network, plen, metric); } @@ -3989,6 +4060,8 @@ ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen static gboolean ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { + metric = nm_utils_ip6_route_metric_normalize (metric); + return ip_route_exists (platform, AF_INET6, ifindex, &network, plen, metric); } @@ -4061,6 +4134,33 @@ cache_announce_changes (NMPlatform *platform, struct nl_cache *new, struct nl_ca nl_cache_free (old); } +/* The cache should always avoid containing objects not handled by NM, like + * e.g. addresses of the AF_PHONET family. */ +static void +cache_remove_unknown (struct nl_cache *cache) +{ + GPtrArray *objects_to_remove = NULL; + struct nl_object *object; + + for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) { + if (object_type_from_nl_object (object) == OBJECT_TYPE_UNKNOWN) { + if (!objects_to_remove) + objects_to_remove = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put); + nl_object_get (object); + g_ptr_array_add (objects_to_remove, object); + } + } + + if (objects_to_remove) { + guint i; + + for (i = 0; i < objects_to_remove->len; i++) + nl_cache_remove (g_ptr_array_index (objects_to_remove, i)); + + g_ptr_array_free (objects_to_remove, TRUE); + } +} + /* Creates and populates the netlink object caches. Called upon platform init and * when we run out of sync (out of buffer space, netlink congestion control). In case * the caches already exist, it finds changed, added and removed objects, announces @@ -4082,6 +4182,11 @@ cache_repopulate_all (NMPlatform *platform) rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache); g_assert (priv->link_cache && priv->address_cache && priv->route_cache); + /* Remove all unknown objects from the caches */ + cache_remove_unknown (priv->link_cache); + cache_remove_unknown (priv->address_cache); + cache_remove_unknown (priv->route_cache); + for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object); } @@ -4139,7 +4244,13 @@ event_handler (GIOChannel *channel, * like to free up some space. We'll read in the status synchronously. */ nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL); do { + errno = 0; + nle = nl_recvmsgs_default (priv->nlh_event); + + /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ + if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + nle = -NLE_AGAIN; } while (nle != -NLE_AGAIN); nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data); cache_repopulate_all (platform); @@ -4473,6 +4584,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_mtu = link_set_mtu; platform_class->link_get_physical_port_id = link_get_physical_port_id; + platform_class->link_get_dev_id = link_get_dev_id; platform_class->link_get_wake_on_lan = link_get_wake_on_lan; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 14fc44c5..ba7708d6 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -258,6 +258,38 @@ nm_platform_sysctl_set (const char *path, const char *value) return klass->sysctl_set (platform, path, value); } +gboolean +nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value) +{ + const char *path; + gint64 cur; + + /* the hop-limit provided via RA is uint8. */ + if (value > 0xFF) + return FALSE; + + /* don't allow unreasonable small values */ + if (value < 10) + return FALSE; + + path = nm_utils_ip6_property_path (iface, "hop_limit"); + cur = nm_platform_sysctl_get_int_checked (path, 10, 1, G_MAXINT32, -1); + + /* only allow increasing the hop-limit to avoid DOS by an attacker + * setting a low hop-limit (CVE-2015-2924, rh#1209902) */ + + if (value < cur) + return FALSE; + if (value != cur) { + char svalue[20]; + + sprintf (svalue, "%d", value); + nm_platform_sysctl_set (path, svalue); + } + + return TRUE; +} + /** * nm_platform_sysctl_get: * @path: Absolute path to sysctl @@ -963,9 +995,15 @@ nm_platform_link_get_mtu (int ifindex) } /** - * nm_platform_link_get_mtu: + * nm_platform_link_get_physical_port_id: * @ifindex: Interface index * + * The physical port ID, if present, indicates some unique identifier of + * the parent interface (eg, the physical port of which this link is a child). + * Two links that report the same physical port ID can be assumed to be + * children of the same physical port and may share resources that limit + * their abilities. + * * Returns: physical port ID for the interface, or %NULL on error * or if the interface has no physical port ID. */ @@ -981,6 +1019,28 @@ nm_platform_link_get_physical_port_id (int ifindex) } /** + * nm_platform_link_get_dev_id: + * @ifindex: Interface index + * + * In contrast to the physical device ID (which indicates which parent a + * child has) the device ID differentiates sibling devices that may share + * the same MAC address. + * + * Returns: device ID for the interface, or 0 on error or if the + * interface has no device ID. + */ +guint +nm_platform_link_get_dev_id (int ifindex) +{ + reset_error (); + + g_return_val_if_fail (ifindex >= 0, 0); + g_return_val_if_fail (klass->link_get_dev_id, 0); + + return klass->link_get_dev_id (platform, ifindex); +} + +/** * nm_platform_link_get_wake_onlan: * @ifindex: Interface index * @@ -1826,6 +1886,7 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint3 * nm_platform_ip6_address_sync: * @ifindex: Interface index * @known_addresses: List of addresses + * @keep_link_local: Don't remove link-local address * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -1834,7 +1895,7 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint3 * Returns: %TRUE on success. */ gboolean -nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses) +nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboolean keep_link_local) { GArray *addresses; NMPlatformIP6Address *address; @@ -1847,7 +1908,7 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses) address = &g_array_index (addresses, NMPlatformIP6Address, i); /* Leave link local address management to the kernel */ - if (IN6_IS_ADDR_LINKLOCAL (&address->address)) + if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) continue; if (!array_contains_ip6_address (known_addresses, address)) @@ -1880,7 +1941,7 @@ gboolean nm_platform_address_flush (int ifindex) { return nm_platform_ip4_address_sync (ifindex, NULL, 0) - && nm_platform_ip6_address_sync (ifindex, NULL); + && nm_platform_ip6_address_sync (ifindex, NULL, FALSE); } /******************************************************************/ @@ -2100,7 +2161,8 @@ nm_platform_ip4_route_sync (int ifindex, const GArray *known_routes) if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route)) continue; - if ((known_route->gateway == 0) ^ (i_type != 0)) { + if ( (i_type == 0 && known_route->gateway != 0) + || (i_type == 1 && known_route->gateway == 0)) { /* Make two runs over the list of routes. On the first, only add * device routes, on the second the others (gateway routes). */ continue; @@ -2174,7 +2236,8 @@ nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes) if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route)) continue; - if (IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway) ^ (i_type != 0)) { + if ( (i_type == 0 && !IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway)) + || (i_type == 1 && IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway))) { /* Make two runs over the list of routes. On the first, only add * device routes, on the second the others (gateway routes). */ continue; @@ -2738,6 +2801,97 @@ log_ip6_route (NMPlatform *p, int ifindex, NMPlatformIP6Route *route, NMPlatform /******************************************************************/ +static gboolean +_vtr_v4_route_add (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src) +{ + return nm_platform_ip4_route_add (ifindex > 0 ? ifindex : route->rx.ifindex, + route->rx.source, + route->r4.network, + route->rx.plen, + route->r4.gateway, + v4_pref_src, + route->rx.metric, + route->rx.mss); +} + +static gboolean +_vtr_v6_route_add (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src) +{ + return nm_platform_ip6_route_add (ifindex > 0 ? ifindex : route->rx.ifindex, + route->rx.source, + route->r6.network, + route->rx.plen, + route->r6.gateway, + route->rx.metric, + route->rx.mss); +} + +static gboolean +_vtr_v4_route_delete (int ifindex, const NMPlatformIPXRoute *route) +{ + return nm_platform_ip4_route_delete (ifindex > 0 ? ifindex : route->rx.ifindex, + route->r4.network, + route->rx.plen, + route->rx.metric); +} + +static gboolean +_vtr_v6_route_delete (int ifindex, const NMPlatformIPXRoute *route) +{ + return nm_platform_ip6_route_delete (ifindex > 0 ? ifindex : route->rx.ifindex, + route->r6.network, + route->rx.plen, + route->rx.metric); +} + +static guint32 +_vtr_v4_metric_normalize (guint32 metric) +{ + return metric; +} + +static gboolean +_vtr_v4_route_delete_default (int ifindex, guint32 metric) +{ + return nm_platform_ip4_route_delete (ifindex, 0, 0, metric); +} + +static gboolean +_vtr_v6_route_delete_default (int ifindex, guint32 metric) +{ + return nm_platform_ip6_route_delete (ifindex, in6addr_any, 0, metric); +} + +/******************************************************************/ + +const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { + .is_ip4 = TRUE, + .addr_family = AF_INET, + .sizeof_route = sizeof (NMPlatformIP4Route), + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip4_route_cmp, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route)) nm_platform_ip4_route_to_string, + .route_get_all = nm_platform_ip4_route_get_all, + .route_add = _vtr_v4_route_add, + .route_delete = _vtr_v4_route_delete, + .route_delete_default = _vtr_v4_route_delete_default, + .metric_normalize = _vtr_v4_metric_normalize, +}; + +const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { + .is_ip4 = FALSE, + .addr_family = AF_INET6, + .sizeof_route = sizeof (NMPlatformIP6Route), + .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip6_route_cmp, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route)) nm_platform_ip6_route_to_string, + .route_get_all = nm_platform_ip6_route_get_all, + .route_add = _vtr_v6_route_add, + .route_delete = _vtr_v6_route_delete, + .route_delete_default = _vtr_v6_route_delete_default, + .metric_normalize = nm_utils_ip6_route_metric_normalize, +}; + +/******************************************************************/ + static void nm_platform_init (NMPlatform *object) { diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 848f78e4..a49e0f11 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -69,7 +69,10 @@ typedef enum { /* Event came from the kernel. */ NM_PLATFORM_REASON_EXTERNAL, /* Event is a result of cache checking and cleanups. */ - NM_PLATFORM_REASON_CACHE_CHECK + NM_PLATFORM_REASON_CACHE_CHECK, + + /* Internal reason to suppress announcing change events */ + _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL, } NMPlatformReason; #define __NMPlatformObject_COMMON \ @@ -244,6 +247,23 @@ typedef union { typedef struct { + gboolean is_ip4; + int addr_family; + gsize sizeof_route; + int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b); + const char *(*route_to_string) (const NMPlatformIPXRoute *route); + GArray *(*route_get_all) (int ifindex, NMPlatformGetRouteMode mode); + gboolean (*route_add) (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src); + gboolean (*route_delete) (int ifindex, const NMPlatformIPXRoute *route); + gboolean (*route_delete_default) (int ifindex, guint32 metric); + guint32 (*metric_normalize) (guint32 metric); +} NMPlatformVTableRoute; + +extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; +extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; + + +typedef struct { int peer; } NMPlatformVethProperties; @@ -367,7 +387,8 @@ typedef struct { guint32 (*link_get_mtu) (NMPlatform *, int ifindex); gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); - char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); + char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); + guint (*link_get_dev_id) (NMPlatform *, int ifindex); gboolean (*link_get_wake_on_lan) (NMPlatform *, int ifindex); gboolean (*link_supports_carrier_detect) (NMPlatform *, int ifindex); @@ -480,6 +501,8 @@ char *nm_platform_sysctl_get (const char *path); gint32 nm_platform_sysctl_get_int32 (const char *path, gint32 fallback); gint64 nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gint64 max, gint64 fallback); +gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value); + gboolean nm_platform_link_get (int ifindex, NMPlatformLink *link); GArray *nm_platform_link_get_all (void); gboolean nm_platform_dummy_add (const char *name); @@ -514,6 +537,7 @@ guint32 nm_platform_link_get_mtu (int ifindex); gboolean nm_platform_link_set_mtu (int ifindex, guint32 mtu); char *nm_platform_link_get_physical_port_id (int ifindex); +guint nm_platform_link_get_dev_id (int ifindex); gboolean nm_platform_link_get_wake_on_lan (int ifindex); gboolean nm_platform_link_supports_carrier_detect (int ifindex); @@ -569,7 +593,7 @@ gboolean nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, i gboolean nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen); gboolean nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen); gboolean nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint32 device_route_metric); -gboolean nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses); +gboolean nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboolean keep_link_local); gboolean nm_platform_address_flush (int ifindex); gboolean nm_platform_ip4_check_reinstall_device_route (int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric); diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index b06e25e0..c10600eb 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -109,7 +109,7 @@ test_cleanup_linux_CPPFLAGS = \ -DKERNEL_HACKS=1 test_cleanup_linux_LDADD = $(PLATFORM_LDADD) -#@VALGRIND_RULES@ +@VALGRIND_RULES@ TESTS = test-link-fake test-address-fake test-route-fake test-cleanup-fake test-address-linux test-route-linux test-cleanup-linux diff --git a/src/platform/tests/Makefile.in b/src/platform/tests/Makefile.in index 052636ab..16a48430 100644 --- a/src/platform/tests/Makefile.in +++ b/src/platform/tests/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.13.4 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -15,7 +15,17 @@ @SET_MAKE@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -89,8 +99,6 @@ TESTS = test-link-fake$(EXEEXT) test-address-fake$(EXEEXT) \ test-address-linux$(EXEEXT) test-route-linux$(EXEEXT) \ test-cleanup-linux$(EXEEXT) subdir = src/platform/tests -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/build-aux/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ $(top_srcdir)/m4/compiler_warnings.m4 \ @@ -107,6 +115,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ $(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -289,6 +298,8 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ @@ -315,7 +326,6 @@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ -DATADIRNAME = @DATADIRNAME@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ @@ -350,6 +360,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@ GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ GTKDOC_MKPDF = @GTKDOC_MKPDF@ @@ -647,7 +658,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/platform/tests/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/platform/tests/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -777,14 +787,14 @@ distclean-compile: @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -1698,9 +1708,13 @@ uninstall-am: mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am +.PRECIOUS: Makefile + @GNOME_CODE_COVERAGE_RULES@ +@VALGRIND_RULES@ + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 3ef13b8e..5561038d 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -247,6 +247,12 @@ test_ip6_address_external (void) } void +init_tests (int *argc, char ***argv) +{ + nmtst_init_with_logging (argc, argv, NULL, "ALL"); +} + +void setup_tests (void) { SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index afbb438c..0d0f3f63 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -87,6 +87,12 @@ test_cleanup_internal (void) } void +init_tests (int *argc, char ***argv) +{ + nmtst_init_with_logging (argc, argv, NULL, "ALL"); +} + +void setup_tests (void) { nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index ab40f652..1980282d 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -125,6 +125,113 @@ link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPl g_error ("Added/changed link not found in the local cache."); } +gboolean +ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric) +{ + gs_free char *arg_network = NULL; + const char *argv[] = { + NULL, + "route", + "list", + "dev", + ifname, + "exact", + NULL, + NULL, + }; + int exit_status; + gs_free char *std_out = NULL, *std_err = NULL; + char *out; + gboolean success; + gs_free_error GError *error = NULL; + gs_free char *metric_pattern = NULL; + + g_assert (ifname && nm_utils_iface_valid_name (ifname)); + g_assert (!strstr (ifname, " metric ")); + g_assert (plen >= 0 && plen <= 32); + + if (!NM_IS_LINUX_PLATFORM (nm_platform_get ())) { + /* If we don't test against linux-platform, we don't actually configure any + * routes in the system. */ + return -1; + } + + argv[0] = nm_utils_file_search_in_paths ("ip", NULL, + (const char *[]) { "/sbin", "/usr/sbin", NULL }, + G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, NULL); + argv[6] = arg_network = g_strdup_printf ("%s/%d", nm_utils_inet4_ntop (network, NULL), plen); + + if (!argv[0]) { + /* Hm. There is no 'ip' binary. Return *unknown* */ + return -1; + } + + success = g_spawn_sync (NULL, + (char **) argv, + (char *[]) { NULL }, + 0, + NULL, + NULL, + &std_out, + &std_err, + &exit_status, + &error); + g_assert_no_error (error); + g_assert (success); + g_assert_cmpstr (std_err, ==, ""); + g_assert (std_out); + + metric_pattern = g_strdup_printf (" metric %u", metric); + out = std_out; + while (out) { + char *eol = strchr (out, '\n'); + gs_free char *line = eol ? g_strndup (out, eol - out) : g_strdup (out); + const char *p; + + out = eol ? &eol[1] : NULL; + if (!line[0]) + continue; + + if (metric == 0) { + if (!strstr (line, " metric ")) + return TRUE; + } + p = strstr (line, metric_pattern); + if (p && NM_IN_SET (p[strlen (metric_pattern)], ' ', '\0')) + return TRUE; + } + return FALSE; +} + +void +_assert_ip4_route_exists (const char *file, guint line, const char *func, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric) +{ + int ifindex; + gboolean exists_checked; + + /* Check for existance of the route by spawning iproute2. Do this because platform + * code might be entirely borked, but we expect ip-route to give a correct result. + * If the ip command cannot be found, we accept this as success. */ + exists_checked = ip4_route_exists (ifname, network, plen, metric); + if (exists_checked != -1 && !exists_checked != !exists) { + g_error ("[%s:%u] %s(): We expect the ip4 route %s/%d metric %u %s, but it %s", + file, line, func, + nm_utils_inet4_ntop (network, NULL), plen, metric, + exists ? "to exist" : "not to exist", + exists ? "doesn't" : "does"); + } + + ifindex = nm_platform_link_get_ifindex (ifname); + g_assert (ifindex > 0); + if (!nm_platform_ip4_route_exists (ifindex, network, plen, metric) != !exists) { + g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u %s, but platform thinks %s", + file, line, func, + nm_utils_inet4_ntop (network, NULL), plen, metric, + exists ? "exists" : "does not exist", + exists ? "it doesn't" : "it does"); + } +} + void run_command (const char *format, ...) { @@ -148,20 +255,22 @@ main (int argc, char **argv) int result; const char *program = *argv; - nmtst_init_with_logging (&argc, &argv, NULL, "ALL"); + init_tests (&argc, &argv); + NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") if (SETUP == nm_linux_platform_setup && getuid() != 0) { /* Try to exec as sudo, this function does not return, if a sudo-cmd is set. */ nmtst_reexec_sudo (); #ifdef REQUIRE_ROOT_TESTS - g_message ("Fail test: requires root privileges (%s)", program); + g_print ("Fail test: requires root privileges (%s)\n", program); return EXIT_FAILURE; #else - g_message ("Skipping test: requires root privileges (%s)", program); + g_print ("Skipping test: requires root privileges (%s)\n", program); return 77; #endif } + NM_PRAGMA_WARNING_REENABLE SETUP (); diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 8f6b3912..367833d1 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -9,6 +9,8 @@ #include "nm-fake-platform.h" #include "nm-linux-platform.h" +#include "nm-test-utils.h" + #define DEVICE_NAME "nm-test-device" #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) @@ -34,9 +36,15 @@ void accept_signal (SignalData *data); void wait_signal (SignalData *data); void free_signal (SignalData *data); +gboolean ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric); + +void _assert_ip4_route_exists (const char *file, guint line, const char *func, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric); +#define assert_ip4_route_exists(exists, ifname, network, plen, metric) _assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, exists, ifname, network, plen, metric) + void link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data); void run_command (const char *format, ...); +void init_tests (int *argc, char ***argv); void setup_tests (void); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 8bd2ef44..99556e0a 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -365,12 +365,14 @@ test_bridge (void) static void test_bond (void) { + NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") if (SETUP == nm_linux_platform_setup && !g_file_test ("/proc/1/net/bonding", G_FILE_TEST_IS_DIR) && system("modprobe --show bonding") != 0) { g_test_skip ("Skipping test for bonding: bonding module not available"); return; } + NM_PRAGMA_WARNING_REENABLE test_software (NM_LINK_TYPE_BOND, "bond"); } @@ -538,6 +540,12 @@ test_external (void) } void +init_tests (int *argc, char ***argv) +{ + nmtst_init_with_logging (argc, argv, NULL, "ALL"); +} + +void setup_tests (void) { nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 2c70b412..2d3caaf1 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -2,6 +2,7 @@ #include "test-common.h" #include "nm-test-utils.h" +#include "NetworkManagerUtils.h" #define DEVICE_NAME "nm-test-device" @@ -50,6 +51,75 @@ ip6_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Route *recei } static void +test_ip4_route_metric0 (void) +{ + int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback); + SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback); + SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback); + in_addr_t network = nmtst_inet4_from_string ("192.0.2.5"); /* from 192.0.2.0/24 (TEST-NET-1) (rfc5737) */ + int plen = 32; + int metric = 22987; + int mss = 1000; + + /* No routes initially */ + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); + + /* add the first route */ + g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss)); + no_error (); + accept_signal (route_added); + + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); + assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); + + /* Deleting route with metric 0 does nothing */ + g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0)); + no_error (); + g_assert (!route_removed->received); + + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); + assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); + + /* add the second route */ + g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss)); + no_error (); + accept_signal (route_added); + + assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, 0); + assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); + + /* Delete route with metric 0 */ + g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0)); + no_error (); + accept_signal (route_removed); + + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); + assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); + + /* Delete route with metric 0 again (we expect nothing to happen) */ + g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0)); + no_error (); + g_assert (!route_removed->received); + + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); + assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); + + /* Delete the other route */ + g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric)); + no_error (); + accept_signal (route_removed); + + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); + + free_signal (route_added); + free_signal (route_changed); + free_signal (route_removed); +} + +static void test_ip4_route (void) { int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); @@ -74,11 +144,11 @@ test_ip4_route (void) accept_signal (route_added); /* Add route */ - g_assert (!nm_platform_ip4_route_exists (ifindex, network, plen, metric)); + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); no_error (); g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); no_error (); - g_assert (nm_platform_ip4_route_exists (ifindex, network, plen, metric)); + assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); no_error (); accept_signal (route_added); @@ -88,11 +158,11 @@ test_ip4_route (void) accept_signal (route_changed); /* Add default route */ - g_assert (!nm_platform_ip4_route_exists (ifindex, 0, 0, metric)); + assert_ip4_route_exists (FALSE, DEVICE_NAME, 0, 0, metric); no_error (); g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); no_error (); - g_assert (nm_platform_ip4_route_exists (ifindex, 0, 0, metric)); + assert_ip4_route_exists (TRUE, DEVICE_NAME, 0, 0, metric); no_error (); accept_signal (route_added); @@ -127,13 +197,13 @@ test_ip4_route (void) rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); g_assert (!memcmp (routes->data, rts, sizeof (rts))); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len); + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); g_array_unref (routes); /* Remove route */ g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric)); no_error (); - g_assert (!nm_platform_ip4_route_exists (ifindex, network, plen, metric)); + assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); accept_signal (route_removed); /* Remove route again */ @@ -205,25 +275,25 @@ test_ip6_route (void) rts[0].plen = 128; rts[0].ifindex = ifindex; rts[0].gateway = in6addr_any; - rts[0].metric = metric; + rts[0].metric = nm_utils_ip6_route_metric_normalize (metric); rts[0].mss = mss; rts[1].source = NM_IP_CONFIG_SOURCE_USER; rts[1].network = network; rts[1].plen = plen; rts[1].ifindex = ifindex; rts[1].gateway = gateway; - rts[1].metric = metric; + rts[1].metric = nm_utils_ip6_route_metric_normalize (metric); rts[1].mss = mss; rts[2].source = NM_IP_CONFIG_SOURCE_USER; rts[2].network = in6addr_any; rts[2].plen = 0; rts[2].ifindex = ifindex; rts[2].gateway = gateway; - rts[2].metric = metric; + rts[2].metric = nm_utils_ip6_route_metric_normalize (metric); rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); g_assert (!memcmp (routes->data, rts, sizeof (rts))); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); g_array_unref (routes); /* Remove route */ @@ -242,6 +312,12 @@ test_ip6_route (void) } void +init_tests (int *argc, char ***argv) +{ + nmtst_init_with_logging (argc, argv, NULL, "ALL"); +} + +void setup_tests (void) { SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); @@ -256,4 +332,5 @@ setup_tests (void) g_test_add_func ("/route/ip4", test_ip4_route); g_test_add_func ("/route/ip6", test_ip6_route); + g_test_add_func ("/route/ip4_metric0", test_ip4_route_metric0); } diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c index 752da926..b31ffa1c 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/wifi-utils-nl80211.c @@ -738,6 +738,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) } } + /* Find number of supported frequencies */ info->num_freqs = 0; nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { @@ -757,6 +758,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) } } + /* Read supported frequencies */ info->freqs = g_malloc0 (sizeof (guint32) * info->num_freqs); freq_idx = 0; @@ -775,10 +777,19 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) info->freqs[freq_idx] = nla_get_u32 (tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + + info->caps |= NM_WIFI_DEVICE_CAP_FREQ_VALID; + + if (info->freqs[freq_idx] > 2400 && info->freqs[freq_idx] < 2500) + info->caps |= NM_WIFI_DEVICE_CAP_FREQ_2GHZ; + if (info->freqs[freq_idx] > 4900 && info->freqs[freq_idx] < 6000) + info->caps |= NM_WIFI_DEVICE_CAP_FREQ_5GHZ; + freq_idx++; } } + /* Read security/encryption support */ if (tb[NL80211_ATTR_CIPHER_SUITES]) { int num; int i; diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c index e0331de7..8428010a 100644 --- a/src/platform/wifi/wifi-utils-wext.c +++ b/src/platform/wifi/wifi-utils-wext.c @@ -569,6 +569,7 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan) guint32 response_len = 0; struct iw_range_with_scan_capa *scan_capa_range; int i; + gboolean freq_valid = FALSE, has_5ghz = FALSE, has_2ghz = FALSE; wext = wifi_data_new (iface, ifindex, sizeof (*wext)); wext->parent.get_mode = wifi_wext_get_mode; @@ -609,8 +610,14 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan) wext->max_qual.updated = range.max_qual.updated; wext->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES); - for (i = 0; i < wext->num_freqs; i++) + for (i = 0; i < wext->num_freqs; i++) { wext->freqs[i] = iw_freq_to_uint32 (&range.freq[i]); + freq_valid = TRUE; + if (wext->freqs[i] > 2400 && wext->freqs[i] < 2500) + has_2ghz = TRUE; + else if (wext->freqs[i] > 4900 && wext->freqs[i] < 6000) + has_5ghz = TRUE; + } /* Check for scanning capability; cards that can't scan are not supported */ if (check_scan && (wext_can_scan (wext) == FALSE)) { @@ -638,6 +645,12 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan) } wext->parent.caps = wext_get_caps (wext, &range); + if (freq_valid) + wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_VALID; + if (has_2ghz) + wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_2GHZ; + if (has_5ghz) + wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_5GHZ; nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): using WEXT for WiFi device control", diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h index f1b57ace..eb37bc22 100644 --- a/src/platform/wifi/wifi-utils.h +++ b/src/platform/wifi/wifi-utils.h @@ -44,7 +44,8 @@ gboolean wifi_utils_set_mode (WifiData *data, const NM80211Mode mode); /* Returns frequency in MHz */ guint32 wifi_utils_get_freq (WifiData *data); -/* Return the first supported frequency in the zero-terminated list */ +/* Return the first supported frequency in the zero-terminated list. + * Frequencies are specified in MHz. */ guint32 wifi_utils_find_freq (WifiData *data, const guint32 *freqs); /* Caller must free returned byte array */ |