summary refs log tree commit diff
path: root/src/core/nm-dcb.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2021-02-11 18:11:46 +0100
committerMichael Biebl <biebl@debian.org>2021-02-11 18:11:46 +0100
commit80ec1decc49c72efec2a8b87c06245c92c0ab807 (patch)
treee3b229aa94e8dcf0590f2317664176e7b8f7607b /src/core/nm-dcb.c
parent65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (diff)
New upstream version 1.29.90 upstream/1.29.90
Diffstat (limited to 'src/core/nm-dcb.c')
-rw-r--r--src/core/nm-dcb.c389
1 files changed, 389 insertions, 0 deletions
diff --git a/src/core/nm-dcb.c b/src/core/nm-dcb.c
new file mode 100644
index 00000000..b3958caa
--- /dev/null
+++ b/src/core/nm-dcb.c
@@ -0,0 +1,389 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ */
+
+#include "src/core/nm-default-daemon.h"
+
+#include <sys/wait.h>
+
+#include "nm-dcb.h"
+#include "platform/nm-platform.h"
+#include "NetworkManagerUtils.h"
+
+static const char *helper_names[] = {"dcbtool", "fcoeadm"};
+
+gboolean
+do_helper(const char *iface,
+          guint       which,
+          DcbFunc     run_func,
+          gpointer    user_data,
+          GError **   error,
+          const char *fmt,
+          ...)
+{
+    gs_free const char **split   = NULL;
+    gs_free char *       cmdline = NULL;
+    gs_free const char **argv    = NULL;
+    gsize                i;
+    gsize                u;
+    va_list              args;
+
+    g_return_val_if_fail(fmt != NULL, FALSE);
+
+    va_start(args, fmt);
+    cmdline = g_strdup_vprintf(fmt, args);
+    va_end(args);
+
+    split = nm_utils_strsplit_set_with_empty(cmdline, " ");
+    if (!split) {
+        g_set_error(error,
+                    NM_MANAGER_ERROR,
+                    NM_MANAGER_ERROR_FAILED,
+                    "failure parsing %s command line",
+                    helper_names[which]);
+        return FALSE;
+    }
+
+    /* Allocate space for path, custom arg, interface name, arguments, and NULL */
+    i         = 0;
+    argv      = g_new(const char *, NM_PTRARRAY_LEN(split) + 4);
+    argv[i++] = NULL; /* Placeholder for dcbtool path */
+    if (which == DCBTOOL) {
+        argv[i++] = "sc";
+        argv[i++] = (char *) iface;
+    }
+    for (u = 0; split[u]; u++)
+        argv[i++] = split[u];
+    argv[i++] = NULL;
+
+    if (!run_func((char **) argv, which, user_data, error)) {
+        g_assert(!error || *error);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+gboolean
+_dcb_enable(const char *iface,
+            gboolean    enable,
+            DcbFunc     run_func,
+            gpointer    user_data,
+            GError **   error)
+{
+    if (enable)
+        return do_helper(iface, DCBTOOL, run_func, user_data, error, "dcb on");
+    else
+        return do_helper(iface, DCBTOOL, run_func, user_data, error, "dcb off");
+}
+
+#define SET_FLAGS(f, tag)                                             \
+    G_STMT_START                                                      \
+    {                                                                 \
+        if (!do_helper(iface,                                         \
+                       DCBTOOL,                                       \
+                       run_func,                                      \
+                       user_data,                                     \
+                       error,                                         \
+                       tag " e:%c a:%c w:%c",                         \
+                       f & NM_SETTING_DCB_FLAG_ENABLE ? '1' : '0',    \
+                       f & NM_SETTING_DCB_FLAG_ADVERTISE ? '1' : '0', \
+                       f & NM_SETTING_DCB_FLAG_WILLING ? '1' : '0'))  \
+            return FALSE;                                             \
+    }                                                                 \
+    G_STMT_END
+
+#define SET_APP(f, s, tag)                                     \
+    G_STMT_START                                               \
+    {                                                          \
+        int prio = nm_setting_dcb_get_app_##tag##_priority(s); \
+                                                               \
+        SET_FLAGS(f, "app:" #tag);                             \
+        if ((f & NM_SETTING_DCB_FLAG_ENABLE) && (prio >= 0)) { \
+            if (!do_helper(iface,                              \
+                           DCBTOOL,                            \
+                           run_func,                           \
+                           user_data,                          \
+                           error,                              \
+                           "app:" #tag " appcfg:%02x",         \
+                           (1 << prio)))                       \
+                return FALSE;                                  \
+        }                                                      \
+    }                                                          \
+    G_STMT_END
+
+gboolean
+_dcb_setup(const char *  iface,
+           NMSettingDcb *s_dcb,
+           DcbFunc       run_func,
+           gpointer      user_data,
+           GError **     error)
+{
+    NMSettingDcbFlags flags;
+    guint             i;
+
+    g_assert(s_dcb);
+
+    /* FCoE */
+    flags = nm_setting_dcb_get_app_fcoe_flags(s_dcb);
+    SET_APP(flags, s_dcb, fcoe);
+
+    /* iSCSI */
+    flags = nm_setting_dcb_get_app_iscsi_flags(s_dcb);
+    SET_APP(flags, s_dcb, iscsi);
+
+    /* FIP */
+    flags = nm_setting_dcb_get_app_fip_flags(s_dcb);
+    SET_APP(flags, s_dcb, fip);
+
+    /* Priority Flow Control */
+    flags = nm_setting_dcb_get_priority_flow_control_flags(s_dcb);
+    SET_FLAGS(flags, "pfc");
+    if (flags & NM_SETTING_DCB_FLAG_ENABLE) {
+        char buf[10];
+
+        for (i = 0; i < 8; i++)
+            buf[i] = nm_setting_dcb_get_priority_flow_control(s_dcb, i) ? '1' : '0';
+        buf[i] = 0;
+        if (!do_helper(iface, DCBTOOL, run_func, user_data, error, "pfc pfcup:%s", buf))
+            return FALSE;
+    }
+
+    /* Priority Groups */
+    flags = nm_setting_dcb_get_priority_group_flags(s_dcb);
+    if (flags & NM_SETTING_DCB_FLAG_ENABLE) {
+        GString *s;
+        gboolean success;
+        guint    id;
+
+        s = g_string_sized_new(150);
+
+        g_string_append_printf(s,
+                               "pg e:1 a:%c w:%c",
+                               flags & NM_SETTING_DCB_FLAG_ADVERTISE ? '1' : '0',
+                               flags & NM_SETTING_DCB_FLAG_WILLING ? '1' : '0');
+
+        /* Priority Groups */
+        g_string_append(s, " pgid:");
+        for (i = 0; i < 8; i++) {
+            id = nm_setting_dcb_get_priority_group_id(s_dcb, i);
+            g_assert(id < 8 || id == 15);
+            g_string_append_c(s, (id < 8) ? ('0' + id) : 'f');
+        }
+
+        /* Priority Group Bandwidth */
+        g_string_append_printf(s,
+                               " pgpct:%u,%u,%u,%u,%u,%u,%u,%u",
+                               nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 0),
+                               nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 1),
+                               nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 2),
+                               nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 3),
+                               nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 4),
+                               nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 5),
+                               nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 6),
+                               nm_setting_dcb_get_priority_group_bandwidth(s_dcb, 7));
+
+        /* Priority Bandwidth */
+        g_string_append_printf(s,
+                               " uppct:%u,%u,%u,%u,%u,%u,%u,%u",
+                               nm_setting_dcb_get_priority_bandwidth(s_dcb, 0),
+                               nm_setting_dcb_get_priority_bandwidth(s_dcb, 1),
+                               nm_setting_dcb_get_priority_bandwidth(s_dcb, 2),
+                               nm_setting_dcb_get_priority_bandwidth(s_dcb, 3),
+                               nm_setting_dcb_get_priority_bandwidth(s_dcb, 4),
+                               nm_setting_dcb_get_priority_bandwidth(s_dcb, 5),
+                               nm_setting_dcb_get_priority_bandwidth(s_dcb, 6),
+                               nm_setting_dcb_get_priority_bandwidth(s_dcb, 7));
+
+        /* Strict Bandwidth */
+        g_string_append(s, " strict:");
+        for (i = 0; i < 8; i++)
+            g_string_append_c(s,
+                              nm_setting_dcb_get_priority_strict_bandwidth(s_dcb, i) ? '1' : '0');
+
+        /* Priority Traffic Class */
+        g_string_append(s, " up2tc:");
+        for (i = 0; i < 8; i++) {
+            id = nm_setting_dcb_get_priority_traffic_class(s_dcb, i);
+            g_assert(id < 8);
+            g_string_append_c(s, '0' + id);
+        }
+
+        success = do_helper(iface, DCBTOOL, run_func, user_data, error, "%s", s->str);
+        g_string_free(s, TRUE);
+        if (!success)
+            return FALSE;
+    } else {
+        /* Ignore disable failure since lldpad <= 0.9.46 does not support disabling
+         * priority groups without specifying an entire PG config.
+         */
+        (void) do_helper(iface, DCBTOOL, run_func, user_data, error, "pg e:0");
+    }
+
+    return TRUE;
+}
+
+gboolean
+_dcb_cleanup(const char *iface, DcbFunc run_func, gpointer user_data, GError **error)
+{
+    const char *cmds[] =
+        {"app:fcoe e:0", "app:iscsi e:0", "app:fip e:0", "pfc e:0", "pg e:0", NULL};
+    const char **iter    = cmds;
+    gboolean     success = TRUE;
+
+    /* Turn everything off and return first error we get (if any) */
+    while (iter && *iter) {
+        if (!do_helper(iface, DCBTOOL, run_func, user_data, success ? error : NULL, "%s", *iter))
+            success = FALSE;
+        iter++;
+    }
+
+    if (!_dcb_enable(iface, FALSE, run_func, user_data, success ? error : NULL))
+        success = FALSE;
+
+    return success;
+}
+
+gboolean
+_fcoe_setup(const char *  iface,
+            NMSettingDcb *s_dcb,
+            DcbFunc       run_func,
+            gpointer      user_data,
+            GError **     error)
+{
+    NMSettingDcbFlags flags;
+
+    g_assert(s_dcb);
+
+    flags = nm_setting_dcb_get_app_fcoe_flags(s_dcb);
+    if (flags & NM_SETTING_DCB_FLAG_ENABLE) {
+        const char *mode = nm_setting_dcb_get_app_fcoe_mode(s_dcb);
+
+        if (!do_helper(NULL, FCOEADM, run_func, user_data, error, "-m %s -c %s", mode, iface))
+            return FALSE;
+    } else {
+        if (!do_helper(NULL, FCOEADM, run_func, user_data, error, "-d %s", iface))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+gboolean
+_fcoe_cleanup(const char *iface, DcbFunc run_func, gpointer user_data, GError **error)
+{
+    return do_helper(NULL, FCOEADM, run_func, user_data, error, "-d %s", iface);
+}
+
+static gboolean
+run_helper(char **argv, guint which, gpointer user_data, GError **error)
+{
+    const char *helper_path;
+    int         exit_status = 0;
+    gboolean    success;
+    char *      errmsg = NULL, *outmsg = NULL;
+    char *      cmdline;
+
+    helper_path = nm_utils_find_helper((which == DCBTOOL) ? "dcbtool" : "fcoeadm", NULL, error);
+    if (!helper_path)
+        return FALSE;
+
+    argv[0] = (char *) helper_path;
+    cmdline = g_strjoinv(" ", argv);
+    nm_log_dbg(LOGD_DCB, "%s", cmdline);
+
+    success = g_spawn_sync("/",
+                           argv,
+                           NULL,
+                           0 /*G_SPAWN_DEFAULT*/,
+                           NULL,
+                           NULL,
+                           &outmsg,
+                           &errmsg,
+                           &exit_status,
+                           error);
+    /* Log any stderr output */
+    if (success && WIFEXITED(exit_status) && WEXITSTATUS(exit_status) && (errmsg || outmsg)) {
+        gboolean ignore_error = FALSE;
+
+        /* Ignore fcoeadm "success" errors like when FCoE is already set up */
+        if (errmsg && strstr(errmsg, "Connection already created"))
+            ignore_error = TRUE;
+
+        if (ignore_error == FALSE) {
+            nm_log_warn(LOGD_DCB,
+                        "'%s' failed: '%s'",
+                        cmdline,
+                        (errmsg && strlen(errmsg)) ? errmsg : outmsg);
+            g_set_error(error,
+                        NM_MANAGER_ERROR,
+                        NM_MANAGER_ERROR_FAILED,
+                        "Failed to run '%s'",
+                        cmdline);
+            success = FALSE;
+        }
+    }
+
+    g_free(outmsg);
+    g_free(errmsg);
+    g_free(cmdline);
+    return success;
+}
+
+gboolean
+nm_dcb_enable(const char *iface, gboolean enable, GError **error)
+{
+    return _dcb_enable(iface, enable, run_helper, GUINT_TO_POINTER(DCBTOOL), error);
+}
+
+gboolean
+nm_dcb_setup(const char *iface, NMSettingDcb *s_dcb, GError **error)
+{
+    gboolean success;
+
+    success = _dcb_setup(iface, s_dcb, run_helper, GUINT_TO_POINTER(DCBTOOL), error);
+    if (success)
+        success = _fcoe_setup(iface, s_dcb, run_helper, GUINT_TO_POINTER(FCOEADM), error);
+
+    return success;
+}
+
+static void
+carrier_wait(const char *iface, guint secs, gboolean up)
+{
+    int ifindex, count = secs * 10;
+
+    g_return_if_fail(iface != NULL);
+
+    ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, iface);
+    if (ifindex > 0) {
+        /* To work around driver quirks and lldpad handling of carrier status,
+         * we must wait a short period of time to see if the carrier goes
+         * down, and then wait for the carrier to come back up again.  Otherwise
+         * subsequent lldpad calls may fail with "Device not found, link down
+         * or DCB not enabled" errors.
+         */
+        nm_log_dbg(LOGD_DCB, "(%s): cleanup waiting for carrier %s", iface, up ? "up" : "down");
+        g_usleep(G_USEC_PER_SEC / 4);
+        while (nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex) != up && count-- > 0) {
+            g_usleep(G_USEC_PER_SEC / 10);
+            nm_platform_link_refresh(NM_PLATFORM_GET, ifindex);
+        }
+    }
+}
+
+gboolean
+nm_dcb_cleanup(const char *iface, GError **error)
+{
+    /* Ignore FCoE cleanup errors */
+    _fcoe_cleanup(iface, run_helper, GUINT_TO_POINTER(FCOEADM), NULL);
+
+    /* Must pause a bit to wait for carrier-up since disabling FCoE may
+     * cause the device to take the link down, making lldpad return errors.
+     */
+    carrier_wait(iface, 2, FALSE);
+    carrier_wait(iface, 4, TRUE);
+
+    return _dcb_cleanup(iface, run_helper, GUINT_TO_POINTER(DCBTOOL), error);
+}