svn commit: r227820 - in projects/diffused_head/sbin/ipfw: .
diffuse_collector
Lawrence Stewart
lstewart at FreeBSD.org
Tue Nov 22 15:50:25 UTC 2011
Author: lstewart
Date: Tue Nov 22 15:50:24 2011
New Revision: 227820
URL: http://svn.freebsd.org/changeset/base/227820
Log:
Add the diffuse_collector program, which can be used to receive export data from
the DIFFUSE kernel module via UDP or export data relayed from a diffuse_exporter
via SCTP, TCP or UDP. The collector instantiates firewall rules based on the
flow and classification data received from one or more classifier nodes.
Sponsored by: FreeBSD Foundation
Reviewed by: bz
Added:
projects/diffused_head/sbin/ipfw/diffuse_collector/
projects/diffused_head/sbin/ipfw/diffuse_collector/Makefile (contents, props changed)
projects/diffused_head/sbin/ipfw/diffuse_collector/collector.conf (contents, props changed)
projects/diffused_head/sbin/ipfw/diffuse_collector/diffuse_collector.c (contents, props changed)
Modified:
projects/diffused_head/sbin/ipfw/Makefile
Modified: projects/diffused_head/sbin/ipfw/Makefile
==============================================================================
--- projects/diffused_head/sbin/ipfw/Makefile Tue Nov 22 15:04:59 2011 (r227819)
+++ projects/diffused_head/sbin/ipfw/Makefile Tue Nov 22 15:50:24 2011 (r227820)
@@ -12,6 +12,6 @@ DPADD= ${LIBUTIL}
DPADD+= ${LIBM}
LDADD= -lutil -lm
MAN= ipfw.8
-SUBDIR= diffuse_exporter
+SUBDIR= diffuse_collector diffuse_exporter
.include <bsd.prog.mk>
Added: projects/diffused_head/sbin/ipfw/diffuse_collector/Makefile
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/diffused_head/sbin/ipfw/diffuse_collector/Makefile Tue Nov 22 15:50:24 2011 (r227820)
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/..
+PROG= diffuse_collector
+SRCS= diffuse_collector.c diffuse_proto.c
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+NO_MAN=
+
+.include <bsd.prog.mk>
Added: projects/diffused_head/sbin/ipfw/diffuse_collector/collector.conf
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/diffused_head/sbin/ipfw/diffuse_collector/collector.conf Tue Nov 22 15:50:24 2011 (r227820)
@@ -0,0 +1,86 @@
+#
+# Example diffuse_collector config file.
+#
+
+#
+# General collector configuration.
+#
+[general]
+
+#
+# Space delimited list of classifier IP addresses that this action node is
+# willing to receive flow classification information from. When the action node
+# starts up, it sends a TCP message to the classifier on default port 4377
+# requesting all current state to be sent to it. If the classifier is listening
+# on a non-default port number for this message, the port number can be
+# specified here as "ip;port".
+#
+classifiers = 127.0.0.1
+
+################################################################################
+
+#
+# Configuration related to the underlying firewall that will be used by the
+# collector.
+#
+[firewall]
+
+#
+# Path to the firewall executable.
+#
+exec = /sbin/ipfw
+
+#
+# How does this firewall manage rules?
+# Valid options are 'cli' and, at a later date when implemented, 'file'.
+#
+# 'cli' implies the firewall uses command line arguments to the firewall
+# executable to perform rule management. The ipfw firewall, for example, works
+# this way.
+#
+# 'file' indicates the firewall expects the complete configuration to exist in a
+# text configuration file which is parsed and executed by the firewall
+# executable. The pf firewall, for example, works this way.
+#
+rule_mgmt = cli
+
+#
+# Set of rule numbers the collector can use for managing rules.
+#
+rule_nums = 1000-2000
+
+#
+# A templatised shell command to run when a rule needs to be added.
+#
+cli_add_rule = @@exec@@ add @@ruleno@@ @@action@@ @@proto@@ from @@srcip@@ @@srcport@@ to @@dstip@@ @@dstport@@ @@keepstate@@
+
+#
+# A templatised shell command to run when a rule needs to be deleted.
+#
+cli_del_rule = @@exec@@ delete @@ruleno@@
+
+#
+# A templatised shell command to run when the packet and byte match counters for
+# a specific firewall rule are required. The command should format the counter
+# values as CSV like so: "pktcount,bytecount".
+#
+cli_get_rule_counters = @@exec@@ show @@ruleno@@ | awk '{ printf("%s,%s", $2, $3) }'
+
+################################################################################
+
+#
+# Configuration for mapping classes identified by particular classifiers to
+# local firewall actions.
+#
+[classactions]
+
+#
+# Set a default action for flows which do not match a more specific action.
+#
+action = default pipe 1
+
+#
+# Flows identified as class 0 by the classifier named 'cname' will match this
+# action.
+#
+action = cname:0 pipe 2
Added: projects/diffused_head/sbin/ipfw/diffuse_collector/diffuse_collector.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/diffused_head/sbin/ipfw/diffuse_collector/diffuse_collector.c Tue Nov 22 15:50:24 2011 (r227820)
@@ -0,0 +1,2055 @@
+/*-
+ * Copyright (c) 2010-2011
+ * Swinburne University of Technology, Melbourne, Australia.
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University of Technology, by Sebastian Zander, made
+ * possible in part by a gift from The Cisco University Research Program Fund, a
+ * corporate advised fund of Silicon Valley Community Foundation.
+ *
+ * Portions of this software were developed at the Centre for Advanced
+ * Internet Architectures, Swinburne University of Technology, Melbourne,
+ * Australia by Lawrence Stewart under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Description:
+ * Rule/flow collector.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+
+#include <arpa/inet.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_fw.h>
+#include <netinet/ip_diffuse.h>
+#define WITH_DIP_INFO
+#include <netinet/ip_diffuse_export.h>
+#include <netinet/sctp.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "../diffuse_ui.h"
+#include "../diffuse_proto.h"
+
+#define STRLEN_LITERAL(literal) (sizeof((literal)) - 1)
+
+#define MAX_ERRORS_BEFORE_IGNORE 12
+
+/* Flow/rule timeouts. */
+/* XXX: Make configurable. */
+#define DEFAULT_TIMEOUT 60
+#define DEFAULT_TIMEOUT2 60
+
+/* Max number of rules to use by default. */
+#define DEFAULT_MAX_RULES 1000
+
+static const char *usage = "Usage: diffuse_collector [-hnv] "
+ "[-c <collector-config>] [-s <sctp-port>] [-t <tcp-port>] [-u <udp-port>]";
+
+static char *config_sections[] = {
+#define INI_SECTION_GENERAL 0
+ "general",
+#define INI_SECTION_CLASSACTIONS 1
+ "classactions",
+#define INI_SECTION_FIREWALL 2
+ "firewall"
+};
+#define NUM_INI_SECTIONS (sizeof(config_sections) / sizeof(*config_sections))
+
+RB_GENERATE(di_template_head, di_template, node, template_compare);
+
+/* Classifier node. */
+struct class_node {
+ int proto;
+ struct in_addr ip;
+ uint16_t port;
+ int sock;
+ int closed;
+ int errors;
+ struct di_template_head templ_list;
+ LIST_ENTRY(class_node) next;
+};
+
+LIST_HEAD(class_node_head, class_node);
+static struct class_node_head cnodes; /* Accepted TCP or SCTP clients. */
+static struct class_node_head mains; /* Main ports (max one for each proto). */
+static int cnode_cnt;
+
+/* List of flow classes per flow entry. */
+struct flow_class {
+ char cname[DI_MAX_NAME_STR_LEN];
+ uint16_t class;
+ SLIST_ENTRY(flow_class) next;
+};
+
+SLIST_HEAD(flow_class_head, flow_class);
+
+struct rule_entry;
+
+struct timeout_entry {
+ struct rule_entry *rule;
+ LIST_ENTRY(timeout_entry) next;
+};
+
+LIST_HEAD(timeout_entry_head, timeout_entry);
+
+/*
+ * Firewall rule numbers to use when adding rules. This is essentially a free
+ * list using a RB tree, so that the latest rule always gets the lowest number.
+ */
+struct rule_no {
+ uint16_t no;
+ RB_ENTRY(rule_no) node;
+};
+
+static inline int
+rule_no_compare(struct rule_no *a, struct rule_no *b)
+{
+
+ return ((a->no != b->no) ? (a->no < b->no ? -1 : 1) : 0);
+}
+
+RB_HEAD(rule_no_head, rule_no);
+static RB_PROTOTYPE(rule_no_head, rule_no, node, rule_no_compare);
+static RB_GENERATE(rule_no_head, rule_no, node, rule_no_compare);
+
+struct rule_entry {
+ struct class_node *cnode; /* Sender/generator of rule. */
+ char export_set[DI_MAX_NAME_STR_LEN];
+ struct ipfw_flow_id id; /* (masked) flow id. */
+ char action[DI_MAX_NAME_STR_LEN];
+ char act_params[DI_MAX_PARAM_STR_LEN];
+ uint64_t pcnt; /* Packets matched counter. */
+ uint64_t bcnt; /* Bytes matched counter. */
+ uint8_t expire_type; /* Rule vs. flow timeout. */
+ uint32_t expire; /* Expire timeout. */
+ uint32_t bucket; /* Which hash table bucket. */
+ uint16_t rtype; /* {Bi|Uni}directional. */
+ uint16_t tcnt; /* Number of tags. */
+ struct flow_class_head flow_classes; /* List of class tags. */
+ struct timeout_entry *to; /* Ptr to timeout list entry. */
+ struct rule_no *rule_no; /* Firewall rule number. */
+ struct rule_entry *next; /* Next rule in list. */
+};
+
+#define DEFAULT_RULE_TABLE_SIZE 4096
+static int rule_table_size = DEFAULT_RULE_TABLE_SIZE;
+static struct rule_entry **rule_table;
+
+#define DEFAULT_TIMEOUT_SIZE 512
+static struct timeout_entry_head timeouts[DEFAULT_TIMEOUT_SIZE];
+static uint16_t timeout_now;
+
+static struct rule_no_head rule_nos;
+#define DEFAULT_MIN_RULE_NO 1000
+#define DEFAULT_MAX_RULE_NO 2000
+#define FW_MAX_RULE_NO 65535
+
+/* Firewall rule actions. */
+#define FW_ADD_RULE 1 /* Add rule. */
+#define FW_DEL_RULE 2 /* Delete rule. */
+#define FW_GET_RULE_COUNTERS 3 /* Get rule counters. */
+
+/* How many rule counters are gathered by the FW_GET_RULE_COUNTERS action. */
+#define NUM_RULE_COUNTERS 2
+
+/*
+ * Array indexes for rule counters gathered by the FW_GET_RULE_COUNTERS
+ * action.
+ */
+#define RULE_COUNTER_NPKTS 0
+#define RULE_COUNTER_NBYTES 1
+
+struct fw_action {
+ FILE *file;
+ int type;
+ struct rule_entry *rule;
+ TAILQ_ENTRY(fw_action) next;
+};
+TAILQ_HEAD(fw_action_head, fw_action);
+
+static struct fw_action_head fw_actions;
+
+/*
+ * Local list of classes and actions to execute. Overrides actions send by
+ * classifier node.
+ */
+struct class_action {
+ char cname[DI_MAX_NAME_STR_LEN];
+ uint16_t class;
+ char action[DI_MAX_NAME_STR_LEN];
+ char act_params[DI_MAX_PARAM_STR_LEN];
+ RB_ENTRY(class_action) node;
+};
+
+static inline int
+class_action_compare(struct class_action *a, struct class_action *b)
+{
+ int cc;
+
+ /* Sort by classes first as its potentially quicker. */
+ cc = a->class != b->class ? (a->class < b->class ? -1 : 1) : 0;
+ if (cc != 0)
+ return (cc);
+ else
+ return (strcmp(a->cname, b->cname));
+}
+
+RB_HEAD(class_action_head, class_action);
+static RB_PROTOTYPE(class_action_head, class_action, node,
+ class_action_compare);
+static RB_GENERATE(class_action_head, class_action, node, class_action_compare);
+
+static struct class_action_head class_actions;
+static struct class_action def_action = { "", 0, "count", "" };
+
+struct pair {
+ char token[32];
+ char value[64];
+};
+
+/* Template tokens available in config file for substitution. */
+static struct pair tokenpairs[] = {
+ { "action", "" },
+#define TOK_ACTION 0
+ { "exec", "" },
+#define TOK_EXEC 1
+ { "dstip", "" },
+#define TOK_DSTIP 2
+ { "dstport", "" },
+#define TOK_DSTPORT 3
+ { "keepstate", "" },
+#define TOK_KEEPSTATE 4
+ { "proto", "" },
+#define TOK_PROTO 5
+ { "ruleno", "" },
+#define TOK_RULENO 6
+ { "srcip", "" },
+#define TOK_SRCIP 7
+ { "srcport", "" }
+#define TOK_SRCPORT 8
+};
+
+#define NUM_TOKEN_PAIRS (sizeof(tokenpairs) / sizeof(tokenpairs[0]))
+
+struct di_collector_config {
+ char cfpath[PATH_MAX]; /* Config file path. */
+ char fw_exe[PATH_MAX]; /* Firewall executable path. */
+ char cli_add_rule[1024]; /* Shell command to execute when
+ * adding a rule. */
+ char cli_del_rule[1024]; /* Shell command to execute when
+ * deleting a rule. */
+ char cli_get_rule_counters[1024]; /* Shell command to execute
+ * to get the required rule
+ * counters. */
+ struct sockaddr *classifiers; /* Details for classifiers. */
+ int num_classifiers; /* Size of classifiers array. */
+ int min_rule_no; /* Minimum rule number available
+ * for use by colelctor. */
+ int max_rule_no; /* Maximum rule number available
+ * for use by colelctor. */
+ uint8_t fw_rule_mgmt_type; /* Rule management method. */
+ uint8_t verbose; /* If non-zero, write runtime
+ * info to console. */
+ uint8_t no_fw; /* Don't execute firewall
+ * commands. */
+ uint8_t fw_default_action; /* Default specified? */
+};
+
+static struct di_collector_config config;
+
+#define FW_CLI_RULE_MGMT 1
+#define FW_FILE_RULE_MGMT 2
+
+/* Global exit flag. */
+static int stop;
+
+static void
+print_collector_config(struct di_collector_config *conf)
+{
+ struct sockaddr *cdetails;
+ int i;
+
+ printf("%-25s %s\n", "Config file path", conf->cfpath);
+ printf("%-25s %s\n", "Firewall executable path", conf->fw_exe);
+ printf("%-25s %s\n", "Add rule cmd", conf->cli_add_rule);
+ printf("%-25s %s\n", "Delete rule cmd", conf->cli_del_rule);
+ printf("%-25s %s\n", "Get rule counters cmd",
+ conf->cli_get_rule_counters);
+ printf("%-25s %s\n", "Firewall rule mgmt type", "FW_CLI_RULE_MGMT");
+ printf("%-25s %d\n", "Min rule number", conf->min_rule_no);
+ printf("%-25s %d\n", "Max rule number", conf->max_rule_no);
+
+ for (i = 0; i < conf->num_classifiers; i++) {
+ cdetails = &conf->classifiers[i];
+ printf("Details for classifier %d: %s;%u\n", i + 1,
+ inet_ntoa(((struct sockaddr_in *)cdetails)->sin_addr),
+ ntohs(((struct sockaddr_in *)cdetails)->sin_port));
+ }
+}
+
+static void
+expand_tokenised_str(char const *instr, char *outstr, int outlen,
+ struct pair const *pairs, int npairs)
+{
+#define TOK_LHS_SEP "@@" /* Left Hand Side token delimiter. */
+#define TOK_RHS_SEP TOK_LHS_SEP /* Right Hand Side token delimiter. */
+
+ char *tmp, *tok;
+ int i, nchars;
+
+ /*
+ * Keep looping until we've reached the end of instr or we've filled
+ * outstr to capacity.
+ */
+ while (*instr != '\0' && outlen > 1) {
+ tok = strstr(instr, TOK_LHS_SEP);
+ /*
+ * Copy all chars from the current position of instr up to the
+ * token separator into outstr. If no token separator was found,
+ * instr has no tokens to replace so copy entire string.
+ */
+ nchars = (tok == NULL ? outlen - 1 : tok - instr);
+ if (nchars >= outlen)
+ nchars = outlen - 1;
+ tmp = stpncpy(outstr, instr, nchars);
+ nchars = tmp - outstr;
+ outlen -= nchars;
+ outstr = tmp;
+ /* stpncpy() doesn't always NULL-terminate. */
+ *outstr = '\0';
+
+ if (tok != NULL && outlen > 0) {
+ /* Move instr to point to start of token. */
+ instr = tok + STRLEN_LITERAL(TOK_LHS_SEP);
+
+ /* Find the end-of-token delimter. */
+ tok = strstr(instr, TOK_RHS_SEP);
+ if (tok != NULL) {
+ /*
+ * Found delimiter, so match token in pairs
+ * array and replace with value if found.
+ */
+ for (i = 0; i < npairs; i++) {
+ if (strncmp(pairs[i].token, instr,
+ tok - instr) == 0) {
+ nchars = snprintf(outstr,
+ outlen, "%s",
+ pairs[i].value);
+ outlen -= nchars;
+ outstr += nchars;
+ }
+ }
+ /*
+ * Finished substitution for this token. Set
+ * nchars such that the token and end-of-token
+ * delimter in instr will be skipped over next
+ * time through the loop.
+ */
+ nchars = (tok - instr) +
+ STRLEN_LITERAL(TOK_RHS_SEP);
+ } else {
+ /*
+ * No end-of-token delimiter found after the
+ * start-of-token delimiter. Most likely an
+ * error in instr, but treat as though the
+ * start-of-token delimiter we found is regular
+ * text and copy to outstr all chars from
+ * start-of-token delimiter through to end of
+ * instr.
+ */
+
+ /*
+ * Backtrack instr so we copy the start-of-token
+ * delimter.
+ */
+ instr -= STRLEN_LITERAL(TOK_LHS_SEP);
+
+ /*
+ * Copy as many chars from instr to
+ * outstr as outlen will allow, leaving
+ * room for the '\0'.
+ */
+ tmp = stpncpy(outstr, instr, outlen - 1);
+ nchars = tmp - outstr;
+ outlen -= nchars;
+ outstr = tmp;
+ /* stpncpy() doesn't always NULL-terminate. */
+ *outstr = '\0';
+ }
+ }
+
+ /*
+ * Shuffle instr along. If we made a substitution this time
+ * through the loop, this will position instr just after the
+ * end-of-token delimiter of the token we just processed. If no
+ * substitution was made, this should position instr at its
+ * NULL-terminator.
+ */
+ instr += nchars;
+ }
+}
+
+/*
+ * Execute a shell command in a non-blocking pipe and return the new file
+ * descriptor.
+ */
+static FILE *
+exec(char *command)
+{
+ FILE *f;
+
+ if (config.no_fw)
+ command = "test";
+ if (config.verbose)
+ printf("Executing shell command: %s\n", command);
+ f = popen(command, "r");
+ fcntl(fileno(f), F_SETFL, O_NONBLOCK);
+
+ return (f);
+}
+
+/*
+ * Build firewall command, exec using a pipe and add pipe fd to our file
+ * descriptor set to check for completion at a later point.
+ */
+static void
+exec_fw(struct rule_entry *r, int type, fd_set *rset, int *max_fd,
+ int no_race_check)
+{
+ struct fw_action *action, *cur, *tmp;
+ struct in_addr addr;
+ FILE *f;
+ char buf[256];
+ int fd;
+
+ /* Avoid race conditions between commands for one rule. */
+ if (!no_race_check) {
+ TAILQ_FOREACH_SAFE(cur, &fw_actions, next, tmp) {
+ if (cur->rule != r)
+ continue;
+
+ if (type == FW_GET_RULE_COUNTERS &&
+ (cur->type == FW_DEL_RULE ||
+ cur->type == FW_ADD_RULE)) {
+ /*
+ * Don't get rule counters if add/delete
+ * is already in progress.
+ */
+ return;
+ } else if (type == FW_DEL_RULE &&
+ cur->type == FW_GET_RULE_COUNTERS) {
+ /* Cancel get rule counters command. */
+ TAILQ_REMOVE(&fw_actions, cur, next);
+ pclose(cur->file);
+ free(cur);
+ }
+ }
+ }
+
+ if (config.fw_rule_mgmt_type == FW_CLI_RULE_MGMT) {
+ /*
+ * Fill in our token-value pairs that will be used for
+ * substitution in the appropriate tokenised string.
+ */
+ strlcpy(tokenpairs[TOK_ACTION].value, r->action,
+ sizeof(tokenpairs[TOK_ACTION].value));
+ addr.s_addr = r->id.dst_ip;
+ sprintf(tokenpairs[TOK_DSTIP].value, "%s",
+ inet_ntoa(addr));
+ sprintf(tokenpairs[TOK_DSTPORT].value, "%u", r->id.dst_port);
+
+ if (r->rtype == DI_ACTION_TYPE_BIDIRECTIONAL) {
+ strlcpy(tokenpairs[TOK_KEEPSTATE].value, "keep-state",
+ sizeof(tokenpairs[TOK_KEEPSTATE].value));
+ } else {
+ tokenpairs[TOK_KEEPSTATE].value[0] = '\0';
+ }
+
+ sprintf(tokenpairs[TOK_PROTO].value, "%u", r->id.proto);
+ sprintf(tokenpairs[TOK_RULENO].value, "%u", r->rule_no->no);
+ addr.s_addr = r->id.src_ip;
+ sprintf(tokenpairs[TOK_SRCIP].value, "%s",
+ inet_ntoa(addr));
+ sprintf(tokenpairs[TOK_SRCPORT].value, "%u", r->id.src_port);
+
+ switch (type) {
+ case FW_ADD_RULE:
+ expand_tokenised_str(config.cli_add_rule, buf,
+ sizeof(buf), tokenpairs, NUM_TOKEN_PAIRS);
+ break;
+
+ case FW_DEL_RULE:
+ expand_tokenised_str(config.cli_del_rule, buf,
+ sizeof(buf), tokenpairs, NUM_TOKEN_PAIRS);
+ break;
+
+ case FW_GET_RULE_COUNTERS:
+ expand_tokenised_str(config.cli_get_rule_counters, buf,
+ sizeof(buf), tokenpairs, NUM_TOKEN_PAIRS);
+ break;
+ }
+
+ } else if (config.fw_rule_mgmt_type == FW_FILE_RULE_MGMT) {
+ /*
+ * XXX: Add support for firewalls which use file-based rule
+ * management.
+ */
+ }
+
+ DID("fw command %s", buf);
+ f = exec(buf);
+
+ if (f != NULL) {
+ action = (struct fw_action *)malloc(sizeof(struct fw_action));
+ if (action == NULL)
+ errx(EX_OSERR, "can't alloc mem for fw_action");
+ action->file = f;
+ action->type = type;
+ action->rule = r;
+ TAILQ_INSERT_TAIL(&fw_actions, action, next);
+ fd = fileno(action->file);
+ FD_SET(fd, rset);
+ if (fd > *max_fd)
+ *max_fd = fd;
+ }
+
+ /* Output handling is done in process_fwaction_sockets(). */
+}
+
+static void
+free_fw_actions(void)
+{
+ struct fw_action *a;
+
+ while (!TAILQ_EMPTY(&fw_actions)) {
+ a = TAILQ_FIRST(&fw_actions);
+ TAILQ_REMOVE(&fw_actions, a, next);
+ pclose(a->file);
+ free(a);
+ }
+}
+
+static void
+free_rule_nos(void)
+{
+ struct rule_no *r, *n;
+
+ for (r = RB_MIN(rule_no_head, &rule_nos); r != NULL; r = n) {
+ n = RB_NEXT(rule_no_head, &rule_nos, r);
+ RB_REMOVE(rule_no_head, &rule_nos, r);
+ free(r);
+ }
+}
+
+static void
+free_class_actions(void)
+{
+ struct class_action *r, *n;
+
+ for (r = RB_MIN(class_action_head, &class_actions); r != NULL; r = n) {
+ n = RB_NEXT(class_action_head, &class_actions, r);
+ RB_REMOVE(class_action_head, &class_actions, r);
+ free(r);
+ }
+}
+
+static inline uint32_t
+hash_packet(struct ipfw_flow_id *id)
+{
+ uint32_t hash;
+
+ hash = id->dst_ip ^ id->src_ip ^ id->dst_port ^ id->src_port;
+ hash &= (rule_table_size - 1);
+
+ return (hash);
+}
+
+/* Find rule in hash table. */
+struct rule_entry *
+find_rule(struct ipfw_flow_id *f, struct rule_entry **prev)
+{
+ struct rule_entry *_prev, *q;
+ uint32_t h;
+
+ _prev = q = NULL;
+ h = hash_packet(f);
+
+ for (q = rule_table[h]; q != NULL;) {
+ if (f->proto == q->id.proto) {
+ if (IS_IP6_FLOW_ID(f)) {
+ if (IN6_ARE_ADDR_EQUAL(&f->src_ip6,
+ &q->id.src_ip6) &&
+ IN6_ARE_ADDR_EQUAL(&f->dst_ip6,
+ &q->id.dst_ip6) &&
+ f->src_port == q->id.src_port &&
+ f->dst_port == q->id.dst_port) {
+ break;
+ }
+ if (q->rtype & DI_FLOW_TYPE_BIDIRECTIONAL) {
+ if (IN6_ARE_ADDR_EQUAL(&f->src_ip6,
+ &q->id.dst_ip6) &&
+ IN6_ARE_ADDR_EQUAL(&f->dst_ip6,
+ &q->id.src_ip6) &&
+ f->src_port == q->id.dst_port &&
+ f->dst_port == q->id.src_port) {
+ break;
+ }
+ }
+ } else {
+ if (f->src_ip == q->id.src_ip &&
+ f->dst_ip == q->id.dst_ip &&
+ f->src_port == q->id.src_port &&
+ f->dst_port == q->id.dst_port) {
+ break;
+ }
+ if (q->rtype & DI_FLOW_TYPE_BIDIRECTIONAL) {
+ if (f->src_ip == q->id.dst_ip &&
+ f->dst_ip == q->id.src_ip &&
+ f->src_port == q->id.dst_port &&
+ f->dst_port == q->id.src_port) {
+ break;
+ }
+ }
+ }
+ }
+ _prev = q;
+ q = q->next;
+ }
+
+ if (q == NULL)
+ goto done;
+
+ if (_prev != NULL) {
+ /* Found and not in front. */
+ _prev->next = q->next;
+ q->next = rule_table[h];
+ rule_table[h] = q;
+ _prev = NULL;
+ }
+
+done:
+ *prev = _prev;
+
+ return (q);
+}
+
+static void
+free_rule(struct rule_entry *r)
+{
+ struct flow_class *s;
+
+ while (!SLIST_EMPTY(&r->flow_classes)) {
+ s = SLIST_FIRST(&r->flow_classes);
+ SLIST_REMOVE_HEAD(&r->flow_classes, next);
+ free(s);
+ }
+
+ if (r->to) {
+ LIST_REMOVE(r->to, next);
+ free(r->to);
+ }
+
+ free(r->rule_no);
+ free(r);
+}
+
+/* Remove rule from hash table and free rule memory if free is set. */
+static void
+remove_rule(struct rule_entry *r, struct rule_entry *prev, int free)
+{
+
+ if (prev != NULL)
+ prev->next = r->next;
+ else
+ rule_table[r->bucket] = r->next;
+
+ if (free)
+ free_rule(r);
+}
+
+/* Find rule and remove if it exists. */
+static struct rule_entry *
+find_remove_rule(struct rule_entry *n, int free)
+{
+ struct rule_entry *prev, *r;
+
+ prev = NULL;
+
+ r = find_rule(&n->id, &prev);
+ if (r != NULL) {
+ remove_rule(r, prev, free);
+ if (free)
+ r = NULL;
+ }
+
+ return (r);
+}
+
+static void
+free_rules(fd_set *rset, int *max_fd)
+{
+ struct rule_entry *next, *q;
+ int i;
+
+ for (i = 0; i < rule_table_size; i++) {
+ for (q = rule_table[i]; q != NULL ; ) {
+ next = q->next;
+ exec_fw(q, FW_DEL_RULE, rset, max_fd, 1);
+ remove_rule(q, NULL, 1);
+ q = next;
+ }
+ }
+ free(rule_table);
+}
+
+/* Add rule in table, add timeout and return pointer to new entry. */
+static struct rule_entry *
+add_rule(struct rule_entry *n)
+{
+ struct rule_no *no;
+ int h;
+ uint16_t t;
+
+ DID2("add rule");
+
+ if (RB_EMPTY(&rule_nos))
+ return (NULL); /* XXX: Do more? e.g. get rid of old rules? */
+
+ h = hash_packet(&n->id);
+ n->bucket = h;
+ n->next = rule_table[h];
+ rule_table[h] = n;
+ no = RB_MIN(rule_no_head, &rule_nos);
+ RB_REMOVE(rule_no_head, &rule_nos, no);
+ n->rule_no = no;
+ t = (timeout_now + n->expire) & (DEFAULT_TIMEOUT_SIZE - 1);
+ n->to = (struct timeout_entry *)malloc(sizeof(struct timeout_entry));
+ if (n->to == NULL)
+ errx(EX_OSERR, "can't alloc mem for a timeout_entry");
+ n->to->rule = n;
+ LIST_INSERT_HEAD(&timeouts[t], n->to, next);
+
+ return (n);
+}
+
+static struct rule_entry *
+update_timeout(struct rule_entry *n, struct rule_entry *r)
+{
+ uint16_t t;
+
+ DID2("update timeout");
+
+ r->expire_type = n->expire_type;
+ r->expire = n->expire;
+ t = (timeout_now + r->expire) & (DEFAULT_TIMEOUT_SIZE - 1);
+ LIST_REMOVE(r->to, next);
+ LIST_INSERT_HEAD(&timeouts[t], r->to, next);
+
+ return (r);
+}
+
+/* Set action based on class and set timeout. */
+static void
+set_action_timeout(struct rule_entry *n)
+{
+ struct class_action s, *r;
+ struct flow_class *c;
+
+ r = NULL;
+
+ /*
+ * Select action in this priority:
+ * 1. Locally defined for first class that has one
+ * 2. Locally defined default action
+ * 3. Defined by classifier node
+ * 4. Build-in default action
+ */
+
+ SLIST_FOREACH(c, &n->flow_classes, next) {
+ strcpy(s.cname, c->cname); /* XXX: Fix so no need to copy. */
+ s.class = c->class;
+ r = RB_FIND(class_action_head, &class_actions, &s);
+ if (r) {
+ strcpy(n->action, r->action);
+ strcpy(n->act_params, r->act_params);
+ break;
+ }
+ }
+
+ if (!r && (config.fw_default_action || !strlen(n->action))) {
+ /* Apply default action. */
+ strcpy(n->action, def_action.action);
+ strcpy(n->act_params, def_action.act_params);
+ }
+
+ if (n->expire == 0)
+ n->expire = DEFAULT_TIMEOUT;
+
+ if (n->expire_type == DIP_TIMEOUT_NONE)
+ n->expire_type = DIP_TIMEOUT_FLOW; /* Local timeout. */
+
+ /*
+ * If we use flow timeouts the classifier node may set a very long
+ * timeout, e.g. TCP, but the flow may end much quicker. Action node's
+ * firewall times out flow according to packets (e.g. tcp flags), but
+ * collector doesn't now.
+ * Solution: do more regular checking.
+ */
+ /* XXX: Get firewall expire value. May work for keep-state flows. */
+ if (n->expire_type == DIP_TIMEOUT_FLOW &&
+ n->expire > DEFAULT_TIMEOUT2) {
+ n->expire = DEFAULT_TIMEOUT2;
+ }
+}
+
+/* Parse rule and remove/add it in hash table and fw. */
+static int
+parse_rule(struct class_node *cnode, struct di_template *t, char *rb,
+ fd_set *rset, int *max_fd)
+{
+ struct flow_class *c;
+ struct rule_entry *n, *prev, *r;
+ int dlen, i, offs, toffs, type;
+
+ offs = 0;
+ type = -1;
+
+ DID2("parse rule");
+
+ n = (struct rule_entry *)calloc(1, sizeof(struct rule_entry));
+ if (n == NULL)
+ errx(EX_OSERR, "can't alloc mem for a rule_entry");
+
+ n->cnode = cnode;
+ SLIST_INIT(&n->flow_classes);
+
+ for (i = 0; i < t->fcnt; i++) {
+ DID2("field %u(%u)\n", t->fields[i].id, offs);
+
+ if (t->fields[i].len == -1) {
+ /* Read dynamic length. */
+ dlen = *((unsigned char *)(rb + offs));
+ offs++;
+
+ switch(t->fields[i].idx) {
+ case DIP_IE_CLASSES:
+ while (offs - toffs < dlen - 1) {
+ c = (struct flow_class *)malloc(
+ sizeof(struct flow_class));
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-projects
mailing list