diff --git a/.travis.yml b/.travis.yml index 37ec8d0209..53d4f666a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,6 @@ addons: - libdbus-1-dev - libiw-dev - libglib2.0-dev - - libnl-3-dev - - libnl-route-3-dev - - libnl-genl-3-dev - libmm-glib-dev - ppp - ppp-dev @@ -43,7 +40,6 @@ addons: - dbus-x11 - libjansson4 - libjansson-dev - - libnl-3-dev - libndp-dev - automake - dnsmasq diff --git a/Makefile.am b/Makefile.am index c19acb2730..029bd4bf72 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1185,7 +1185,6 @@ src_cppflags = \ -DDHCPCD_PATH=\"$(DHCPCD_PATH)\" \ \ $(LIBUDEV_CFLAGS) \ - $(LIBNL_CFLAGS) \ $(LIBNDP_CFLAGS) \ $(LIBPSL_CFLAGS) \ $(LIBCURL_CFLAGS) \ @@ -1643,7 +1642,6 @@ src_libNetworkManager_la_LIBADD = \ src/libsystemd-nm.la \ $(GLIB_LIBS) \ $(LIBUDEV_LIBS) \ - $(LIBNL_LIBS) \ $(SYSTEMD_LOGIN_LIBS) \ $(LIBNDP_LIBS) \ $(DL_LIBS) \ @@ -1684,8 +1682,7 @@ src_libNetworkManagerTest_la_LIBADD = \ src/libNetworkManager.la \ $(CODE_COVERAGE_LDFLAGS) \ $(GLIB_LIBS) \ - $(LIBUDEV_LIBS) \ - $(LIBNL_LIBS) + $(LIBUDEV_LIBS) $(src_libNetworkManagerTest_la_OBJECTS): $(libnm_core_lib_h_pub_mkenums) @@ -1726,7 +1723,6 @@ src_nm_iface_helper_LDADD = \ src/libsystemd-nm.la \ $(GLIB_LIBS) \ $(LIBUDEV_LIBS) \ - $(LIBNL_LIBS) \ $(LIBNDP_LIBS) \ $(DL_LIBS) @@ -2898,8 +2894,7 @@ src_platform_tests_ldflags = \ src_platform_tests_libadd = \ src/libNetworkManagerTest.la \ $(GLIB_LIBS) \ - $(LIBUDEV_LIBS) \ - $(LIBNL_LIBS) + $(LIBUDEV_LIBS) check_programs_norun += \ src/platform/tests/monitor diff --git a/configure.ac b/configure.ac index 7c6e62007e..211c2d0ee3 100644 --- a/configure.ac +++ b/configure.ac @@ -559,9 +559,6 @@ else fi AC_SUBST(NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT) -# libnl support for the linux platform -PKG_CHECK_MODULES(LIBNL, libnl-3.0 >= 3.2.8) - # uuid library PKG_CHECK_MODULES(UUID, uuid) diff --git a/contrib/fedora/REQUIRED_PACKAGES b/contrib/fedora/REQUIRED_PACKAGES index a63a4196b1..27606d1946 100644 --- a/contrib/fedora/REQUIRED_PACKAGES +++ b/contrib/fedora/REQUIRED_PACKAGES @@ -30,7 +30,6 @@ yum install \ jansson-devel \ libcurl-devel \ libndp-devel \ - libnl3-devel \ libpsl-devel \ libselinux-devel \ libtool \ diff --git a/contrib/fedora/nm-live-vm/build.sh b/contrib/fedora/nm-live-vm/build.sh index 3033fbc321..6333861705 100755 --- a/contrib/fedora/nm-live-vm/build.sh +++ b/contrib/fedora/nm-live-vm/build.sh @@ -32,7 +32,7 @@ BUILD_PACKAGES="qemu febootstrap mock rpmdevtools" ARCH=i386 ROOT="${ROOT:-"fedora-20-$ARCH"}" TREE="/var/lib/mock/$ROOT/root" -PACKAGES="kernel passwd git autoconf automake libtool intltool gtk-doc libnl3-devel +PACKAGES="kernel passwd git autoconf automake libtool intltool gtk-doc dbus-glib-devel libuuid-devel nss-devel ppp-devel newt-devel libndp-devel readline-devel gobject-introspection-devel diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index 8f94808026..c6678c1d11 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -10,7 +10,6 @@ %global dbus_glib_version 0.100 %global wireless_tools_version 1:28-0pre9 -%global libnl3_version 3.2.7 %global ppp_version %(sed -n 's/^#define\\s*VERSION\\s*"\\([^\\s]*\\)"$/\\1/p' %{_includedir}/pppd/patchlevel.h 2>/dev/null | grep . || echo bad) %global glib2_version %(pkg-config --modversion glib-2.0 2>/dev/null || echo bad) @@ -130,7 +129,6 @@ BuildRequires: glib2-devel >= 2.40.0 BuildRequires: gobject-introspection-devel >= 0.10.3 BuildRequires: gettext-devel BuildRequires: pkgconfig -BuildRequires: libnl3-devel >= %{libnl3_version} BuildRequires: automake autoconf intltool libtool %if %{with ppp} BuildRequires: ppp-devel >= 2.4.5 diff --git a/meson.build b/meson.build index 7b902486a2..912073c42f 100644 --- a/meson.build +++ b/meson.build @@ -184,9 +184,6 @@ libudev_dep = dependency('libudev', version: '>= 175') dbus_dep = dependency('dbus-1', version: '>= 1.1') libndp_dep = dependency('libndp') -# libnl support for the linux platform -libnl_dep = dependency('libnl-3.0', version: '>= 3.2.8', required: false) - jansson_dep = dependency('jansson', version: '>= 2.5', required: false) config_h.set10('WITH_JANSSON', jansson_dep.found()) diff --git a/src/meson.build b/src/meson.build index 981a4794e4..5c57f5fc59 100644 --- a/src/meson.build +++ b/src/meson.build @@ -72,7 +72,6 @@ sources = files( ) deps = [ - libnl_dep, libnmdbus_dep, libsystemd_dep, libudev_dep, @@ -170,7 +169,6 @@ sources = files( deps = [ dl_dep, libndp_dep, - libnl_dep, # FIXME: Some files use introspection/dbus* headers, so # this dependency might be needed #libnmdbus_dep, @@ -252,7 +250,6 @@ network_manager = executable( deps = [ dl_dep, libndp_dep, - libnl_dep, libudev_dep, nm_core_dep ] @@ -279,7 +276,6 @@ if enable_tests ) deps = [ - libnl_dep, libudev_dep, nm_core_dep ] diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 1e141eeb90..0532bc6165 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -21,6 +21,7 @@ #include "nm-linux-platform.h" +#include #include #include #include @@ -79,10 +80,6 @@ enum { #define VLAN_FLAG_MVRP 0x8 -/* nm-internal error codes for libnl. Make sure they don't overlap. */ -#define _NLE_NM_NOBUFS 500 -#define _NLE_MSG_TRUNC 501 - /*****************************************************************************/ #define IFQDISCSIZ 32 @@ -297,6 +294,7 @@ typedef enum { WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS, } WaitForNlResponseResult; typedef void (*WaitForNlResponseCallback) (NMPlatform *platform, @@ -951,106 +949,6 @@ _nl_addattr_l (struct nlmsghdr *n, return TRUE; } -static const char * -_nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) -{ - const char *b; - const char *s; - guint flags, flags_before; - const char *prefix; - - nm_utils_to_string_buffer_init (&buf, &len); - b = buf; - - switch (hdr->nlmsg_type) { - case RTM_NEWLINK: s = "RTM_NEWLINK"; break; - case RTM_DELLINK: s = "RTM_DELLINK"; break; - case RTM_NEWADDR: s = "RTM_NEWADDR"; break; - case RTM_DELADDR: s = "RTM_DELADDR"; break; - case RTM_NEWROUTE: s = "RTM_NEWROUTE"; break; - case RTM_DELROUTE: s = "RTM_DELROUTE"; break; - case RTM_NEWQDISC: s = "RTM_NEWQDISC"; break; - case RTM_DELQDISC: s = "RTM_DELQDISC"; break; - case RTM_NEWTFILTER: s = "RTM_NEWTFILTER"; break; - case RTM_DELTFILTER: s = "RTM_DELTFILTER"; break; - case NLMSG_NOOP: s = "NLMSG_NOOP"; break; - case NLMSG_ERROR: s = "NLMSG_ERROR"; break; - case NLMSG_DONE: s = "NLMSG_DONE"; break; - case NLMSG_OVERRUN: s = "NLMSG_OVERRUN"; break; - default: s = NULL; break; - } - - if (s) - nm_utils_strbuf_append_str (&buf, &len, s); - else - nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); - - flags = hdr->nlmsg_flags; - - if (!flags) { - nm_utils_strbuf_append_str (&buf, &len, ", flags 0"); - goto flags_done; - } - -#define _F(f, n) \ - G_STMT_START { \ - if (NM_FLAGS_ALL (flags, f)) { \ - flags &= ~(f); \ - nm_utils_strbuf_append (&buf, &len, "%s%s", prefix, n); \ - if (!flags) \ - goto flags_done; \ - prefix = ","; \ - } \ - } G_STMT_END - - prefix = ", flags "; - flags_before = flags; - _F (NLM_F_REQUEST, "request"); - _F (NLM_F_MULTI, "multi"); - _F (NLM_F_ACK, "ack"); - _F (NLM_F_ECHO, "echo"); - _F (NLM_F_DUMP_INTR, "dump_intr"); - _F (0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); - - if (flags_before != flags) - prefix = ";"; - - switch (hdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - case RTM_NEWQDISC: - case RTM_NEWTFILTER: - _F (NLM_F_REPLACE, "replace"); - _F (NLM_F_EXCL, "excl"); - _F (NLM_F_CREATE, "create"); - _F (NLM_F_APPEND, "append"); - break; - case RTM_GETLINK: - case RTM_GETADDR: - case RTM_GETROUTE: - case RTM_DELQDISC: - case RTM_DELTFILTER: - _F (NLM_F_DUMP, "dump"); - _F (NLM_F_ROOT, "root"); - _F (NLM_F_MATCH, "match"); - _F (NLM_F_ATOMIC, "atomic"); - break; - } - -#undef _F - - if (flags_before != flags) - prefix = ";"; - nm_utils_strbuf_append (&buf, &len, "%s0x%04x", prefix, flags); - -flags_done: - - nm_utils_strbuf_append (&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); - - return b; -} - /****************************************************************** * NMPObject/netlink functions ******************************************************************/ @@ -2637,8 +2535,7 @@ _nl_msg_new_link (int nlmsg_type, nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK)); - if (!(msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags))) - g_return_val_if_reached (NULL); + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -2680,8 +2577,6 @@ _nl_msg_new_address (int nlmsg_type, nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWADDR, RTM_DELADDR)); msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); - if (!msg) - g_return_val_if_reached (NULL); if (scope == -1) { /* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */ @@ -2794,8 +2689,6 @@ _nl_msg_new_route (int nlmsg_type, nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags); - if (!msg) - g_return_val_if_reached (NULL); if (nlmsg_append (msg, &rtmsg, sizeof (rtmsg), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -2893,8 +2786,6 @@ _nl_msg_new_qdisc (int nlmsg_type, }; msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); - if (!msg) - return NULL; if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -2969,8 +2860,6 @@ _nl_msg_new_tfilter (int nlmsg_type, }; msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); - if (!msg) - return NULL; if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0) goto nla_put_failure; @@ -3553,28 +3442,59 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform, g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx); } +static void +delayed_action_wait_for_nl_response_complete_check (NMPlatform *platform, + WaitForNlResponseResult force_result, + guint32 *out_next_seq_number, + gint64 *out_next_timeout_abs_ns, + gint64 *p_now_ns) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + guint i; + guint32 next_seq_number = 0; + gint64 next_timeout_abs_ns = 0; + gint now_ns = 0; + + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; ) { + const DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); + + if (data->seq_result) + delayed_action_wait_for_nl_response_complete (platform, i, data->seq_result); + else if ( p_now_ns + && ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_ns ())) >= data->timeout_abs_ns)) { + /* the caller can optionally check for timeout by providing a p_now_ns argument. */ + delayed_action_wait_for_nl_response_complete (platform, i, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); + } else if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) + delayed_action_wait_for_nl_response_complete (platform, i, force_result); + else { + if ( next_seq_number == 0 + || next_timeout_abs_ns > data->timeout_abs_ns) { + next_seq_number = data->seq_number; + next_timeout_abs_ns = data->timeout_abs_ns; + } + i++; + } + } + + if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) { + nm_assert (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); + nm_assert (priv->delayed_action.list_wait_for_nl_response->len == 0); + } + + NM_SET_OUT (out_next_seq_number, next_seq_number); + NM_SET_OUT (out_next_timeout_abs_ns, next_timeout_abs_ns); + NM_SET_OUT (p_now_ns, now_ns); +} + static void delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform, WaitForNlResponseResult fallback_result) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - while (priv->delayed_action.list_wait_for_nl_response->len > 0) { - const DelayedActionWaitForNlResponseData *data; - guint idx = priv->delayed_action.list_wait_for_nl_response->len - 1; - WaitForNlResponseResult r; - - data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, idx); - - /* prefer the result that we already have. */ - r = data->seq_result ? : fallback_result; - - delayed_action_wait_for_nl_response_complete (platform, idx, r); - } - } - nm_assert (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - nm_assert (priv->delayed_action.list_wait_for_nl_response->len == 0); + delayed_action_wait_for_nl_response_complete_check (platform, + fallback_result, + NULL, + NULL, + NULL); } /*****************************************************************************/ @@ -4051,7 +3971,7 @@ _nlh_seq_next_get (NMLinuxPlatformPrivate *priv) * @response_type: * @response_out_data: * - * Returns: 0 on success or a negative errno. Beware, it's an errno, not nlerror. + * Returns: 0 on success or a negative errno. */ static int _nl_send_nlmsghdr (NMPlatform *platform, @@ -4235,8 +4155,6 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio * because we need the sequence number. */ nlmsg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP); - if (!nlmsg) - continue; if ( klass->obj_type == NMP_OBJECT_TYPE_QDISC || klass->obj_type == NMP_OBJECT_TYPE_TFILTER) { @@ -4363,7 +4281,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event obj = nmp_object_new_from_nl (platform, cache, msg, id_only); if (!obj) { _LOGT ("event-notification: %s: ignore", - _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); + nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); return; } @@ -4381,7 +4299,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event } _LOGT ("event-notification: %s%s: %s", - _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)), + nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)), is_dump ? ", in-dump" : "", nmp_object_to_string (obj, id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, @@ -6561,15 +6479,12 @@ event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); struct nl_sock *sk = priv->nlh; - int n, err = 0, multipart = 0, interrupted = 0; + int n; + int err = 0; + gboolean multipart = 0; + gboolean interrupted = FALSE; struct nlmsghdr *hdr; WaitForNlResponseResult seq_result; - - /* - nla is passed on to not only to nl_recv() but may also be passed - to a function pointer provided by the caller which may or may not - initialize the variable. Thomas Graf. - */ struct sockaddr_nl nla = {0}; nm_auto_free struct ucred *creds = NULL; nm_auto_free unsigned char *buf = NULL; @@ -6577,56 +6492,28 @@ event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events) continue_reading: g_clear_pointer (&buf, free); g_clear_pointer (&creds, free); - errno = 0; n = nl_recv (sk, &nla, &buf, &creds); if (n <= 0) { - /* workaround libnl3 <= 3.2.15 returning danling pointers in case nl_recv() - * fails. Fixed by libnl3 69468517d0de1675d80f24661ff57a5dbac7275c. */ - buf = NULL; - creds = NULL; - } - switch (n) { - case 0: - /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ - if (errno == EAGAIN) { - /* EAGAIN is equal to EWOULDBLOCK. If it would not be, we'd have to - * workaround libnl3 mapping EWOULDBLOCK to -NLE_FAILURE. */ - G_STATIC_ASSERT (EAGAIN == EWOULDBLOCK); - n = -NLE_AGAIN; - } - break; - case -NLE_MSG_TRUNC: { - int buf_size; + if (n == -NLE_MSG_TRUNC) { + int buf_size; - /* the message receive buffer was too small. We lost one message, which - * is unfortunate. Try to double the buffer size for the next time. */ - buf_size = nl_socket_get_msg_buf_size (sk); - if (buf_size < 512*1024) { - buf_size *= 2; - _LOGT ("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes", buf_size); - if (nl_socket_set_msg_buf_size (sk, buf_size) < 0) - nm_assert_not_reached (); - if (!handle_events) - goto continue_reading; + /* the message receive buffer was too small. We lost one message, which + * is unfortunate. Try to double the buffer size for the next time. */ + buf_size = nl_socket_get_msg_buf_size (sk); + if (buf_size < 512*1024) { + buf_size *= 2; + _LOGT ("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes", buf_size); + if (nl_socket_set_msg_buf_size (sk, buf_size) < 0) + nm_assert_not_reached (); + if (!handle_events) + goto continue_reading; + } } - n = -_NLE_MSG_TRUNC; - break; - } - case -NLE_NOMEM: - if (errno == ENOBUFS) { - /* we are very much interested in a overrun of the receive buffer. - * nl_recv() maps all kinds of errors to NLE_NOMEM, so check also - * for errno explicitly. And if so, hack our own return code to signal - * the overrun. */ - n = -_NLE_NM_NOBUFS; - } - break; - } - if (n <= 0) return n; + } hdr = (struct nlmsghdr *) buf; while (nlmsg_ok (hdr, n)) { @@ -6636,11 +6523,7 @@ continue_reading: guint32 seq_number; char buf_nlmsghdr[400]; - msg = nlmsg_convert (hdr); - if (!msg) { - err = -NLE_NOMEM; - goto out; - } + msg = nlmsg_alloc_convert (hdr); nlmsg_set_proto (msg, NETLINK_ROUTE); nlmsg_set_src (msg, &nla); @@ -6655,13 +6538,13 @@ continue_reading: } _LOGt ("netlink: recvmsg: new message %s", - _nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); + nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); if (creds) nlmsg_set_creds (msg, creds); if (hdr->nlmsg_flags & NLM_F_MULTI) - multipart = 1; + multipart = TRUE; if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { /* @@ -6669,7 +6552,7 @@ continue_reading: * all messages until a NLMSG_DONE is * received and report the inconsistency. */ - interrupted = 1; + interrupted = TRUE; } /* Other side wishes to see an ack for this message */ @@ -6684,7 +6567,7 @@ continue_reading: * usually the end of a message and therefore we slip * out of the loop by default. the user may overrule * this action by skipping this packet. */ - multipart = 0; + multipart = FALSE; seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } else if (hdr->nlmsg_type == NLMSG_NOOP) { /* Message to be ignored, the default action is to @@ -6763,9 +6646,9 @@ stop: * Repeat reading. */ goto continue_reading; } -out: + if (interrupted) - err = -NLE_DUMP_INTR; + return -NLE_DUMP_INTR; return err; } @@ -6776,46 +6659,50 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) { nm_auto_pop_netns NMPNetns *netns = NULL; NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int r, nle; + int r; struct pollfd pfd; gboolean any = FALSE; - gint64 now_ns; int timeout_ms; - guint i; struct { guint32 seq_number; gint64 timeout_abs_ns; - } data_next; + gint64 now_ns; + } next; - if (!nm_platform_netns_push (platform, &netns)) + if (!nm_platform_netns_push (platform, &netns)) { + delayed_action_wait_for_nl_response_complete_all (platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS); return FALSE; + } - while (TRUE) { - - while (TRUE) { + for (;;) { + for (;;) { + int nle; nle = event_handler_recvmsgs (platform, TRUE); if (nle < 0) { switch (nle) { - case -NLE_AGAIN: + case -EAGAIN: goto after_read; case -NLE_DUMP_INTR: _LOGD ("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); break; - case -_NLE_MSG_TRUNC: - case -_NLE_NM_NOBUFS: + case -NLE_MSG_TRUNC: + case -ENOBUFS: _LOGI ("netlink: read: %s. Need to resynchronize platform cache", ({ const char *_reason = "unknown"; switch (nle) { - case -_NLE_MSG_TRUNC: _reason = "message truncated"; break; - case -_NLE_NM_NOBUFS: _reason = "too many netlink events"; break; + case -NLE_MSG_TRUNC: _reason = "message truncated"; break; + case -ENOBUFS: _reason = "too many netlink events"; break; } _reason; })); event_handler_recvmsgs (platform, FALSE); - delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); + delayed_action_wait_for_nl_response_complete_all (platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | @@ -6839,39 +6726,23 @@ after_read: if (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) return any; - now_ns = 0; - data_next.seq_number = 0; - data_next.timeout_abs_ns = 0; - - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; ) { - DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); - - if (data->seq_result) - delayed_action_wait_for_nl_response_complete (platform, i, data->seq_result); - else if ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_ns ())) > data->timeout_abs_ns) - delayed_action_wait_for_nl_response_complete (platform, i, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); - else { - i++; - - if ( data_next.seq_number == 0 - || data_next.timeout_abs_ns > data->timeout_abs_ns) { - data_next.seq_number = data->seq_number; - data_next.timeout_abs_ns = data->timeout_abs_ns; - } - } - } + delayed_action_wait_for_nl_response_complete_check (platform, + WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN, + &next.seq_number, + &next.timeout_abs_ns, + &next.now_ns); if ( !wait_for_acks || !NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) return any; - nm_assert (data_next.seq_number); - nm_assert (data_next.timeout_abs_ns > 0); - nm_assert (now_ns > 0); + nm_assert (next.seq_number); + nm_assert (next.now_ns > 0); + nm_assert (next.timeout_abs_ns > next.now_ns); - _LOGT ("netlink: read: wait for ACK for sequence number %u...", data_next.seq_number); + _LOGT ("netlink: read: wait for ACK for sequence number %u...", next.seq_number); - timeout_ms = (data_next.timeout_abs_ns - now_ns) / (NM_UTILS_NS_PER_SECOND / 1000); + timeout_ms = (next.timeout_abs_ns - next.now_ns) / (NM_UTILS_NS_PER_SECOND / 1000); memset (&pfd, 0, sizeof (pfd)); pfd.fd = nl_socket_get_fd (priv->nlh); @@ -6882,6 +6753,7 @@ after_read: /* timeout and there is nothing to read. */ goto after_read; } + if (r < 0) { int errsv = errno; @@ -7152,7 +7024,8 @@ dispose (GObject *object) _LOGD ("dispose"); - delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); + delayed_action_wait_for_nl_response_complete_all (platform, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE; g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0); diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c index 45bc1cf75b..7d794f7737 100644 --- a/src/platform/nm-netlink.c +++ b/src/platform/nm-netlink.c @@ -22,9 +22,669 @@ #include "nm-netlink.h" -/***************************************************************************** - * Reimplementations/copied from libnl3/genl - *****************************************************************************/ +#include +#include + +/*****************************************************************************/ + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +/*****************************************************************************/ + +#define NL_SOCK_PASSCRED (1<<1) +#define NL_MSG_PEEK (1<<3) +#define NL_MSG_PEEK_EXPLICIT (1<<4) +#define NL_NO_AUTO_ACK (1<<5) + +#define NL_MSG_CRED_PRESENT 1 + +struct nl_msg { + int nm_protocol; + int nm_flags; + struct sockaddr_nl nm_src; + struct sockaddr_nl nm_dst; + struct ucred nm_creds; + struct nlmsghdr * nm_nlh; + size_t nm_size; + int nm_refcnt; +}; + +struct nl_sock { + struct sockaddr_nl s_local; + struct sockaddr_nl s_peer; + int s_fd; + int s_proto; + unsigned int s_seq_next; + unsigned int s_seq_expect; + int s_flags; + size_t s_bufsize; +}; + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, int, + NM_UTILS_LOOKUP_DEFAULT (NULL), + NM_UTILS_LOOKUP_ITEM (NLE_UNSPEC, "NLE_UNSPEC"), + NM_UTILS_LOOKUP_ITEM (NLE_BUG, "NLE_BUG"), + NM_UTILS_LOOKUP_ITEM (NLE_NATIVE_ERRNO, "NLE_NATIVE_ERRNO"), + + NM_UTILS_LOOKUP_ITEM (NLE_ATTRSIZE, "NLE_ATTRSIZE"), + NM_UTILS_LOOKUP_ITEM (NLE_BAD_SOCK, "NLE_BAD_SOCK"), + NM_UTILS_LOOKUP_ITEM (NLE_DUMP_INTR, "NLE_DUMP_INTR"), + NM_UTILS_LOOKUP_ITEM (NLE_MSG_OVERFLOW, "NLE_MSG_OVERFLOW"), + NM_UTILS_LOOKUP_ITEM (NLE_MSG_TOOSHORT, "NLE_MSG_TOOSHORT"), + NM_UTILS_LOOKUP_ITEM (NLE_MSG_TRUNC, "NLE_MSG_TRUNC"), + NM_UTILS_LOOKUP_ITEM (NLE_SEQ_MISMATCH, "NLE_SEQ_MISMATCH"), +) + +const char * +nl_geterror (int err) +{ + const char *s; + + err = nl_errno (err); + + if (err >= _NLE_BASE) { + s = _geterror (err); + if (s) + return s; + } + return g_strerror (err); +} + +/*****************************************************************************/ + +NM_UTILS_ENUM2STR_DEFINE (nl_nlmsgtype2str, int, + NM_UTILS_ENUM2STR (NLMSG_NOOP, "NOOP"), + NM_UTILS_ENUM2STR (NLMSG_ERROR, "ERROR"), + NM_UTILS_ENUM2STR (NLMSG_DONE, "DONE"), + NM_UTILS_ENUM2STR (NLMSG_OVERRUN, "OVERRUN"), +); + +NM_UTILS_FLAGS2STR_DEFINE (nl_nlmsg_flags2str, int, + NM_UTILS_FLAGS2STR (NLM_F_REQUEST, "REQUEST"), + NM_UTILS_FLAGS2STR (NLM_F_MULTI, "MULTI"), + NM_UTILS_FLAGS2STR (NLM_F_ACK, "ACK"), + NM_UTILS_FLAGS2STR (NLM_F_ECHO, "ECHO"), + NM_UTILS_FLAGS2STR (NLM_F_ROOT, "ROOT"), + NM_UTILS_FLAGS2STR (NLM_F_MATCH, "MATCH"), + NM_UTILS_FLAGS2STR (NLM_F_ATOMIC, "ATOMIC"), + NM_UTILS_FLAGS2STR (NLM_F_REPLACE, "REPLACE"), + NM_UTILS_FLAGS2STR (NLM_F_EXCL, "EXCL"), + NM_UTILS_FLAGS2STR (NLM_F_CREATE, "CREATE"), + NM_UTILS_FLAGS2STR (NLM_F_APPEND, "APPEND"), +); + +/*****************************************************************************/ + +const char * +nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) +{ + const char *b; + const char *s; + guint flags, flags_before; + const char *prefix; + + if (!nm_utils_to_string_buffer_init_null (hdr, &buf, &len)) + return buf; + + b = buf; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: s = "RTM_NEWLINK"; break; + case RTM_DELLINK: s = "RTM_DELLINK"; break; + case RTM_NEWADDR: s = "RTM_NEWADDR"; break; + case RTM_DELADDR: s = "RTM_DELADDR"; break; + case RTM_NEWROUTE: s = "RTM_NEWROUTE"; break; + case RTM_DELROUTE: s = "RTM_DELROUTE"; break; + case RTM_NEWQDISC: s = "RTM_NEWQDISC"; break; + case RTM_DELQDISC: s = "RTM_DELQDISC"; break; + case RTM_NEWTFILTER: s = "RTM_NEWTFILTER"; break; + case RTM_DELTFILTER: s = "RTM_DELTFILTER"; break; + case NLMSG_NOOP: s = "NLMSG_NOOP"; break; + case NLMSG_ERROR: s = "NLMSG_ERROR"; break; + case NLMSG_DONE: s = "NLMSG_DONE"; break; + case NLMSG_OVERRUN: s = "NLMSG_OVERRUN"; break; + default: s = NULL; break; + } + + if (s) + nm_utils_strbuf_append_str (&buf, &len, s); + else + nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); + + flags = hdr->nlmsg_flags; + + if (!flags) { + nm_utils_strbuf_append_str (&buf, &len, ", flags 0"); + goto flags_done; + } + +#define _F(f, n) \ + G_STMT_START { \ + if (NM_FLAGS_ALL (flags, f)) { \ + flags &= ~(f); \ + nm_utils_strbuf_append (&buf, &len, "%s%s", prefix, n); \ + if (!flags) \ + goto flags_done; \ + prefix = ","; \ + } \ + } G_STMT_END + + prefix = ", flags "; + flags_before = flags; + _F (NLM_F_REQUEST, "request"); + _F (NLM_F_MULTI, "multi"); + _F (NLM_F_ACK, "ack"); + _F (NLM_F_ECHO, "echo"); + _F (NLM_F_DUMP_INTR, "dump_intr"); + _F (0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); + + if (flags_before != flags) + prefix = ";"; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_NEWROUTE: + case RTM_NEWQDISC: + case RTM_NEWTFILTER: + _F (NLM_F_REPLACE, "replace"); + _F (NLM_F_EXCL, "excl"); + _F (NLM_F_CREATE, "create"); + _F (NLM_F_APPEND, "append"); + break; + case RTM_GETLINK: + case RTM_GETADDR: + case RTM_GETROUTE: + case RTM_DELQDISC: + case RTM_DELTFILTER: + _F (NLM_F_DUMP, "dump"); + _F (NLM_F_ROOT, "root"); + _F (NLM_F_MATCH, "match"); + _F (NLM_F_ATOMIC, "atomic"); + break; + } + +#undef _F + + if (flags_before != flags) + prefix = ";"; + nm_utils_strbuf_append (&buf, &len, "%s0x%04x", prefix, flags); + +flags_done: + + nm_utils_strbuf_append (&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); + + return b; +} + +/*****************************************************************************/ + +struct nlmsghdr * +nlmsg_hdr (struct nl_msg *n) +{ + return n->nm_nlh; +} + +void * +nlmsg_reserve (struct nl_msg *n, size_t len, int pad) +{ + char *buf = (char *) n->nm_nlh; + size_t nlmsg_len = n->nm_nlh->nlmsg_len; + size_t tlen; + + if (len > n->nm_size) + return NULL; + + tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; + + if ((tlen + nlmsg_len) > n->nm_size) + return NULL; + + buf += nlmsg_len; + n->nm_nlh->nlmsg_len += tlen; + + if (tlen > len) + memset(buf + len, 0, tlen - len); + + return buf; +} + +/*****************************************************************************/ + +static int + get_default_page_size (void) +{ + static int val = 0; + int v; + + if (G_UNLIKELY (val == 0)) { + v = getpagesize (); + g_assert (v > 0); + val = v; + } + return val; +} + +struct nlattr * +nla_reserve (struct nl_msg *msg, int attrtype, int attrlen) +{ + struct nlattr *nla; + int tlen; + + if (attrlen < 0) + return NULL; + + tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); + + if (tlen > msg->nm_size) + return NULL; + + nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + nla->nla_type = attrtype; + nla->nla_len = nla_attr_size(attrlen); + + if (attrlen) + memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); + msg->nm_nlh->nlmsg_len = tlen; + + return nla; +} + +struct nl_msg * +nlmsg_alloc_size (size_t len) +{ + struct nl_msg *nm; + + if (len < sizeof (struct nlmsghdr)) + len = sizeof (struct nlmsghdr); + + nm = g_slice_new0 (struct nl_msg); + + nm->nm_refcnt = 1; + nm->nm_protocol = -1; + nm->nm_size = len; + nm->nm_nlh = g_malloc0 (len); + nm->nm_nlh->nlmsg_len = nlmsg_total_size (0); + return nm; +} + +/** + * Allocate a new netlink message with the default maximum payload size. + * + * Allocates a new netlink message without any further payload. The + * maximum payload size defaults to PAGESIZE or as otherwise specified + * with nlmsg_set_default_size(). + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg * +nlmsg_alloc (void) +{ + return nlmsg_alloc_size (get_default_page_size ()); +} + +/** + * Allocate a new netlink message with maximum payload size specified. + */ +struct nl_msg * +nlmsg_alloc_inherit (struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = nlmsg_alloc (); + if (hdr) { + struct nlmsghdr *new = nm->nm_nlh; + + new->nlmsg_type = hdr->nlmsg_type; + new->nlmsg_flags = hdr->nlmsg_flags; + new->nlmsg_seq = hdr->nlmsg_seq; + new->nlmsg_pid = hdr->nlmsg_pid; + } + + return nm; +} + +struct nl_msg * +nlmsg_alloc_convert (struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = nlmsg_alloc_size (NLMSG_ALIGN (hdr->nlmsg_len)); + memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); + return nm; +} + +struct nl_msg * +nlmsg_alloc_simple (int nlmsgtype, int flags) +{ + struct nlmsghdr nlh = { + .nlmsg_type = nlmsgtype, + .nlmsg_flags = flags, + }; + + return nlmsg_alloc_inherit (&nlh); +} + +int +nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad) +{ + void *tmp; + + tmp = nlmsg_reserve (n, len, pad); + if (tmp == NULL) + return -ENOMEM; + + memcpy(tmp, data, len); + return 0; +} + +int +nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + if (!nlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + return nla_parse (tb, maxtype, nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), policy); +} + +struct nlmsghdr * +nlmsg_put (struct nl_msg *n, uint32_t pid, uint32_t seq, + int type, int payload, int flags) +{ + struct nlmsghdr *nlh; + + if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) + g_return_val_if_reached (NULL); + + nlh = (struct nlmsghdr *) n->nm_nlh; + nlh->nlmsg_type = type; + nlh->nlmsg_flags = flags; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = seq; + + if (payload > 0 && + nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) + return NULL; + + return nlh; +} + +uint64_t +nla_get_u64 (const struct nlattr *nla) +{ + uint64_t tmp = 0; + + if (nla && nla_len(nla) >= sizeof (tmp)) + memcpy(&tmp, nla_data(nla), sizeof (tmp)); + + return tmp; +} + +size_t +nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize) +{ + size_t srclen = nla_len(nla); + const char *src = nla_data(nla); + + if (srclen > 0 && src[srclen - 1] == '\0') + srclen--; + + if (dstsize > 0) { + size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen; + + memset(dst, 0, dstsize); + memcpy(dst, src, len); + } + + return srclen; +} + +int +nla_memcpy (void *dest, const struct nlattr *src, int count) +{ + int minlen; + + if (!src) + return 0; + + minlen = NM_MIN (count, (int) nla_len (src)); + memcpy(dest, nla_data(src), minlen); + + return minlen; +} + +int +nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data) +{ + struct nlattr *nla; + + nla = nla_reserve(msg, attrtype, datalen); + if (!nla) { + if (datalen < 0) + g_return_val_if_reached (-NLE_BUG); + + return -ENOMEM; + } + + if (datalen > 0) + memcpy (nla_data(nla), data, datalen); + + return 0; +} + +struct nlattr * +nla_find (const struct nlattr *head, int len, int attrtype) +{ + const struct nlattr *nla; + int rem; + + nla_for_each_attr (nla, head, len, rem) { + if (nla_type (nla) == attrtype) + return (struct nlattr*)nla; + } + + return NULL; +} + +void +nla_nest_cancel (struct nl_msg *msg, const struct nlattr *attr) +{ + ssize_t len; + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; + if (len < 0) + g_return_if_reached (); + else if (len > 0) { + msg->nm_nlh->nlmsg_len -= len; + memset(nlmsg_tail(msg->nm_nlh), 0, len); + } +} + +struct nlattr * +nla_nest_start (struct nl_msg *msg, int attrtype) +{ + struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + + if (nla_put(msg, attrtype, 0, NULL) < 0) + return NULL; + + return start; +} + +static int +_nest_end (struct nl_msg *msg, struct nlattr *start, int keep_empty) +{ + size_t pad, len; + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; + + if ( len > USHRT_MAX + || (!keep_empty && len == NLA_HDRLEN)) { + /* + * Max nlattr size exceeded or empty nested attribute, trim the + * attribute header again + */ + nla_nest_cancel(msg, start); + + /* Return error only if nlattr size was exceeded */ + return (len == NLA_HDRLEN) ? 0 : -NLE_ATTRSIZE; + } + + start->nla_len = len; + + pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; + if (pad > 0) { + /* + * Data inside attribute does not end at a alignment boundry. + * Pad accordingly and accoun for the additional space in + * the message. nlmsg_reserve() may never fail in this situation, + * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. + */ + if (!nlmsg_reserve(msg, pad, 0)) + g_return_val_if_reached (-NLE_BUG); + } + + return 0; +} + +int +nla_nest_end (struct nl_msg *msg, struct nlattr *start) +{ + return _nest_end (msg, start, 0); +} + +static const uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = { + [NLA_U8] = sizeof (uint8_t), + [NLA_U16] = sizeof (uint16_t), + [NLA_U32] = sizeof (uint32_t), + [NLA_U64] = sizeof (uint64_t), + [NLA_STRING] = 1, + [NLA_FLAG] = 0, +}; + +static int +validate_nla (const struct nlattr *nla, int maxtype, + const struct nla_policy *policy) +{ + const struct nla_policy *pt; + unsigned int minlen = 0; + int type = nla_type(nla); + + if (type < 0 || type > maxtype) + return 0; + + pt = &policy[type]; + + if (pt->type > NLA_TYPE_MAX) + g_return_val_if_reached (-NLE_BUG); + + if (pt->minlen) + minlen = pt->minlen; + else if (pt->type != NLA_UNSPEC) + minlen = nla_attr_minlen[pt->type]; + + if (nla_len(nla) < minlen) + return -NLE_UNSPEC; + + if (pt->maxlen && nla_len(nla) > pt->maxlen) + return -NLE_UNSPEC; + + if (pt->type == NLA_STRING) { + const char *data = nla_data(nla); + if (data[nla_len(nla) - 1] != '\0') + return -NLE_UNSPEC; + } + + return 0; +} + +int +nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy) +{ + struct nlattr *nla; + int rem, err; + + memset(tb, 0, sizeof (struct nlattr *) * (maxtype + 1)); + + nla_for_each_attr(nla, head, len, rem) { + int type = nla_type(nla); + + if (type > maxtype) + continue; + + if (policy) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + goto errout; + } + + tb[type] = nla; + } + + err = 0; +errout: + return err; +} + +/*****************************************************************************/ + +void nlmsg_free (struct nl_msg *msg) +{ + if (!msg) + return; + + if (msg->nm_refcnt < 1) + g_return_if_reached (); + + msg->nm_refcnt--; + + if (msg->nm_refcnt <= 0) { + g_free (msg->nm_nlh); + g_slice_free (struct nl_msg, msg); + } +} + +int +nlmsg_get_proto (struct nl_msg *msg) +{ + return msg->nm_protocol; +} + +void +nlmsg_set_proto (struct nl_msg *msg, int protocol) +{ + msg->nm_protocol = protocol; +} + +void +nlmsg_set_src (struct nl_msg *msg, struct sockaddr_nl *addr) +{ + memcpy (&msg->nm_src, addr, sizeof (*addr)); +} + +struct ucred * +nlmsg_get_creds (struct nl_msg *msg) +{ + if (msg->nm_flags & NL_MSG_CRED_PRESENT) + return &msg->nm_creds; + return NULL; +} + +void +nlmsg_set_creds (struct nl_msg *msg, struct ucred *creds) +{ + memcpy (&msg->nm_creds, creds, sizeof (*creds)); + msg->nm_flags |= NL_MSG_CRED_PRESENT; +} + +/*****************************************************************************/ void * genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family, @@ -120,3 +780,624 @@ genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], } /*****************************************************************************/ + +struct nl_sock * +nl_socket_alloc (void) +{ + struct nl_sock *sk; + + sk = g_slice_new0 (struct nl_sock); + + sk->s_fd = -1; + sk->s_local.nl_family = AF_NETLINK; + sk->s_peer.nl_family = AF_NETLINK; + sk->s_seq_expect = sk->s_seq_next = time(NULL); + + return sk; +} + +void +nl_socket_free (struct nl_sock *sk) +{ + if (!sk) + return; + + if (sk->s_fd >= 0) + nm_close (sk->s_fd); + g_slice_free (struct nl_sock, sk); +} + +int +nl_socket_get_fd (const struct nl_sock *sk) +{ + return sk->s_fd; +} + +uint32_t +nl_socket_get_local_port (const struct nl_sock *sk) +{ + return sk->s_local.nl_pid; +} + +size_t +nl_socket_get_msg_buf_size (struct nl_sock *sk) +{ + return sk->s_bufsize; +} + +int +nl_socket_set_passcred (struct nl_sock *sk, int state) +{ + int err; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt (sk->s_fd, SOL_SOCKET, SO_PASSCRED, + &state, sizeof (state)); + if (err < 0) + return -nl_syserr2nlerr (errno); + + if (state) + sk->s_flags |= NL_SOCK_PASSCRED; + else + sk->s_flags &= ~NL_SOCK_PASSCRED; + + return 0; +} + +int +nl_socket_set_msg_buf_size (struct nl_sock *sk, size_t bufsize) +{ + sk->s_bufsize = bufsize; + + return 0; +} + +struct sockaddr_nl * +nlmsg_get_dst (struct nl_msg *msg) +{ + return &msg->nm_dst; +} + +int +nl_socket_set_nonblocking (const struct nl_sock *sk) +{ + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) + return -nl_syserr2nlerr (errno); + + return 0; +} + +int +nl_socket_set_buffer_size (struct nl_sock *sk, int rxbuf, int txbuf) +{ + int err; + + if (rxbuf <= 0) + rxbuf = 32768; + + if (txbuf <= 0) + txbuf = 32768; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt (sk->s_fd, SOL_SOCKET, SO_SNDBUF, + &txbuf, sizeof (txbuf)); + if (err < 0) { + return -nl_syserr2nlerr (errno); + } + + err = setsockopt (sk->s_fd, SOL_SOCKET, SO_RCVBUF, + &rxbuf, sizeof (rxbuf)); + if (err < 0) { + return -nl_syserr2nlerr (errno); + } + + return 0; +} + +int +nl_socket_add_memberships (struct nl_sock *sk, int group, ...) +{ + int err; + va_list ap; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + va_start(ap, group); + + while (group != 0) { + if (group < 0) { + va_end(ap); + g_return_val_if_reached (-NLE_BUG); + } + + err = setsockopt (sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof (group)); + if (err < 0) { + va_end(ap); + return -nl_syserr2nlerr (errno); + } + + group = va_arg(ap, int); + } + + va_end(ap); + + return 0; +} + +void nl_socket_disable_msg_peek (struct nl_sock *sk) +{ + sk->s_flags |= NL_MSG_PEEK_EXPLICIT; + sk->s_flags &= ~NL_MSG_PEEK; +} + +int +nl_connect (struct nl_sock *sk, int protocol) +{ + int err; + socklen_t addrlen; + struct sockaddr_nl local = { 0 }; + + if (sk->s_fd != -1) + return -NLE_BAD_SOCK; + + sk->s_fd = socket (AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); + if (sk->s_fd < 0) { + err = -nl_syserr2nlerr (errno); + goto errout; + } + + err = nl_socket_set_buffer_size(sk, 0, 0); + if (err < 0) + goto errout; + + nm_assert (sk->s_local.nl_pid == 0); + + err = bind (sk->s_fd, (struct sockaddr*) &sk->s_local, + sizeof (sk->s_local)); + if (err != 0) { + err = -nl_syserr2nlerr (errno); + goto errout; + } + + addrlen = sizeof (local); + err = getsockname (sk->s_fd, (struct sockaddr *) &local, + &addrlen); + if (err < 0) { + err = -nl_syserr2nlerr (errno); + goto errout; + } + + if (addrlen != sizeof (local)) { + err = -NLE_UNSPEC; + goto errout; + } + + if (local.nl_family != AF_NETLINK) { + err = -NLE_UNSPEC; + goto errout; + } + + sk->s_local = local; + sk->s_proto = protocol; + + return 0; + +errout: + if (sk->s_fd != -1) { + close(sk->s_fd); + sk->s_fd = -1; + } + return err; +} + +/*****************************************************************************/ + +static void +_cb_init (struct nl_cb *dst, const struct nl_cb *src) +{ + nm_assert (dst); + + if (src) + *dst = *src; + else + memset (dst, 0, sizeof (*dst)); +} + +static int ack_wait_handler(struct nl_msg *msg, void *arg) +{ + return NL_STOP; +} + +int +nl_wait_for_ack (struct nl_sock *sk, + const struct nl_cb *cb) +{ + struct nl_cb cb2; + + _cb_init (&cb2, cb); + cb2.ack_cb = ack_wait_handler; + return nl_recvmsgs (sk, &cb2); +} + +#define NL_CB_CALL(cb, type, msg) \ +do { \ + const struct nl_cb *_cb = (cb); \ + \ + if (_cb->type##_cb) { \ + err = _cb->type##_cb ((msg), _cb->type##_arg); \ + switch (err) { \ + case NL_OK: \ + err = 0; \ + break; \ + case NL_SKIP: \ + goto skip; \ + case NL_STOP: \ + goto stop; \ + default: \ + goto out; \ + } \ + } \ +} while (0) + +int +nl_recvmsgs (struct nl_sock *sk, const struct nl_cb *cb) +{ + int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0; + gs_free unsigned char *buf = NULL; + struct nlmsghdr *hdr; + struct sockaddr_nl nla = { 0 }; + gs_free struct ucred *creds = NULL; + +continue_reading: + n = nl_recv (sk, &nla, &buf, &creds); + if (n <= 0) + return n; + + hdr = (struct nlmsghdr *) buf; + while (nlmsg_ok (hdr, n)) { + nm_auto_nlmsg struct nl_msg *msg = NULL; + + msg = nlmsg_alloc_convert (hdr); + + nlmsg_set_proto (msg, sk->s_proto); + nlmsg_set_src (msg, &nla); + if (creds) + nlmsg_set_creds (msg, creds); + + nrecv++; + + /* Only do sequence checking if auto-ack mode is enabled */ + if (!(sk->s_flags & NL_NO_AUTO_ACK)) { + if (hdr->nlmsg_seq != sk->s_seq_expect) { + err = -NLE_SEQ_MISMATCH; + goto out; + } + } + + if (hdr->nlmsg_type == NLMSG_DONE || + hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == NLMSG_NOOP || + hdr->nlmsg_type == NLMSG_OVERRUN) { + /* We can't check for !NLM_F_MULTI since some netlink + * users in the kernel are broken. */ + sk->s_seq_expect++; + } + + if (hdr->nlmsg_flags & NLM_F_MULTI) + multipart = 1; + + if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { + /* + * We have to continue reading to clear + * all messages until a NLMSG_DONE is + * received and report the inconsistency. + */ + interrupted = 1; + } + + /* messages terminates a multipart message, this is + * usually the end of a message and therefore we slip + * out of the loop by default. the user may overrule + * this action by skipping this packet. */ + if (hdr->nlmsg_type == NLMSG_DONE) { + multipart = 0; + NL_CB_CALL(cb, finish, msg); + } + + /* Message to be ignored, the default action is to + * skip this message if no callback is specified. The + * user may overrule this action by returning + * NL_PROCEED. */ + else if (hdr->nlmsg_type == NLMSG_NOOP) + goto skip; + + /* Data got lost, report back to user. The default action is to + * quit parsing. The user may overrule this action by retuning + * NL_SKIP or NL_PROCEED (dangerous) */ + else if (hdr->nlmsg_type == NLMSG_OVERRUN) { + err = -NLE_MSG_OVERFLOW; + goto out; + } + + /* Message carries a nlmsgerr */ + else if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *e = nlmsg_data(hdr); + + if (hdr->nlmsg_len < nlmsg_size(sizeof (*e))) { + /* Truncated error message, the default action + * is to stop parsing. The user may overrule + * this action by returning NL_SKIP or + * NL_PROCEED (dangerous) */ + err = -NLE_MSG_TRUNC; + goto out; + } + if (e->error) { + /* Error message reported back from kernel. */ + if (cb->err_cb) { + err = cb->err_cb (&nla, e, + cb->err_arg); + if (err < 0) + goto out; + else if (err == NL_SKIP) + goto skip; + else if (err == NL_STOP) { + err = -e->error; + goto out; + } + } else { + err = -e->error; + goto out; + } + } else + NL_CB_CALL(cb, ack, msg); + } else { + /* Valid message (not checking for MULTIPART bit to + * get along with broken kernels. NL_SKIP has no + * effect on this. */ + NL_CB_CALL(cb, valid, msg); + } +skip: + err = 0; + hdr = nlmsg_next(hdr, &n); + } + + if (multipart) { + /* Multipart message not yet complete, continue reading */ + nm_clear_g_free (&creds); + nm_clear_g_free (&buf); + + goto continue_reading; + } + +stop: + err = 0; + +out: + if (interrupted) + err = -NLE_DUMP_INTR; + + return err ?: nrecv; +} + +int +nl_sendmsg (struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) +{ + int ret; + + if (sk->s_fd < 0) + return -NLE_BAD_SOCK; + + nlmsg_set_src (msg, &sk->s_local); + + ret = sendmsg(sk->s_fd, hdr, 0); + if (ret < 0) + return -nl_syserr2nlerr (errno); + + return ret; +} + +int +nl_send_iovec (struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) +{ + struct sockaddr_nl *dst; + struct ucred *creds; + struct msghdr hdr = { + .msg_name = (void *) &sk->s_peer, + .msg_namelen = sizeof (struct sockaddr_nl), + .msg_iov = iov, + .msg_iovlen = iovlen, + }; + char buf[CMSG_SPACE(sizeof (struct ucred))]; + + /* Overwrite destination if specified in the message itself, defaults + * to the peer address of the socket. + */ + dst = nlmsg_get_dst(msg); + if (dst->nl_family == AF_NETLINK) + hdr.msg_name = dst; + + /* Add credentials if present. */ + creds = nlmsg_get_creds(msg); + if (creds != NULL) { + struct cmsghdr *cmsg; + + hdr.msg_control = buf; + hdr.msg_controllen = sizeof (buf); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof (struct ucred)); + memcpy(CMSG_DATA(cmsg), creds, sizeof (struct ucred)); + } + + return nl_sendmsg(sk, msg, &hdr); +} + +void +nl_complete_msg (struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh; + + nlh = nlmsg_hdr(msg); + if (nlh->nlmsg_pid == NL_AUTO_PORT) + nlh->nlmsg_pid = nl_socket_get_local_port(sk); + + if (nlh->nlmsg_seq == NL_AUTO_SEQ) + nlh->nlmsg_seq = sk->s_seq_next++; + + if (msg->nm_protocol == -1) + msg->nm_protocol = sk->s_proto; + + nlh->nlmsg_flags |= NLM_F_REQUEST; + + if (!(sk->s_flags & NL_NO_AUTO_ACK)) + nlh->nlmsg_flags |= NLM_F_ACK; +} + +int +nl_send (struct nl_sock *sk, struct nl_msg *msg) +{ + struct iovec iov = { + .iov_base = (void *) nlmsg_hdr(msg), + .iov_len = nlmsg_hdr(msg)->nlmsg_len, + }; + + return nl_send_iovec(sk, msg, &iov, 1); +} + +int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) +{ + nl_complete_msg(sk, msg); + + return nl_send(sk, msg); +} + +int +nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla, + unsigned char **buf, struct ucred **creds) +{ + ssize_t n; + int flags = 0; + static int page_size = 0; + struct iovec iov; + struct msghdr msg = { + .msg_name = (void *) nla, + .msg_namelen = sizeof (struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + gs_free struct ucred* tmpcreds = NULL; + int retval; + + nm_assert (nla); + nm_assert (buf && !*buf); + nm_assert (!creds || !*creds); + + if ( (sk->s_flags & NL_MSG_PEEK) + || ( !(sk->s_flags & NL_MSG_PEEK_EXPLICIT) + && sk->s_bufsize == 0)) + flags |= MSG_PEEK | MSG_TRUNC; + + if (page_size == 0) + page_size = getpagesize() * 4; + + iov.iov_len = sk->s_bufsize ? : page_size; + iov.iov_base = g_malloc (iov.iov_len); + + if ( creds + && (sk->s_flags & NL_SOCK_PASSCRED)) { + msg.msg_controllen = CMSG_SPACE (sizeof (struct ucred)); + msg.msg_control = g_malloc (msg.msg_controllen); + } + +retry: + n = recvmsg(sk->s_fd, &msg, flags); + if (!n) { + retval = 0; + goto abort; + } + + if (n < 0) { + if (errno == EINTR) + goto retry; + + retval = -nl_syserr2nlerr (errno); + goto abort; + } + + if (msg.msg_flags & MSG_CTRUNC) { + if (msg.msg_controllen == 0) { + retval = -NLE_MSG_TRUNC; + goto abort; + } + + msg.msg_controllen *= 2; + msg.msg_control = g_realloc (msg.msg_control, msg.msg_controllen); + goto retry; + } + + if ( iov.iov_len < n + || (msg.msg_flags & MSG_TRUNC)) { + /* respond with error to an incomplete message */ + if (flags == 0) { + retval = -NLE_MSG_TRUNC; + goto abort; + } + + /* Provided buffer is not long enough, enlarge it + * to size of n (which should be total length of the message) + * and try again. */ + iov.iov_base = g_realloc (iov.iov_base, n); + iov.iov_len = n; + flags = 0; + goto retry; + } + + if (flags != 0) { + /* Buffer is big enough, do the actual reading */ + flags = 0; + goto retry; + } + + if (msg.msg_namelen != sizeof (struct sockaddr_nl)) { + retval = -NLE_UNSPEC; + goto abort; + } + + if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + if (cmsg->cmsg_type != SCM_CREDENTIALS) + continue; + tmpcreds = g_memdup (CMSG_DATA(cmsg), sizeof (*tmpcreds)); + break; + } + } + + retval = n; + +abort: + g_free (msg.msg_control); + + if (retval <= 0) { + g_free (iov.iov_base); + return retval; + } + + *buf = iov.iov_base; + NM_SET_OUT (creds, g_steal_pointer (&tmpcreds)); + return retval; +} diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h index 64fcbfa4ad..42e46d6c69 100644 --- a/src/platform/nm-netlink.h +++ b/src/platform/nm-netlink.h @@ -21,40 +21,375 @@ #ifndef __NM_NETLINK_H__ #define __NM_NETLINK_H__ -#include -#include +#include +#include +#include -/***************************************************************************** - * libnl3 compat code - *****************************************************************************/ +/*****************************************************************************/ + +#define _NLE_BASE 100000 +#define NLE_UNSPEC (_NLE_BASE + 0) +#define NLE_BUG (_NLE_BASE + 1) +#define NLE_NATIVE_ERRNO (_NLE_BASE + 2) +#define NLE_SEQ_MISMATCH (_NLE_BASE + 3) +#define NLE_MSG_TRUNC (_NLE_BASE + 4) +#define NLE_MSG_TOOSHORT (_NLE_BASE + 5) +#define NLE_DUMP_INTR (_NLE_BASE + 6) +#define NLE_ATTRSIZE (_NLE_BASE + 7) +#define NLE_BAD_SOCK (_NLE_BASE + 8) +#define NLE_NOADDR (_NLE_BASE + 9) +#define NLE_MSG_OVERFLOW (_NLE_BASE + 10) + +#define _NLE_BASE_END (_NLE_BASE + 11) static inline int -_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, - const struct nla_policy *policy) +nl_errno (int err) { - return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy); + /* the error codes from our netlink implementation are plain errno + * extended with our own error in a particular range starting from + * _NLE_BASE. + * + * However, often we encode errors as negative values. This function + * normalizes the error and returns it's positive value. */ + return err >= 0 + ? err + : ((err == G_MININT) ? NLE_BUG : -errno); } -#define nla_parse(...) _nl_nla_parse(__VA_ARGS__) static inline int -_nl_nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], - int maxtype, const struct nla_policy *policy) +nl_syserr2nlerr (int err) { - return nlmsg_parse (nlh, hdrlen, tb, maxtype, (struct nla_policy *) policy); + if (err == G_MININT) + return NLE_NATIVE_ERRNO; + if (err < 0) + err = -err; + return (err >= _NLE_BASE && err < _NLE_BASE_END) + ? NLE_NATIVE_ERRNO + : err; } -#define nlmsg_parse(...) _nl_nlmsg_parse(__VA_ARGS__) + +const char *nl_geterror (int err); + +/*****************************************************************************/ + +/* Basic attribute data types */ +enum { + NLA_UNSPEC, /* Unspecified type, binary data chunk */ + NLA_U8, /* 8 bit integer */ + NLA_U16, /* 16 bit integer */ + NLA_U32, /* 32 bit integer */ + NLA_U64, /* 64 bit integer */ + NLA_STRING, /* NUL terminated character string */ + NLA_FLAG, /* Flag */ + NLA_MSECS, /* Micro seconds (64bit) */ + NLA_NESTED, /* Nested attributes */ + NLA_NESTED_COMPAT, + NLA_NUL_STRING, + NLA_BINARY, + NLA_S8, + NLA_S16, + NLA_S32, + NLA_S64, + __NLA_TYPE_MAX, +}; + +#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) + +struct nl_msg; + +/*****************************************************************************/ + +const char *nl_nlmsgtype2str (int type, char *buf, size_t size); + +const char *nl_nlmsg_flags2str (int flags, char *buf, size_t len); + +const char *nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len); + +/*****************************************************************************/ + +struct nla_policy { + /* Type of attribute or NLA_UNSPEC */ + uint16_t type; + + /* Minimal length of payload required */ + uint16_t minlen; + + /* Maximal length of payload allowed */ + uint16_t maxlen; +}; + +/*****************************************************************************/ static inline int -_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, - const struct nla_policy *policy) +nla_attr_size(int payload) { - return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy); -} -#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__) + nm_assert (payload >= 0); -/***************************************************************************** - * Reimplementations/copied from libnl3/genl - *****************************************************************************/ + return NLA_HDRLEN + payload; +} + +static inline int +nla_total_size (int payload) +{ + return NLA_ALIGN (nla_attr_size (payload)); +} + +static inline int +nla_padlen (int payload) +{ + return nla_total_size(payload) - nla_attr_size(payload); +} + +struct nlattr *nla_reserve (struct nl_msg *msg, int attrtype, int attrlen); + +static inline int +nla_len (const struct nlattr *nla) +{ + return nla->nla_len - NLA_HDRLEN; +} + +static inline int +nla_type (const struct nlattr *nla) +{ + return nla->nla_type & NLA_TYPE_MASK; +} + +static inline void * +nla_data (const struct nlattr *nla) +{ + nm_assert (nla); + return (char *) nla + NLA_HDRLEN; +} + +static inline uint8_t +nla_get_u8 (const struct nlattr *nla) +{ + return *(const uint8_t *) nla_data (nla); +} + +static inline uint16_t +nla_get_u16 (const struct nlattr *nla) +{ + return *(const uint16_t *) nla_data (nla); +} + +static inline uint32_t +nla_get_u32(const struct nlattr *nla) +{ + return *(const uint32_t *) nla_data (nla); +} + +uint64_t nla_get_u64 (const struct nlattr *nla); + +static inline char * +nla_get_string (const struct nlattr *nla) +{ + return (char *) nla_data (nla); +} + +size_t nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize); + +int nla_memcpy (void *dest, const struct nlattr *src, int count); + +int nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data); + +static inline int +nla_put_string (struct nl_msg *msg, int attrtype, const char *str) +{ + return nla_put(msg, attrtype, strlen(str) + 1, str); +} + +#define NLA_PUT(msg, attrtype, attrlen, data) \ + do { \ + if (nla_put(msg, attrtype, attrlen, data) < 0) \ + goto nla_put_failure; \ + } while(0) + +#define NLA_PUT_TYPE(msg, type, attrtype, value) \ + do { \ + type __tmp = value; \ + NLA_PUT(msg, attrtype, sizeof(type), &__tmp); \ + } while(0) + +#define NLA_PUT_U8(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint8_t, attrtype, value) + +#define NLA_PUT_U16(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint16_t, attrtype, value) + +#define NLA_PUT_U32(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint32_t, attrtype, value) + +#define NLA_PUT_U64(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint64_t, attrtype, value) + +#define NLA_PUT_STRING(msg, attrtype, value) \ + NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value) + +struct nlattr *nla_find (const struct nlattr *head, int len, int attrtype); + +static inline int +nla_ok (const struct nlattr *nla, int remaining) +{ + return remaining >= sizeof(*nla) && + nla->nla_len >= sizeof(*nla) && + nla->nla_len <= remaining; +} + +static inline struct nlattr * +nla_next(const struct nlattr *nla, int *remaining) +{ + int totlen = NLA_ALIGN(nla->nla_len); + + *remaining -= totlen; + return (struct nlattr *) ((char *) nla + totlen); +} + +#define nla_for_each_attr(pos, head, len, rem) \ + for (pos = head, rem = len; \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +#define nla_for_each_nested(pos, nla, rem) \ + for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +void nla_nest_cancel (struct nl_msg *msg, const struct nlattr *attr); +struct nlattr *nla_nest_start (struct nl_msg *msg, int attrtype); +int nla_nest_end (struct nl_msg *msg, struct nlattr *start); + +int nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy); + +static inline int +nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy) +{ + return nla_parse (tb, maxtype, nla_data(nla), nla_len(nla), policy); +} + +/*****************************************************************************/ + +struct nl_msg *nlmsg_alloc (void); + +struct nl_msg *nlmsg_alloc_size (size_t max); + +struct nl_msg *nlmsg_alloc_inherit (struct nlmsghdr *hdr); + +struct nl_msg *nlmsg_alloc_convert (struct nlmsghdr *hdr); + +struct nl_msg *nlmsg_alloc_simple (int nlmsgtype, int flags); + +void *nlmsg_reserve (struct nl_msg *n, size_t len, int pad); + +int nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad); + +void nlmsg_free (struct nl_msg *msg); + +static inline int +nlmsg_size (int payload) +{ + nm_assert (payload >= 0 && payload < G_MAXINT - NLMSG_HDRLEN - 4); + return NLMSG_HDRLEN + payload; +} + +static inline int +nlmsg_total_size (int payload) +{ + return NLMSG_ALIGN (nlmsg_size (payload)); +} + +static inline int +nlmsg_ok (const struct nlmsghdr *nlh, int remaining) +{ + return (remaining >= (int)sizeof(struct nlmsghdr) && + nlh->nlmsg_len >= sizeof(struct nlmsghdr) && + nlh->nlmsg_len <= remaining); +} + +static inline struct nlmsghdr * +nlmsg_next (struct nlmsghdr *nlh, int *remaining) +{ + int totlen = NLMSG_ALIGN(nlh->nlmsg_len); + + *remaining -= totlen; + + return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); +} + +int nlmsg_get_proto (struct nl_msg *msg); +void nlmsg_set_proto (struct nl_msg *msg, int protocol); + +void nlmsg_set_src (struct nl_msg *msg, struct sockaddr_nl *addr); + +struct ucred *nlmsg_get_creds (struct nl_msg *msg); +void nlmsg_set_creds (struct nl_msg *msg, struct ucred *creds); + +static inline void +_nm_auto_nl_msg_cleanup (struct nl_msg **ptr) +{ + nlmsg_free (*ptr); +} +#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) + +static inline void * +nlmsg_data (const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_HDRLEN; +} + +static inline void * +nlmsg_tail (const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); +} + +struct nlmsghdr *nlmsg_hdr (struct nl_msg *n); + +static inline int +nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) +{ + if (nlh->nlmsg_len < nlmsg_size (hdrlen)) + return 0; + + return 1; +} + +static inline int +nlmsg_datalen (const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - NLMSG_HDRLEN; +} + +static inline int +nlmsg_attrlen (const struct nlmsghdr *nlh, int hdrlen) +{ + return NM_MAX ((int) (nlmsg_datalen (nlh) - NLMSG_ALIGN (hdrlen)), 0); +} + +static inline struct nlattr * +nlmsg_attrdata (const struct nlmsghdr *nlh, int hdrlen) +{ + unsigned char *data = nlmsg_data(nlh); + return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); +} + +static inline struct nlattr * +nlmsg_find_attr (struct nlmsghdr *nlh, int hdrlen, int attrtype) +{ + return nla_find (nlmsg_attrdata (nlh, hdrlen), + nlmsg_attrlen (nlh, hdrlen), + attrtype); +} + +int nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy); + +struct nlmsghdr *nlmsg_put (struct nl_msg *n, uint32_t pid, uint32_t seq, + int type, int payload, int flags); + +/*****************************************************************************/ void *genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family, int hdrlen, int flags, uint8_t cmd, uint8_t version); @@ -69,16 +404,85 @@ int genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen); int genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy); -/***************************************************************************** - * helpers - *****************************************************************************/ +/*****************************************************************************/ -static inline void -_nm_auto_nl_msg_cleanup (struct nl_msg **ptr) -{ - nlmsg_free (*ptr); -} -#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) +#define NL_AUTO_PORT 0 +#define NL_AUTO_SEQ 0 + +struct nl_sock; + +struct nl_sock *nl_socket_alloc (void); + +void nl_socket_free (struct nl_sock *sk); + +int nl_socket_get_fd (const struct nl_sock *sk); + +struct sockaddr_nl *nlmsg_get_dst (struct nl_msg *msg); + +size_t nl_socket_get_msg_buf_size (struct nl_sock *sk); +int nl_socket_set_msg_buf_size (struct nl_sock *sk, size_t bufsize); + +int nl_socket_set_buffer_size (struct nl_sock *sk, int rxbuf, int txbuf); + +int nl_socket_set_passcred (struct nl_sock *sk, int state); + +int nl_socket_set_nonblocking (const struct nl_sock *sk); + +void nl_socket_disable_msg_peek (struct nl_sock *sk); + +uint32_t nl_socket_get_local_port (const struct nl_sock *sk); + +int nl_socket_add_memberships (struct nl_sock *sk, int group, ...); + +int nl_connect (struct nl_sock *sk, int protocol); + +int nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla, + unsigned char **buf, struct ucred **creds); + +int nl_send (struct nl_sock *sk, struct nl_msg *msg); + +int nl_send_auto (struct nl_sock *sk, struct nl_msg *msg); + +/*****************************************************************************/ + +enum nl_cb_action { + /* Proceed with wathever would come next */ + NL_OK, + /* Skip this message */ + NL_SKIP, + /* Stop parsing altogether and discard remaining messages */ + NL_STOP, +}; + +typedef int (*nl_recvmsg_msg_cb_t) (struct nl_msg *msg, void *arg); + +typedef int (*nl_recvmsg_err_cb_t) (struct sockaddr_nl *nla, + struct nlmsgerr *nlerr, void *arg); + +struct nl_cb { + nl_recvmsg_msg_cb_t valid_cb; + void * valid_arg; + + nl_recvmsg_msg_cb_t finish_cb; + void * finish_arg; + + nl_recvmsg_msg_cb_t ack_cb; + void * ack_arg; + + nl_recvmsg_err_cb_t err_cb; + void * err_arg; +}; + +int nl_sendmsg (struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr); + +int nl_send_iovec (struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen); + +void nl_complete_msg (struct nl_sock *sk, struct nl_msg *msg); + +int nl_recvmsgs (struct nl_sock *sk, const struct nl_cb *cb); + +int nl_wait_for_ack (struct nl_sock *sk, + const struct nl_cb *cb); /*****************************************************************************/ diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c index cdab00da57..e0a9633591 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/wifi-utils-nl80211.c @@ -79,55 +79,41 @@ probe_response (struct nl_msg *msg, void *arg) static int genl_ctrl_resolve (struct nl_sock *sk, const char *name) { - struct nl_msg *msg; - struct nl_cb *cb, *orig; - int rc; - int result = -NLE_OBJ_NOTFOUND; + nm_auto_nlmsg struct nl_msg *msg = NULL; + int result = -ENOMEM; gint32 response_data = -1; - - if (!(orig = nl_socket_get_cb (sk))) - goto out; - - cb = nl_cb_clone (orig); - nl_cb_put (orig); - if (!cb) - goto out; + const struct nl_cb cb = { + .valid_cb = probe_response, + .valid_arg = &response_data, + }; msg = nlmsg_alloc (); - if (!msg) - goto out_cb_free; if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1)) - goto out_msg_free; + goto out; if (nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name) < 0) - goto out_msg_free; + goto out; - rc = nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, &response_data); - if (rc < 0) - goto out_msg_free; + result = nl_send_auto (sk, msg); + if (result < 0) + goto out; - rc = nl_send_auto_complete (sk, msg); - if (rc < 0) - goto out_msg_free; - - rc = nl_recvmsgs (sk, cb); - if (rc < 0) - goto out_msg_free; + result = nl_recvmsgs (sk, &cb); + if (result < 0) + goto out; /* If search was successful, request may be ACKed after data */ - rc = nl_wait_for_ack (sk); - if (rc < 0) - goto out_msg_free; + result = nl_wait_for_ack (sk, NULL); + if (result < 0) + goto out; if (response_data > 0) result = response_data; + else + result = -ENOENT; -out_msg_free: - nlmsg_free (msg); -out_cb_free: - nl_cb_put (cb); out: if (result >= 0) _LOGD (LOGD_WIFI, "genl_ctrl_resolve: resolved \"%s\" as 0x%x", name, result); @@ -143,7 +129,6 @@ out: typedef struct { WifiData parent; struct nl_sock *nl_sock; - struct nl_cb *nl_cb; guint32 *freqs; int id; int num_freqs; @@ -178,19 +163,16 @@ error_handler (struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) static struct nl_msg * _nl80211_alloc_msg (int id, int ifindex, int phy, guint32 cmd, guint32 flags) { - struct nl_msg *msg; + nm_auto_nlmsg struct nl_msg *msg = NULL; msg = nlmsg_alloc (); - if (msg) { - genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0); - NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex); - if (phy != -1) - NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY, phy); - } - return msg; + genlmsg_put (msg, 0, 0, id, 0, flags, cmd, 0); + NLA_PUT_U32 (msg, NL80211_ATTR_IFINDEX, ifindex); + if (phy != -1) + NLA_PUT_U32 (msg, NL80211_ATTR_WIPHY, phy); + return g_steal_pointer (&msg); - nla_put_failure: - nlmsg_free (msg); +nla_put_failure: return NULL; } @@ -203,39 +185,36 @@ nl80211_alloc_msg (WifiDataNl80211 *nl80211, guint32 cmd, guint32 flags) /* NOTE: this function consumes 'msg' */ static int _nl80211_send_and_recv (struct nl_sock *nl_sock, - struct nl_cb *nl_cb, struct nl_msg *msg, int (*valid_handler) (struct nl_msg *, void *), void *valid_data) { - struct nl_cb *cb; - int err, done; + nm_auto_nlmsg struct nl_msg *msg_free = msg; + int err; + int done = 0; + const struct nl_cb cb = { + .err_cb = error_handler, + .err_arg = &done, + .finish_cb = finish_handler, + .finish_arg = &done, + .ack_cb = ack_handler, + .ack_arg = &done, + .valid_cb = valid_handler, + .valid_arg = valid_data, + }; g_return_val_if_fail (msg != NULL, -ENOMEM); - cb = nl_cb_clone (nl_cb); - if (!cb) { - err = -ENOMEM; - goto out; - } - - err = nl_send_auto_complete (nl_sock, msg); + err = nl_send_auto (nl_sock, msg); if (err < 0) - goto out; - - done = 0; - nl_cb_err (cb, NL_CB_CUSTOM, error_handler, &done); - nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &done); - nl_cb_set (cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &done); - if (valid_handler) - nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data); + return err; /* Loop until one of our NL callbacks says we're done; on success * done will be 1, on error it will be < 0. */ while (!done) { - err = nl_recvmsgs (nl_sock, cb); - if (err && err != -NLE_AGAIN) { + err = nl_recvmsgs (nl_sock, &cb); + if (err < 0 && err != -EAGAIN) { /* Kernel scan list can change while we are dumping it, as new scan * results from H/W can arrive. BSS info is assured to be consistent * and we don't need consistent view of whole scan list. Hence do @@ -250,12 +229,9 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock, break; } } - if (err == 0 && done < 0) - err = done; - out: - nl_cb_put (cb); - nlmsg_free (msg); + if (err >= 0 && done < 0) + err = done; return err; } @@ -265,7 +241,7 @@ nl80211_send_and_recv (WifiDataNl80211 *nl80211, int (*valid_handler) (struct nl_msg *, void *), void *valid_data) { - return _nl80211_send_and_recv (nl80211->nl_sock, nl80211->nl_cb, msg, + return _nl80211_send_and_recv (nl80211->nl_sock, msg, valid_handler, valid_data); } @@ -276,8 +252,6 @@ wifi_nl80211_deinit (WifiData *parent) if (nl80211->nl_sock) nl_socket_free (nl80211->nl_sock); - if (nl80211->nl_cb) - nl_cb_put (nl80211->nl_cb); g_free (nl80211->freqs); } @@ -326,7 +300,7 @@ wifi_nl80211_get_mode (WifiData *data) msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_INTERFACE, 0); if (nl80211_send_and_recv (nl80211, msg, nl80211_iface_info_handler, - &iface_info) < 0) + &iface_info) < 0) return NM_802_11_MODE_UNKNOWN; return iface_info.mode; @@ -356,7 +330,7 @@ wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode) } err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); - return err ? FALSE : TRUE; + return err >= 0; nla_put_failure: nlmsg_free (msg); @@ -374,7 +348,7 @@ wifi_nl80211_set_powersave (WifiData *data, guint32 powersave) NLA_PUT_U32 (msg, NL80211_ATTR_PS_STATE, powersave == 1 ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); - return err ? FALSE : TRUE; + return err >= 0; nla_put_failure: nlmsg_free (msg); @@ -702,7 +676,7 @@ wifi_nl80211_indicate_addressing_running (WifiData *data, gboolean running) } err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); - return err ? FALSE : TRUE; + return err >= 0; nla_put_failure: nlmsg_free (msg); @@ -991,10 +965,6 @@ wifi_nl80211_init (int ifindex) if (nl80211->id < 0) goto error; - nl80211->nl_cb = nl_cb_alloc (NL_CB_DEFAULT); - if (nl80211->nl_cb == NULL) - goto error; - nl80211->phy = -1; msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WIPHY, 0);