mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-26 14:40:08 +01:00
Our coding style recommends C style comments (/* */) instead of C++ (//). Also, systemd (which we partly fork) uses C style comments for the SPDX-License-Identifier. Unify the style. $ sed -i '1 s#// SPDX-License-Identifier: \([^ ]\+\)$#/* SPDX-License-Identifier: \1 */#' -- $(git ls-files -- '*.[hc]' '*.[hc]pp')
389 lines
13 KiB
C
389 lines
13 KiB
C
/* SPDX-License-Identifier: GPL-2.0+ */
|
|
/*
|
|
* Copyright (C) 2013 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.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);
|
|
}
|