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