[systemd-devel] [PATCH] Added Uplink failure detection feature to networkd
Alin Rauta
alin.rauta at intel.com
Fri Jan 23 09:20:30 PST 2015
---
Makefile.am | 4 +
man/systemd.netdev.xml | 72 +-
src/libsystemd/sd-network/sd-network.c | 117 +++
src/network/networkctl.c | 153 ++++
src/network/networkd-link.c | 35 +
src/network/networkd-manager.c | 36 +
src/network/networkd-netdev-gperf.gperf | 3 +
src/network/networkd-netdev-ufd-group.c | 298 +++++++
src/network/networkd-netdev-ufd-group.h | 85 ++
src/network/networkd-netdev.c | 36 +
src/network/networkd-netdev.h | 6 +
src/network/networkd-ufd-daemon.c | 1321 +++++++++++++++++++++++++++++++
src/network/networkd-ufd-daemon.h | 34 +
src/network/networkd.c | 7 +
src/network/networkd.h | 6 +
src/systemd/sd-network.h | 20 +
16 files changed, 2231 insertions(+), 2 deletions(-)
create mode 100644 src/network/networkd-netdev-ufd-group.c
create mode 100644 src/network/networkd-netdev-ufd-group.h
create mode 100644 src/network/networkd-ufd-daemon.c
create mode 100644 src/network/networkd-ufd-daemon.h
diff --git a/Makefile.am b/Makefile.am
index 45d7a34..604173b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5575,6 +5575,8 @@ libsystemd_networkd_core_la_SOURCES = \
src/network/networkd-netdev-tuntap.h \
src/network/networkd-netdev-bond.h \
src/network/networkd-netdev-bridge.h \
+ src/network/networkd-netdev-ufd-group.h \
+ src/network/networkd-ufd-daemon.h \
src/network/networkd-netdev.c \
src/network/networkd-netdev-tunnel.c \
src/network/networkd-netdev-veth.c \
@@ -5586,6 +5588,8 @@ libsystemd_networkd_core_la_SOURCES = \
src/network/networkd-netdev-tuntap.c \
src/network/networkd-netdev-bond.c \
src/network/networkd-netdev-bridge.c \
+ src/network/networkd-netdev-ufd-group.c \
+ src/network/networkd-ufd-daemon.c \
src/network/networkd-link.c \
src/network/networkd-ipv4ll.c \
src/network/networkd-dhcp4.c \
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 7edec36..3c60441 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -168,8 +168,8 @@
<literal>ipip</literal>, <literal>gre</literal>,
<literal>gretap</literal>, <literal>sit</literal>,
<literal>vti</literal>, <literal>veth</literal>,
- <literal>tun</literal>, <literal>tap</literal> and
- <literal>dummy</literal>
+ <literal>tun</literal>, <literal>tap</literal>,
+ <literal>ufd</literal> and <literal>dummy</literal>
are supported. This option is compulsory.</para>
</listitem>
</varlistentry>
@@ -553,6 +553,52 @@
</refsect1>
<refsect1>
+ <title>[UFDGroup] Section Options</title>
+
+ <para>The <literal>[UFDGroup]</literal> section is used to define uplink failure detection group parameters.
+ The section only applies for netdevs of kind <literal>ufd</literal>, and accepts the following key:</para>
+
+ <variablelist class='network-directives'>
+
+ <varlistentry>
+ <term><varname>Id=</varname></term>
+ <listitem>
+ <para>Uplink failure detection group Id. This option is compulsory.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[UFDLink] Section Options</title>
+
+ <para>The <literal>[UFDLink]</literal> section is used to define one or more uplink failure detection links.
+ The section only applies for netdevs of kind <literal>ufd</literal>, and accepts the following key:</para>
+
+ <variablelist class='network-directives'>
+
+ <varlistentry>
+ <term><varname>Name=</varname></term>
+ <listitem>
+ <para>An interface name or an enumeration of interface names separated by comma.
+ This option is compulsory.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Type=</varname></term>
+ <listitem>
+ <para>A string defining the link(s) type.
+ It can only take the following string values: <literal>uplink</literal>
+ or <literal>downlink</literal>. This option is compulsory.</para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
<title>Example</title>
<example>
<title>/etc/systemd/network/bridge.netdev</title>
@@ -645,6 +691,28 @@ Name=veth-peer</programlisting>
</example>
<example>
+ <title>/etc/systemd/network/ufd.netdev</title>
+ <programlisting>[NetDev]
+Name=group1
+Kind=ufd
+
+[UFDGroup]
+Id=45
+
+[UFDLink]
+Name=sw0p5,sw0p10
+Type=uplink
+
+[UFDLink]
+Name=sw0p1
+Type=uplink
+
+[UFDLink]
+Name=sw0p2
+Type=downlink</programlisting>
+ </example>
+
+ <example>
<title>/etc/systemd/network/dummy.netdev</title>
<programlisting>[NetDev]
Name=dummy-test
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index c735cac..d765c97 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -368,3 +368,120 @@ _public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *tim
*timeout_usec = (uint64_t) -1;
return 0;
}
+
+_public_ int sd_network_ufd_get_setup_state(int group_id, char **state) {
+ _cleanup_free_ char *s = NULL, *p = NULL;
+ int r;
+
+ assert_return(state, -EINVAL);
+
+ if (asprintf(&p, "/run/systemd/netif/ufd/groups/%d", group_id) < 0)
+ return -ENOMEM;
+
+ r = parse_env_file(p, NEWLINE, "GROUP_STATE", &s, NULL);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+ if (isempty(s))
+ return -ENODATA;
+
+ *state = s;
+ s = NULL;
+
+ return 0;
+}
+
+_public_ int sd_network_ufd_get_config_file(int group_id, char **filename) {
+ _cleanup_free_ char *s = NULL, *p = NULL;
+ int r;
+
+ assert_return(filename, -EINVAL);
+
+ if (asprintf(&p, "/run/systemd/netif/ufd/groups/%d", group_id) < 0)
+ return -ENOMEM;
+
+ r = parse_env_file(p, NEWLINE, "CONFIG_FILE", &s, NULL);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+ if (isempty(s))
+ return -ENODATA;
+
+ *filename = s;
+ s = NULL;
+
+ return 0;
+}
+
+static int network_get_ufd_strv(const char *key, int group_id, char ***ret) {
+ _cleanup_free_ char *p = NULL, *s = NULL;
+ _cleanup_strv_free_ char **a = NULL;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ if (asprintf(&p, "/run/systemd/netif/ufd/groups/%d", group_id) < 0)
+ return -ENOMEM;
+
+ r = parse_env_file(p, NEWLINE, key, &s, NULL);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+ if (isempty(s)) {
+ *ret = NULL;
+ return 0;
+ }
+
+ a = strv_split(s, " ");
+ if (!a)
+ return -ENOMEM;
+
+ strv_uniq(a);
+ r = strv_length(a);
+
+ *ret = a;
+ a = NULL;
+
+ return r;
+}
+
+_public_ int sd_network_ufd_get_uplinks(int group_id, char ***ret) {
+ return network_get_ufd_strv("UPLINKS", group_id, ret);
+}
+
+_public_ int sd_network_ufd_get_downlinks(int group_id, char ***ret) {
+ return network_get_ufd_strv("DOWNLINKS", group_id, ret);
+}
+
+_public_ int sd_network_ufd_get_group_list(char ***ret) {
+ _cleanup_free_ char *p = NULL, *s = NULL;
+ _cleanup_strv_free_ char **a = NULL;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = parse_env_file("/run/systemd/netif/ufd/state", NEWLINE, "GROUP_LIST", &s, NULL);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+ if (isempty(s)) {
+ *ret = NULL;
+ return 0;
+ }
+
+ a = strv_split(s, " ");
+ if (!a)
+ return -ENOMEM;
+
+ strv_uniq(a);
+ r = strv_length(a);
+
+ *ret = a;
+ a = NULL;
+
+ return 0;
+}
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index aa83f32..89633b4 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -1008,6 +1008,157 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
return 0;
}
+static int ufd_show_one(int group_id) {
+ _cleanup_free_ char *setup_state = NULL;
+ _cleanup_free_ char *config_file = NULL;
+ _cleanup_strv_free_ char **up_links = NULL;
+ _cleanup_strv_free_ char **down_links = NULL;
+ const char *green;
+ const char *yellow;
+ const char *off_color;
+ int r;
+
+ green = ansi_highlight_green();
+ yellow = ansi_highlight_yellow();
+ off_color = ansi_highlight_off();
+
+ r = sd_network_ufd_get_setup_state(group_id, &setup_state);
+ if (r < 0) {
+ log_error("UFD group: %d not found or problems reading UFD files", group_id);
+ return r;
+ }
+
+ sd_network_ufd_get_config_file(group_id, &config_file);
+ sd_network_ufd_get_uplinks(group_id, &up_links);
+ sd_network_ufd_get_downlinks(group_id, &down_links);
+
+ printf("%s%s%s %s: %d\n",
+ green, draw_special_char(DRAW_BLACK_CIRCLE), off_color, "UFD Group", group_id);
+
+ printf("Config File: %s\n"
+ " State: %s%s%s\n",
+ strna(config_file),
+ green, strna(setup_state), off_color);
+
+ printf(" Uplinks:\n");
+
+ if (!strv_isempty(up_links)) {
+ char **i;
+
+ STRV_FOREACH(i, up_links) {
+ char ifname[IF_NAMESIZE+1] = "";
+ int ifindex = atoi(*i);
+
+ if (ifindex > 0)
+ printf(" %s%s%s %d: ",
+ yellow, draw_special_char(DRAW_ARROW), off_color, ifindex);
+
+ if (if_indextoname(ifindex, ifname))
+ printf("%s", ifname);
+
+ printf("\n");
+ }
+ }
+ else
+ printf(" List is empty\n");
+
+ printf(" Downlinks:\n");
+
+ if (!strv_isempty(down_links)) {
+ char **i;
+
+ STRV_FOREACH(i, down_links) {
+ char ifname[IF_NAMESIZE+1] = "";
+ int ifindex = atoi(*i);
+
+ if (ifindex > 0)
+ printf(" %s%s%s %d: ",
+ yellow, draw_special_char(DRAW_ARROW), off_color, ifindex);
+
+ if (if_indextoname(ifindex, ifname))
+ printf("%s", ifname);
+
+ printf("\n");
+ }
+ }
+ else
+ printf(" List is empty\n");
+
+ return 0;
+}
+
+static int ufd_show(int argc, char *argv[], void *userdata) {
+ char **in_arg;
+ int r;
+
+ if (argc <= 1 && !arg_all) {
+ _cleanup_strv_free_ char **group_list = NULL;
+
+ sd_network_ufd_get_group_list(&group_list);
+
+ if (!strv_isempty(group_list)) {
+ char **i;
+
+ STRV_FOREACH(i, group_list) {
+ const char *on_color = ansi_highlight_green();
+ const char *off_color = ansi_highlight_off();
+
+ printf("%s%s%s %s: %s\n",
+ on_color, draw_special_char(DRAW_BLACK_CIRCLE), off_color, "UFD Group", *i);
+ }
+ }
+ else
+ printf("UFD Group list is empty\n");
+ }
+
+ if (arg_all) {
+ _cleanup_strv_free_ char **group_list = NULL;
+
+ sd_network_ufd_get_group_list(&group_list);
+
+ if (!strv_isempty(group_list)) {
+ char **i;
+ bool first = true;
+
+ STRV_FOREACH(i, group_list) {
+ int group_id;
+
+ group_id = atoi(*i);
+
+ if (!first)
+ fputc('\n', stdout);
+
+ first = false;
+
+ r = ufd_show_one(group_id);
+ if (r < 0) {
+ log_error("UFD: Failed to print info");
+ return r;
+ }
+ }
+ }
+ else
+ printf("UFD Group list is empty\n");
+ }
+
+ STRV_FOREACH(in_arg, argv + 1) {
+ int group_id;
+
+ if (in_arg != argv + 1)
+ fputc('\n', stdout);
+
+ group_id = atoi(*in_arg);
+
+ r = ufd_show_one(group_id);
+ if (r < 0) {
+ log_error("UFD: Failed to print info");
+ return r;
+ }
+ }
+
+ return 0;
+}
+
static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Query and control the networking subsystem.\n\n"
@@ -1020,6 +1171,7 @@ static void help(void) {
" list List links\n"
" status [LINK...] Show link status\n"
" lldp Show lldp information\n"
+ " ufd [GROUP_ID...] Show Uplink failure detection groups\n"
, program_invocation_short_name);
}
@@ -1086,6 +1238,7 @@ static int networkctl_main(int argc, char *argv[]) {
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_links },
{ "status", 1, VERB_ANY, 0, link_status },
{ "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status },
+ { "ufd", 1, VERB_ANY, 0, ufd_show },
{}
};
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 310eb6c..9ea9ea8 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -575,6 +575,18 @@ static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
if (link->link_messages == 0) {
log_link_debug(link, "routes set");
link->static_configured = true;
+
+ /* this link is now static configured,
+ so decrease the number of links that needs to be configured. */
+ link->manager->links_to_configure --;
+
+ /* check if all links are configured. If yes, start configuring the groups. */
+ if ((0 == link->manager->links_to_configure) && (false == link->manager->groups_configured)) {
+ r = manager_enumerate_groups(link->manager);
+ if(r < 0)
+ log_error("Could not enumerate groups error: %s", strerror(-r));
+ }
+
link_client_handler(link);
}
@@ -606,6 +618,18 @@ static int link_enter_set_routes(Link *link) {
if (link->link_messages == 0) {
link->static_configured = true;
+
+ /* this link is now static configured,
+ so decrease the number of links that needs to be configured. */
+ link->manager->links_to_configure --;
+
+ /* check if all links are configured. If yes, start configuring the groups. */
+ if ((0 == link->manager->links_to_configure) && (false == link->manager->groups_configured)) {
+ r = manager_enumerate_groups(link->manager);
+ if(r < 0)
+ log_error("Could not enumerate groups error: %s", strerror(-r));
+ }
+
link_client_handler(link);
} else
log_link_debug(link, "setting routes");
@@ -1377,6 +1401,17 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m,
&link->mac, &network);
if (r == -ENOENT) {
link_enter_unmanaged(link);
+
+ /* this link is unmanaged,
+ so decrease the number of links that needs to be configured. */
+ link->manager->links_to_configure --;
+
+ /* check if all links are configured. If yes, start configuring the groups. */
+ if ((0 == link->manager->links_to_configure) && (false == link->manager->groups_configured)) {
+ r = manager_enumerate_groups(link->manager);
+ if(r < 0)
+ log_error("Could not enumerate groups error: %s", strerror(-r));
+ }
return 1;
} else if (r < 0)
return r;
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 4c90434..8423adb 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -83,6 +83,9 @@ int manager_new(Manager **ret) {
if (!m)
return -ENOMEM;
+ m->links_to_configure = 0;
+ m->groups_configured = false;
+
m->state_file = strdup("/run/systemd/netif/state");
if (!m->state_file)
return -ENOMEM;
@@ -126,6 +129,10 @@ int manager_new(Manager **ret) {
if (!m->netdevs)
return -ENOMEM;
+ m->group_netdevs = hashmap_new(&string_hash_ops);
+ if (!m->netdevs)
+ return -ENOMEM;
+
LIST_HEAD_INIT(m->networks);
r = setup_default_address_pool(m);
@@ -143,6 +150,7 @@ void manager_free(Manager *m) {
NetDev *netdev;
Link *link;
AddressPool *pool;
+ Iterator i;
if (!m)
return;
@@ -162,6 +170,15 @@ void manager_free(Manager *m) {
while ((network = m->networks))
network_free(network);
+ /* Close groups. */
+ netdev_clear_groups();
+
+ HASHMAP_FOREACH(netdev, m->group_netdevs, i) {
+ hashmap_remove(m->group_netdevs, netdev->ifname);
+ netdev_unref(netdev);
+ }
+ hashmap_free(m->group_netdevs);
+
while ((netdev = hashmap_first(m->netdevs)))
netdev_unref(netdev);
hashmap_free(m->netdevs);
@@ -306,6 +323,23 @@ static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, vo
return 1;
}
+int manager_enumerate_groups(Manager *m) {
+ NetDev *netdev;
+ Iterator i;
+ int r;
+ int k = 0;
+
+ m->groups_configured = true;
+
+ HASHMAP_FOREACH(netdev, m->group_netdevs, i) {
+ r = netdev_create_group(netdev);
+ if (r < 0)
+ k = r;
+ }
+
+ return k;
+}
+
int manager_rtnl_enumerate_links(Manager *m) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
sd_rtnl_message *link;
@@ -329,6 +363,8 @@ int manager_rtnl_enumerate_links(Manager *m) {
for (link = reply; link; link = sd_rtnl_message_next(link)) {
int k;
+ m->links_to_configure ++;
+
k = manager_rtnl_process_link(m->rtnl, link, m);
if (k < 0)
r = k;
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index 963c47c..27d3d48 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -64,3 +64,6 @@ Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0,
Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
+UFDGroup.Id, config_parse_int, 0, offsetof(UfdGroup, id)
+UFDLink.Name, config_parse_ufd_link_name, 0, 0
+UFDLink.Type, config_parse_ufd_link_type, 0, 0
diff --git a/src/network/networkd-netdev-ufd-group.c b/src/network/networkd-netdev-ufd-group.c
new file mode 100644
index 0000000..9571c3d
--- /dev/null
+++ b/src/network/networkd-netdev-ufd-group.c
@@ -0,0 +1,298 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "missing.h"
+#include "conf-parser.h"
+#include "network-internal.h"
+#include "networkd-ufd-daemon.h"
+#include "networkd-netdev-ufd-group.h"
+
+static const char* const ufd_type_table[_UFD_TYPE_MAX] = {
+ [UFD_TYPE_UP_LINK] = "uplink",
+ [UFD_TYPE_DOWN_LINK] = "downlink",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ufd_type, UfdType);
+
+/* clears previous UFD configuration. */
+void ufd_groups_clear(void) {
+ return ufd_daemon_close();
+}
+
+static void ufd_group_init(NetDev *n) {
+ UfdGroup *ufd = UFDGROUP(n);
+
+ assert(n);
+ assert(ufd);
+
+ /* init the list of links and the hashmap. */
+ LIST_HEAD_INIT(ufd->links);
+
+ ufd->ufd_links_by_section = hashmap_new(NULL);
+ if (!ufd->ufd_links_by_section)
+ log_error("UFD: Failed to create hashmap for UFD groups");
+}
+
+static int add_link_to_list(const char *const link_name,
+ char **ret) {
+ size_t len;
+ char *links = *ret;
+
+ assert(link_name);
+
+ len = strlen(link_name);
+
+ if (!links) {
+ links = malloc(len + 1);
+ if (!links)
+ return -ENOMEM;
+
+ strncpy(links, link_name, len + 1);
+ }
+ else {
+ links = realloc(links, strlen(links) + len + 2);
+ if (!links)
+ return -ENOMEM;
+
+ strncat(links, ",", 1);
+ strncat(links, link_name, len);
+ }
+
+ *ret = links;
+
+ return 0;
+}
+
+static int netdev_create_ufd_group(NetDev *netdev) {
+ UfdGroup *ufd_group = UFDGROUP(netdev);
+ UfdLink *ufd_link;
+ char *up_links = NULL;
+ char *down_links = NULL;
+ int r;
+
+ assert(netdev);
+ assert(ufd_group);
+
+ r = ufd_daemon_init();
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(links, ufd_link, ufd_group->links) {
+ switch (ufd_link->type) {
+ case UFD_TYPE_UP_LINK:
+ r = add_link_to_list(ufd_link->name, &up_links);
+ if (r < 0) {
+ free(up_links);
+ free(down_links);
+ return r;
+ }
+
+ break;
+
+ case UFD_TYPE_DOWN_LINK:
+ r = add_link_to_list(ufd_link->name, &down_links);
+ if (r < 0) {
+ free(up_links);
+ free(down_links);
+ return r;
+ }
+
+ break;
+
+ default:
+ assert_not_reached("UFD: Received invalid UFD link type.");
+ }
+ }
+
+ r = ufd_daemon_set_group(ufd_group->id, netdev->filename, up_links, down_links);
+ if (r < 0)
+ log_error("UFD: Could not set Uplink failure detection group Id: %d error: %s",
+ ufd_group->id, strerror(-r));
+ else
+ log_debug("UFD: Created uplink failure detection group Id: %d", ufd_group->id);
+
+ free(up_links);
+ free(down_links);
+
+ return r;
+}
+
+static int ufd_link_new_static(UfdGroup *const ufd_group,
+ const unsigned section,
+ UfdLink **ret) {
+ _cleanup_ufdlink_free_ UfdLink *ufd_link = NULL;
+
+ assert(ufd_group);
+
+ /* check if the hashmap exists. */
+ if (!ufd_group->ufd_links_by_section)
+ return -ENOMEM;
+
+ /* search entry in hashmap first. */
+ if (section) {
+ ufd_link = hashmap_get(ufd_group->ufd_links_by_section, UINT_TO_PTR(section));
+ if (ufd_link) {
+ *ret = ufd_link;
+ ufd_link = NULL;
+
+ return 0;
+ }
+ }
+
+ ufd_link = new0(UfdLink, 1);
+ if (!ufd_link)
+ return -ENOMEM;
+
+ ufd_link->group = ufd_group;
+
+ LIST_PREPEND(links, ufd_group->links, ufd_link);
+
+ if (section) {
+ ufd_link->section = section;
+ hashmap_put(ufd_group->ufd_links_by_section,
+ UINT_TO_PTR(ufd_link->section), ufd_link);
+ }
+
+ *ret = ufd_link;
+ ufd_link = NULL;
+
+ return 0;
+}
+
+
+void ufd_link_free(UfdLink *ufd_link) {
+ if (!ufd_link)
+ return;
+
+ if (ufd_link->group) {
+ LIST_REMOVE(links, ufd_link->group->links, ufd_link);
+
+ if (ufd_link->section)
+ hashmap_remove(ufd_link->group->ufd_links_by_section,
+ UINT_TO_PTR(ufd_link->section));
+ }
+
+ free(ufd_link->name);
+
+ free(ufd_link);
+}
+
+static void ufd_group_done(NetDev *n) {
+ UfdGroup *ufd = UFDGROUP(n);
+ UfdLink *ufd_link;
+
+ assert(n);
+ assert(ufd);
+
+ while ((ufd_link = ufd->links))
+ ufd_link_free(ufd_link);
+
+ hashmap_free(ufd->ufd_links_by_section);
+}
+
+int config_parse_ufd_link_name(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ UfdGroup *ufd_group = userdata;
+ _cleanup_ufdlink_free_ UfdLink *ufd_link = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = ufd_link_new_static(ufd_group, section_line, &ufd_link);
+ if (r < 0) {
+ log_error("UFD: Failed to allocate a new ufd_link: %s", strerror(-r));
+ return r;
+ }
+
+ r = config_parse_string(unit, filename, line, section,
+ section_line, lvalue, ltype,
+ rvalue, &ufd_link->name, userdata);
+ if (r < 0) {
+ log_error("UFD: Failed to parse ufd_link name: %s", strerror(-r));
+ return r;
+ }
+
+ ufd_link = NULL;
+
+ return 0;
+}
+
+int config_parse_ufd_link_type(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ UfdGroup *ufd_group = userdata;
+ UfdType type;
+ _cleanup_ufdlink_free_ UfdLink *ufd_link = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = ufd_link_new_static(ufd_group, section_line, &ufd_link);
+ if (r < 0) {
+ log_error("UFD: Failed to allocate a new ufd_link: %s", strerror(-r));
+ return r;
+ }
+
+ if ((type = ufd_type_from_string(rvalue)) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -type,
+ "UFD: Failed to parse Ufd type, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ ufd_link->type = type;
+
+ ufd_link = NULL;
+
+ return 0;
+}
+
+const NetDevVTable ufd_group_vtable = {
+ .object_size = sizeof(UfdGroup),
+ .init = ufd_group_init,
+ .sections = "Match\0NetDev\0UFDGroup\0UFDLink\0",
+ .done = ufd_group_done,
+ .create_type = NETDEV_CREATE_GROUP,
+ .create = netdev_create_ufd_group,
+};
diff --git a/src/network/networkd-netdev-ufd-group.h b/src/network/networkd-netdev-ufd-group.h
new file mode 100644
index 0000000..f792142
--- /dev/null
+++ b/src/network/networkd-netdev-ufd-group.h
@@ -0,0 +1,85 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+typedef struct UfdGroup UfdGroup;
+typedef struct UfdLink UfdLink;
+
+#include "networkd-netdev.h"
+
+typedef enum UfdType {
+ UFD_TYPE_UP_LINK,
+ UFD_TYPE_DOWN_LINK,
+ _UFD_TYPE_MAX,
+ _UFD_TYPE_INVALID = -1
+}UfdType;
+
+struct UfdLink {
+ char *name;
+ UfdGroup *group;
+
+ unsigned section;
+ UfdType type;
+ LIST_FIELDS(UfdLink, links);
+};
+
+struct UfdGroup {
+ NetDev meta;
+
+ int id;
+
+ LIST_HEAD(UfdLink, links);
+ Hashmap *ufd_links_by_section;
+};
+
+void ufd_link_free(UfdLink *ufd_link);
+void ufd_groups_clear(void);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(UfdLink*, ufd_link_free);
+#define _cleanup_ufdlink_free_ _cleanup_(ufd_link_freep)
+
+extern const NetDevVTable ufd_group_vtable;
+
+const char *ufd_type_to_string(UfdType d) _const_;
+UfdType ufd_type_from_string(const char *d) _pure_;
+
+int config_parse_ufd_link_name(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata);
+
+int config_parse_ufd_link_type(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata);
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index 8119205..698331c 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -49,6 +49,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_TUN] = &tun_vtable,
[NETDEV_KIND_TAP] = &tap_vtable,
[NETDEV_KIND_IP6TNL] = &ip6tnl_vtable,
+ [NETDEV_KIND_UFDGROUP] = &ufd_group_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
@@ -70,6 +71,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_TUN] = "tun",
[NETDEV_KIND_TAP] = "tap",
[NETDEV_KIND_IP6TNL] = "ip6tnl",
+ [NETDEV_KIND_UFDGROUP] = "ufd",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
@@ -592,6 +594,29 @@ static int netdev_create(NetDev *netdev, Link *link,
return 0;
}
+void netdev_clear_groups(void) {
+ /* for now only Uplink failure detection groups are defined. */
+ return ufd_groups_clear();
+}
+
+int netdev_create_group(NetDev *netdev) {
+ int r;
+ assert(netdev);
+
+ switch (NETDEV_VTABLE(netdev)->create_type) {
+ case NETDEV_CREATE_GROUP:
+ r = NETDEV_VTABLE(netdev)->create(netdev);
+ if (r < 0)
+ return r;
+ break;
+ default:
+ assert_not_reached("Can not create group netdev");
+
+ }
+
+ return 0;
+}
+
/* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
int netdev_join(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
int r;
@@ -733,6 +758,17 @@ static int netdev_load_one(Manager *manager, const char *filename) {
return 0;
break;
+
+ case NETDEV_CREATE_GROUP:
+ r = hashmap_put(manager->group_netdevs, netdev->ifname, netdev);
+ if (r < 0) {
+ log_error("Can not add Group '%s' to manager: %s",
+ netdev->ifname, strerror(-r));
+ return 0;
+ }
+
+ break;
+
default:
break;
}
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index 3756b1e..3674b59 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -57,6 +57,7 @@ typedef enum NetDevKind {
NETDEV_KIND_DUMMY,
NETDEV_KIND_TUN,
NETDEV_KIND_TAP,
+ NETDEV_KIND_UFDGROUP,
_NETDEV_KIND_MAX,
_NETDEV_KIND_INVALID = -1
} NetDevKind;
@@ -74,6 +75,7 @@ typedef enum NetDevCreateType {
NETDEV_CREATE_INDEPENDENT,
NETDEV_CREATE_MASTER,
NETDEV_CREATE_STACKED,
+ NETDEV_CREATE_GROUP,
_NETDEV_CREATE_MAX,
_NETDEV_CREATE_INVALID = -1,
} NetDevCreateType;
@@ -111,6 +113,7 @@ struct NetDev {
#include "networkd-netdev-tunnel.h"
#include "networkd-netdev-dummy.h"
#include "networkd-netdev-tuntap.h"
+#include "networkd-netdev-ufd-group.h"
struct NetDevVTable {
/* How much memory does an object of this unit type need */
@@ -177,6 +180,7 @@ DEFINE_CAST(VETH, Veth);
DEFINE_CAST(DUMMY, Dummy);
DEFINE_CAST(TUN, TunTap);
DEFINE_CAST(TAP, TunTap);
+DEFINE_CAST(UFDGROUP, UfdGroup);
int netdev_load(Manager *manager);
void netdev_drop(NetDev *netdev);
@@ -192,6 +196,8 @@ int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *newlink);
int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback);
int netdev_get_mac(const char *ifname, struct ether_addr **ret);
int netdev_join(NetDev *netdev, Link *link, sd_rtnl_message_handler_t cb);
+int netdev_create_group(NetDev *netdev);
+void netdev_clear_groups(void);
const char *netdev_kind_to_string(NetDevKind d) _const_;
NetDevKind netdev_kind_from_string(const char *d) _pure_;
diff --git a/src/network/networkd-ufd-daemon.c b/src/network/networkd-ufd-daemon.c
new file mode 100644
index 0000000..106465f
--- /dev/null
+++ b/src/network/networkd-ufd-daemon.c
@@ -0,0 +1,1321 @@
+
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pthread.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <linux/genetlink.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <net/if.h>
+
+#include "sd-network.h"
+#include "sd-rtnl.h"
+#include "rtnl-util.h"
+#include "util.h"
+#include "ctype.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "networkd-ufd-daemon.h"
+
+#define UFD_UPLINK 0x01
+#define UFD_DOWNLINK 0x02
+
+#define UFD_RCV_PORT_EVENT 0x00
+#define UFD_IGNORE_PORT_DOWN_EVENT 0x01
+#define UFD_IGNORE_PORT_UP_EVENT 0x02
+
+typedef struct UfdDaemon UfdDaemon;
+typedef struct UfddPortInfo UfddPortInfo;
+typedef struct UfddGroup UfddGroup;
+typedef struct UfddPort UfddPort;
+
+typedef enum PortState {
+ UFD_PORT_ADMIN_UP = 1,
+ UFD_PORT_ADMIN_DOWN,
+ UFD_PORT_OPER_UP,
+ UFD_PORT_OPER_DOWN,
+ _UFD_PORT_STATE_MAX,
+ _UFD_PORT_STATE_INVALID = -1
+} PortState;
+
+/* struct to hold uplink and downlink info */
+struct UfddPort {
+ unsigned int if_index;
+ unsigned char link_type;
+ unsigned char rcv_port_event;
+ PortState admin_state;
+ PortState ufd_state;
+ UfddPort *next_port;
+};
+
+/* struct to hold group info */
+struct UfddGroup {
+ char *state_file;
+ char *config_file;
+ unsigned int grp_id;
+ unsigned int act_uplink_count;
+ UfddPort *up_link_ports;
+ UfddPort *down_link_ports;
+ UfddGroup *next_group;
+};
+
+/* struct to hold port info */
+struct UfddPortInfo {
+ unsigned int if_idx;
+ UfddGroup *grp_info;
+ UfddPort *if_idx_info;
+ UfddPortInfo *next_if_idx;
+};
+
+struct UfdDaemon {
+ char *state_file;
+ sd_rtnl *rtnl;
+ sd_event *event;
+ UfddPortInfo *port_info;
+ UfddGroup *group_info;
+};
+
+/* Global UFD structure. */
+static UfdDaemon g_ufdd;
+
+static int ufd_port_get_state(int ifindex,
+ PortState *admin_state,
+ PortState *oper_state) {
+ _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
+ sd_rtnl *rtnl = g_ufdd.rtnl;
+ sd_rtnl_message *link;
+ int r;
+
+ assert(admin_state);
+ assert(oper_state);
+ assert(rtnl);
+
+ r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_message_request_dump(req, true);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_call(rtnl, req, 0, &reply);
+ if (r < 0)
+ return r;
+
+ for (link = reply; link; link = sd_rtnl_message_next(link)) {
+ int link_ifindex;
+
+ r = sd_rtnl_message_link_get_ifindex(link, &link_ifindex);
+ if (r < 0 || ifindex <= 0) {
+ log_warning("UFD rtnl: received link message without valid ifindex");
+ continue;
+ }
+
+ if (link_ifindex == ifindex) {
+ unsigned int flags;
+ r = sd_rtnl_message_link_get_flags(link, &flags);
+ if (r < 0) {
+ log_error("UFD: Could not get link flags");
+ return r;
+ }
+
+ if ((flags & IFF_UP) == IFF_UP)
+ *admin_state = UFD_PORT_ADMIN_UP;
+ else
+ *admin_state = UFD_PORT_ADMIN_DOWN;
+
+ if ((flags & IFF_RUNNING) == IFF_RUNNING)
+ *oper_state = UFD_PORT_OPER_UP;
+ else
+ *oper_state = UFD_PORT_OPER_DOWN;
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int port_set_handler(sd_rtnl *rtnl,
+ sd_rtnl_message *m,
+ void *userdata) {
+ int r;
+
+ r = sd_rtnl_message_get_errno(m);
+ if (r < 0)
+ log_error("UFD: Failed to set port state, error: %s", strerror(-r));
+
+ return 1;
+}
+
+static int ufd_port_set_flags(unsigned int ifindex,
+ unsigned flags) {
+ _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
+ sd_rtnl *rtnl = g_ufdd.rtnl;
+ int r;
+
+ assert(rtnl);
+
+ r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, ifindex);
+ if (r < 0) {
+ log_error("UFD: Could not allocate RTM_SETLINK message: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_message_link_set_flags(req, flags, IFF_UP);
+ if (r < 0) {
+ log_error("UFD: Could not set link flags: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_rtnl_call_async(rtnl, req, port_set_handler, NULL, 0, NULL);
+ if (r < 0) {
+ log_error("UFD: Could not send rtnetlink message: %s", strerror(-r));
+ return r;
+ }
+
+ return 0;
+}
+
+static int ufd_port_info_add(unsigned int port_id,
+ UfddGroup *grp_ptr,
+ UfddPort *ufd_port_ptr) {
+ UfddPortInfo *port_info_ptr = NULL;
+ UfddPortInfo *cur_ptr = NULL;
+
+ assert(grp_ptr);
+ assert(ufd_port_ptr);
+
+ port_info_ptr = (UfddPortInfo *)calloc(1, sizeof(UfddPortInfo));
+ if (!port_info_ptr)
+ return -ENOMEM;
+
+ port_info_ptr->if_idx = port_id;
+ port_info_ptr->grp_info = grp_ptr;
+ port_info_ptr->if_idx_info = ufd_port_ptr;
+ port_info_ptr->next_if_idx = NULL;
+
+ /* Add port info into g_ufdd.port_info database */
+ if (!g_ufdd.port_info) {
+ g_ufdd.port_info = port_info_ptr;
+ } else {
+ cur_ptr = g_ufdd.port_info;
+
+ while (cur_ptr->next_if_idx)
+ cur_ptr = cur_ptr->next_if_idx;
+
+ cur_ptr->next_if_idx = port_info_ptr;
+ }
+
+ return 0;
+}
+
+/* To check if the new ports are already present in ufd_port_info database */
+static bool is_port_present(unsigned int *port_list,
+ unsigned int port_count,
+ unsigned int *group_id,
+ unsigned int *port_id) {
+ UfddPortInfo *cur_ptr = g_ufdd.port_info;
+ unsigned int *port_ptr = port_list;
+ unsigned int i = 0;
+
+ assert(group_id);
+ assert(port_id);
+
+ if (!port_list || 0 == port_count)
+ return false;
+
+ if (!cur_ptr)
+ return false;
+
+ /* Check if any of the new ports is present in g_ufdd.port_info */
+ for (i = 0; i < port_count; i++) {
+ while (cur_ptr) {
+ if (cur_ptr->if_idx == *port_ptr) {
+ *group_id = cur_ptr->grp_info->grp_id;
+ *port_id = cur_ptr->if_idx;
+
+ return true;
+ }
+
+ cur_ptr = cur_ptr->next_if_idx;
+ }
+
+ cur_ptr = g_ufdd.port_info;
+ port_ptr++;
+ }
+
+ return false;
+}
+
+/* Get group structure from g_ufdd.group_info DB */
+static UfddGroup *get_group(unsigned int group_id) {
+ UfddGroup *cur_ptr = g_ufdd.group_info;
+
+ while (cur_ptr) {
+ if (cur_ptr->grp_id == group_id)
+ return cur_ptr;
+
+ cur_ptr = cur_ptr->next_group;
+ }
+
+ return NULL;
+}
+
+/* Add Ports list to the Group */
+static int ufd_group_ports_add(UfddGroup *grp_ptr,
+ unsigned char link_type,
+ unsigned int *port_list,
+ unsigned int port_count) {
+ UfddPort *port_ptr = NULL;
+ UfddPort *ufd_port_ptr = NULL;
+ unsigned int act_up_port = 0;
+ unsigned int *port_to_add = port_list;
+ unsigned int i;
+ int r;
+
+ assert(grp_ptr);
+
+ if (!port_to_add || 0 == port_count) {
+ log_debug("UFD: No ports found to add in the group:%d", grp_ptr->grp_id);
+ return 0;
+ }
+
+ if (UFD_UPLINK == link_type) {
+ ufd_port_ptr = grp_ptr->up_link_ports;
+ act_up_port = grp_ptr->act_uplink_count;
+ } else if (UFD_DOWNLINK == link_type) {
+ ufd_port_ptr = grp_ptr->down_link_ports;
+ } else {
+ log_error("UFD: Unknown link_type:%d", link_type);
+ return -EINVAL;
+ }
+
+ /* Move the ptr to end of post list */
+ if (ufd_port_ptr) {
+ while (ufd_port_ptr->next_port)
+ ufd_port_ptr = ufd_port_ptr->next_port;
+ }
+
+ /* Add ports to the port list of the group */
+ for (i = 0; i < port_count; i++) {
+ port_ptr = (UfddPort *)calloc(1, sizeof(UfddPort));
+ if (!port_ptr)
+ return -ENOMEM;
+
+ port_ptr->if_index = *port_to_add;
+
+ r = ufd_port_get_state(port_ptr->if_index, &port_ptr->admin_state, &port_ptr->ufd_state);
+ if (r < 0) {
+ free(port_ptr);
+ return r;
+ }
+
+ port_ptr->rcv_port_event = UFD_RCV_PORT_EVENT;
+ port_ptr->link_type = link_type;
+ port_ptr->next_port = NULL;
+
+ if ((UFD_UPLINK == link_type) && (UFD_PORT_ADMIN_UP == port_ptr->admin_state) &&
+ (UFD_PORT_OPER_UP == port_ptr->ufd_state))
+ act_up_port++;
+
+ if (ufd_port_ptr) {
+ ufd_port_ptr->next_port = port_ptr;
+ ufd_port_ptr = ufd_port_ptr->next_port;
+ } else {
+ ufd_port_ptr = port_ptr;
+ if (UFD_UPLINK == link_type)
+ grp_ptr->up_link_ports = port_ptr;
+ else
+ grp_ptr->down_link_ports = port_ptr;
+ }
+
+ r = ufd_port_info_add(*port_to_add, grp_ptr, port_ptr);
+ if (r < 0)
+ return r;
+
+ port_to_add++;
+ }
+
+ if (UFD_UPLINK == port_ptr->link_type) {
+ grp_ptr->act_uplink_count = act_up_port;
+ log_debug("UFD: Active uplink port count:%d for group:%d", grp_ptr->act_uplink_count, grp_ptr->grp_id);
+ }
+
+ return 0;
+}
+
+/* Update downlink ports based on uplink events */
+static int update_downlink_ports(UfddPort *port_ptr,
+ PortState port_state) {
+ unsigned flags = 0;
+ int r;
+
+ while (port_ptr) {
+ if ((port_ptr->admin_state == UFD_PORT_ADMIN_UP) &&
+ (port_ptr->ufd_state != port_state)) {
+
+ if (port_state == UFD_PORT_OPER_UP) {
+ port_ptr->ufd_state = UFD_PORT_OPER_UP;
+ port_ptr->rcv_port_event = UFD_IGNORE_PORT_UP_EVENT;
+ flags = IFF_UP | IFF_RUNNING;
+ } else {
+ port_ptr->ufd_state = UFD_PORT_OPER_DOWN;
+ port_ptr->rcv_port_event = UFD_IGNORE_PORT_DOWN_EVENT;
+ flags &= (unsigned int)~IFF_UP;
+ }
+
+ r = ufd_port_set_flags(port_ptr->if_index, flags);
+ if (r < 0)
+ return r;
+ }
+
+ port_ptr = port_ptr->next_port;
+ }
+
+ return 0;
+}
+
+/* Delete port info from g_ufdd.port_info DB */
+static void ufd_port_info_del(unsigned int port_id) {
+ UfddPortInfo *prev_port_ptr = NULL;
+ UfddPortInfo *cur_port_ptr = g_ufdd.port_info;
+
+ while (cur_port_ptr) {
+ if (cur_port_ptr->if_idx == port_id) {
+
+ if (cur_port_ptr == g_ufdd.port_info) {
+ g_ufdd.port_info = cur_port_ptr->next_if_idx;
+ } else {
+ prev_port_ptr->next_if_idx = cur_port_ptr->next_if_idx;
+ }
+
+ free(cur_port_ptr);
+ cur_port_ptr = NULL;
+
+ break;
+ }
+
+ prev_port_ptr = cur_port_ptr;
+ cur_port_ptr = cur_port_ptr->next_if_idx;
+ }
+}
+
+/* Delete port from the groupDB */
+static unsigned int ufd_group_port_del(UfddGroup *group_ptr,
+ char link_type,
+ unsigned int port_id) {
+ UfddPort *prev_port_ptr = NULL;
+ UfddPort *cur_port_ptr = NULL;
+ unsigned int act_uplink_port = 0;
+
+ assert(group_ptr);
+
+ cur_port_ptr = (link_type == UFD_UPLINK) ? group_ptr->up_link_ports : group_ptr->down_link_ports;
+ prev_port_ptr = cur_port_ptr;
+
+ while (cur_port_ptr) {
+ if (cur_port_ptr->if_index == port_id) {
+
+ if (cur_port_ptr == prev_port_ptr) {
+ if (UFD_UPLINK == link_type)
+ group_ptr->up_link_ports = cur_port_ptr->next_port;
+ else
+ group_ptr->down_link_ports = cur_port_ptr->next_port;
+ } else {
+ prev_port_ptr->next_port = cur_port_ptr->next_port;
+ }
+
+ if ((UFD_PORT_OPER_UP == cur_port_ptr->ufd_state) && (UFD_UPLINK == cur_port_ptr->link_type))
+ act_uplink_port = 1;
+
+ log_debug("UFD: Deleted port:%d ufd_state:%d link_state:%d, uplink_port:%d",
+ port_id, cur_port_ptr->ufd_state, cur_port_ptr->link_type, act_uplink_port);
+
+ free(cur_port_ptr);
+ cur_port_ptr = NULL;
+
+ /* delete port information from port_info db */
+ ufd_port_info_del(port_id);
+
+ return act_uplink_port;
+ }
+
+ prev_port_ptr = cur_port_ptr;
+ cur_port_ptr = cur_port_ptr->next_port;
+ }
+
+ return act_uplink_port;
+}
+
+/* Delete port list from GroupDB */
+static unsigned int ufd_group_port_list_del(UfddGroup *group_ptr,
+ char link_type,
+ unsigned int *port_list,
+ unsigned int port_count) {
+ unsigned int act_port_count = 0;
+ unsigned int i = 0;
+ unsigned int *port_to_del = port_list;
+
+ assert(group_ptr);
+
+ for (i = 0; i < port_count; i++) {
+ unsigned int count = 0;
+
+ count = ufd_group_port_del(group_ptr, link_type, *port_to_del);
+
+ act_port_count += count;
+
+ port_to_del++;
+ }
+
+ return act_port_count;
+}
+
+/* Check if all the ports to be deleted are present in group */
+static bool ufd_group_port_check(UfddPort *port_ptr,
+ unsigned int *port_list,
+ unsigned int port_count) {
+ unsigned int port_found_cnt = 0;
+ unsigned int *port_to_check = port_list;
+ unsigned int i;
+
+ for (i = 0; i < port_count; i++) {
+ UfddPort *cur_port_ptr = port_ptr;
+
+ while (cur_port_ptr) {
+ if (cur_port_ptr->if_index == *port_to_check) {
+ port_found_cnt++;
+ break;
+ }
+
+ cur_port_ptr = cur_port_ptr->next_port;
+ }
+
+ port_to_check++;
+ }
+
+ if (port_found_cnt != port_count)
+ return false;
+ else
+ return true;
+}
+
+/* Delete complete portlist of the group */
+static void ufd_group_all_port_del(UfddGroup *group_ptr,
+ char link_type) {
+ UfddPort *prev_port_ptr = NULL;
+ UfddPort *cur_port_ptr = NULL;
+
+ if (UFD_UPLINK == link_type) {
+ cur_port_ptr = group_ptr->up_link_ports;
+ group_ptr->up_link_ports = NULL;
+ } else {
+ cur_port_ptr = group_ptr->down_link_ports;
+ group_ptr->down_link_ports = NULL;
+ }
+
+ while (cur_port_ptr) {
+ prev_port_ptr = cur_port_ptr;
+ cur_port_ptr = cur_port_ptr->next_port;
+
+ ufd_port_info_del(prev_port_ptr->if_index);
+
+ free(prev_port_ptr);
+ prev_port_ptr = NULL;
+ }
+}
+
+/* Delete group */
+static int ufd_group_del(unsigned int group_id,
+ unsigned int *uplink_ports,
+ unsigned int uplink_count,
+ unsigned int *downlink_ports,
+ unsigned int downlink_count) {
+ UfddGroup *group_ptr = NULL;
+ UfddGroup *cur_grp_ptr = NULL;
+ UfddGroup *prev_grp_ptr = NULL;
+ UfddPort *cur_port_ptr = NULL;
+ unsigned int act_port_count = 0;
+ int r;
+
+ group_ptr = get_group(group_id);
+ if (!group_ptr)
+ return -EINVAL;
+
+ /* If uplink_count and downlink_count are zero, delete all uplink and downlink ports */
+ if ((0 == uplink_count) && (0 == downlink_count)) {
+ log_debug("UFD: Deleting uplink & downlink ports for group:%d", group_id);
+
+ ufd_group_all_port_del(group_ptr, UFD_UPLINK);
+ ufd_group_all_port_del(group_ptr, UFD_DOWNLINK);
+
+ /* if pointer to up_link_ports and down_link_ports are NULL in the group, delete group */
+ if ((!group_ptr->up_link_ports) && (!group_ptr->down_link_ports)) {
+ cur_grp_ptr = g_ufdd.group_info;
+
+ while (cur_grp_ptr) {
+ if (cur_grp_ptr->grp_id == group_id) {
+
+ if (cur_grp_ptr == g_ufdd.group_info)
+ g_ufdd.group_info = cur_grp_ptr->next_group;
+ else
+ prev_grp_ptr->next_group = cur_grp_ptr->next_group;
+
+ unlink(cur_grp_ptr->state_file);
+ free(cur_grp_ptr->state_file);
+ free(cur_grp_ptr->config_file);
+
+ free(cur_grp_ptr);
+ cur_grp_ptr = NULL;
+
+ log_debug("UFD: Group: %d deleted", group_id);
+
+ return 0;
+ }
+
+ prev_grp_ptr = cur_grp_ptr;
+ cur_grp_ptr = cur_grp_ptr->next_group;
+ }
+
+ } else {
+ log_debug("UFD: failed to delete group: %d", group_id);
+ return -EINVAL;
+ }
+ }
+
+ /* delete uplink ports */
+ if (uplink_count != 0) {
+ if (false == ufd_group_port_check(group_ptr->up_link_ports, uplink_ports, uplink_count)) {
+ log_error("UFD: Invalid uplink ports list group:%d", group_id);
+ return -EINVAL;
+ }
+
+ act_port_count = ufd_group_port_list_del(group_ptr, UFD_UPLINK, uplink_ports, uplink_count);
+ group_ptr->act_uplink_count = group_ptr->act_uplink_count - act_port_count;
+
+ log_debug("UFD: Active uplink ports number for group:%d is %d", group_id, group_ptr->act_uplink_count);
+ }
+
+ /* delete downlink ports */
+ if (downlink_count != 0) {
+ if (false == ufd_group_port_check(group_ptr->down_link_ports, downlink_ports, downlink_count)) {
+ log_error("UFD: Invalid uplink ports list group:%d", group_id);
+ return -EINVAL;
+ }
+
+ ufd_group_port_list_del(group_ptr, UFD_DOWNLINK, downlink_ports, downlink_count);
+ }
+
+ /* down downlink port states if no active/up Uplink ports */
+ if (0 == group_ptr->act_uplink_count) {
+ cur_port_ptr = group_ptr->down_link_ports;
+
+ while (cur_port_ptr) {
+ if (cur_port_ptr->ufd_state != UFD_PORT_OPER_DOWN) {
+ r = update_downlink_ports(cur_port_ptr, UFD_PORT_OPER_DOWN);
+ if (r < 0) {
+ log_error("UFD: Could not update downlink ports state, error: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ cur_port_ptr = cur_port_ptr->next_port;
+ }
+ }
+
+ return 0;
+}
+
+static int verify_ports(unsigned int *uplink_ports,
+ unsigned int uplink_count,
+ unsigned int *downlink_ports,
+ unsigned int downlink_count) {
+ unsigned int group_nr = 0;
+ unsigned int port_id = 0;
+ unsigned int i;
+ unsigned int j;
+
+ if (true == is_port_present(uplink_ports, uplink_count, &group_nr, &port_id)) {
+ log_error("UFD: Uplink Port:%d is already mapped to a group:%d", port_id, group_nr);
+ return -EEXIST;
+ }
+
+ group_nr = 0;
+ port_id = 0;
+
+ if (true == is_port_present(downlink_ports, downlink_count, &group_nr, &port_id)) {
+ log_error("UFD: Downlink Port:%d is already mapped to a group:%d", port_id, group_nr);
+ return -EEXIST;
+ }
+
+ for (i = 0; i < uplink_count; i++)
+ for (j = 0; j < downlink_count; j++)
+ if (uplink_ports[i] == downlink_ports[j]) {
+ log_error("UFD: Uplink port %d found in the downlink port list", uplink_ports[i]);
+ return -EEXIST;
+ }
+
+ return 0;
+}
+
+/* Add group to the group DB */
+static int ufd_group_add(unsigned int group_id,
+ unsigned int *uplink_ports,
+ unsigned int uplink_count,
+ unsigned int *downlink_ports,
+ unsigned int downlink_count) {
+ UfddGroup *group_ptr = NULL;
+ UfddGroup *cur_ptr = NULL;
+ UfddPort *cur_port_ptr = NULL;
+ int r;
+ bool add_new_group = false;
+
+ cur_ptr = g_ufdd.group_info;
+
+ r = verify_ports(uplink_ports, uplink_count, downlink_ports, downlink_count);
+ if (r < 0)
+ return r;
+
+ /* Check if group is already present */
+ group_ptr = get_group(group_id);
+
+ if (!group_ptr) {
+ /*if not present create*/
+ group_ptr = (UfddGroup *)calloc(1, sizeof(UfddGroup));
+
+ if (!group_ptr)
+ return -ENOMEM;
+
+ group_ptr->grp_id = group_id;
+ group_ptr->act_uplink_count = 0;
+ group_ptr->up_link_ports = NULL;
+ group_ptr->down_link_ports = NULL;
+ group_ptr->next_group = NULL;
+
+ add_new_group = true;
+ }
+
+ /* Add uplink ports to the group */
+ r = ufd_group_ports_add(group_ptr, UFD_UPLINK, uplink_ports, uplink_count);
+ if (r < 0) {
+ int del_err;
+ log_error("UFD: Uplink Port addition to group:%d failed error: %s", group_id, strerror(-r));
+
+ del_err = ufd_group_del(group_id, uplink_ports, uplink_count, NULL, 0);
+ if (del_err < 0)
+ log_error("UFD: Uplink Port deletion failed. Error: %s", strerror(-del_err));
+
+ return r;
+ }
+
+ /* Add downlink ports to the group */
+ r = ufd_group_ports_add(group_ptr, UFD_DOWNLINK, downlink_ports, downlink_count);
+ if (r < 0) {
+ int del_err;
+ log_error("UFD: Downlink Port addition to group:%d failed error: %s", group_id, strerror(-r));
+
+ del_err = ufd_group_del(group_id, NULL, 0, downlink_ports, downlink_count);
+ if (del_err < 0)
+ log_error("UFD: Downlink Port deletion failed. Error: %s", strerror(-del_err));
+
+ return r;
+ }
+
+ /* Add group to group_info DB if new group */
+ if (add_new_group) {
+ if (!g_ufdd.group_info) {
+ g_ufdd.group_info = group_ptr;
+ } else {
+ while (cur_ptr->next_group)
+ cur_ptr = cur_ptr->next_group;
+
+ cur_ptr->next_group = group_ptr;
+ }
+
+ log_debug("UFD: Added UFD Group:%d to UFD database", group_id);
+ }
+
+ /* down downlink port states if no active/up Uplink ports */
+ if (group_ptr->act_uplink_count == 0) {
+ cur_port_ptr = group_ptr->down_link_ports;
+
+ while (cur_port_ptr) {
+ if (cur_port_ptr->ufd_state != UFD_PORT_OPER_DOWN) {
+ r = update_downlink_ports(cur_port_ptr, UFD_PORT_OPER_DOWN);
+ if (r < 0) {
+ log_error("UFD: Could not update downlink ports state, error: %s", strerror(-r));
+ return r;
+ }
+ }
+
+ cur_port_ptr = cur_port_ptr->next_port;
+ }
+ }
+
+ return 0;
+}
+
+/* Update UFD DB based on the port event received */
+static int ufd_update_port_event(unsigned int port,
+ char port_event) {
+ UfddPortInfo *cur_port_info_ptr = NULL;
+ UfddGroup *group_ptr = NULL;
+ UfddPort *port_ptr = NULL;
+ bool is_port_found = false;
+ int r;
+
+ cur_port_info_ptr = g_ufdd.port_info;
+
+ while (cur_port_info_ptr) {
+
+ if (cur_port_info_ptr->if_idx == port) {
+ group_ptr = cur_port_info_ptr->grp_info;
+ port_ptr = cur_port_info_ptr->if_idx_info;
+ is_port_found = true;
+ break;
+ }
+
+ cur_port_info_ptr = cur_port_info_ptr->next_if_idx;
+ }
+
+ if (!is_port_found) {
+ log_error("UFD: Port:%d not found", port);
+ return -EINVAL;
+ }
+
+ if (!group_ptr) {
+ log_error("UFD: Group not found for port:%d", port);
+ return -EINVAL;
+ }
+
+ if (!port_ptr) {
+ log_error("UFD: Port info not found for port:%d", port);
+ return -EINVAL;
+ }
+
+ switch (port_event) {
+ case UFD_PORT_OPER_DOWN:
+
+ if (UFD_RCV_PORT_EVENT == port_ptr->rcv_port_event) {
+ port_ptr->admin_state = UFD_PORT_ADMIN_UP;
+ } else {
+ port_ptr->rcv_port_event--;
+ break;
+ }
+
+ if (UFD_PORT_OPER_UP == port_ptr->ufd_state) {
+ port_ptr->ufd_state = UFD_PORT_OPER_DOWN;
+
+ if (UFD_UPLINK == port_ptr->link_type) {
+
+ group_ptr->act_uplink_count--;
+ log_debug("UFD: Uplink count decremented: %d", group_ptr->act_uplink_count);
+
+ if (0 == group_ptr->act_uplink_count) {
+ r = update_downlink_ports(group_ptr->down_link_ports, UFD_PORT_OPER_DOWN);
+ if (r < 0) {
+ log_error("UFD: Could not update downlink ports state, error: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+ }
+ }
+
+ break;
+
+ case UFD_PORT_OPER_UP:
+
+ if (port_ptr->rcv_port_event != UFD_RCV_PORT_EVENT) {
+ port_ptr->rcv_port_event--;
+ break;
+ }
+
+ if (port_ptr->admin_state != UFD_PORT_ADMIN_UP)
+ break;
+
+ port_ptr->ufd_state = UFD_PORT_OPER_UP;
+
+ if (UFD_UPLINK == port_ptr->link_type) {
+ group_ptr->act_uplink_count++;
+ log_debug("UFD: Uplink count incremented: %d", group_ptr->act_uplink_count);
+
+ if (1 == group_ptr->act_uplink_count) {
+ r = update_downlink_ports(group_ptr->down_link_ports, UFD_PORT_OPER_UP);
+ if (r < 0) {
+ log_error("UFD: Could not update downlink ports state, error: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+ }
+
+ break;
+
+ case UFD_PORT_ADMIN_DOWN:
+
+ if (UFD_RCV_PORT_EVENT == port_ptr->rcv_port_event) {
+ port_ptr->admin_state = UFD_PORT_ADMIN_DOWN;
+ } else {
+ port_ptr->rcv_port_event--;
+ break;
+ }
+
+ port_ptr->ufd_state = UFD_PORT_OPER_DOWN;
+
+ if (UFD_UPLINK == port_ptr->link_type) {
+ group_ptr->act_uplink_count--;
+ log_debug("UFD: Uplink count decremented: %d", group_ptr->act_uplink_count);
+
+ if (0 == group_ptr->act_uplink_count) {
+ r = update_downlink_ports(group_ptr->down_link_ports, UFD_PORT_OPER_DOWN);
+ if (r < 0) {
+ log_error("UFD: Could not update downlink ports state, error: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+ }
+
+ break;
+
+ default:
+ log_error("UFD: Unknown port Event:%d received on port:%d", port_event, port);
+ return -EINVAL;
+
+ }
+
+ return 0;
+}
+
+/* Cleanup for UFD db */
+static void ufd_db_cleanup(void) {
+ UfddGroup *cur_grp_ptr = NULL;
+ UfddGroup *tmp_grp_ptr = NULL;
+ UfddPortInfo *cur_port_ptr = NULL;
+ UfddPortInfo *temp_port_ptr = NULL;
+
+ log_debug("UFD: Free database");
+
+ cur_grp_ptr = g_ufdd.group_info;
+
+ /* cleanup of group_info DB */
+ while (cur_grp_ptr) {
+ ufd_group_all_port_del(cur_grp_ptr, UFD_UPLINK);
+ ufd_group_all_port_del(cur_grp_ptr, UFD_DOWNLINK);
+
+ tmp_grp_ptr = cur_grp_ptr;
+ cur_grp_ptr = cur_grp_ptr->next_group;
+
+ unlink(tmp_grp_ptr->state_file);
+ free(tmp_grp_ptr->state_file);
+ free(tmp_grp_ptr->config_file);
+
+ free(tmp_grp_ptr);
+ tmp_grp_ptr = NULL;
+ }
+
+ cur_port_ptr = g_ufdd.port_info;
+
+ /* Cleanup of port_info DB */
+ while (cur_port_ptr) {
+ temp_port_ptr = cur_port_ptr;
+ cur_port_ptr = cur_port_ptr->next_if_idx;
+
+ free(temp_port_ptr);
+ temp_port_ptr = NULL;
+ }
+}
+
+static int ufd_rtnl_event(sd_rtnl *rtnl,
+ sd_rtnl_message *message,
+ void *userdata) {
+ unsigned int flags;
+ char state;
+ int r;
+ int ifindex;
+
+ r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
+ if (r < 0 || ifindex <= 0) {
+ log_warning("UFD rtnl: received link message without valid ifindex");
+ return 0;
+ }
+
+ r = sd_rtnl_message_link_get_flags(message, &flags);
+ if (r < 0) {
+ log_warning("UFD: Could not get link flags");
+ return r;
+ }
+
+ if ((flags & IFF_UP) == IFF_UP) {
+ if ((flags & IFF_RUNNING) == IFF_RUNNING)
+ state = UFD_PORT_OPER_UP;
+ else
+ state = UFD_PORT_OPER_DOWN;
+ } else {
+ state = UFD_PORT_ADMIN_DOWN;
+ }
+
+ r = ufd_update_port_event(ifindex, state);
+ if (r < 0) {
+ log_error("UFD: Could not update port event");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int monitoring_init(UfdDaemon *ufdd) {
+ int r;
+
+ assert(ufdd);
+
+ r = sd_rtnl_open(&ufdd->rtnl, 1, RTNLGRP_LINK);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_attach_event(ufdd->rtnl, ufdd->event, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_add_match(ufdd->rtnl, RTM_NEWLINK, ufd_rtnl_event, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_add_match(ufdd->rtnl, RTM_DELLINK, ufd_rtnl_event, NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int parse_ports_list(char *port_list,
+ uint32_t **out_port_list,
+ uint32_t *port_num) {
+ char *temp_list = NULL;
+ char *token = NULL;
+ uint32_t port_count = 0;
+ uint32_t *port_ptr = NULL;
+ uint32_t ifindex;
+ uint32_t i = 0;
+
+ assert(port_list);
+
+ temp_list = (char *) malloc(strlen(port_list) + 1);
+ if (!temp_list)
+ return -ENOMEM;
+
+ strncpy(temp_list, port_list, strlen(port_list) + 1);
+
+ /* check for validity of the port_list first */
+ token = strtok(port_list, ",");
+ while (token) {
+ ifindex = if_nametoindex(token);
+
+ if (0 == ifindex) {
+ log_error("UFD got invalid portname = %s", token);
+ free(temp_list);
+ return -EINVAL;
+ }
+
+ port_count++;
+ token = strtok(NULL, ",");
+ }
+
+ port_ptr = (uint32_t *)malloc(port_count * sizeof(uint32_t));
+ if (!port_ptr) {
+ free(temp_list);
+ return -ENOMEM;
+ }
+
+ token = strtok(temp_list, ",");
+ while (token) {
+ ifindex = if_nametoindex(token);
+ *(port_ptr+i) = ifindex;
+ token = strtok(NULL, ",");
+ i++;
+ }
+
+ *out_port_list = port_ptr;
+ *port_num = port_count;
+
+ free(temp_list);
+
+ return 0;
+}
+
+static int ufd_daemon_save(void) {
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *group_list = NULL;
+ UfddGroup *ufd_group;
+ bool space;
+ int r;
+
+ assert(g_ufdd.state_file);
+
+ r = fopen_temporary(g_ufdd.state_file, &f, &temp_path);
+ if (r < 0)
+ return r;
+
+ fchmod(fileno(f), 0644);
+
+ fprintf(f, "# This is private data. Do not parse.\n");
+
+ fputs("GROUP_LIST=", f);
+ space = false;
+
+ ufd_group = g_ufdd.group_info;
+ while (ufd_group) {
+ char num[32];
+
+ if (space)
+ fputc(' ', f);
+
+ snprintf(num, sizeof(num), "%d", ufd_group->grp_id);
+
+ fputs(num, f);
+ space = true;
+
+ ufd_group = ufd_group->next_group;
+ }
+
+ fprintf(f, "\n");
+
+ r = fflush_and_check(f);
+ if (r < 0) {
+ log_error("Failed to save ufd data to %s: %s", g_ufdd.state_file, strerror(-r));
+ unlink(g_ufdd.state_file);
+ unlink(temp_path);
+ return r;
+ }
+
+ if (rename(temp_path, g_ufdd.state_file) < 0) {
+ r = -errno;
+ unlink(g_ufdd.state_file);
+ unlink(temp_path);
+ return r;
+ }
+
+ return 0;
+}
+
+static int ufd_group_save(UfddGroup *ufd_group) {
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ UfddPort *ufd_port;
+ bool space;
+ int r;
+
+ assert(ufd_group);
+ assert(ufd_group->state_file);
+
+ r = fopen_temporary(ufd_group->state_file, &f, &temp_path);
+ if (r < 0)
+ return r;
+
+ fchmod(fileno(f), 0644);
+
+ fprintf(f,
+ "# This is private data. Do not parse.\n"
+ "GROUP_STATE=configured\n");
+
+ if (ufd_group->config_file)
+ fprintf(f, "CONFIG_FILE=%s\n", ufd_group->config_file);
+
+ fputs("UPLINKS=", f);
+ space = false;
+
+ ufd_port = ufd_group->up_link_ports;
+ while (ufd_port) {
+ char num[32];
+
+ if (space)
+ fputc(' ', f);
+
+ snprintf(num, sizeof(num), "%d", ufd_port->if_index);
+
+ fputs(num, f);
+ space = true;
+
+ ufd_port = ufd_port->next_port;
+ }
+
+ fprintf(f, "\n");
+
+ fputs("DOWNLINKS=", f);
+ space = false;
+
+ ufd_port = ufd_group->down_link_ports;
+ while (ufd_port) {
+ char num[32];
+
+ if (space)
+ fputc(' ', f);
+
+ snprintf(num, sizeof(num), "%d", ufd_port->if_index);
+
+ fputs(num, f);
+
+ space = true;
+
+ ufd_port = ufd_port->next_port;
+ }
+
+ fprintf(f, "\n");
+
+ r = fflush_and_check(f);
+ if (r < 0) {
+ log_error("Failed to save group data to %s: %s", ufd_group->state_file, strerror(-r));
+ unlink(ufd_group->state_file);
+ unlink(temp_path);
+ return r;
+ }
+
+ if (rename(temp_path, ufd_group->state_file) < 0) {
+ r = -errno;
+ unlink(ufd_group->state_file);
+ unlink(temp_path);
+ return r;
+ }
+
+ r = ufd_daemon_save();
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int ufd_daemon_set_group(int group_id,
+ char *config_file,
+ char *up_links,
+ char *down_links) {
+ uint32_t uplink_count = 0;
+ uint32_t downlink_count = 0;
+ uint32_t *uplink_ptr = NULL;
+ uint32_t *downlink_ptr = NULL;
+ UfddGroup *ufd_group;
+ int r = 0;
+
+ if (group_id < 0) {
+ log_error("UFD: Group ID should be a positive number. Got: %d", group_id);
+ return -EINVAL;
+ }
+
+ ufd_group = get_group(group_id);
+ if (ufd_group) {
+ log_error("UFD: Group %d already configured", group_id);
+ return -EEXIST;
+ }
+
+ if (!up_links) {
+ log_error("UFD: Uplink port list is empty for group %d", group_id);
+ return -EINVAL;
+ }
+
+ if (!down_links) {
+ log_error("UFD: Downlink port list is empty for group %d", group_id);
+ return -EINVAL;
+ }
+
+ r = parse_ports_list(up_links, &uplink_ptr, &uplink_count);
+ if (r < 0) {
+ free(uplink_ptr);
+ return r;
+ }
+
+ r = parse_ports_list(down_links, &downlink_ptr, &downlink_count);
+ if (r < 0) {
+ free(uplink_ptr);
+ free(downlink_ptr);
+ return r;
+ }
+
+ r = ufd_group_add(group_id, uplink_ptr, uplink_count, downlink_ptr, downlink_count);
+ if (r < 0) {
+ free(uplink_ptr);
+ free(downlink_ptr);
+ return r;
+ }
+
+ ufd_group = get_group(group_id);
+ if (!ufd_group) {
+ log_error("UFD: Group %d not found", group_id);
+ free(uplink_ptr);
+ free(downlink_ptr);
+ return -EINVAL;
+ }
+
+ r = asprintf(&ufd_group->state_file, "/run/systemd/netif/ufd/groups/%d", group_id);
+ if (r < 0) {
+ free(uplink_ptr);
+ free(downlink_ptr);
+ return -ENOMEM;
+ }
+
+ ufd_group->config_file = malloc(strlen(config_file) + 1);
+ if (!ufd_group->config_file) {
+ free(uplink_ptr);
+ free(downlink_ptr);
+ return -ENOMEM;
+ }
+
+ strncpy(ufd_group->config_file, config_file, strlen(config_file) + 1);
+
+ ufd_group_save(ufd_group);
+
+ free(uplink_ptr);
+ free(downlink_ptr);
+
+ return r;
+}
+
+int ufd_daemon_init(void) {
+ int r;
+
+ if (!g_ufdd.rtnl) {
+ r = monitoring_init(&g_ufdd);
+ if (r < 0) {
+ log_error("UFD: Could not init the UFD daemon");
+ return r;
+ }
+
+ g_ufdd.state_file = strdup("/run/systemd/netif/ufd/state");
+ if (!g_ufdd.state_file) {
+ log_error("UFD: Could not init the UFD daemon");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+void ufd_daemon_close(void) {
+ sd_rtnl_unref(g_ufdd.rtnl);
+ ufd_db_cleanup();
+
+ unlink(g_ufdd.state_file);
+ free(g_ufdd.state_file);
+}
diff --git a/src/network/networkd-ufd-daemon.h b/src/network/networkd-ufd-daemon.h
new file mode 100644
index 0000000..b625d6f
--- /dev/null
+++ b/src/network/networkd-ufd-daemon.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+/* Init the Uplink failure detection daemon. */
+int ufd_daemon_init(void);
+
+/* Close the Uplink failure detection daemon. */
+void ufd_daemon_close(void);
+
+/* Set Uplink failure detection group. */
+int ufd_daemon_set_group(int group_id,
+ char *config_file,
+ char *up_links,
+ char *down_links);
diff --git a/src/network/networkd.c b/src/network/networkd.c
index ced319d..c475582 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -69,6 +69,13 @@ int main(int argc, char *argv[]) {
log_error("Could not create runtime directory 'lldp': %s",
strerror(-r));
+ r = mkdir_safe_label("/run/systemd/netif/ufd", 0755, uid, gid);
+ if (r < 0)
+ log_error_errno(r, "Could not create runtime directory 'ufd': %m");
+
+ r = mkdir_safe_label("/run/systemd/netif/ufd/groups", 0755, uid, gid);
+ if (r < 0)
+ log_error_errno(r, "Could not create runtime directory 'ufd': %m");
r = drop_privileges(uid, gid,
(1ULL << CAP_NET_ADMIN) |
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 719a75b..bd40143 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -206,8 +206,13 @@ struct Manager {
char *state_file;
+ unsigned links_to_configure;
+ bool groups_configured;
+
Hashmap *links;
Hashmap *netdevs;
+ Hashmap *group_netdevs;
+
LIST_HEAD(Network, networks);
LIST_HEAD(AddressPool, address_pools);
@@ -226,6 +231,7 @@ bool manager_should_reload(Manager *m);
int manager_rtnl_enumerate_links(Manager *m);
int manager_rtnl_enumerate_addresses(Manager *m);
+int manager_enumerate_groups(Manager *m);
int manager_rtnl_listen(Manager *m);
int manager_udev_listen(Manager *m);
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index 027730d..693db6a 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -141,6 +141,26 @@ int sd_network_monitor_get_events(sd_network_monitor *m);
/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */
int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec);
+/* Get setup state for uplink failure detection group_id.
+ * Possible states:
+ * configured: link configured successfully
+ * Possible return codes:
+ * -ENODATA: networkd is not aware of the group_id
+ */
+int sd_network_ufd_get_setup_state(int group_id, char **state);
+
+/* Get path to .ufd file applied to group_id */
+int sd_network_ufd_get_config_file(int group_id, char **filename);
+
+/* Get uplinks for an uplink failure detection group. */
+int sd_network_ufd_get_uplinks(int group_id, char ***uplinks);
+
+/* Get downlinks for an uplink failure detection group. */
+int sd_network_ufd_get_downlinks(int group_id, char ***downlinks);
+
+/* Get list of groups configured by the system */
+int sd_network_ufd_get_group_list(char ***groups);
+
_SD_END_DECLARATIONS;
#endif
--
1.9.3
More information about the systemd-devel
mailing list