Date: Tue, 22 Nov 2011 15:50:25 +0000 (UTC) From: Lawrence Stewart <lstewart@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r227820 - in projects/diffused_head/sbin/ipfw: . diffuse_collector Message-ID: <201111221550.pAMFoPdw098665@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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 ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201111221550.pAMFoPdw098665>