1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Copyright (C) 2013 Red Hat, Inc.
21 #include "nm-default.h"
27 #include "nm-platform.h"
28 #include "NetworkManagerUtils.h"
30 static const char *helper_names[] = { "dcbtool", "fcoeadm" };
33 do_helper (const char *iface,
41 char **argv = NULL, **split = NULL, *cmdline, *errmsg = NULL;
42 gboolean success = FALSE;
46 g_return_val_if_fail (fmt != NULL, FALSE);
49 cmdline = g_strdup_vprintf (fmt, args);
52 split = g_strsplit_set (cmdline, " ", 0);
54 g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
55 "failure parsing %s command line", helper_names[which]);
59 /* Allocate space for path, custom arg, interface name, arguments, and NULL */
61 argv = g_new0 (char *, g_strv_length (split) + 4);
62 argv[i++] = NULL; /* Placeholder for dcbtool path */
63 if (which == DCBTOOL) {
65 argv[i++] = (char *) iface;
67 while (u < g_strv_length (split))
68 argv[i++] = split[u++];
70 success = run_func (argv, which, user_data, error);
71 if (!success && error)
84 _dcb_enable (const char *iface,
91 return do_helper (iface, DCBTOOL, run_func, user_data, error, "dcb on");
93 return do_helper (iface, DCBTOOL, run_func, user_data, error, "dcb off");
96 #define SET_FLAGS(f, tag) \
98 if (!do_helper (iface, DCBTOOL, run_func, user_data, error, tag " e:%c a:%c w:%c", \
99 f & NM_SETTING_DCB_FLAG_ENABLE ? '1' : '0', \
100 f & NM_SETTING_DCB_FLAG_ADVERTISE ? '1' : '0', \
101 f & NM_SETTING_DCB_FLAG_WILLING ? '1' : '0')) \
105 #define SET_APP(f, s, tag) \
107 gint prio = nm_setting_dcb_get_app_##tag##_priority (s); \
109 SET_FLAGS (f, "app:" #tag); \
110 if ((f & NM_SETTING_DCB_FLAG_ENABLE) && (prio >= 0)) { \
111 if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "app:" #tag " appcfg:%02x", (1 << prio))) \
117 _dcb_setup (const char *iface,
123 NMSettingDcbFlags flags;
129 flags = nm_setting_dcb_get_app_fcoe_flags (s_dcb);
130 SET_APP (flags, s_dcb, fcoe);
133 flags = nm_setting_dcb_get_app_iscsi_flags (s_dcb);
134 SET_APP (flags, s_dcb, iscsi);
137 flags = nm_setting_dcb_get_app_fip_flags (s_dcb);
138 SET_APP (flags, s_dcb, fip);
140 /* Priority Flow Control */
141 flags = nm_setting_dcb_get_priority_flow_control_flags (s_dcb);
142 SET_FLAGS (flags, "pfc");
143 if (flags & NM_SETTING_DCB_FLAG_ENABLE) {
146 for (i = 0; i < 8; i++)
147 buf[i] = nm_setting_dcb_get_priority_flow_control (s_dcb, i) ? '1' : '0';
149 if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "pfc pfcup:%s", buf))
153 /* Priority Groups */
154 flags = nm_setting_dcb_get_priority_group_flags (s_dcb);
155 if (flags & NM_SETTING_DCB_FLAG_ENABLE) {
160 s = g_string_sized_new (150);
162 g_string_append_printf (s, "pg e:1 a:%c w:%c",
163 flags & NM_SETTING_DCB_FLAG_ADVERTISE ? '1' : '0',
164 flags & NM_SETTING_DCB_FLAG_WILLING ? '1' : '0');
166 /* Priority Groups */
167 g_string_append (s, " pgid:");
168 for (i = 0; i < 8; i++) {
169 id = nm_setting_dcb_get_priority_group_id (s_dcb, i);
170 g_assert (id < 8 || id == 15);
171 g_string_append_c (s, (id < 8) ? ('0' + id) : 'f');
174 /* Priority Group Bandwidth */
175 g_string_append_printf (s, " pgpct:%u,%u,%u,%u,%u,%u,%u,%u",
176 nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 0),
177 nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 1),
178 nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 2),
179 nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 3),
180 nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 4),
181 nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 5),
182 nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 6),
183 nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 7));
185 /* Priority Bandwidth */
186 g_string_append_printf (s, " uppct:%u,%u,%u,%u,%u,%u,%u,%u",
187 nm_setting_dcb_get_priority_bandwidth (s_dcb, 0),
188 nm_setting_dcb_get_priority_bandwidth (s_dcb, 1),
189 nm_setting_dcb_get_priority_bandwidth (s_dcb, 2),
190 nm_setting_dcb_get_priority_bandwidth (s_dcb, 3),
191 nm_setting_dcb_get_priority_bandwidth (s_dcb, 4),
192 nm_setting_dcb_get_priority_bandwidth (s_dcb, 5),
193 nm_setting_dcb_get_priority_bandwidth (s_dcb, 6),
194 nm_setting_dcb_get_priority_bandwidth (s_dcb, 7));
196 /* Strict Bandwidth */
197 g_string_append (s, " strict:");
198 for (i = 0; i < 8; i++)
199 g_string_append_c (s, nm_setting_dcb_get_priority_strict_bandwidth (s_dcb, i) ? '1' : '0');
201 /* Priority Traffic Class */
202 g_string_append (s, " up2tc:");
203 for (i = 0; i < 8; i++) {
204 id = nm_setting_dcb_get_priority_traffic_class (s_dcb, i);
206 g_string_append_c (s, '0' + id);
209 success = do_helper (iface, DCBTOOL, run_func, user_data, error, "%s", s->str);
210 g_string_free (s, TRUE);
214 /* Ignore disable failure since lldpad <= 0.9.46 does not support disabling
215 * priority groups without specifying an entire PG config.
217 (void) do_helper (iface, DCBTOOL, run_func, user_data, error, "pg e:0");
224 _dcb_cleanup (const char *iface,
229 const char *cmds[] = {
237 const char **iter = cmds;
238 gboolean success = TRUE;
240 /* Turn everything off and return first error we get (if any) */
241 while (iter && *iter) {
242 if (!do_helper (iface, DCBTOOL, run_func, user_data, success ? error : NULL, "%s", *iter))
247 if (!_dcb_enable (iface, FALSE, run_func, user_data, success ? error : NULL))
254 _fcoe_setup (const char *iface,
260 NMSettingDcbFlags flags;
264 flags = nm_setting_dcb_get_app_fcoe_flags (s_dcb);
265 if (flags & NM_SETTING_DCB_FLAG_ENABLE) {
266 const char *mode = nm_setting_dcb_get_app_fcoe_mode (s_dcb);
268 if (!do_helper (NULL, FCOEADM, run_func, user_data, error, "-m %s -c %s", mode, iface))
271 if (!do_helper (NULL, FCOEADM, run_func, user_data, error, "-d %s", iface))
279 _fcoe_cleanup (const char *iface,
284 return do_helper (NULL, FCOEADM, run_func, user_data, error, "-d %s", iface);
288 run_helper (char **argv, guint which, gpointer user_data, GError **error)
290 const char *helper_path;
293 char *errmsg = NULL, *outmsg = NULL;
296 helper_path = nm_utils_find_helper ((which == DCBTOOL) ? "dcbtool" : "fcoeadm", NULL, error);
300 argv[0] = (char *) helper_path;
301 cmdline = g_strjoinv (" ", argv);
302 nm_log_dbg (LOGD_DCB, "%s", cmdline);
304 success = g_spawn_sync ("/", argv, NULL, 0 /*G_SPAWN_DEFAULT*/,
306 &outmsg, &errmsg, &exit_status, error);
307 /* Log any stderr output */
308 if (success && WIFEXITED (exit_status) && WEXITSTATUS (exit_status) && (errmsg || outmsg)) {
309 gboolean ignore_error = FALSE;
311 /* Ignore fcoeadm "success" errors like when FCoE is already set up */
312 if (errmsg && strstr (errmsg, "Connection already created"))
315 if (ignore_error == FALSE) {
316 nm_log_warn (LOGD_DCB, "'%s' failed: '%s'",
317 cmdline, (errmsg && strlen (errmsg)) ? errmsg : outmsg);
318 g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
319 "Failed to run '%s'", cmdline);
331 nm_dcb_enable (const char *iface, gboolean enable, GError **error)
333 return _dcb_enable (iface, enable, run_helper, GUINT_TO_POINTER (DCBTOOL), error);
337 nm_dcb_setup (const char *iface, NMSettingDcb *s_dcb, GError **error)
341 success = _dcb_setup (iface, s_dcb, run_helper, GUINT_TO_POINTER (DCBTOOL), error);
343 success = _fcoe_setup (iface, s_dcb, run_helper, GUINT_TO_POINTER (FCOEADM), error);
349 carrier_wait (const char *iface, guint secs, gboolean up)
351 int ifindex, count = secs * 10;
353 g_return_if_fail (iface != NULL);
355 ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, iface);
357 /* To work around driver quirks and lldpad handling of carrier status,
358 * we must wait a short period of time to see if the carrier goes
359 * down, and then wait for the carrier to come back up again. Otherwise
360 * subsequent lldpad calls may fail with "Device not found, link down
361 * or DCB not enabled" errors.
363 nm_log_dbg (LOGD_DCB, "(%s): cleanup waiting for carrier %s",
364 iface, up ? "up" : "down");
365 g_usleep (G_USEC_PER_SEC / 4);
366 while (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex) != up && count-- > 0) {
367 g_usleep (G_USEC_PER_SEC / 10);
368 nm_platform_link_refresh (NM_PLATFORM_GET, ifindex);
374 nm_dcb_cleanup (const char *iface, GError **error)
376 /* Ignore FCoE cleanup errors */
377 _fcoe_cleanup (iface, run_helper, GUINT_TO_POINTER (FCOEADM), NULL);
379 /* Must pause a bit to wait for carrier-up since disabling FCoE may
380 * cause the device to take the link down, making lldpad return errors.
382 carrier_wait (iface, 2, FALSE);
383 carrier_wait (iface, 4, TRUE);
385 return _dcb_cleanup (iface, run_helper, GUINT_TO_POINTER (DCBTOOL), error);