new file mode 100644
@@ -0,0 +1,75 @@
+/*******************************************************************************
+
+ Flow Library - Helpers for working on Flow API
+ Copyright(c) 2014 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Author: John Fastabend <john.r.fastabend@intel.com>
+
+*******************************************************************************/
+#ifndef _FLOWLIB_NL_H
+#define _FLOWLIB_NL_H
+struct flow_msg {
+ void *msg;
+ struct nl_msg *nlbuf;
+ uint32_t seq;
+};
+
+struct nl_sock *flow_nl_get_socket(void);
+
+struct net_flow_hdr *flow_nl_get_headers(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family);
+struct net_flow_action *flow_nl_get_actions(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family);
+struct net_flow_tbl *flow_nl_get_tables(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family);
+struct net_flow_hdr_node *flow_nl_get_hdr_graph(struct nl_sock *nsd,
+ uint32_t pid,
+ unsigned int ifindex,
+ int family);
+struct net_flow_tbl_node *flow_nl_get_tbl_graph(struct nl_sock *nsd,
+ uint32_t pid,
+ unsigned int ifindex,
+ int family);
+
+int flow_nl_set_flows(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family,
+ struct net_flow_flow *flow);
+int flow_nl_create_table(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family,
+ struct net_flow_tbl *table);
+
+uint32_t flow_nl_find_header(struct net_flow_hdr *hdr,
+ struct net_flow_hdr *search);
+uint32_t flow_nl_find_action_by_name(char *name,
+ struct net_flow_action *action);
+uint32_t flow_nl_find_instance(struct net_flow_hdr_node *graph,
+ uint32_t uid, uint32_t next);
+uint32_t flow_nl_find_table_with_action(struct net_flow_tbl *tbls,
+ uint32_t action, uint32_t next);
+
+void flow_nl_free_msg(struct flow_msg *msg);
+struct flow_msg *flow_nl_wrap_msg(struct nlmsghdr *buf);
+struct flow_msg *flow_nl_recv_msg(struct nl_sock *nsd, int *err);
+int flow_nl_table_cmd_to_type(FILE *fp, bool print, int valid,
+ struct nlattr *tb[]);
+struct flow_msg *flow_nl_alloc_msg(uint8_t type, uint32_t pid, int flags,
+ int size, int family);
+struct flow_msg *flow_nl_get_msg(struct nl_sock *nsd, uint8_t cmd, uint32_t pid,
+ unsigned int ifindex, int family);
+#endif
@@ -36,7 +36,7 @@ libflowies_la_CFLAGS = $(AM_CFLAGS) $(IES_CFLAGS) $(CFLAGS)
libflowies_la_LDFLAGS = -release @FLOW_API_VERSION@
lib_LTLIBRARIES += libflow.la
-libflow_la_SOURCES = flowlib.c
+libflow_la_SOURCES = flowlib_nl.c flowlib.c
libflow_la_LDFLAGS = -release @FLOW_API_VERSION@
lib_LTLIBRARIES += libflowd.la
new file mode 100644
@@ -0,0 +1,657 @@
+/*******************************************************************************
+
+ Functional Flow API
+ Copyright(c) 2014 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Author: John Fastabend <john.r.fastabend@intel.com>
+
+*******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <getopt.h>
+
+#include <libnl3/netlink/netlink.h>
+#include <libnl3/netlink/socket.h>
+#include <libnl3/netlink/genl/genl.h>
+#include <libnl3/netlink/genl/ctrl.h>
+#include <libnl3/netlink/route/link.h>
+
+#include <linux/if_ether.h>
+
+#include "if_flow.h"
+#include "flowlib.h"
+#include "flowlib_nl.h"
+
+struct nl_cache *link_cache;
+int verbose = 0;
+static struct nla_policy flow_get_tables_policy[NET_FLOW_MAX+1] = {
+ [NET_FLOW_IDENTIFIER_TYPE] = { .type = NLA_U32 },
+ [NET_FLOW_IDENTIFIER] = { .type = NLA_U32 },
+ [NET_FLOW_TABLES] = { .type = NLA_NESTED },
+ [NET_FLOW_HEADERS] = { .type = NLA_NESTED },
+ [NET_FLOW_ACTIONS] = { .type = NLA_NESTED },
+ [NET_FLOW_HEADER_GRAPH] = { .type = NLA_NESTED },
+ [NET_FLOW_TABLE_GRAPH] = { .type = NLA_NESTED },
+ [NET_FLOW_FLOWS] = { .type = NLA_NESTED },
+};
+
+static void pfprintf(FILE *fp, bool p, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ if (p)
+ vfprintf(fp, format, args);
+
+ va_end(args);
+}
+
+void flow_nl_free_msg(struct flow_msg *msg)
+{
+ if (msg->nlbuf)
+ nlmsg_free(msg->nlbuf);
+ else
+ free(msg->msg);
+ free(msg);
+}
+
+struct nl_sock *flow_nl_get_socket(void)
+{
+ struct nl_sock *nsd = nl_socket_alloc();
+
+ nl_connect(nsd, NETLINK_GENERIC);
+
+ return nsd;
+}
+
+struct net_flow_hdr *flow_nl_get_headers(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family)
+{
+ uint8_t cmd = NET_FLOW_TABLE_CMD_GET_HEADERS;
+ struct net_flow_hdr *hdrs = NULL;
+ struct flow_msg *msg;
+
+ msg = flow_nl_get_msg(nsd, cmd, pid, ifindex, family);
+
+ if (msg) {
+ struct nlmsghdr *nlh = msg->msg;
+ struct nlattr *tb[NET_FLOW_MAX+1];
+ int err;
+
+ err = genlmsg_parse(nlh, 0, tb,
+ NET_FLOW_MAX, flow_get_tables_policy);
+ if (err < 0) {
+ fprintf(stderr, "Warning unable to parse get tables msg\n");
+ goto out;
+ }
+
+ if (flow_nl_table_cmd_to_type(stdout, true,
+ NET_FLOW_HEADERS, tb))
+ goto out;
+
+ if (tb[NET_FLOW_HEADERS])
+ flow_get_headers(stdout, verbose,
+ tb[NET_FLOW_HEADERS], &hdrs);
+ }
+ return hdrs;
+out:
+ flow_nl_free_msg(msg);
+ return NULL;
+}
+
+struct net_flow_action *flow_nl_get_actions(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family)
+{
+ uint8_t cmd = NET_FLOW_TABLE_CMD_GET_ACTIONS;
+ struct net_flow_action *actions = NULL;
+ struct flow_msg *msg;
+
+ msg = flow_nl_get_msg(nsd, cmd, pid, ifindex, family);
+
+ if (msg) {
+ struct nlmsghdr *nlh = msg->msg;
+ struct nlattr *tb[NET_FLOW_MAX+1];
+ int err;
+
+ err = genlmsg_parse(nlh, 0, tb,
+ NET_FLOW_MAX, flow_get_tables_policy);
+ if (err < 0) {
+ fprintf(stderr, "Warning unable to parse get tables msg\n");
+ goto out;
+ }
+
+ if (flow_nl_table_cmd_to_type(stdout, true,
+ NET_FLOW_ACTIONS, tb))
+ goto out;
+
+ if (tb[NET_FLOW_ACTIONS])
+ flow_get_actions(stdout, verbose,
+ tb[NET_FLOW_ACTIONS], &actions);
+ }
+ return actions;
+out:
+ flow_nl_free_msg(msg);
+ return NULL;
+}
+
+struct net_flow_tbl *flow_nl_get_tables(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family)
+{
+ uint8_t cmd = NET_FLOW_TABLE_CMD_GET_TABLES;
+ struct net_flow_tbl *tables = NULL;
+ struct flow_msg *msg;
+
+ msg = flow_nl_get_msg(nsd, cmd, pid, ifindex, family);
+
+ if (msg) {
+ struct nlattr *tb[NET_FLOW_MAX+1];
+ struct nlmsghdr *nlh = msg->msg;
+ int err;
+
+ err = genlmsg_parse(nlh, 0, tb,
+ NET_FLOW_MAX, flow_get_tables_policy);
+ if (err < 0) {
+ fprintf(stderr, "Warning unable to parse get tables msg\n");
+ goto out;
+ }
+
+ if (flow_nl_table_cmd_to_type(stdout, true,
+ NET_FLOW_TABLES, tb))
+ goto out;
+
+ if (tb[NET_FLOW_TABLES])
+ flow_get_tables(stdout, verbose,
+ tb[NET_FLOW_TABLES], &tables);
+ }
+ return tables;
+out:
+ flow_nl_free_msg(msg);
+ return NULL;
+}
+
+struct net_flow_hdr_node *flow_nl_get_hdr_graph(struct nl_sock *nsd,
+ uint32_t pid,
+ unsigned int ifindex,
+ int family)
+{
+ uint8_t cmd = NET_FLOW_TABLE_CMD_GET_HDR_GRAPH;
+ struct net_flow_hdr_node *hdr_nodes = NULL;
+ struct flow_msg *msg;
+
+ msg = flow_nl_get_msg(nsd, cmd, pid, ifindex, family);
+
+ if (msg) {
+ struct nlmsghdr *nlh = msg->msg;
+ struct nlattr *tb[NET_FLOW_MAX+1];
+ int err;
+
+ err = genlmsg_parse(nlh, 0, tb,
+ NET_FLOW_MAX, flow_get_tables_policy);
+ if (err < 0) {
+ fprintf(stderr, "Warning unable to parse get tables msg\n");
+ goto out;
+ }
+
+ if (flow_nl_table_cmd_to_type(stdout, true,
+ NET_FLOW_HEADER_GRAPH, tb))
+ goto out;
+
+ if (tb[NET_FLOW_HEADER_GRAPH])
+ flow_get_hdrs_graph(stdout, verbose,
+ tb[NET_FLOW_HEADER_GRAPH],
+ &hdr_nodes);
+ }
+
+ return hdr_nodes;
+out:
+ flow_nl_free_msg(msg);
+ return NULL;
+}
+
+struct net_flow_tbl_node *flow_nl_get_tbl_graph(struct nl_sock*nsd,
+ uint32_t pid,
+ unsigned int ifindex,
+ int family)
+{
+ uint8_t cmd = NET_FLOW_TABLE_CMD_GET_TABLE_GRAPH;
+ struct net_flow_tbl_node *nodes = NULL;
+ struct flow_msg *msg;
+
+ msg = flow_nl_get_msg(nsd, cmd, pid, ifindex, family);
+
+ if (msg) {
+ struct nlmsghdr *nlh = msg->msg;
+ struct nlattr *tb[NET_FLOW_MAX+1];
+ int err;
+
+ err = genlmsg_parse(nlh, 0, tb,
+ NET_FLOW_MAX, flow_get_tables_policy);
+ if (err < 0) {
+ fprintf(stderr, "Warning unable to parse get tables msg\n");
+ goto out;
+ }
+
+ if (flow_nl_table_cmd_to_type(stdout, true,
+ NET_FLOW_TABLE_GRAPH, tb))
+ goto out;
+
+ if (tb[NET_FLOW_TABLE_GRAPH])
+ flow_get_tbl_graph(stdout, verbose,
+ tb[NET_FLOW_TABLE_GRAPH], &nodes);
+ }
+
+ return nodes;
+out:
+ flow_nl_free_msg(msg);
+ return NULL;
+}
+
+int flow_nl_set_flows(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family,
+ struct net_flow_flow *flow)
+{
+ uint8_t cmd = NET_FLOW_TABLE_CMD_SET_FLOWS;
+ struct nlattr *tb[NET_FLOW_MAX+1];
+ struct flow_msg *msg;
+ struct nlmsghdr *nlh;
+ struct nlattr *flows;
+ sigset_t bs;
+ int err = 0;
+
+ pp_flow(stdout, true, flow);
+
+ msg = flow_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
+ if (!msg) {
+ fprintf(stderr, "Error: Allocation failure\n");
+ return -ENOMSG;
+ }
+
+ if (nla_put_u32(msg->nlbuf,
+ NET_FLOW_IDENTIFIER_TYPE,
+ NET_FLOW_IDENTIFIER_IFINDEX) ||
+ nla_put_u32(msg->nlbuf, NET_FLOW_IDENTIFIER, ifindex)) {
+ fprintf(stderr, "Error: Identifier put failed\n");
+ return -EMSGSIZE;
+ }
+
+ err = flow_put_flow_error(msg->nlbuf, NET_FLOW_FLOWS_ERROR_CONT_LOG);
+ if (err)
+ return err;
+
+ flows = nla_nest_start(msg->nlbuf, NET_FLOW_FLOWS);
+ if (!flows)
+ return -EMSGSIZE;
+ flow_put_flow(msg->nlbuf, flow);
+ nla_nest_end(msg->nlbuf, flows);
+
+ nl_send_auto(nsd, msg->nlbuf);
+
+ /* message sent handle recv */
+ sigemptyset(&bs);
+ sigaddset(&bs, SIGINT);
+ sigprocmask(SIG_UNBLOCK, &bs, NULL);
+
+ msg = flow_nl_recv_msg(nsd, &err);
+ sigprocmask(SIG_BLOCK, &bs, NULL);
+
+ if (!msg)
+ return -EINVAL;
+
+ nlh = msg->msg;
+ err = genlmsg_parse(nlh, 0, tb, NET_FLOW_MAX, flow_get_tables_policy);
+ if (err < 0) {
+ fprintf(stderr, "Warning unable to parse set flows msg\n");
+ flow_nl_free_msg(msg);
+ return err;
+ }
+
+ err = flow_nl_table_cmd_to_type(stdout, true, 0, tb);
+ if (err)
+ return err;
+
+ if (tb[NET_FLOW_FLOWS]) {
+ fprintf(stderr, "Failed to set:\n");
+ flow_get_flows(stdout, verbose, tb[NET_FLOW_FLOWS], NULL);
+ flow_nl_free_msg(msg);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int flow_nl_create_table(struct nl_sock *nsd, uint32_t pid,
+ unsigned int ifindex, int family,
+ struct net_flow_tbl *table)
+{
+ uint8_t cmd = NET_FLOW_TABLE_CMD_CREATE_TABLE;
+ struct nlattr *tb[NET_FLOW_MAX+1];
+ struct nlattr *nest, *nest1;
+ struct nlmsghdr *nlh;
+ struct flow_msg *msg;
+ sigset_t bs;
+ int err = 0;
+
+ msg = flow_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
+ if (!msg) {
+ fprintf(stderr, "Error: Allocation failure\n");
+ return -ENOMSG;
+ }
+
+ if (nla_put_u32(msg->nlbuf,
+ NET_FLOW_IDENTIFIER_TYPE,
+ NET_FLOW_IDENTIFIER_IFINDEX) ||
+ nla_put_u32(msg->nlbuf, NET_FLOW_IDENTIFIER, ifindex)) {
+ fprintf(stderr, "Error: Identifier put failed\n");
+ return -EMSGSIZE;
+ }
+
+ nest = nla_nest_start(msg->nlbuf, NET_FLOW_TABLES);
+ if (!nest)
+ return -EMSGSIZE;
+ nest1 = nla_nest_start(msg->nlbuf, NET_FLOW_TABLE);
+ flow_put_table(msg->nlbuf, table);
+ nla_nest_end(msg->nlbuf, nest1);
+ nla_nest_end(msg->nlbuf, nest);
+ nl_send_auto(nsd, msg->nlbuf);
+
+ sigemptyset(&bs);
+ sigaddset(&bs, SIGINT);
+ sigprocmask(SIG_UNBLOCK, &bs, NULL);
+
+ msg = flow_nl_recv_msg(nsd, &err);
+ sigprocmask(SIG_BLOCK, &bs, NULL);
+
+ if (!msg)
+ return -EINVAL;
+
+ nlh = msg->msg;
+ err = genlmsg_parse(nlh, 0, tb, NET_FLOW_MAX, flow_get_tables_policy);
+ if (err < 0) {
+ fprintf(stderr, "Warning unable to parse create table msg\n");
+ flow_nl_free_msg(msg);
+ return err;
+ }
+ return 0;
+}
+
+uint32_t flow_nl_find_header(struct net_flow_hdr *hdr,
+ struct net_flow_hdr *search)
+{
+ uint32_t i, j;
+
+ for (i = 0; search[i].uid; i++) {
+ if (hdr->field_sz != search[i].field_sz)
+ continue;
+
+ for (j = 0; j < hdr->field_sz; j++) {
+ if (hdr->fields[j].bitwidth != search[i].fields[j].bitwidth)
+ continue;
+ }
+
+ if (j == hdr->field_sz)
+ return search[i].uid;
+ }
+ return 0;
+}
+
+uint32_t flow_nl_find_action_by_name(char *name, struct net_flow_action *acts)
+{
+ uint32_t i;
+
+ for (i = 0; acts[i].uid; i++) {
+ if (strcmp(name, acts[i].name) == 0)
+ return acts[i].uid;
+ }
+
+ return 0;
+}
+
+uint32_t flow_nl_find_instance(struct net_flow_hdr_node *graph,
+ uint32_t uid, uint32_t next)
+{
+ uint32_t i, j;
+
+ for (i = 0; graph[i].uid; i++) {
+ if (graph[i].uid < next)
+ continue;
+
+ for (j = 0; graph[i].hdrs[j]; j++) {
+ if (graph[i].hdrs[j] != uid)
+ continue;
+
+ return graph[i].uid;
+ }
+ }
+
+ return 0;
+}
+
+uint32_t flow_nl_find_table_with_action(struct net_flow_tbl *tbls,
+ uint32_t action, uint32_t next)
+{
+ uint32_t i, j;
+
+ for (i = 0; tbls[i].uid; i++) {
+ if (i < next)
+ continue;
+
+ for (j = 0; tbls[i].actions[j]; j++) {
+ if (tbls[i].actions[j] == action)
+ return tbls[i].uid;
+ }
+ }
+
+ return 0;
+}
+
+struct flow_msg *flow_nl_wrap_msg(struct nlmsghdr *buf)
+{
+ struct flow_msg *msg;
+
+ msg = (struct flow_msg *) malloc(sizeof(struct flow_msg));
+ if (msg) {
+ msg->msg = buf;
+ msg->nlbuf = NULL;
+ }
+
+ return msg;
+}
+
+static void flow_nl_handle_error(struct nlmsgerr *errmsg)
+{
+ fprintf(stderr, "Error processing request: %s\n",
+ strerror(errmsg->error));
+}
+
+struct flow_msg *flow_nl_recv_msg(struct nl_sock *nsd, int *err)
+{
+ static unsigned char *buf;
+ struct flow_msg *msg;
+ struct genlmsghdr *glm;
+ struct sockaddr_nl nla;
+ int type;
+ int rc;
+
+ *err = 0;
+
+ do {
+ rc = nl_recv(nsd, &nla, &buf, NULL);
+ if (rc < 0) {
+ switch (errno) {
+ case EINTR:
+ return NULL;
+ default:
+ perror("Receive operation failed:");
+ return NULL;
+ }
+ }
+ } while (rc == 0);
+
+ msg = flow_nl_wrap_msg((struct nlmsghdr *)buf);
+ if (!msg) {
+ fprintf(stderr, "Error: Message is empty\n");
+ free(buf);
+ return NULL;
+ }
+ type = ((struct nlmsghdr *)msg->msg)->nlmsg_type;
+
+ /*
+ * Note the NLMSG_ERROR is overloaded
+ * Its also used to deliver ACKs
+ */
+ if (type == NLMSG_ERROR) {
+ struct nlmsgerr *errm = nlmsg_data(msg->msg);
+
+ if (errm->error) {
+ flow_nl_handle_error(errm);
+ flow_nl_free_msg(msg);
+ return NULL;
+ }
+
+ flow_nl_free_msg(msg);
+ return NULL;
+ }
+
+ glm = nlmsg_data(msg->msg);
+ type = glm->cmd;
+
+ if (type < 0 || type > NET_FLOW_CMD_MAX) {
+ fprintf(stderr, "Received message of unknown type %d\n", type);
+ flow_nl_free_msg(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+int flow_nl_table_cmd_to_type(FILE *fp, bool print, int valid,
+ struct nlattr *tb[])
+{
+ unsigned int type, ifindex;
+ char iface[IFNAMSIZ];
+
+ if (!tb[NET_FLOW_IDENTIFIER_TYPE]) {
+ fprintf(stderr,
+ "Warning: received flow msg without identifier type!\n");
+ return -EINVAL;
+ }
+ if (!tb[NET_FLOW_IDENTIFIER]) {
+ fprintf(stderr,
+ "Warning: received flow msg without identifier!\n");
+ return -EINVAL;
+ }
+
+ if (valid > 0 && !tb[valid]){
+ fprintf(stderr, "Warning received cmd without valid attribute expected %i\n", valid);
+ return -ENOMSG;
+ }
+
+ if (nla_len(tb[NET_FLOW_IDENTIFIER_TYPE]) < (int)sizeof(type)) {
+ fprintf(stderr, "Warning invalid identifier type len\n");
+ return -EINVAL;
+ }
+
+ type = nla_get_u32(tb[NET_FLOW_IDENTIFIER_TYPE]);
+
+ switch (type) {
+ case NET_FLOW_IDENTIFIER_IFINDEX:
+ ifindex = nla_get_u32(tb[NET_FLOW_IDENTIFIER]);
+ rtnl_link_i2name(link_cache, (int)ifindex, iface, IFNAMSIZ);
+ pfprintf(fp, print, "%s (%u):\n", iface, ifindex);
+ break;
+ default:
+ fprintf(stderr, "Warning unknown interface identifier type %i\n", type);
+ break;
+ }
+
+ return 0;
+}
+
+struct flow_msg *flow_nl_alloc_msg(uint8_t type, uint32_t pid,
+ int flags, int size, int family)
+{
+ struct flow_msg *msg;
+ static uint32_t seq = 0;
+
+ msg = (struct flow_msg *) malloc(sizeof(struct flow_msg));
+ if (!msg)
+ return NULL;
+
+ msg->nlbuf = nlmsg_alloc();
+
+ msg->msg = genlmsg_put(msg->nlbuf, 0, seq, family, (int)size, flags,
+ type, NET_FLOW_GENL_VERSION);
+
+ msg->seq = seq++;
+
+ if (pid) {
+ struct nl_msg *nl_msg = msg->nlbuf;
+ struct sockaddr_nl nladdr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = pid,
+ .nl_groups = 0,
+ };
+
+ nlmsg_set_dst(nl_msg, &nladdr);
+ }
+ return msg;
+}
+
+struct flow_msg *flow_nl_get_msg(struct nl_sock *nsd, uint8_t cmd, uint32_t pid,
+ unsigned int ifindex, int family)
+{
+ struct flow_msg *msg;
+ sigset_t bs;
+ int err;
+
+ msg = flow_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
+ if (!msg) {
+ fprintf(stderr, "Error: Allocation failure\n");
+ return NULL;
+ }
+
+ nla_put_u32(msg->nlbuf,
+ NET_FLOW_IDENTIFIER_TYPE,
+ NET_FLOW_IDENTIFIER_IFINDEX);
+ nla_put_u32(msg->nlbuf, NET_FLOW_IDENTIFIER, ifindex);
+
+ nl_send_auto(nsd, msg->nlbuf);
+
+ sigemptyset(&bs);
+ sigaddset(&bs, SIGINT);
+ sigprocmask(SIG_UNBLOCK, &bs, NULL);
+
+ msg = flow_nl_recv_msg(nsd, &err);
+ sigprocmask(SIG_BLOCK, &bs, NULL);
+ return msg;
+}
@@ -55,6 +55,7 @@
#include "if_flow.h"
#include "flowlib.h"
+#include "flowlib_nl.h"
#ifdef PRIx64
#undef PRIx64
@@ -72,18 +73,6 @@
#endif /* SCNu64 */
static struct nl_sock *nsd;
-struct flow_msg {
- void *msg;
- struct nl_msg *nlbuf;
- uint32_t seq;
-};
-
-static struct flow_msg *
-alloc_flow_msg(uint8_t type, uint32_t pid, int flags, int size, int family);
-
-static struct flow_msg *wrap_netlink_msg(struct nlmsghdr *buf);
-static void free_flow_msg(struct flow_msg *msg);
-static struct flow_msg *recv_flow_msg(int *err);
static void process_rx_message(int verbose);
static int
@@ -225,71 +214,6 @@ static void get_flows_usage(char *progname)
}
-static void pfprintf(FILE *fp, bool p, const char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
-
- if (p)
- vfprintf(fp, format, args);
-
- va_end(args);
-}
-
-struct flow_msg *
-alloc_flow_msg(uint8_t type, uint32_t pid, int flags, int size, int family)
-{
- struct flow_msg *msg;
- static uint32_t seq;
-
- msg = malloc(sizeof(*msg));
- if (!msg)
- return NULL;
-
- msg->nlbuf = nlmsg_alloc();
-
- msg->msg = genlmsg_put(msg->nlbuf, 0, seq, family, (int)size, flags,
- type, NET_FLOW_GENL_VERSION);
-
- msg->seq = seq++;
-
- if (pid) {
- struct nl_msg *nl_msg = msg->nlbuf;
- struct sockaddr_nl nladdr = {
- .nl_family = AF_NETLINK,
- .nl_pid = pid,
- .nl_groups = 0,
- };
-
- nlmsg_set_dst(nl_msg, &nladdr);
- }
-
- return msg;
-}
-
-struct flow_msg *wrap_netlink_msg(struct nlmsghdr *buf)
-{
- struct flow_msg *msg;
-
- msg = (struct flow_msg *) malloc(sizeof(struct flow_msg));
- if (msg) {
- msg->msg = buf;
- msg->nlbuf = NULL;
- }
-
- return msg;
-}
-
-void free_flow_msg(struct flow_msg *msg)
-{
- if (msg->nlbuf)
- nlmsg_free(msg->nlbuf);
- else
- free(msg->msg);
- free(msg);
-}
-
static struct nla_policy flow_get_tables_policy[NET_FLOW_MAX+1] = {
[NET_FLOW_IDENTIFIER_TYPE] = { .type = NLA_U32 },
[NET_FLOW_IDENTIFIER] = { .type = NLA_U32 },
@@ -304,49 +228,6 @@ static struct nla_policy flow_get_tables_policy[NET_FLOW_MAX+1] = {
struct nl_cache *link_cache;
static int
-flow_table_cmd_to_type(FILE *fp, bool p, int valid, struct nlattr *tb[])
-{
- unsigned int type, ifindex;
- char iface[IFNAMSIZ];
-
- if (!tb[NET_FLOW_IDENTIFIER_TYPE]) {
- fprintf(stderr,
- "Warning: received flow msg without identifier type!\n");
- return -EINVAL;
- }
- if (!tb[NET_FLOW_IDENTIFIER]) {
- fprintf(stderr,
- "Warning: received flow msg without identifier!\n");
- return -EINVAL;
- }
-
- if (valid > 0 && !tb[valid]) {
- fprintf(stderr, "Warning received cmd without valid attribute expected %i\n", valid);
- return -ENOMSG;
- }
-
- if (nla_len(tb[NET_FLOW_IDENTIFIER_TYPE]) < (int)sizeof(type)) {
- fprintf(stderr, "Warning invalid identifier type len\n");
- return -EINVAL;
- }
-
- type = nla_get_u32(tb[NET_FLOW_IDENTIFIER_TYPE]);
-
- switch (type) {
- case NET_FLOW_IDENTIFIER_IFINDEX:
- ifindex = nla_get_u32(tb[NET_FLOW_IDENTIFIER]);
- rtnl_link_i2name(link_cache, (int)ifindex, iface, IFNAMSIZ);
- pfprintf(fp, p, "%s (%u):\n", iface, ifindex);
- break;
- default:
- fprintf(stderr, "Warning unknown interface identifier type %i\n", type);
- break;
- }
-
- return 0;
-}
-
-static int
flow_macaddr2u64(__u64 *dst, size_t n, char *src)
{
__u8 tmp[ETH_ALEN];
@@ -379,7 +260,7 @@ static void flow_table_cmd_get_tables(struct flow_msg *msg, int verbose)
return;
}
- if (flow_table_cmd_to_type(stdout, false, NET_FLOW_TABLES, tb))
+ if (flow_nl_table_cmd_to_type(stdout, false, NET_FLOW_TABLES, tb))
return;
if (tb[NET_FLOW_TABLES])
@@ -398,7 +279,7 @@ static void flow_table_cmd_get_headers(struct flow_msg *msg, int verbose)
return;
}
- if (flow_table_cmd_to_type(stdout, false, NET_FLOW_HEADERS, tb))
+ if (flow_nl_table_cmd_to_type(stdout, false, NET_FLOW_HEADERS, tb))
return;
if (tb[NET_FLOW_HEADERS])
@@ -417,7 +298,7 @@ static void flow_table_cmd_get_actions(struct flow_msg *msg, int verbose)
return;
}
- if (flow_table_cmd_to_type(stdout, false, NET_FLOW_ACTIONS, tb))
+ if (flow_nl_table_cmd_to_type(stdout, false, NET_FLOW_ACTIONS, tb))
return;
if (tb[NET_FLOW_ACTIONS])
@@ -436,7 +317,7 @@ static void flow_table_cmd_get_headers_graph(struct flow_msg *msg, int verbose)
return;
}
- if (flow_table_cmd_to_type(stdout, false, NET_FLOW_HEADER_GRAPH, tb))
+ if (flow_nl_table_cmd_to_type(stdout, false, NET_FLOW_HEADER_GRAPH, tb))
return;
if (tb[NET_FLOW_HEADER_GRAPH])
@@ -456,7 +337,7 @@ static void flow_table_cmd_get_table_graph(struct flow_msg *msg, int verbose)
return;
}
- if (flow_table_cmd_to_type(stdout, false, NET_FLOW_TABLE_GRAPH, tb))
+ if (flow_nl_table_cmd_to_type(stdout, false, NET_FLOW_TABLE_GRAPH, tb))
return;
if (tb[NET_FLOW_TABLE_GRAPH])
@@ -476,7 +357,7 @@ static void flow_table_cmd_get_flows(struct flow_msg *msg, int verbose)
return;
}
- err = flow_table_cmd_to_type(stdout, false, 0, tb);
+ err = flow_nl_table_cmd_to_type(stdout, false, 0, tb);
if (err == -ENOMSG) {
fprintf(stdout, "Table empty\n");
return;
@@ -501,7 +382,7 @@ static void flow_table_cmd_set_flows(struct flow_msg *msg, int verbose)
return;
}
- err = flow_table_cmd_to_type(stdout, false, 0, tb);
+ err = flow_nl_table_cmd_to_type(stdout, false, 0, tb);
if (err)
return;
@@ -569,73 +450,6 @@ flow_table_cmd_destroy_table(struct flow_msg *msg, int verbose __unused)
}
}
-static void handle_error(struct nlmsgerr *errmsg)
-{
- fprintf(stderr, "Error processing request: %s\n",
- strerror(errmsg->error));
-}
-
-struct flow_msg *recv_flow_msg(int *err)
-{
- static unsigned char *buf;
- struct flow_msg *msg;
- struct genlmsghdr *glm;
- struct sockaddr_nl nla;
- int type;
- int rc;
-
- *err = 0;
-
- do {
- rc = nl_recv(nsd, &nla, &buf, NULL);
- if (rc < 0) {
- switch (errno) {
- case EINTR:
- return NULL;
- default:
- perror("Receive operation failed:");
- return NULL;
- }
- }
- } while (rc == 0);
-
- msg = wrap_netlink_msg((struct nlmsghdr *)buf);
- if (msg == NULL) {
- fprintf(stderr, "Error: Message is empty\n");
- free(buf);
- return NULL;
- }
- type = ((struct nlmsghdr *)msg->msg)->nlmsg_type;
-
- /*
- * Note the NLMSG_ERROR is overloaded
- * Its also used to deliver ACKs
- */
- if (type == NLMSG_ERROR) {
- struct nlmsgerr *errm = nlmsg_data(msg->msg);
-
- if (errm->error) {
- handle_error(errm);
- free_flow_msg(msg);
- return NULL;
- }
-
- free_flow_msg(msg);
- return NULL;
- }
-
- glm = nlmsg_data(msg->msg);
- type = glm->cmd;
-
- if (type < 0 || type > NET_FLOW_CMD_MAX) {
- fprintf(stderr, "Received message of unknown type %d\n", type);
- free_flow_msg(msg);
- return NULL;
- }
-
- return msg;
-}
-
static void(*type_cb[NET_FLOW_CMD_MAX+1])(struct flow_msg *, int verbose) = {
flow_table_cmd_get_tables,
flow_table_cmd_get_headers,
@@ -667,7 +481,7 @@ void process_rx_message(int verbose)
*/
for (;;) {
sigprocmask(SIG_UNBLOCK, &bs, NULL);
- msg = recv_flow_msg(&err);
+ msg = flow_nl_recv_msg(nsd, &err);
sigprocmask(SIG_BLOCK, &bs, NULL);
if (msg) {
@@ -675,7 +489,7 @@ void process_rx_message(int verbose)
struct genlmsghdr *glh = nlmsg_data(nlh);
if (nlh->nlmsg_type == NLMSG_DONE) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
break;
}
@@ -683,10 +497,10 @@ void process_rx_message(int verbose)
type_cb[type](msg, verbose);
if (!(nlh->nlmsg_flags & NLM_F_MULTI)) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
break;
}
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
} else {
break;
}
@@ -1188,7 +1002,7 @@ flow_destroy_tbl_send(int verbose, uint32_t pid, int family,
nsd = nl_socket_alloc();
nl_connect(nsd, NETLINK_GENERIC);
- msg = alloc_flow_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
+ msg = flow_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
if (!msg) {
fprintf(stderr, "Error: Allocation failure\n");
return -ENOMSG;
@@ -1198,13 +1012,13 @@ flow_destroy_tbl_send(int verbose, uint32_t pid, int family,
NET_FLOW_IDENTIFIER_IFINDEX) ||
nla_put_u32(msg->nlbuf, NET_FLOW_IDENTIFIER, ifindex)) {
fprintf(stderr, "Error: Identifier put failed\n");
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
nest = nla_nest_start(msg->nlbuf, NET_FLOW_TABLES);
if (!nest) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
nest1 = nla_nest_start(msg->nlbuf, NET_FLOW_TABLE);
@@ -1215,7 +1029,7 @@ flow_destroy_tbl_send(int verbose, uint32_t pid, int family,
nl_send_auto(nsd, msg->nlbuf);
process_rx_message(verbose);
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return 0;
}
@@ -1352,7 +1166,7 @@ flow_create_tbl_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
nsd = nl_socket_alloc();
nl_connect(nsd, NETLINK_GENERIC);
- msg = alloc_flow_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
+ msg = flow_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
if (!msg) {
fprintf(stderr, "Error: Allocation failure\n");
return -ENOMEM;
@@ -1362,13 +1176,13 @@ flow_create_tbl_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
NET_FLOW_IDENTIFIER_IFINDEX) ||
nla_put_u32(msg->nlbuf, NET_FLOW_IDENTIFIER, ifindex)) {
fprintf(stderr, "Error: Identifier put failed\n");
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
nest = nla_nest_start(msg->nlbuf, NET_FLOW_TABLES);
if (!nest) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
nest1 = nla_nest_start(msg->nlbuf, NET_FLOW_TABLE);
@@ -1378,7 +1192,7 @@ flow_create_tbl_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
nl_send_auto(nsd, msg->nlbuf);
process_rx_message(verbose);
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return 0;
}
@@ -1453,7 +1267,7 @@ flow_del_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
nsd = nl_socket_alloc();
nl_connect(nsd, NETLINK_GENERIC);
- msg = alloc_flow_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
+ msg = flow_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
if (!msg) {
fprintf(stderr, "Error: Allocation failure\n");
return -ENOMSG;
@@ -1463,19 +1277,19 @@ flow_del_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
NET_FLOW_IDENTIFIER_IFINDEX) ||
nla_put_u32(msg->nlbuf, NET_FLOW_IDENTIFIER, ifindex)) {
fprintf(stderr, "Error: Identifier put failed\n");
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
err = flow_put_flow_error(msg->nlbuf, NET_FLOW_FLOWS_ERROR_ABORT);
if (err) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return err;
}
flows = nla_nest_start(msg->nlbuf, NET_FLOW_FLOWS);
if (!flows) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
flow_put_flow(msg->nlbuf, &flow);
@@ -1484,7 +1298,7 @@ flow_del_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
nl_send_auto(nsd, msg->nlbuf);
process_rx_message(verbose);
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return 0;
}
@@ -1560,7 +1374,7 @@ flow_get_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
nsd = nl_socket_alloc();
nl_connect(nsd, NETLINK_GENERIC);
- msg = alloc_flow_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
+ msg = flow_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
if (!msg) {
fprintf(stderr, "Error: Allocation failure\n");
return -ENOMSG;
@@ -1570,20 +1384,20 @@ flow_get_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
NET_FLOW_IDENTIFIER_IFINDEX) ||
nla_put_u32(msg->nlbuf, NET_FLOW_IDENTIFIER, ifindex)) {
fprintf(stderr, "Error: Identifier put failed\n");
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
flows = nla_nest_start(msg->nlbuf, NET_FLOW_FLOWS);
if (!flows) {
fprintf(stderr, "Error: get_flows attributes failed\n");
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -ENOMSG;
}
err = nla_put_u32(msg->nlbuf, NET_FLOW_TABLE_FLOWS_TABLE, tableid);
if (err) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
@@ -1591,7 +1405,7 @@ flow_get_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
err = nla_put_u32(msg->nlbuf, NET_FLOW_TABLE_FLOWS_MINPRIO,
min);
if (err) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return err;
}
}
@@ -1600,7 +1414,7 @@ flow_get_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
err = nla_put_u32(msg->nlbuf, NET_FLOW_TABLE_FLOWS_MAXPRIO,
max);
if (err) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return err;
}
}
@@ -1610,14 +1424,14 @@ flow_get_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
err = flow_put_flow_error(msg->nlbuf, NET_FLOW_FLOWS_ERROR_ABORT);
if (err) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return err;
}
nl_send_auto(nsd, msg->nlbuf);
process_rx_message(verbose);
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return 0;
}
@@ -1723,7 +1537,7 @@ flow_set_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
nsd = nl_socket_alloc();
nl_connect(nsd, NETLINK_GENERIC);
- msg = alloc_flow_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
+ msg = flow_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
if (!msg) {
fprintf(stderr, "Error: Allocation failure\n");
return -ENOMSG;
@@ -1733,19 +1547,19 @@ flow_set_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
NET_FLOW_IDENTIFIER_IFINDEX) ||
nla_put_u32(msg->nlbuf, NET_FLOW_IDENTIFIER, ifindex)) {
fprintf(stderr, "Error: Identifier put failed\n");
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
err = flow_put_flow_error(msg->nlbuf, NET_FLOW_FLOWS_ERROR_ABORT);
if (err) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return err;
}
flows = nla_nest_start(msg->nlbuf, NET_FLOW_FLOWS);
if (!flows) {
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return -EMSGSIZE;
}
flow_put_flow(msg->nlbuf, &flow);
@@ -1754,7 +1568,7 @@ flow_set_send(int verbose, uint32_t pid, int family, uint32_t ifindex,
nl_send_auto(nsd, msg->nlbuf);
process_rx_message(verbose);
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return 0;
}
@@ -1768,7 +1582,7 @@ flow_send_recv(int verbose, uint32_t pid, int family, uint32_t ifindex,
nsd = nl_socket_alloc();
nl_connect(nsd, NETLINK_GENERIC);
- msg = alloc_flow_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
+ msg = flow_nl_alloc_msg(cmd, pid, NLM_F_REQUEST|NLM_F_ACK, 0, family);
if (!msg) {
fprintf(stderr, "Error: Allocation failure\n");
return -ENOMSG;
@@ -1781,7 +1595,7 @@ flow_send_recv(int verbose, uint32_t pid, int family, uint32_t ifindex,
nl_send_auto(nsd, msg->nlbuf);
process_rx_message(verbose);
- free_flow_msg(msg);
+ flow_nl_free_msg(msg);
return 0;
}