[libteam][PATCH] teamd: Add ZeroMQ interface.
by Jiří Župka
enable ZeroMQ interface
teamd -Z "tcp:\\127.0.0.1:5555"
teamdctl -Z "tcp:\\127.0.0.1:5555" config dump
Take care when you use this type of connection.
Security must be ensured by firewall or other tools.
There could be added verification by certificate with SSL layer.
It could be added in next version.
Signed-off-by: Jiří Župka <jzupka(a)redhat.com>
---
configure.ac | 13 ++
include/teamdctl.h | 2 +-
libteamdctl/Makefile.am | 4 +-
libteamdctl/cli_zmq.c | 253 ++++++++++++++++++++++++++++++++++++
libteamdctl/libteamdctl.c | 7 +-
libteamdctl/teamdctl_private.h | 2 +
teamd/Makefile.am | 4 +-
teamd/teamd.c | 30 ++++-
teamd/teamd.h | 12 ++
teamd/teamd_state.c | 17 +++
teamd/teamd_zmq.c | 282 +++++++++++++++++++++++++++++++++++++++++
teamd/teamd_zmq.h | 26 ++++
teamd/teamd_zmq_common.h | 58 +++++++++
utils/teamdctl.c | 68 +++++++++-
14 files changed, 760 insertions(+), 18 deletions(-)
create mode 100644 libteamdctl/cli_zmq.c
create mode 100644 teamd/teamd_zmq.c
create mode 100644 teamd/teamd_zmq.h
create mode 100644 teamd/teamd_zmq_common.h
diff --git a/configure.ac b/configure.ac
index b72a282..2397a70 100644
--- a/configure.ac
+++ b/configure.ac
@@ -76,6 +76,19 @@ AC_ARG_ENABLE([dbus],
fi
fi
+have_zmq=no
+AC_ARG_ENABLE([zmq],
+ AS_HELP_STRING([--disable-zmq], [disable ZeroMQ API @<:@default=enabled@:>@]))
+ if test "x$enable_zmq" != "xno"; then
+ PKG_CHECK_MODULES([ZMQ], [libzmq],
+ [AC_DEFINE(ENABLE_ZMQ, [1], [ZMQ API.]) have_zmq=yes],
+ have_zmq=no)
+ if test "x$have_zmq$enable_zmq" = xnoyes; then
+ AC_MSG_ERROR([*** ZeroMQ support requested but libraries not found])
+ fi
+fi
+
+
AC_CONFIG_FILES([Makefile
include/Makefile \
libteam/Makefile \
diff --git a/include/teamdctl.h b/include/teamdctl.h
index 9026411..b773bc4 100644
--- a/include/teamdctl.h
+++ b/include/teamdctl.h
@@ -43,7 +43,7 @@ void teamdctl_set_log_fn(struct teamdctl *tdc,
int teamdctl_get_log_priority(struct teamdctl *tdc);
void teamdctl_set_log_priority(struct teamdctl *tdc, int priority);
int teamdctl_connect(struct teamdctl *tdc, const char *team_name,
- const char *cli_type);
+ const char *addr, const char *cli_type);
void teamdctl_disconnect(struct teamdctl *tdc);
int teamdctl_refresh(struct teamdctl *tdc);
int teamdctl_port_add(struct teamdctl *tdc, const char *port_devname);
diff --git a/libteamdctl/Makefile.am b/libteamdctl/Makefile.am
index d3854fc..edec72b 100644
--- a/libteamdctl/Makefile.am
+++ b/libteamdctl/Makefile.am
@@ -6,9 +6,9 @@ AM_CFLAGS = -fvisibility=hidden -ffunction-sections -fdata-sections
AM_LDFLAGS = -Wl,--gc-sections -Wl,--as-needed
lib_LTLIBRARIES = libteamdctl.la
-libteamdctl_la_SOURCES = libteamdctl.c cli_usock.c cli_dbus.c
+libteamdctl_la_SOURCES = libteamdctl.c cli_usock.c cli_dbus.c cli_zmq.c
libteamdctl_la_CFLAGS= $(AM_CFLAGS) $(JANSSON_CFLAGS) $(DBUS_CFLAGS) -I${top_srcdir}/include -D_GNU_SOURCE
-libteamdctl_la_LIBADD= $(JANSSON_LIBS) $(DBUS_LIBS)
+libteamdctl_la_LIBADD= $(JANSSON_LIBS) $(DBUS_LIBS) $(ZMQ_LIBS) $(LIBDAEMON_LIBS)
libteamdctl_la_LDFLAGS = $(AM_LDFLAGS) -version-info @LIBTEAMDCTL_CURRENT@:@LIBTEAMDCTL_REVISION@:@LIBTEAMDCTL_AGE@
pkgconfigdir = $(libdir)/pkgconfig
diff --git a/libteamdctl/cli_zmq.c b/libteamdctl/cli_zmq.c
new file mode 100644
index 0000000..55b94a7
--- /dev/null
+++ b/libteamdctl/cli_zmq.c
@@ -0,0 +1,253 @@
+/*
+ * cli_usock.c - Teamd daemon control library teamd Unix Domain socket client
+ * Copyright (C) 2013 Jiri Pirko <jiri(a)resnulli.us>
+ *
+ * This library 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.
+ *
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <zmq.h>
+#include <errno.h>
+#include <unistd.h>
+#include <teamdctl.h>
+#include "teamdctl_private.h"
+#include "../teamd/teamd_zmq_common.h"
+
+struct cli_zmq_priv {
+ void *context;
+ void *sock;
+};
+
+static int cli_zmq_process_msg(struct teamdctl *tdc, char *msg,
+ char **p_replystr)
+{
+ char *str;
+ char *rest = msg;
+
+ str = teamd_zmq_msg_getline(&rest);
+ if (!str) {
+ err(tdc, "zmq: Incomplete message.\n");
+ return -EINVAL;;
+ }
+
+ if (!strcmp(TEAMD_ZMQ_REPLY_SUCC_PREFIX, str)) {
+ *p_replystr = rest;
+ } else if (!strcmp(TEAMD_ZMQ_REPLY_ERR_PREFIX, str)) {
+ str = teamd_zmq_msg_getline(&rest);
+ if (!str) {
+ err(tdc, "zmq: Incomplete message.\n");
+ return -EINVAL;;
+ }
+ err(tdc, "Error message received: \"%s\"", str);
+ str = teamd_zmq_msg_getline(&rest);
+ if (!str) {
+ err(tdc, "zmq: Incomplete message.\n");
+ return -EINVAL;;
+ }
+ err(tdc, "Error message content: \"%s\"", str);
+ return -EINVAL;;
+ } else {
+ err(tdc, "Unsupported message type.\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void my_free_msg(void *data, void *hind){
+ free(data);
+}
+
+static int cli_zmq_send(void *sock, char *buf)
+{
+ int ret;
+ int buflen = strlen(buf);
+
+ ret = zmq_send(sock, buf, buflen, 0);
+
+ if (ret == -1){
+ teamd_log_warn("zmq: send failed: %s", strerror(errno));
+ return errno;
+ }
+
+ return 0;
+}
+
+static int cli_zmq_recv(void *sock, char **p_str)
+{
+ int ret;
+ zmq_msg_t msg;
+ char *buf;
+
+ if (zmq_msg_init(&msg) == -1){
+ teamd_log_dbg("zmq: Unable initiate message for receive.");
+ return errno;
+ }
+
+ ret = zmq_msg_recv(&msg, sock, 0);
+
+ if (ret == -1){
+ teamd_log_warn("zmq: send failed: %s", strerror(errno));
+ return errno;
+ }
+
+ buf = malloc(ret + 1);
+ memcpy(buf, zmq_msg_data(&msg), ret);
+ buf[ret] = '\0';
+
+ if (zmq_msg_close (&msg) == -1){
+ free(buf);
+ teamd_log_dbg("zmq: Unable close message.");
+ return errno;
+ }
+
+ *p_str = buf;
+ return 0;
+}
+
+
+#define WAIT_MSEC (TEAMDCTL_REPLY_TIMEOUT)
+
+static int myasprintf(char **p_str, const char *fmt, ...)
+{
+ char *newstr;
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&newstr, fmt, ap);
+ va_end(ap);
+ if (ret == -1)
+ return -ENOMEM;
+ free(*p_str);
+ *p_str = newstr;
+ return 0;
+}
+
+static int cli_zmq_method_call(struct teamdctl *tdc, const char *method_name,
+ char **p_reply, void *priv,
+ const char *fmt, va_list ap)
+{
+ struct cli_zmq_priv *cli_zmq = priv;
+ char *str;
+ char *msg = NULL;
+ char *recvmsg = recvmsg;
+ char *replystr;
+ int err;
+
+ dbg(tdc, "Calling method \"%s\"", method_name);
+ err= myasprintf(&msg, "%s\n%s\n", TEAMD_ZMQ_REQUEST_PREFIX,
+ method_name);
+ if (err)
+ return err;
+ while (*fmt) {
+ switch (*fmt++) {
+ case 's': /* string */
+ str = va_arg(ap, char *);
+ err = myasprintf(&msg, "%s%s\n", msg, str);
+ if (err)
+ goto free_msg;
+ break;
+ default:
+ err(tdc, "Unknown argument type requested.");
+ err = -EINVAL;
+ goto free_msg;
+ }
+ }
+
+ err = cli_zmq_send(cli_zmq->sock, msg);
+ if (err)
+ goto send_err;
+
+ err = cli_zmq_recv(cli_zmq->sock, &recvmsg);
+ if (err)
+ goto send_err;
+
+ err = cli_zmq_process_msg(tdc, recvmsg, &replystr);
+ if (err)
+ goto free_recvmsg;
+
+ if (p_reply) {
+ replystr = strdup(replystr);
+ if (!replystr) {
+ err = -ENOMEM;
+ goto free_recvmsg;
+ }
+ *p_reply = replystr;
+ }
+
+free_recvmsg:
+ free(recvmsg);
+ goto send_err;
+free_msg:
+ free(msg);
+send_err:
+ return err;
+}
+
+static int cli_zmq_init(struct teamdctl *tdc, const char *team_name,
+ void *priv)
+{
+ int err;
+ struct cli_zmq_priv *cli_zmq = priv;
+
+ void* context = zmq_ctx_new();
+ void* sock = zmq_socket(context, ZMQ_REQ);
+ if (sock == NULL){
+ teamd_log_err("zmq: Failed to create socket.");
+ return -errno;
+ }
+
+ int recv_timeo = WAIT_MSEC;
+
+ err = zmq_connect (sock, tdc->addr);
+
+ zmq_setsockopt(sock, ZMQ_RCVTIMEO, &recv_timeo, 1);
+
+ if (err == -1) {
+ err(tdc, "Failed to connect socket (%s).", "tcp://localhost:5555");
+ zmq_close(sock);
+ zmq_ctx_destroy(context);
+ return -errno;
+ }
+ cli_zmq->sock = sock;
+ cli_zmq->context = context;
+
+ return 0;
+}
+
+void cli_zmq_fini(struct teamdctl *tdc, void *priv)
+{
+ struct cli_zmq_priv *cli_zmq = priv;
+
+ zmq_close (cli_zmq->sock);
+ zmq_ctx_destroy (cli_zmq->context);
+}
+
+static const struct teamdctl_cli cli_zmq = {
+ .name = "zmq",
+ .init = cli_zmq_init,
+ .fini = cli_zmq_fini,
+ .method_call = cli_zmq_method_call,
+ .priv_size = sizeof(struct cli_zmq_priv),
+};
+
+const struct teamdctl_cli *teamdctl_cli_zmq_get(void)
+{
+ return &cli_zmq;
+}
diff --git a/libteamdctl/libteamdctl.c b/libteamdctl/libteamdctl.c
index 33cbb3f..02daed0 100644
--- a/libteamdctl/libteamdctl.c
+++ b/libteamdctl/libteamdctl.c
@@ -226,7 +226,7 @@ static void cli_fini(struct teamdctl *tdc)
**/
TEAMDCTL_EXPORT
int teamdctl_connect(struct teamdctl *tdc, const char *team_name,
- const char *cli_type)
+ const char *addr, const char *cli_type)
{
int err;
int i;
@@ -235,6 +235,9 @@ int teamdctl_connect(struct teamdctl *tdc, const char *team_name,
#ifdef ENABLE_DBUS
teamdctl_cli_dbus_get(),
#endif
+#ifdef ENABLE_ZMQ
+ teamdctl_cli_zmq_get(),
+#endif
};
#define TEAMDCTL_CLI_LIST_SIZE ARRAY_SIZE(teamdctl_cli_list)
@@ -258,6 +261,8 @@ int teamdctl_connect(struct teamdctl *tdc, const char *team_name,
if (!cli_type && orig_log_prio < LOG_DEBUG)
teamdctl_set_log_priority(tdc, LOG_EMERG);
+ tdc->addr = (char *) addr;
+
err = cli_init(tdc, team_name);
/* restore original log priority */
diff --git a/libteamdctl/teamdctl_private.h b/libteamdctl/teamdctl_private.h
index 0e36212..026832a 100644
--- a/libteamdctl/teamdctl_private.h
+++ b/libteamdctl/teamdctl_private.h
@@ -40,6 +40,7 @@ struct teamdctl {
const char *file, int line, const char *fn,
const char *format, va_list args);
int log_priority;
+ char *addr;
const struct teamdctl_cli *cli;
void *cli_priv;
struct {
@@ -97,6 +98,7 @@ struct teamdctl_cli {
const struct teamdctl_cli *teamdctl_cli_usock_get(void);
const struct teamdctl_cli *teamdctl_cli_dbus_get(void);
+const struct teamdctl_cli *teamdctl_cli_zmq_get(void);
#define TEAMDCTL_REPLY_TIMEOUT 5000 /* ms */
diff --git a/teamd/Makefile.am b/teamd/Makefile.am
index c5f3401..7d933cf 100644
--- a/teamd/Makefile.am
+++ b/teamd/Makefile.am
@@ -8,13 +8,13 @@ AM_CPPFLAGS='-DLOCALSTATEDIR="$(localstatedir)"'
teamd_CFLAGS= $(LIBDAEMON_CFLAGS) $(JANSSON_CFLAGS) $(DBUS_CFLAGS) -I${top_srcdir}/include -D_GNU_SOURCE
-teamd_LDADD = $(top_builddir)/libteam/libteam.la $(LIBDAEMON_LIBS) $(JANSSON_LIBS) $(DBUS_LIBS)
+teamd_LDADD = $(top_builddir)/libteam/libteam.la $(LIBDAEMON_LIBS) $(JANSSON_LIBS) $(DBUS_LIBS) $(ZMQ_LIBS)
bin_PROGRAMS=teamd
teamd_SOURCES=teamd.c teamd_common.c teamd_json.c teamd_config.c teamd_state.c \
teamd_workq.c teamd_events.c teamd_per_port.c \
teamd_option_watch.c teamd_ifinfo_watch.c teamd_link_watch.c \
- teamd_ctl.c teamd_dbus.c teamd_usock.c teamd_sriov.c \
+ teamd_ctl.c teamd_dbus.c teamd_zmq.c teamd_usock.c teamd_sriov.c \
teamd_bpf_chef.c teamd_hash_func.c teamd_balancer.c \
teamd_runner_basic_ones.c teamd_runner_activebackup.c \
teamd_runner_loadbalance.c teamd_runner_lacp.c
diff --git a/teamd/teamd.c b/teamd/teamd.c
index 341b135..b6806d1 100644
--- a/teamd/teamd.c
+++ b/teamd/teamd.c
@@ -49,6 +49,7 @@
#include "teamd_state.h"
#include "teamd_usock.h"
#include "teamd_dbus.h"
+#include "teamd_zmq.h"
#include "teamd_sriov.h"
static const struct teamd_runner *teamd_runner_list[] = {
@@ -97,7 +98,7 @@ static void print_help(const struct teamd_context *ctx) {
" -v --version Show version\n"
" -f --config-file=FILE Load the specified configuration file\n"
" -c --config=TEXT Use given config string (This causes configuration\n"
- " file will be ignored)\n"
+ " file will be ignored)\n"
" -p --pid-file=FILE Use the specified PID file\n"
" -g --debug Increase verbosity\n"
" -r --force-recreate Force team device recreation in case it\n"
@@ -105,6 +106,7 @@ static void print_help(const struct teamd_context *ctx) {
" -t --team-dev=DEVNAME Use the specified team device\n"
" -n --no-ports Start without ports\n"
" -D --dbus-enable Enable D-Bus interface\n"
+ " -Z --zmq-enable=ADDRESS Enable ZeroMQ interface\n"
" -U --usock-enable Enable UNIX domain socket interface\n"
" -u --usock-disable Disable UNIX domain socket interface\n",
ctx->argv0);
@@ -134,12 +136,13 @@ static int parse_command_line(struct teamd_context *ctx,
{ "team-dev", required_argument, NULL, 't' },
{ "no-ports", no_argument, NULL, 'n' },
{ "dbus-enable", no_argument, NULL, 'D' },
+ { "zmq-enable", required_argument, NULL, 'Z' },
{ "usock-enable", no_argument, NULL, 'U' },
{ "usock-disable", no_argument, NULL, 'u' },
{ NULL, 0, NULL, 0 }
};
- while ((opt = getopt_long(argc, argv, "hdkevf:c:p:grt:nDUu",
+ while ((opt = getopt_long(argc, argv, "hdkevf:c:p:grt:nDZ:Uu:",
long_options, NULL)) >= 0) {
switch(opt) {
@@ -196,6 +199,15 @@ static int parse_command_line(struct teamd_context *ctx,
ctx->dbus.enabled = true;
#endif
break;
+ case 'Z':
+#ifndef ENABLE_ZMQ
+ fprintf(stderr, "ZeroMQ support is not compiled-in\n");
+ return -1;
+#else
+ ctx->zmq.enabled = true;
+ ctx->zmq.addr = optarg;
+#endif
+ break;
case 'U':
ctx->usock.enabled = true;
break;
@@ -1189,10 +1201,16 @@ static int teamd_init(struct teamd_context *ctx)
goto usock_fini;
}
+ err = teamd_zmq_init(ctx);
+ if (err) {
+ teamd_log_err("Failed to init zmq.");
+ goto dbus_fini;
+ }
+
err = teamd_add_ports(ctx);
if (err) {
teamd_log_err("Failed to add ports.");
- goto dbus_fini;
+ goto zmq_fini;
}
/*
@@ -1202,11 +1220,12 @@ static int teamd_init(struct teamd_context *ctx)
err = teamd_dbus_expose_name(ctx);
if (err) {
teamd_log_err("Failed to expose dbus name.");
- goto dbus_fini;
+ goto zmq_fini;
}
return 0;
-
+zmq_fini:
+ teamd_zmq_fini(ctx);
dbus_fini:
teamd_dbus_fini(ctx);
usock_fini:
@@ -1246,6 +1265,7 @@ team_free:
static void teamd_fini(struct teamd_context *ctx)
{
+ teamd_zmq_fini(ctx);
teamd_dbus_fini(ctx);
teamd_usock_fini(ctx);
teamd_sriov_fini(ctx);
diff --git a/teamd/teamd.h b/teamd/teamd.h
index b39c71f..de499e1 100644
--- a/teamd/teamd.h
+++ b/teamd/teamd.h
@@ -42,6 +42,10 @@
#include <dbus/dbus.h>
#endif
+#ifdef ENABLE_ZMQ
+#include <zmq.h>
+#endif
+
#define teamd_log_err(args...) daemon_log(LOG_ERR, ##args)
#define teamd_log_warn(args...) daemon_log(LOG_WARNING, ##args)
#define teamd_log_info(args...) daemon_log(LOG_INFO, ##args)
@@ -127,6 +131,14 @@ struct teamd_context {
DBusConnection * con;
} dbus;
#endif
+#ifdef ENABLE_ZMQ
+ struct {
+ bool enabled;
+ void * context;
+ void * sock;
+ char * addr;
+ } zmq;
+#endif
struct {
bool enabled;
int sock;
diff --git a/teamd/teamd_state.c b/teamd/teamd_state.c
index f4aeb4d..6191ea1 100644
--- a/teamd/teamd_state.c
+++ b/teamd/teamd_state.c
@@ -617,6 +617,18 @@ static int setup_state_dbus_enabled_get(struct teamd_context *ctx,
return 0;
}
+static int setup_state_zmq_enabled_get(struct teamd_context *ctx,
+ struct team_state_gsc *gsc,
+ void *priv)
+{
+#ifdef ENABLE_ZMQ
+ gsc->data.bool_val = ctx->zmq.enabled;
+#else
+ gsc->data.bool_val = false;
+#endif
+ return 0;
+}
+
static int setup_state_debug_level_get(struct teamd_context *ctx,
struct team_state_gsc *gsc,
void *priv)
@@ -666,6 +678,11 @@ static const struct teamd_state_val setup_state_vals[] = {
.getter = setup_state_dbus_enabled_get,
},
{
+ .subpath = "zmq_enabled",
+ .type = TEAMD_STATE_ITEM_TYPE_BOOL,
+ .getter = setup_state_zmq_enabled_get,
+ },
+ {
.subpath = "debug_level",
.type = TEAMD_STATE_ITEM_TYPE_INT,
.getter = setup_state_debug_level_get,
diff --git a/teamd/teamd_zmq.c b/teamd/teamd_zmq.c
new file mode 100644
index 0000000..25b5f32
--- /dev/null
+++ b/teamd/teamd_zmq.c
@@ -0,0 +1,282 @@
+/*
+ * teamd_zmq.c - Teamd ZeroMQ socket api
+ * Copyright (C) 2012 Jiří Župka <jzupka(a)redhat.com>
+ *
+ * This library 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.
+ *
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <ctype.h>
+#include <private/misc.h>
+#include <private/list.h>
+#include <team.h>
+
+#include "teamd.h"
+#include "teamd_zmq.h"
+#include "teamd_zmq_common.h"
+#include "teamd_ctl.h"
+#include "teamd_config.h"
+
+struct zmq_ops_priv {
+ char *rcv_msg_args;
+ void *sock;
+};
+
+struct zmq_acc_conn {
+ struct list_item list;
+ int sock;
+};
+
+static int zmq_op_get_args(void *ops_priv, const char *fmt, ...)
+{
+ va_list ap;
+ struct zmq_ops_priv *zmq_ops_priv = ops_priv;
+ char **pstr;
+ char *str;
+ char *rest = zmq_ops_priv->rcv_msg_args;
+ int err = 0;
+
+ va_start(ap, fmt);
+ while (*fmt) {
+ switch (*fmt++) {
+ case 's': /* string */
+ pstr = va_arg(ap, char **);
+ str = teamd_zmq_msg_getline(&rest);
+ if (!str) {
+ teamd_log_err("Insufficient number of arguments in message.");
+ err = -EINVAL;
+ goto out;
+ }
+ *pstr = str;
+ break;
+ default:
+ teamd_log_err("Unknown argument type requested");
+ err = -EINVAL;
+ goto out;
+ }
+ }
+out:
+ va_end(ap);
+ return err;
+}
+
+void my_free_msg(void *data, void *hind){
+ free(data);
+}
+
+static void zmq_custom_send(struct zmq_ops_priv *zmq_ops_priv,
+ char *buf, size_t buflen)
+{
+ int ret;
+
+ ret = zmq_send(zmq_ops_priv->sock, buf, buflen, 0);
+
+ if (ret == -1)
+ teamd_log_warn("zmq: send failed: %s", strerror(errno));
+}
+
+static int zmq_op_reply_err(void *ops_priv, const char *err_code,
+ const char *err_msg)
+{
+ struct zmq_ops_priv *zmq_ops_priv = ops_priv;
+ char *strbuf;
+ int err;
+
+ err = asprintf(&strbuf, "%s\n%s\n%s\n", TEAMD_ZMQ_REPLY_ERR_PREFIX,
+ err_code, err_msg);
+ if (err == -1)
+ return -ENOMEM;
+ zmq_custom_send(zmq_ops_priv, strbuf, strlen(strbuf));
+ free(strbuf);
+ return 0;
+}
+
+static int zmq_op_reply_succ(void *ops_priv, const char *msg)
+{
+ struct zmq_ops_priv *zmq_ops_priv = ops_priv;
+ char *strbuf;
+ int err;
+
+ err = asprintf(&strbuf, "%s\n%s", TEAMD_ZMQ_REPLY_SUCC_PREFIX,
+ msg ? msg : "");
+ if (err == -1)
+ return -ENOMEM;
+ zmq_custom_send(zmq_ops_priv, strbuf, strlen(strbuf));
+ free(strbuf);
+ return 0;
+}
+
+static const struct teamd_ctl_method_ops teamd_zmq_ctl_method_ops = {
+ .get_args = zmq_op_get_args,
+ .reply_err = zmq_op_reply_err,
+ .reply_succ = zmq_op_reply_succ,
+};
+
+static int process_rcv_msg(struct teamd_context *ctx, char *rcv_msg)
+{
+ struct zmq_ops_priv zmq_ops_priv;
+ char *str;
+ char *rest = rcv_msg;
+
+ str = teamd_zmq_msg_getline(&rest);
+ if (!str) {
+ teamd_log_dbg("zmq: Incomplete message.");
+ return 0;
+ }
+ if (strcmp(TEAMD_ZMQ_REQUEST_PREFIX, str)) {
+ teamd_log_dbg("zmq: Unsupported message type.");
+ return 0;
+ }
+
+ str = teamd_zmq_msg_getline(&rest);
+ if (!str) {
+ teamd_log_dbg("zmq: Incomplete message.");
+ return 0;
+ }
+ if (!teamd_ctl_method_exists(str)) {
+ teamd_log_dbg("zmq: Unknown method \"%s\".", str);
+ return 0;
+ }
+
+ zmq_ops_priv.sock = ctx->zmq.sock;
+ zmq_ops_priv.rcv_msg_args = rest;
+
+ teamd_log_dbg("zmq: calling method \"%s\"", str);
+
+ return teamd_ctl_method_call(ctx, str, &teamd_zmq_ctl_method_ops,
+ &zmq_ops_priv);
+}
+
+
+static int callback_zmq(struct teamd_context *ctx, int events, void *priv)
+{
+ int err = 0;
+ int poolmask;
+ size_t poolmask_size = sizeof(poolmask);
+ err = zmq_getsockopt(ctx->zmq.sock, ZMQ_EVENTS, &poolmask, &poolmask_size);
+ if (err == -1){
+ return -errno;
+ }
+ while (poolmask & ZMQ_POLLIN){
+
+ zmq_msg_t msg;
+ zmq_msg_init(&msg);
+ if (zmq_msg_recv(&msg, ctx->zmq.sock, 0) == -1){
+ zmq_msg_close(&msg);
+ return -errno;
+ }
+
+ err = process_rcv_msg(ctx, zmq_msg_data(&msg));
+
+ zmq_msg_close(&msg);
+
+ if (err == -1)
+ break;
+
+ err = zmq_getsockopt(ctx->zmq.sock, ZMQ_EVENTS, &poolmask, &poolmask_size);
+ if (err == -1){
+ return -errno;
+ }
+ }
+ return err;
+}
+
+#define ZMQ_MAX_CLIENT_COUNT 10
+
+static int teamd_zmq_sock_open(struct teamd_context *ctx)
+{
+ int err;
+ void* context = zmq_ctx_new();
+
+ void* sock = zmq_socket(context, ZMQ_REP);
+ if (sock == NULL){
+ teamd_log_err("zmq: Failed to create socket.");
+ return -errno;
+ }
+
+ const char* addr;
+ if (ctx->zmq.addr)
+ addr = ctx->zmq.addr;
+ else{
+ err = teamd_config_string_get(ctx, &addr, "$.runner.addr");
+ if (err)
+ teamd_log_err("zmq: Failed to get address from config.");
+ return err;
+ }
+
+ int rc = zmq_bind(sock, addr);
+ if (rc != 0) {
+ teamd_log_err("zmq: Failed to bind socket.");
+ err = -errno;
+ goto close_sock;
+ }
+
+ ctx->zmq.context = context;
+ ctx->zmq.sock = sock;
+ return 0;
+
+close_sock:
+ zmq_close(sock);
+ zmq_ctx_destroy(context);
+ return err;
+}
+
+static void teamd_zmq_sock_close(struct teamd_context *ctx)
+{
+ zmq_close(ctx->zmq.sock);
+ zmq_ctx_destroy(ctx->zmq.context);
+}
+
+#define ZMQ_CB_NAME "zmq"
+
+int teamd_zmq_init(struct teamd_context *ctx)
+{
+ int err;
+
+ if (!ctx->zmq.enabled)
+ return 0;
+ err = teamd_zmq_sock_open(ctx);
+ if (err)
+ return err;
+
+ int fd;
+ size_t fd_size = sizeof (fd);
+ zmq_getsockopt(ctx->zmq.sock, ZMQ_FD, &fd, &fd_size);
+
+ err = teamd_loop_callback_fd_add(ctx, ZMQ_CB_NAME, ctx, callback_zmq,
+ fd, TEAMD_LOOP_FD_EVENT_READ);
+ if (err)
+ goto sock_close;
+ teamd_loop_callback_enable(ctx, ZMQ_CB_NAME, ctx);
+ return 0;
+sock_close:
+ teamd_zmq_sock_close(ctx);
+ return err;
+}
+
+void teamd_zmq_fini(struct teamd_context *ctx)
+{
+ if (!ctx->zmq.enabled)
+ return;
+ teamd_loop_callback_del(ctx, ZMQ_CB_NAME, ctx);
+ teamd_zmq_sock_close(ctx);
+}
diff --git a/teamd/teamd_zmq.h b/teamd/teamd_zmq.h
new file mode 100644
index 0000000..0a33c83
--- /dev/null
+++ b/teamd/teamd_zmq.h
@@ -0,0 +1,26 @@
+/*
+ * teamd_usock.h - Teamd ZeroMQ api
+ * Copyright (C) 2012 Jiri Pirko <jpirko(a)redhat.com>
+ *
+ * This library 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.
+ *
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _TEAMD_ZMQ_H_
+#define _TEAMD_ZMQ_H_
+
+int teamd_zmq_init(struct teamd_context *ctx);
+void teamd_zmq_fini(struct teamd_context *ctx);
+
+#endif /* _TEAMD_USOCK_H_ */
diff --git a/teamd/teamd_zmq_common.h b/teamd/teamd_zmq_common.h
new file mode 100644
index 0000000..b61b8b4
--- /dev/null
+++ b/teamd/teamd_zmq_common.h
@@ -0,0 +1,58 @@
+/*
+ * teamd_usock_common.h - Teamd unix socket api common things
+ * Copyright (C) 2013 Jiri Pirko <jiri(a)resnulli.us>
+ *
+ * This library 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.
+ *
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _TEAMD_ZMQ_COMMON_H_
+#define _TEAMD_ZMQ_COMMON_H_
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+
+#include "teamd.h"
+
+#define TEAMD_ZMQ_REQUEST_PREFIX "REQUEST"
+#define TEAMD_ZMQ_REPLY_ERR_PREFIX "REPLY_ERROR"
+#define TEAMD_ZMQ_REPLY_SUCC_PREFIX "REPLY_SUCCESS"
+
+static inline char *teamd_zmq_msg_getline(char **p_rest)
+{
+ char *start = NULL;
+ char *rest = NULL;
+ char *str = *p_rest;
+
+ if (!str)
+ return NULL;
+ while (1) {
+ if (*str == '\0')
+ break;
+ if ((*str != '\n') && !start)
+ start = str;
+ if ((*str == '\n') && start) {
+ *str = '\0';
+ rest = str + 1;
+ break;
+ }
+ str++;
+ }
+ *p_rest = rest;
+ return start;
+}
+
+#endif /* _TEAMD_ZMQ_COMMON_H_ */
diff --git a/utils/teamdctl.c b/utils/teamdctl.c
index 69a00d1..a1782d0 100644
--- a/utils/teamdctl.c
+++ b/utils/teamdctl.c
@@ -148,17 +148,19 @@ static int stateview_json_setup_process(char **prunner_name, json_t *dump_json)
char *runner_name;
char *kernel_team_mode_name;
int dbus_enabled;
+ int zmq_enabled;
int debug_level;
int daemonized;
int pid;
char *pid_file;
pr_out("setup:\n");
- err = json_unpack(dump_json, "{s:{s:s, s:s, s:b, s:i, s:b, s:i, s:s}}",
+ err = json_unpack(dump_json, "{s:{s:s, s:s, s:b, s:b, s:i, s:b, s:i, s:s}}",
"setup",
"runner_name", &runner_name,
"kernel_team_mode_name", &kernel_team_mode_name,
"dbus_enabled", &dbus_enabled,
+ "zmq_enabled", &zmq_enabled,
"debug_level", &debug_level,
"daemonized", &daemonized,
"pid", &pid,
@@ -171,6 +173,7 @@ static int stateview_json_setup_process(char **prunner_name, json_t *dump_json)
pr_out("runner: %s\n", runner_name);
pr_out2("kernel team mode: %s\n", kernel_team_mode_name);
pr_out2("D-BUS enabled: %s\n", boolyesno(dbus_enabled));
+ pr_out2("ZeroMQ enabled: %s\n", boolyesno(zmq_enabled));
pr_out2("debug level: %d\n", debug_level);
pr_out2("daemonized: %s\n", boolyesno(daemonized));
pr_out2("PID: %d\n", pid);
@@ -876,6 +879,33 @@ static int check_team_devname(char *team_devname)
return 0;
}
+
+static int check_teamd_team_devname(struct teamdctl *tdc,
+ const char *team_devname)
+{
+ int ret = 0;
+ json_t* root;
+ json_error_t error;
+ json_t* j_device_name;
+ const char* teamd_device_name;
+
+ root = json_loads(teamdctl_config_get_raw(tdc), 0, &error);
+ j_device_name = json_object_get(root, "device");
+
+ teamd_device_name = json_string_value(j_device_name);
+
+ if (strcmp(team_devname, teamd_device_name) != 0){
+ pr_err("Unable to access to %s through connected teamd daemon because daemon controls %s.\n",
+ team_devname, teamd_device_name);
+ ret = -1;
+ }
+
+ json_decref(j_device_name);
+ json_decref(root);
+ return ret;
+}
+
+
static int call_command(struct teamdctl *tdc, int argc, char **argv,
struct command_type *command_type)
{
@@ -900,7 +930,9 @@ static void print_help(const char *argv0) {
" -v --verbose Increase output verbosity\n"
" -o --oneline Force output to one line if possible\n"
" -D --force-dbus Force to use D-Bus interface\n"
- " -U --force-usock Force to use UNIX domain socket interface\n",
+ " -Z --force-zmq=ADDRESS Force to use ZeroMQ interface [-Z[Address]]\n"
+ " -U --force-usock Force to use UNIX domain socket interface\n"
+ " -A --address Address for connection over UNIX domain socket, ZeroMQ.",
argv0);
pr_out("Commands:\n");
for (i = 0; i < COMMAND_TYPE_COUNT; i++) {
@@ -924,6 +956,7 @@ int main(int argc, char **argv)
{ "verbose", no_argument, NULL, 'v' },
{ "oneline", no_argument, NULL, 'o' },
{ "force-dbus", no_argument, NULL, 'D' },
+ { "force-zmq", required_argument, NULL, 'Z' },
{ "force-usock", no_argument, NULL, 'U' },
{ NULL, 0, NULL, 0 }
};
@@ -932,10 +965,12 @@ int main(int argc, char **argv)
struct command_type *command_type;
struct teamdctl *tdc;
int ret;
+ char *addr = NULL;
bool force_dbus = false;
+ bool force_zmq = false;
bool force_usock = false;
- while ((opt = getopt_long(argc, argv, "hvoDU",
+ while ((opt = getopt_long(argc, argv, "hvoDZ:U",
long_options, NULL)) >= 0) {
switch(opt) {
@@ -956,6 +991,15 @@ int main(int argc, char **argv)
force_dbus = true;
#endif
break;
+ case 'Z':
+#ifndef ENABLE_ZMQ
+ fprintf(stderr, "ZeroMQ support is not compiled-in\n");
+ return EXIT_FAILURE;
+#else
+ force_zmq = true;
+ addr = optarg;
+#endif
+ break;
case 'U':
force_usock = true;
break;
@@ -970,8 +1014,10 @@ int main(int argc, char **argv)
}
}
- if (force_usock && force_dbus) {
- pr_err("Either UNIX domain socket interface or D-Bus interface can be forced at a time.\n");
+ if ((force_usock && force_dbus) ||
+ (force_usock && force_zmq) ||
+ (force_dbus && force_zmq)){
+ pr_err("Only one interface could be forced at a time (UNIX domain socket, D-Bus, ZMQ).\n");
print_help(argv0);
return EXIT_FAILURE;
}
@@ -1005,13 +1051,21 @@ int main(int argc, char **argv)
pr_err("teamdctl_alloc failed\n");
return EXIT_FAILURE;
}
- err = teamdctl_connect(tdc, team_devname,
- force_usock ? "usock" : (force_dbus ? "dbus" : NULL));
+
+ err = teamdctl_connect(tdc, team_devname, addr,
+ (force_usock ? "usock" : (force_dbus ?
+ "dbus": (force_zmq ? "zmq" :NULL))));
if (err) {
pr_err("teamdctl_connect failed (%s)\n", strerror(-err));
ret = EXIT_FAILURE;
goto teamdctl_free;
}
+
+ if (check_teamd_team_devname(tdc, team_devname)){
+ ret = EXIT_FAILURE;
+ goto teamdctl_disconnect;
+ }
+
err = call_command(tdc, argc, argv, command_type);
if (err) {
pr_err("command call failed (%s)\n", strerror(-err));
--
1.8.3.1
10 years, 9 months
teamd: Add ZeroMQ interface and repair typo
by Jiří Župka
enable ZeroMQ interface
teamd -Z "tcp:\127.0.0.1:5555"
teamdctl -Z "tcp:\127.0.0.1:5555" config dump
Take care when you use this type of connection.
Security must be ensured by firewall or other tools.
There could be added verification by certificate with SSL layer.
It could be added in next version.
Signed-off-by: Jiří Župka jzupka(a)redhat.com
10 years, 9 months
[PATCH] teamd: improved TX hashing code to handle L4 and vlan headers
by Flavio Leitner
The code tries to avoid as much as possible to use BPF
scratch memory areas, do maths or move data due to
performance reasons. Also, it's not possible to jump back.
The packet information is implict on the each branch taken.
The BPF code is quite long if all protocols are enabled, but
that shouldn't be a problem because the largest jump offset
~120 (goto vlan branch) is not close to socket_filter->k limit
of 256, it is expense as much as a jump to the next instruction
and the CPU should always take the shortest and quick path
possible for any scenario.
Signed-off-by: Flavio Leitner <fbl(a)redhat.com>
---
man/teamd.conf.5 | 21 ++
teamd/teamd_bpf_chef.c | 938 +++++++++++++++++++++++++++++++++++++++++++++---
teamd/teamd_bpf_chef.h | 30 +-
teamd/teamd_hash_func.c | 133 +++----
4 files changed, 965 insertions(+), 157 deletions(-)
diff --git a/man/teamd.conf.5 b/man/teamd.conf.5
index 24eb011..2453b8b 100644
--- a/man/teamd.conf.5
+++ b/man/teamd.conf.5
@@ -103,11 +103,32 @@ List of fragment types (strings) which should be used for packet Tx hash computa
.BR "eth "\(em
Uses source and destination MAC addresses.
.PP
+.BR "vlan "\(em
+Uses VLAN id.
+.PP
.BR "ipv4 "\(em
Uses source and destination IPv4 addresses.
.PP
.BR "ipv6 "\(em
Uses source and destination IPv6 addresses.
+.PP
+.BR "ip "\(em
+Uses source and destination IPv4 and IPv6 addresses.
+.PP
+.BR "l3 "\(em
+Uses source and destination IPv4 and IPv6 addresses.
+.PP
+.BR "tcp "\(em
+Uses source and destination TCP ports.
+.PP
+.BR "udp "\(em
+Uses source and destination UDP ports.
+.PP
+.BR "sctp "\(em
+Uses source and destination SCTP ports.
+.PP
+.BR "l4 "\(em
+Uses source and destination TCP and UDP and SCTP ports.
.RE
.TP
.BR "runner.tx_balancer.name " (string)
diff --git a/teamd/teamd_bpf_chef.c b/teamd/teamd_bpf_chef.c
index a38513f..c6a2429 100644
--- a/teamd/teamd_bpf_chef.c
+++ b/teamd/teamd_bpf_chef.c
@@ -19,10 +19,38 @@
#include <stdlib.h>
#include <errno.h>
+#include <stdbool.h>
#include <linux/filter.h>
#include "teamd_bpf_chef.h"
+/* protocol offsets */
+#define ETH_TYPE_OFFSET 12
+#define IPV4_FLAGS_OFFSET 20
+#define IPV4_PROTO_OFFSET 23
+#define IPV4_FRAG_BITS 0x1fff
+#define IPV6_NEXTHEADER_OFFSET 20
+
+
+/* protocol codes */
+#define PROTOID_IPV4 0x800
+#define PROTOID_IPV6 0x86dd
+#define PROTOID_TCP 0x6
+#define PROTOID_UDP 0x11
+#define PROTOID_SCTP 0x84
+
+/* jump stack flags */
+#define FIX_JT 0x1
+#define FIX_JF 0x2
+#define FIX_K 0x4
+
+
+#define VLAN_HEADER_SIZE 2
+static int vlan_hdr_shift(unsigned int offset)
+{
+ return offset + VLAN_HEADER_SIZE;
+}
+
static int __add_inst(struct sock_fprog *fprog, struct sock_filter *inst)
{
ssize_t newsize;
@@ -46,86 +74,888 @@ static int __add_inst(struct sock_fprog *fprog, struct sock_filter *inst)
goto err_add_inst; \
}
-static void __compile_init(struct sock_fprog *fprog)
+#define bpf_load_byte(pos) \
+ add_inst(fprog, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, pos))
+
+#define bpf_load_half(pos) \
+ add_inst(fprog, BPF_STMT(BPF_LD + BPF_H + BPF_ABS, pos))
+
+#define bpf_load_word(pos) \
+ add_inst(fprog, BPF_STMT(BPF_LD + BPF_W + BPF_ABS, pos))
+
+#define bpf_push_a() \
+ add_inst(fprog, BPF_STMT(BPF_ST, 0))
+
+#define bpf_push_x() \
+ add_inst(fprog, BPF_STMT(BPF_STX, 0))
+
+#define bpf_pop_x() \
+ add_inst(fprog, BPF_STMT(BPF_LDX + BPF_W + BPF_MEM, 0))
+
+#define bpf_calc_hash() \
+ add_inst(fprog, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, \
+ SKF_AD_OFF + SKF_AD_ALU_XOR_X))
+
+#define bpf_move_to_x() \
+ add_inst(fprog, BPF_STMT(BPF_MISC + BPF_TAX, 0));
+
+#define bpf_move_to_a() \
+ add_inst(fprog, BPF_STMT(BPF_MISC + BPF_TXA, 0));
+
+#define bpf_return_a() \
+ add_inst(fprog, BPF_STMT(BPF_RET + BPF_A, 0));
+
+#define bpf_cmp(jt, jf, k, flags) \
+ do { \
+ add_inst(fprog, BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, \
+ k, jt, jf)); \
+ push_addr(fprog, flags); \
+ } while (0)
+
+#define bpf_jump(k) \
+ do { \
+ add_inst(fprog, BPF_JUMP(BPF_JMP + BPF_JA, k, 0, 0)); \
+ push_addr(fprog, FIX_K); \
+ } while (0)
+
+
+#define bpf_and(jt, jf, k, flags) \
+ do { \
+ add_inst(fprog, BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, \
+ k, jt, jf)); \
+ push_addr(fprog, flags); \
+ } while (0)
+
+
+#define bpf_vlan_tag_present() \
+ add_inst(fprog, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, \
+ SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT))
+
+#define bpf_vlan_tag_id() \
+ add_inst(fprog, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, \
+ SKF_AD_OFF + SKF_AD_VLAN_TAG))
+
+#define bpf_ipv4_len_to_x(pos) \
+ add_inst(fprog, BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, pos))
+
+#define bpf_l4v4_port_to_a(pos) \
+ add_inst(fprog, BPF_STMT(BPF_LD + BPF_H + BPF_IND, pos))
+
+#define bpf_hash_return() \
+ do { \
+ bpf_move_to_a(); \
+ bpf_return_a(); \
+ } while(0)
+
+
+enum bpf_labels {
+ LABEL_VLAN_BRANCH,
+ LABEL_NOVLAN_IPV6,
+ LABEL_NOVLAN_L4v4_OUT,
+ LABEL_NOVLAN_TRY_UDP4,
+ LABEL_NOVLAN_L4v4_HASH,
+ LABEL_NOVLAN_TRY_STCP4,
+ LABEL_NOVLAN_IPV6_CONTINUE,
+ LABEL_NOVLAN_TRY_UDP6,
+ LABEL_NOVLAN_L4v6_OUT,
+ LABEL_NOVLAN_L4v6_HASH,
+ LABEL_NOVLAN_TRY_STCP6,
+ LABEL_VLAN_IPV6,
+ LABEL_VLAN_L4v4_OUT,
+ LABEL_VLAN_TRY_UDP4,
+ LABEL_VLAN_L4v4_HASH,
+ LABEL_VLAN_TRY_STCP4,
+ LABEL_VLAN_IPV6_CONTINUE,
+ LABEL_VLAN_TRY_UDP6,
+ LABEL_VLAN_L4v6_OUT,
+ LABEL_VLAN_L4v6_HASH,
+ LABEL_VLAN_TRY_STCP6,
+};
+
+/* stack */
+struct stack_entry {
+ struct stack_entry *next;
+ unsigned int addr;
+ union {
+ enum bpf_labels label;
+ int flags;
+ };
+};
+
+static struct stack_entry *stack_labels;
+static struct stack_entry *stack_addrs;
+
+enum hashing_flags {
+ HASH_ETH,
+ HASH_VLAN,
+ HASH_VLAN_IPV4,
+ HASH_VLAN_IPV6,
+ HASH_VLAN_UDP4,
+ HASH_VLAN_TCP4,
+ HASH_VLAN_SCTP4,
+ HASH_VLAN_UDP6,
+ HASH_VLAN_TCP6,
+ HASH_VLAN_SCTP6,
+ HASH_NOVLAN_IPV4,
+ HASH_NOVLAN_IPV6,
+ HASH_NOVLAN_UDP4,
+ HASH_NOVLAN_TCP4,
+ HASH_NOVLAN_SCTP4,
+ HASH_NOVLAN_UDP6,
+ HASH_NOVLAN_TCP6,
+ HASH_NOVLAN_SCTP6,
+};
+
+struct hash_flags {
+ unsigned int required;
+ unsigned int built;
+};
+
+static struct hash_flags hflags;
+
+static void hash_flags_init(struct hash_flags *flags)
{
- fprog->len = 0;
- fprog->filter = NULL;
+ flags->required = 0;
+ flags->built = 0;
}
-void teamd_bpf_desc_compile_start(struct sock_fprog *fprog)
+static int hash_test_and_set_flag(struct hash_flags *flags,
+ enum hashing_flags hflag)
{
- __compile_init(fprog);
+ int flag = 1 << hflag;
+ int ret;
+
+ ret = 0;
+ if (flags->required & flag) {
+ flags->built |= flag;
+ ret = 1;
+ }
+
+ return ret;
}
-void teamd_bpf_desc_compile_release(struct sock_fprog *fprog)
+static int hash_is_complete(struct hash_flags *flags)
{
- free(fprog->filter);
- __compile_init(fprog);
+ return flags->built == flags->required;
}
-int teamd_bpf_desc_compile_finish(struct sock_fprog *fprog)
+static int hash_set_enable(struct hash_flags *flags, enum hashing_flags hflag)
+{
+ flags->required |= (1 << hflag);
+ return 0;
+}
+
+static int hash_is_enabled(struct hash_flags *flags, enum hashing_flags hflag)
+{
+ return (flags->required & (1 << hflag));
+}
+
+static int hash_is_l4v4_enabled(struct hash_flags *flags)
+{
+ if (hash_is_enabled(flags, HASH_NOVLAN_TCP4) ||
+ hash_is_enabled(flags, HASH_NOVLAN_UDP4) ||
+ hash_is_enabled(flags, HASH_NOVLAN_SCTP4) ||
+ hash_is_enabled(flags, HASH_VLAN_TCP4) ||
+ hash_is_enabled(flags, HASH_VLAN_UDP4) ||
+ hash_is_enabled(flags, HASH_VLAN_SCTP4))
+ return 1;
+
+ return 0;
+}
+
+static int hash_is_l4v6_enabled(struct hash_flags *flags)
+{
+ if (hash_is_enabled(flags, HASH_NOVLAN_TCP6) ||
+ hash_is_enabled(flags, HASH_NOVLAN_UDP6) ||
+ hash_is_enabled(flags, HASH_NOVLAN_SCTP6) ||
+ hash_is_enabled(flags, HASH_VLAN_TCP6) ||
+ hash_is_enabled(flags, HASH_VLAN_UDP6) ||
+ hash_is_enabled(flags, HASH_VLAN_SCTP6))
+ return 1;
+
+ return 0;
+}
+
+static int hash_is_novlan_l3l4_enabled(struct hash_flags *flags)
+{
+
+ if (hash_is_enabled(flags, HASH_NOVLAN_IPV4) ||
+ hash_is_enabled(flags, HASH_NOVLAN_IPV6) ||
+ hash_is_enabled(flags, HASH_NOVLAN_TCP4) ||
+ hash_is_enabled(flags, HASH_NOVLAN_UDP4) ||
+ hash_is_enabled(flags, HASH_NOVLAN_SCTP4) ||
+ hash_is_enabled(flags, HASH_NOVLAN_TCP6) ||
+ hash_is_enabled(flags, HASH_NOVLAN_UDP6) ||
+ hash_is_enabled(flags, HASH_NOVLAN_SCTP6))
+ return 1;
+
+ return 0;
+}
+
+static void stack_init(void)
+{
+ stack_labels = NULL;
+ stack_addrs = NULL;
+}
+
+static void __stack_release(struct stack_entry *root)
+{
+ struct stack_entry *p;
+ struct stack_entry *pn;
+
+ p = root;
+ while (p) {
+ pn = p->next;
+ free(p);
+ p = pn;
+ }
+}
+
+static void stack_release(void)
+{
+ __stack_release(stack_labels);
+ __stack_release(stack_addrs);
+ stack_init();
+}
+
+static int push_addr(struct sock_fprog *fprog, int flags)
+{
+ struct stack_entry *pa;
+
+ pa = malloc(sizeof(struct stack_entry));
+ if (!pa)
+ return -ENOMEM;
+
+ pa->addr = fprog->len - 1;
+ pa->flags = flags;
+
+ pa->next = stack_addrs;
+ stack_addrs = pa;
+
+ return 0;
+}
+
+static struct stack_entry *__find_label(enum bpf_labels label)
+{
+ struct stack_entry *p;
+
+ p = stack_labels;
+ while (p) {
+ if (p->label == label)
+ return p;
+
+ p = p->next;
+ }
+
+ return NULL;
+}
+
+static int push_label(struct sock_fprog *fprog, enum bpf_labels label)
+{
+ struct stack_entry *pl;
+
+ if (__find_label(label))
+ return -EEXIST;
+
+ pl = malloc(sizeof(struct stack_entry));
+ if (!pl)
+ return -ENOMEM;
+
+ pl->addr = fprog->len;
+ pl->label = label;
+
+ pl->next = stack_labels;
+ stack_labels = pl;
+
+ return 0;
+}
+
+static int stack_resolve_offsets(struct sock_fprog *fprog)
+{
+ struct stack_entry *paddr;
+ struct stack_entry *naddr;
+ struct stack_entry *plabel;
+ struct stack_entry *nlabel;
+ struct sock_filter *sf;
+ int offset;
+
+ paddr = stack_addrs;
+ while (paddr) {
+ sf = fprog->filter + paddr->addr;
+
+ if (paddr->flags & ~(FIX_K|FIX_JT|FIX_JF))
+ return -EINVAL;
+
+ if (paddr->flags & FIX_K) {
+ plabel = __find_label(sf->k);
+ if (!plabel)
+ return -ENOENT;
+
+ offset = plabel->addr - paddr->addr - 1;
+ if (offset < 0 || offset > 255)
+ return -EINVAL;
+ sf->k = offset;
+ }
+
+ if (paddr->flags & FIX_JT) {
+ plabel = __find_label(sf->jt);
+ if (!plabel)
+ return -ENOENT;
+
+ offset = plabel->addr - paddr->addr - 1;
+ if (offset < 0 || offset > 255)
+ return -EINVAL;
+ sf->jt = offset;
+ }
+
+ if (paddr->flags & FIX_JF) {
+ plabel = __find_label(sf->jf);
+ if (!plabel)
+ return -ENOENT;
+
+ offset = plabel->addr - paddr->addr - 1;
+ if (offset < 0 || offset > 255)
+ return -EINVAL;
+ sf->jf = offset;
+ }
+
+ naddr = paddr->next;
+ free(paddr);
+ paddr = naddr;
+ }
+
+ plabel = stack_labels;
+ while (plabel) {
+ nlabel = plabel->next;
+ free(plabel);
+ plabel = nlabel;
+ }
+
+ stack_init();
+ return 0;
+}
+
+static int bpf_eth_hash(struct sock_fprog *fprog)
{
int err;
- /*
- * Return hash which is in X. Note that in case of no pattern match,
- * X will have value 0.
- */
- add_inst(fprog, BPF_STMT(BPF_MISC + BPF_TXA, 0));
- add_inst(fprog, BPF_STMT(BPF_RET + BPF_A, 0));
+ /* hash dest mac addr */
+ bpf_load_word(2);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_half(0);
+ bpf_calc_hash();
+ bpf_move_to_x();
+
+ /* hash source mac addr */
+ bpf_load_word(8);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_half(6);
+ bpf_calc_hash();
+ bpf_move_to_x();
return 0;
+
err_add_inst:
return err;
}
-int teamd_bpf_desc_compile_frag(struct sock_fprog *fprog,
- const struct teamd_bpf_desc_frag *frag)
+static int bpf_vlan_hash(struct sock_fprog *fprog)
{
int err;
- int i;
- unsigned short start_index = fprog->len;
-
- /*
- * Put pattern matching first. Patterns are checked sequentially,
- * if one pattern match fails, jump to the end is going to be done.
- * Note that end of frag is not known atm so put __JMP_END there for
- * now. Last loop in this function will correct this.
- */
-#define __JMP_END 0xFF
-
- for (i = 0; i < frag->pattern_count; i++) {
- const struct teamd_bpf_pattern *pattern = &frag->pattern[i];
-
- add_inst(fprog, BPF_STMT(BPF_LD + pattern->type + BPF_ABS,
- pattern->offset));
- add_inst(fprog, BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
- pattern->value, 0, __JMP_END));
- }
- /*
- * If patterns matches (no JUMP_TO_END), compute hash from specified
- * memory fields. Store the hash in X.
- */
- for (i = 0; i < frag->hash_field_count; i++) {
- const struct teamd_bpf_hash_field *hfield = &frag->hash_field[i];
-
- add_inst(fprog, BPF_STMT(BPF_LD + hfield->type + BPF_ABS,
- hfield->offset));
- add_inst(fprog, BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
- SKF_AD_OFF + SKF_AD_ALU_XOR_X));
- add_inst(fprog, BPF_STMT(BPF_MISC + BPF_TAX, 0));
+ bpf_vlan_tag_id();
+ bpf_calc_hash();
+ bpf_move_to_x();
+
+err_add_inst:
+ return err;
+}
+
+static int __bpf_ipv4_hash(struct sock_fprog *fprog, bool vlan)
+{
+ int vlan_shift = vlan ? vlan_hdr_shift(0) : 0;
+ int err;
+
+ bpf_load_word(26 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_word(30 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ return 0;
+
+err_add_inst:
+ return err;
+}
+
+static int bpf_vlan_ipv4_hash(struct sock_fprog *fprog)
+{
+ return __bpf_ipv4_hash(fprog, true);
+}
+
+static int bpf_novlan_ipv4_hash(struct sock_fprog *fprog)
+{
+ return __bpf_ipv4_hash(fprog, false);
+}
+
+static int __bpf_ipv6_hash(struct sock_fprog *fprog, bool vlan)
+{
+ int vlan_shift = vlan ? vlan_hdr_shift(0) : 0;
+ int err;
+
+ bpf_load_word(22 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_word(26 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_word(30 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_word(34 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_word(38 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_word(42 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_word(46 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ bpf_load_word(50 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ return 0;
+
+err_add_inst:
+ return err;
+}
+
+static int bpf_vlan_ipv6_hash(struct sock_fprog *fprog)
+{
+ return __bpf_ipv6_hash(fprog, true);
+}
+
+static int bpf_novlan_ipv6_hash(struct sock_fprog *fprog)
+{
+ return __bpf_ipv6_hash(fprog, false);
+}
+
+static int __bpf_l4v4_hash(struct sock_fprog *fprog, bool vlan)
+{
+ int vlan_shift = vlan ? vlan_hdr_shift(0) : 0;
+ int err;
+
+ bpf_push_x();
+ bpf_ipv4_len_to_x(14 + vlan_shift);
+ /* source port offset */
+ bpf_l4v4_port_to_a(14 + vlan_shift);
+ bpf_pop_x();
+ bpf_calc_hash();
+ bpf_push_a();
+ bpf_ipv4_len_to_x(14 + vlan_shift);
+ /* dest port offset */
+ bpf_l4v4_port_to_a(16 + vlan_shift);
+ bpf_pop_x();
+ bpf_calc_hash();
+ bpf_move_to_x();
+ return 0;
+
+err_add_inst:
+ return err;
+}
+
+static int bpf_vlan_l4v4_hash(struct sock_fprog *fprog)
+{
+ return __bpf_l4v4_hash(fprog, true);
+}
+
+static int bpf_novlan_l4v4_hash(struct sock_fprog *fprog)
+{
+ return __bpf_l4v4_hash(fprog, false);
+}
+
+static int __bpf_l4v6_hash(struct sock_fprog *fprog, bool vlan)
+{
+ int vlan_shift = vlan ? vlan_hdr_shift(0) : 0;
+ int err;
+
+ bpf_load_half(54 + vlan_shift);
+ bpf_calc_hash();
+ bpf_load_half(56 + vlan_shift);
+ bpf_calc_hash();
+ bpf_move_to_x();
+ return 0;
+
+err_add_inst:
+ return err;
+}
+
+static int bpf_vlan_l4v6_hash(struct sock_fprog *fprog)
+{
+ return __bpf_l4v6_hash(fprog, true);
+}
+
+static int bpf_novlan_l4v6_hash(struct sock_fprog *fprog)
+{
+ return __bpf_l4v6_hash(fprog, false);
+}
+
+/* bpf_create_code:
+ * This function creates the entire bpf hashing code and follows
+ * this scheme:
+ *
+ * start
+ * V
+ * handle ethernet
+ * V
+ * check vlan tag presense
+ * |yes | no
+ * handle |
+ * vlan |
+ * V V
+ * +----- ipv4/ipv6? +-- ipv4/ipv6?--+
+ * |ipv4 |ipv6 | ipv4 |ipv6
+ * +--frag?-+ | +-frag?-+ |
+ * |yes |no | |yes |no |
+ * return V V return V V
+ * handle handle handle handle
+ * ipv4 ipv6 ipv4 ipv6
+ * | | | |
+ * proto? proto? proto? proto?
+ *
+ * for each branch above:
+ * |tcp |udp |sctp |none
+ * | | | |
+ * handle |
+ * return <------+
+ */
+static int bpf_create_code(struct sock_fprog *fprog, struct hash_flags *flags)
+{
+ int err;
+
+ /* generate the ethernet hashing code */
+ if (hash_test_and_set_flag(flags, HASH_ETH))
+ bpf_eth_hash(fprog);
+
+ if (hash_is_complete(flags)) {
+ bpf_hash_return();
+ /* there is no need to keep going, all done */
+ return 0;
}
- /* Correct jump offsets */
- for (i = start_index; i < fprog->len; i++) {
- struct sock_filter *filter = &fprog->filter[i];
+ bpf_vlan_tag_present();
+ bpf_cmp(0, LABEL_VLAN_BRANCH, 0, FIX_JF);
+ if (!hash_is_novlan_l3l4_enabled(flags))
+ bpf_hash_return();
+
+ /* no vlan branch */
+ bpf_load_half(ETH_TYPE_OFFSET);
+ bpf_cmp(0, LABEL_NOVLAN_IPV6, PROTOID_IPV4, FIX_JF);
+
+ /* no vlan ipv4 branch */
+ if (hash_test_and_set_flag(flags, HASH_NOVLAN_IPV4))
+ bpf_novlan_ipv4_hash(fprog);
+
+ if (!hash_is_l4v4_enabled(flags))
+ bpf_hash_return();
+
+ /* no vlan ipv4 L4 */
+ /* ignore IP frags */
+ bpf_load_half(IPV4_FLAGS_OFFSET);
+ bpf_and(LABEL_NOVLAN_L4v4_OUT, 0, IPV4_FRAG_BITS, FIX_JT);
+
+ /* L4 protocol check */
+ bpf_load_byte(IPV4_PROTO_OFFSET);
+ bpf_cmp(0, LABEL_NOVLAN_TRY_UDP4, PROTOID_TCP, FIX_JF);
+
+ if (hash_test_and_set_flag(flags, HASH_NOVLAN_TCP4))
+ bpf_jump(LABEL_NOVLAN_L4v4_HASH);
+ else
+ bpf_jump(LABEL_NOVLAN_L4v4_OUT);
+
+ /* no vlan try udp4 */
+ push_label(fprog, LABEL_NOVLAN_TRY_UDP4);
+ bpf_cmp(0, LABEL_NOVLAN_TRY_STCP4, PROTOID_UDP, FIX_JF);
+ if (hash_test_and_set_flag(flags, HASH_NOVLAN_UDP4))
+ bpf_jump(LABEL_NOVLAN_L4v4_HASH);
+ else
+ bpf_jump(LABEL_NOVLAN_L4v4_OUT);
+
+ /* no vlan try sctp4 */
+ push_label(fprog, LABEL_NOVLAN_TRY_STCP4);
+ bpf_cmp(0, LABEL_NOVLAN_L4v4_OUT, PROTOID_SCTP, FIX_JF);
+ if (!hash_test_and_set_flag(flags, HASH_NOVLAN_SCTP4))
+ bpf_jump(LABEL_NOVLAN_L4v4_OUT);
+
+ /* no vlan L4v4 hashing */
+ push_label(fprog, LABEL_NOVLAN_L4v4_HASH);
+ bpf_novlan_l4v4_hash(fprog);
+
+ /* no vlan L4v4 out: */
+ push_label(fprog, LABEL_NOVLAN_L4v4_OUT);
+ bpf_hash_return();
+
+ /* no vlan ipv6 branch */
+ push_label(fprog, LABEL_NOVLAN_IPV6);
+ bpf_cmp(LABEL_NOVLAN_IPV6_CONTINUE, 0, PROTOID_IPV6, FIX_JT);
+ bpf_hash_return();
- if (filter->code == BPF_JMP + BPF_JEQ + BPF_K &&
- filter->jf == __JMP_END)
- filter->jf = fprog->len - i - 1;
+ /* no vlan ipv6 continue */
+ push_label(fprog, LABEL_NOVLAN_IPV6_CONTINUE);
+ if (hash_test_and_set_flag(flags, HASH_NOVLAN_IPV6))
+ bpf_novlan_ipv6_hash(fprog);
+
+ if (!hash_is_l4v6_enabled(flags))
+ bpf_hash_return();
+
+ /* no vlan ipv6 l4 branch */
+ /* L4 protocol check (Next Header) */
+ bpf_load_byte(IPV6_NEXTHEADER_OFFSET);
+ bpf_cmp(0, LABEL_NOVLAN_TRY_UDP6, PROTOID_TCP, FIX_JF);
+
+ if (hash_test_and_set_flag(flags, HASH_NOVLAN_TCP6))
+ bpf_jump(LABEL_NOVLAN_L4v6_HASH);
+ else
+ bpf_jump(LABEL_NOVLAN_L4v6_OUT);
+
+ /* no vlan check udp6 */
+ push_label(fprog, LABEL_NOVLAN_TRY_UDP6);
+ bpf_cmp(0, LABEL_NOVLAN_TRY_STCP6, PROTOID_UDP, FIX_JF);
+ if (hash_test_and_set_flag(flags, HASH_NOVLAN_UDP6))
+ bpf_jump(LABEL_NOVLAN_L4v6_HASH);
+ else
+ bpf_jump(LABEL_NOVLAN_L4v6_OUT);
+
+ /* no vlan check sctp6 */
+ push_label(fprog, LABEL_NOVLAN_TRY_STCP6);
+ bpf_cmp(0, LABEL_NOVLAN_L4v6_OUT, PROTOID_SCTP, FIX_JF);
+ if (!hash_test_and_set_flag(flags, HASH_NOVLAN_SCTP4))
+ bpf_jump(LABEL_NOVLAN_L4v6_OUT);
+
+ /* no vlan l4v6 hashing */
+ push_label(fprog, LABEL_NOVLAN_L4v6_HASH);
+ bpf_novlan_l4v6_hash(fprog);
+
+ /* no vlan l4v6 out */
+ push_label(fprog, LABEL_NOVLAN_L4v6_OUT);
+ bpf_hash_return();
+
+ /* vlan branch */
+ push_label(fprog, LABEL_VLAN_BRANCH);
+ if (hash_test_and_set_flag(flags, HASH_VLAN))
+ bpf_vlan_hash(fprog);
+
+ if (hash_is_complete(flags)) {
+ bpf_hash_return();
+ return 0;
}
+ bpf_load_half(vlan_hdr_shift(ETH_TYPE_OFFSET));
+ bpf_cmp(0, LABEL_VLAN_IPV6, PROTOID_IPV4, FIX_JF);
+ /* vlan ipv4 branch */
+ if (hash_test_and_set_flag(flags, HASH_VLAN_IPV4))
+ bpf_vlan_ipv4_hash(fprog);
+
+ if (!hash_is_l4v4_enabled(flags))
+ bpf_hash_return();
+
+ /* vlan ipv4 L4 */
+ /* ignore IP frags */
+ bpf_load_half(vlan_hdr_shift(IPV4_FLAGS_OFFSET));
+ bpf_and(LABEL_VLAN_L4v4_OUT, 0, IPV4_FRAG_BITS, FIX_JT);
+
+ /* L4 protocol check */
+ bpf_load_byte(vlan_hdr_shift(IPV4_PROTO_OFFSET));
+ bpf_cmp(0, LABEL_VLAN_TRY_UDP4, PROTOID_TCP, FIX_JF);
+
+ if (hash_test_and_set_flag(flags, HASH_VLAN_TCP4))
+ bpf_jump(LABEL_VLAN_L4v4_HASH);
+ else
+ bpf_jump(LABEL_VLAN_L4v4_OUT);
+
+ /* vlan try udp4 */
+ push_label(fprog, LABEL_VLAN_TRY_UDP4);
+ bpf_cmp(0, LABEL_VLAN_TRY_STCP4, PROTOID_UDP, FIX_JF);
+ if (hash_test_and_set_flag(flags, HASH_VLAN_UDP4))
+ bpf_jump(LABEL_VLAN_L4v4_HASH);
+ else
+ bpf_jump(LABEL_VLAN_L4v4_OUT);
+
+ /* vlan try sctp4 */
+ push_label(fprog, LABEL_VLAN_TRY_STCP4);
+ bpf_cmp(0, LABEL_VLAN_L4v4_OUT, PROTOID_SCTP, FIX_JF);
+ if (!hash_test_and_set_flag(flags, HASH_VLAN_SCTP4))
+ bpf_jump(LABEL_VLAN_L4v4_OUT);
+
+ /* vlan L4v4 hashing */
+ push_label(fprog, LABEL_VLAN_L4v4_HASH);
+ bpf_vlan_l4v4_hash(fprog);
+
+ /* vlan L4v4 out: */
+ push_label(fprog, LABEL_VLAN_L4v4_OUT);
+ bpf_hash_return();
+
+ /* vlan ipv6 branch */
+ push_label(fprog, LABEL_VLAN_IPV6);
+ bpf_cmp(LABEL_VLAN_IPV6_CONTINUE, 0, PROTOID_IPV6, FIX_JT);
+ bpf_hash_return();
+
+ /* vlan ipv6 continue */
+ push_label(fprog, LABEL_VLAN_IPV6_CONTINUE);
+ if (hash_test_and_set_flag(flags, HASH_VLAN_IPV6))
+ bpf_vlan_ipv6_hash(fprog);
+
+ if (!hash_is_l4v6_enabled(flags))
+ bpf_hash_return();
+
+ /* vlan ipv6 l4 branch */
+ /* L4 protocol check (Next Header) */
+ bpf_load_byte(vlan_hdr_shift(IPV6_NEXTHEADER_OFFSET));
+ bpf_cmp(0, LABEL_VLAN_TRY_UDP6, PROTOID_TCP, FIX_JF);
+
+ if (hash_test_and_set_flag(flags, HASH_VLAN_TCP6))
+ bpf_jump(LABEL_VLAN_L4v6_HASH);
+ else
+ bpf_jump(LABEL_VLAN_L4v6_OUT);
+
+ /* vlan check udp6 */
+ push_label(fprog, LABEL_VLAN_TRY_UDP6);
+ bpf_cmp(0, LABEL_VLAN_TRY_STCP6, PROTOID_UDP, FIX_JF);
+ if (hash_test_and_set_flag(flags, HASH_VLAN_UDP6))
+ bpf_jump(LABEL_VLAN_L4v6_HASH);
+ else
+ bpf_jump(LABEL_VLAN_L4v6_OUT);
+
+ /* vlan check sctp6 */
+ push_label(fprog, LABEL_VLAN_TRY_STCP6);
+ bpf_cmp(0, LABEL_VLAN_L4v6_OUT, PROTOID_SCTP, FIX_JF);
+ if (!hash_test_and_set_flag(flags, HASH_VLAN_SCTP4))
+ bpf_jump(LABEL_VLAN_L4v6_OUT);
+
+ /* vlan l4v6 hashing */
+ push_label(fprog, LABEL_VLAN_L4v6_HASH);
+ bpf_vlan_l4v6_hash(fprog);
+
+ /* vlan l4v6 out */
+ push_label(fprog, LABEL_VLAN_L4v6_OUT);
+ bpf_hash_return();
return 0;
+
err_add_inst:
return err;
}
+
+int teamd_bpf_desc_add_frag(struct sock_fprog *fprog,
+ const struct teamd_bpf_desc_frag *frag)
+{
+ switch (frag->hproto) {
+ case PROTO_ETH:
+ hash_set_enable(&hflags, HASH_ETH);
+ break;
+
+ case PROTO_VLAN:
+ hash_set_enable(&hflags, HASH_VLAN);
+ break;
+
+ case PROTO_IP:
+ case PROTO_L3:
+ hash_set_enable(&hflags, HASH_VLAN_IPV4);
+ hash_set_enable(&hflags, HASH_VLAN_IPV6);
+ hash_set_enable(&hflags, HASH_NOVLAN_IPV4);
+ hash_set_enable(&hflags, HASH_NOVLAN_IPV6);
+ break;
+
+ case PROTO_IPV4:
+ hash_set_enable(&hflags, HASH_VLAN_IPV4);
+ hash_set_enable(&hflags, HASH_NOVLAN_IPV4);
+ break;
+
+ case PROTO_IPV6:
+ hash_set_enable(&hflags, HASH_VLAN_IPV6);
+ hash_set_enable(&hflags, HASH_NOVLAN_IPV6);
+ break;
+
+ case PROTO_L4:
+ hash_set_enable(&hflags, HASH_VLAN_TCP4);
+ hash_set_enable(&hflags, HASH_VLAN_TCP6);
+ hash_set_enable(&hflags, HASH_VLAN_UDP4);
+ hash_set_enable(&hflags, HASH_VLAN_UDP6);
+ hash_set_enable(&hflags, HASH_VLAN_SCTP4);
+ hash_set_enable(&hflags, HASH_VLAN_SCTP6);
+ hash_set_enable(&hflags, HASH_NOVLAN_TCP4);
+ hash_set_enable(&hflags, HASH_NOVLAN_TCP6);
+ hash_set_enable(&hflags, HASH_NOVLAN_UDP4);
+ hash_set_enable(&hflags, HASH_NOVLAN_UDP6);
+ hash_set_enable(&hflags, HASH_NOVLAN_SCTP4);
+ hash_set_enable(&hflags, HASH_NOVLAN_SCTP6);
+ break;
+
+ case PROTO_TCP:
+ hash_set_enable(&hflags, HASH_VLAN_TCP4);
+ hash_set_enable(&hflags, HASH_VLAN_TCP6);
+ hash_set_enable(&hflags, HASH_NOVLAN_TCP4);
+ hash_set_enable(&hflags, HASH_NOVLAN_TCP6);
+ break;
+
+ case PROTO_UDP:
+ hash_set_enable(&hflags, HASH_VLAN_UDP4);
+ hash_set_enable(&hflags, HASH_VLAN_UDP6);
+ hash_set_enable(&hflags, HASH_NOVLAN_UDP4);
+ hash_set_enable(&hflags, HASH_NOVLAN_UDP6);
+ break;
+
+ case PROTO_SCTP:
+ hash_set_enable(&hflags, HASH_VLAN_SCTP4);
+ hash_set_enable(&hflags, HASH_VLAN_SCTP6);
+ hash_set_enable(&hflags, HASH_NOVLAN_SCTP4);
+ hash_set_enable(&hflags, HASH_NOVLAN_SCTP6);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void __compile_init(struct sock_fprog *fprog)
+{
+ fprog->len = 0;
+ fprog->filter = NULL;
+ stack_init();
+ hash_flags_init(&hflags);
+}
+
+void teamd_bpf_desc_compile_start(struct sock_fprog *fprog)
+{
+ __compile_init(fprog);
+}
+
+void teamd_bpf_desc_compile_release(struct sock_fprog *fprog)
+{
+ free(fprog->filter);
+ __compile_init(fprog);
+ stack_release();
+ hash_flags_init(&hflags);
+}
+
+int teamd_bpf_desc_compile_finish(struct sock_fprog *fprog)
+{
+ return 0;
+}
+
+int teamd_bpf_desc_compile(struct sock_fprog *fprog)
+{
+ int err;
+
+ err = bpf_create_code(fprog, &hflags);
+ if (err)
+ return err;
+
+ err = stack_resolve_offsets(fprog);
+ return err;
+}
diff --git a/teamd/teamd_bpf_chef.h b/teamd/teamd_bpf_chef.h
index f86405c..3b7d373 100644
--- a/teamd/teamd_bpf_chef.h
+++ b/teamd/teamd_bpf_chef.h
@@ -23,15 +23,17 @@
#include <stdint.h>
#include <sys/types.h>
-struct teamd_bpf_pattern {
- size_t offset;
- uint8_t type; /* BPF_B / BPF_H / BPF_W */
- uint32_t value;
-};
-
-struct teamd_bpf_hash_field {
- size_t offset; /* offset of element in header */
- uint8_t type; /* BPF_B / BPF_H / BPF_W */
+enum hashing_protos {
+ PROTO_ETH,
+ PROTO_VLAN,
+ PROTO_IP,
+ PROTO_IPV4,
+ PROTO_IPV6,
+ PROTO_L3,
+ PROTO_TCP,
+ PROTO_UDP,
+ PROTO_SCTP,
+ PROTO_L4,
};
/*
@@ -41,16 +43,14 @@ struct teamd_bpf_hash_field {
*/
struct teamd_bpf_desc_frag {
char * name;
- const struct teamd_bpf_pattern * pattern;
- unsigned int pattern_count;
- const struct teamd_bpf_hash_field * hash_field;
- unsigned int hash_field_count;
+ enum hashing_protos hproto;
};
void teamd_bpf_desc_compile_start(struct sock_fprog *fprog);
void teamd_bpf_desc_compile_release(struct sock_fprog *fprog);
+int teamd_bpf_desc_compile(struct sock_fprog *fprog);
int teamd_bpf_desc_compile_finish(struct sock_fprog *fprog);
-int teamd_bpf_desc_compile_frag(struct sock_fprog *fprog,
- const struct teamd_bpf_desc_frag *frag);
+int teamd_bpf_desc_add_frag(struct sock_fprog *fprog,
+ const struct teamd_bpf_desc_frag *frag);
#endif /* _TEAMD_BPF_CHEF_H_ */
diff --git a/teamd/teamd_hash_func.c b/teamd/teamd_hash_func.c
index ff4094a..e7d57ae 100644
--- a/teamd/teamd_hash_func.c
+++ b/teamd/teamd_hash_func.c
@@ -26,113 +26,65 @@
#include "teamd_config.h"
#include "teamd_bpf_chef.h"
-static const struct teamd_bpf_hash_field eth_hdr_hash_field[] = {
- { /* First 4 bytes of src addr */
- .offset = 0,
- .type = BPF_W,
- },
- { /* Last 2 bytes of src addr */
- .offset = 4,
- .type = BPF_H,
- },
- { /* First 4 bytes of dst addr */
- .offset = 6,
- .type = BPF_W,
- },
- { /* Last 2 bytes of dst addr */
- .offset = 10,
- .type = BPF_H,
- },
-};
-
static const struct teamd_bpf_desc_frag eth_hdr_frag = {
.name = "eth",
- .hash_field = eth_hdr_hash_field,
- .hash_field_count = ARRAY_SIZE(eth_hdr_hash_field),
+ .hproto = PROTO_ETH,
};
-static const struct teamd_bpf_pattern ipv4_hdr_pattern[] = {
- { /* type IPv4 */
- .offset = 12,
- .type = BPF_H,
- .value = 0x0800,
- },
-};
-
-static const struct teamd_bpf_hash_field ipv4_hdr_hash_field[] = {
- { /* 4 bytes of src addr */
- .offset = 26,
- .type = BPF_W,
- },
- { /* 4 bytes of dst addr */
- .offset = 30,
- .type = BPF_W,
- },
+static const struct teamd_bpf_desc_frag vlan_hdr_frag = {
+ .name = "vlan",
+ .hproto = PROTO_VLAN,
};
static const struct teamd_bpf_desc_frag ipv4_hdr_frag = {
.name = "ipv4",
- .pattern = ipv4_hdr_pattern,
- .pattern_count = ARRAY_SIZE(ipv4_hdr_pattern),
- .hash_field = ipv4_hdr_hash_field,
- .hash_field_count = ARRAY_SIZE(ipv4_hdr_hash_field),
+ .hproto = PROTO_IPV4,
};
-static const struct teamd_bpf_pattern ipv6_hdr_pattern[] = {
- { /* type IPv6 */
- .offset = 12,
- .type = BPF_H,
- .value = 0x86dd,
- },
+static const struct teamd_bpf_desc_frag ipv6_hdr_frag = {
+ .name = "ipv6",
+ .hproto = PROTO_IPV6,
};
-static const struct teamd_bpf_hash_field ipv6_hdr_hash_field[] = {
- { /* first 4 bytes of src addr */
- .offset = 22,
- .type = BPF_W,
- },
- { /* second 4 bytes of src addr */
- .offset = 26,
- .type = BPF_W,
- },
- { /* third 4 bytes of src addr */
- .offset = 30,
- .type = BPF_W,
- },
- { /* fourth 4 bytes of src addr */
- .offset = 34,
- .type = BPF_W,
- },
- { /* first 4 bytes of dst addr */
- .offset = 38,
- .type = BPF_W,
- },
- { /* second 4 bytes of dst addr */
- .offset = 42,
- .type = BPF_W,
- },
- { /* third 4 bytes of dst addr */
- .offset = 46,
- .type = BPF_W,
- },
- { /* fourth 4 bytes of dst addr */
- .offset = 50,
- .type = BPF_W,
- },
+static const struct teamd_bpf_desc_frag ip_hdr_frag = {
+ .name = "ip",
+ .hproto = PROTO_IP,
};
-static const struct teamd_bpf_desc_frag ipv6_hdr_frag = {
- .name = "ipv6",
- .pattern = ipv6_hdr_pattern,
- .pattern_count = ARRAY_SIZE(ipv6_hdr_pattern),
- .hash_field = ipv6_hdr_hash_field,
- .hash_field_count = ARRAY_SIZE(ipv6_hdr_hash_field),
+static const struct teamd_bpf_desc_frag l3_hdr_frag = {
+ .name = "l3",
+ .hproto = PROTO_L3,
+};
+
+static const struct teamd_bpf_desc_frag l4_hdr_frag = {
+ .name = "l4",
+ .hproto = PROTO_L4,
+};
+
+static const struct teamd_bpf_desc_frag tcp_hdr_frag = {
+ .name = "tcp",
+ .hproto = PROTO_TCP,
+};
+static const struct teamd_bpf_desc_frag udp_hdr_frag = {
+ .name = "udp",
+ .hproto = PROTO_UDP,
+};
+static const struct teamd_bpf_desc_frag sctp_hdr_frag = {
+ .name = "sctp",
+ .hproto = PROTO_SCTP,
};
static const struct teamd_bpf_desc_frag *frags[] = {
ð_hdr_frag,
+ &vlan_hdr_frag,
&ipv4_hdr_frag,
&ipv6_hdr_frag,
+ &ip_hdr_frag,
+ &l3_hdr_frag,
+ &l4_hdr_frag,
+ &tcp_hdr_frag,
+ &udp_hdr_frag,
+ &sctp_hdr_frag,
};
static const size_t frags_count = ARRAY_SIZE(frags);
@@ -169,10 +121,15 @@ static int teamd_hash_func_init(struct teamd_context *ctx, struct sock_fprog *fp
frag_name);
continue;
}
- err = teamd_bpf_desc_compile_frag(fprog, frag);
+ err = teamd_bpf_desc_add_frag(fprog, frag);
if (err)
goto release;
}
+
+ err = teamd_bpf_desc_compile(fprog);
+ if (err)
+ goto release;
+
err = teamd_bpf_desc_compile_finish(fprog);
if (err)
goto release;
--
1.8.1.4
10 years, 9 months
[jpirko/libteam] c34415: teamd: improved TX hashing code to handle L4 and v...
by GitHub
Branch: refs/heads/master
Home: https://github.com/jpirko/libteam
Commit: c3441566c5fffcd7b7c53d32f594b84e4cb4e5ad
https://github.com/jpirko/libteam/commit/c3441566c5fffcd7b7c53d32f594b84e...
Author: Flavio Leitner <fbl(a)redhat.com>
Date: 2013-07-02 (Tue, 02 Jul 2013)
Changed paths:
M man/teamd.conf.5
M teamd/teamd_bpf_chef.c
M teamd/teamd_bpf_chef.h
M teamd/teamd_hash_func.c
Log Message:
-----------
teamd: improved TX hashing code to handle L4 and vlan headers
The code tries to avoid as much as possible to use BPF
scratch memory areas, do maths or move data due to
performance reasons. Also, it's not possible to jump back.
The packet information is implict on the each branch taken.
The BPF code is quite long if all protocols are enabled, but
that shouldn't be a problem because the largest jump offset
~120 (goto vlan branch) is not close to socket_filter->k limit
of 256, it is expense as much as a jump to the next instruction
and the CPU should always take the shortest and quick path
possible for any scenario.
Signed-off-by: Flavio Leitner <fbl(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
10 years, 9 months