--- Integrate DIFFUSE related hooks and supporting infrastructure into the --- IPFW kernel code. DIFFUSE is called both as part of packet processing (for --- per-flow management, feature calculation and classification) and IPFW --- related configuration (for handling DIFFUSE related rule configuration). --- The interface between IPFW and DIFFUSE was intentionally kept hook based to --- ensure minimal coupling between them. It should therefore be quite simple --- to integrate DIFFUSE with other firewalls in future if desired. --- --- Sponsored by: FreeBSD Foundation --- Reviewed by: bz --- MFC after: 1 month --- diff -r 038951bdc42a sys/netinet/ip_diffuse.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/netinet/ip_diffuse.h Tue Oct 04 22:37:38 2011 +1100 @@ -0,0 +1,333 @@ +/*- + * Copyright (c) 2010-2011 + * Swinburne University of Technology, Melbourne, Australia. + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _NETINET_IP_DIFFUSE_H_ +#define _NETINET_IP_DIFFUSE_H_ + +/* + * Definition of the kernel-userland API for DIFFUSE. + * Use the same type of interface as dummynet. + * + * Setsockopt() and getsockopt() pass a batch of objects, each of them starting + * with a "struct di_oid" which should fully identify the object and its + * relation with others in the sequence. + * The first object in each request should have: + * type = DI_CMD_*, id = DI_API_VERSION. + * For other objects, type and subtype specify the object, len indicates the + * total length including the header, and 'id' identifies the specific object. + */ + +/* Max feature/classifier name string length (including terminator). */ +#define DI_MAX_NAME_STR_LEN 8 + +#define DI_MAX_MODEL_STR_LEN 256 + +/* Max action parameter string length (including terminator). */ +#define DI_MAX_PARAM_STR_LEN 16 + +/* Max number of features in list. */ +#define DI_MAX_FEATURES 12 + +/* Max number of statistics/features used by classifier. */ +#define DI_MAX_FEATURE_STATS 64 + +/* Max number of classes/tags used by classifier. */ +#define DI_MAX_CLASSES 25 + +/* Char to indicate class numbers. */ +#define DI_CLASS_NO_CHAR '#' + +#define DI_API_VERSION 1 +#define DI_MAX_ID 0x10000 + +#define DI_UNIDIRECTIONAL 0x00 +#define DI_BIDIRECTIONAL 0x01 + +#define DI_ACTION_TYPE_UNIDIRECTIONAL DI_UNIDIRECTIONAL +#define DI_ACTION_TYPE_BIDIRECTIONAL DI_BIDIRECTIONAL +#define DI_FEATURE_ALG_UNIDIRECTIONAL DI_UNIDIRECTIONAL +#define DI_FEATURE_ALG_BIDIRECTIONAL DI_BIDIRECTIONAL +#define DI_FLOW_TYPE_UNIDIRECTIONAL DI_UNIDIRECTIONAL +#define DI_FLOW_TYPE_BIDIRECTIONAL DI_BIDIRECTIONAL + +/* Enable debugging output. */ +/* #define DIFFUSE_DEBUG 1 */ + +/* Enable more debugging output. */ +/* #define DIFFUSE_DEBUG2 1 */ + +/* Debugging support. */ +#ifdef DIFFUSE_DEBUG +#define DID(fmt, ...) printf("diffuse: %-10s: " fmt "\n", __func__, \ + ## __VA_ARGS__) +#else +#define DID(fmt, ...) do {} while (0) +#endif + +#ifdef DIFFUSE_DEBUG2 +#define DID2(fmt, ...) printf("diffuse: %-10s: " fmt "\n", __func__, \ + ## __VA_ARGS__) +#else +#define DID2(fmt, ...) do {} while (0) +#endif + +struct di_oid { + uint32_t len; /* Total obj len including this header + * (16 bit too small for flowtable show). */ + uint32_t id; /* Generic id. */ + uint16_t flags; /* Data we can pass in the oid. */ + uint8_t type; /* Type, e.g. delete or show. */ + uint8_t subtype; /* Object, e.g. feature, classifier. */ +}; + +/* + * These values are in the type field of struct di_oid. To preserve the ABI, + * never rearrange the list or delete entries with the exception of DI_LAST. + */ +enum { + DI_NONE = 0, + + DI_CMD_CONFIG, /* Objects follow. */ + DI_CMD_DELETE, /* Subtype + list of entries. */ + DI_CMD_GET, /* Subtype + list of entries. */ + DI_CMD_FLUSH, + DI_CMD_ZERO, + + DI_FEATURE, + DI_FEATURE_CONFIG, + DI_CLASSIFIER, + DI_CLASSIFIER_CONFIG, + DI_EXPORT, + DI_FLOW_TABLE, + + DI_LAST +}; + +/* Flow table export. */ +#define DI_FT_GET_NONE 0x00 +#define DI_FT_GET_EXPIRED 0x01 + +struct di_ft_flow_class { + char cname[DI_MAX_NAME_STR_LEN]; + uint16_t class; +}; + +/* This is the data sent to userspace for a show command. */ +struct di_ft_export_entry { + uint16_t ruleno; + uint16_t setno; + + struct ipfw_flow_id id; /* (masked) flow id */ + uint64_t pcnt; /* Packet match counter. */ + uint64_t bcnt; /* Byte match counter. */ + uint32_t expire; /* Expire time. */ + uint32_t bucket; /* Which bucket in hash table. */ + uint32_t state; /* State of this rule (typically a + * combination of TCP flags). */ + uint8_t fcnt; /* Number of features. */ + uint8_t tcnt; /* Number of class tags. */ + uint8_t final; /* Equals 1 if final entry. */ + uint8_t ftype; /* Bidirectional vs unidirectional. */ +#if 0 + /* + * The variable length data component which will appear after the above + * fixed size header is structured as follows: + */ + uint8_t fidx[fcnt]; /* Index for each feature in feature + * list. */ + uint8_t scnt[fcnt]; /* Number of stats per feature. */ + uint32_t fwd_svals[fcnt][scnt]; /* Forward statistics. */ + uint32_t bck_svals[fcnt][scnt]; /* Backward statistics. */ + di_ft_flow_class_t class_tags[tcnt]; /* Class tags. */ +#endif +}; + +/* Feature related types. */ +struct di_ctl_feature +{ + struct di_oid oid; + char name[DI_MAX_NAME_STR_LEN]; /* Feature name. */ + char mod_name[DI_MAX_NAME_STR_LEN]; /* Algorithm name. */ +}; + +struct di_feature_stat { + uint8_t fdir; /* Flow direction. */ + char sname[DI_MAX_NAME_STR_LEN]; /* Stat name. */ + char fname[DI_MAX_NAME_STR_LEN]; /* Feature name. */ +}; + +/* Classifier related types. */ +struct di_ctl_classifier +{ + struct di_oid oid; + char name[DI_MAX_NAME_STR_LEN]; /* Classifier name. */ + char mod_name[DI_MAX_NAME_STR_LEN]; /* Algorithm name. */ + uint16_t confirm; /* Confirm threshold for + * classification. */ + uint8_t ccnt; /* Number of class names. */ + uint8_t fscnt; /* Number of feature stats. */ + struct di_feature_stat fstats[]; /* Features. */ +}; + +/* Exporter related types. */ +struct di_export_config { + uint8_t proto; /* Fixed to UDP for now. */ + uint8_t addr_type; /* 4=ip4, 6=ip6 */ + uint16_t port; /* Port exporter is listening. */ + struct in_addr ip; /* IPv4 address of exporter. */ + struct in6_addr ip6; /* IPv6 address of exporter. */ + + uint16_t confirm; /* Need N consistent consecutive + * classifications. */ + uint16_t min_batch; + uint16_t max_batch; + uint32_t max_delay; /* Max ms delay for exporting. */ + uint32_t flags; /* e.g. retransmit. */ + uint8_t atype; /* Uni vs bidirectional action. */ + char action[DI_MAX_NAME_STR_LEN]; /* Opaque action for action node. */ + char action_param[DI_MAX_PARAM_STR_LEN]; /* Opaque action params. */ +}; + +struct di_ctl_export +{ + struct di_oid oid; + char name[DI_MAX_NAME_STR_LEN]; + struct di_export_config conf; +}; + +/* Classification policy defines. */ +#define DI_MATCH_ONCE 1 +#define DI_MATCH_SAMPLE_REG 2 +#define DI_MATCH_SAMPLE_RAND 3 +#define DI_MATCH_ONCE_CLASS 4 +#define DI_MATCH_ONCE_EXP 5 + +/* + * Instruction definitions. + */ + +typedef struct _ipfw_insn_features { + ipfw_insn o; + uint8_t ftype; /* Bidirectional, unidirectional, ... */ + uint8_t fcnt; /* Number of features. */ + uint16_t sample_int; /* Regular sampling interval. */ + uint32_t sample_prob; /* Random sampling. */ + char fnames[DI_MAX_FEATURES][DI_MAX_NAME_STR_LEN]; + struct di_feature *fptrs[DI_MAX_FEATURES]; /* Feature ptrs. */ +} ipfw_insn_features; + +/* Feature match instruction. */ +#define DI_MATCH_DIR_NONE 0x00 +#define DI_MATCH_DIR_FWD 0x01 +#define DI_MATCH_DIR_BCK 0x02 + +enum di_comp_types { + DI_COMP_LT = 0, + DI_COMP_LE, + DI_COMP_EQ, + DI_COMP_GE, + DI_COMP_GT +}; + +typedef struct _ipfw_insn_feature_match { + ipfw_insn o; + struct di_feature *fptr; /* Feature ptr. */ + int32_t thresh; /* Value we compare against. */ + uint8_t sidx; /* Stat index. */ + uint8_t fdir; /* Feature direction. */ + uint8_t comp; /* Comparison type. */ + char sname[DI_MAX_NAME_STR_LEN]; /* Feature statistic. */ + char fname[DI_MAX_NAME_STR_LEN]; /* Feature name. */ +} ipfw_insn_feature_match; + +/* Match if class instruction. */ + +/* Max number of classes in match-if. */ +#define DI_MAX_MATCH_CLASSES 1 + +typedef struct _ipfw_insn_match_if_class { + ipfw_insn o; + uint8_t mcnt; /* Number of classes that match. */ + uint8_t match_classes[DI_MAX_MATCH_CLASSES]; /* Class number of matching + * classes. */ + struct di_classifier *clptr; /* Classifier ptr. */ + char cname[DI_MAX_NAME_STR_LEN]; /* Classifier name. */ + char clnames[][DI_MAX_NAME_STR_LEN]; /* Class names. */ +} ipfw_insn_match_if_class; + +/* Tag using ipfw tags. */ +typedef struct _ipfw_insn_class_tags { + ipfw_insn o; + char cname[DI_MAX_NAME_STR_LEN]; /* Classifier + * name. */ + struct di_classifier *clptr; /* Classifier ptr. */ + uint8_t tcnt; /* Number of tags + * (<= number of classes). */ + uint16_t tags[]; /* One tag per class. */ +} ipfw_insn_class_tags; + +/* Classifier action instruction. */ +typedef struct _ipfw_insn_ml_classify { + ipfw_insn o; + char cname[DI_MAX_NAME_STR_LEN]; /* Classifier + * name. */ + struct di_classifier *clptr; /* Classifier ptr. */ + struct _ipfw_insn_class_tags *tcmd; /* Link to optional tag command. */ +} ipfw_insn_ml_classify; + +/* Export action instruction. */ +typedef struct _ipfw_insn_export { + ipfw_insn o; + char ename[DI_MAX_NAME_STR_LEN]; /* Export name. */ + struct di_export *eptr; /* Export instance ptr. */ +} ipfw_insn_export; + +/* + * Stores all the persistent data required across multiple calls to + * diffuse_chk_pkt(). + */ +struct di_chk_pkt_args { + struct di_ft_entry *q; + ipfw_insn_class_tags *tcmd; + int no_class; +}; + +/* + * Stores all the persistent data required (currently none) across multiple + * calls to diffuse_chk_rule_cmd(). + */ +struct di_chk_rule_cmd_args { +}; + +#endif /* _NETINET_IP_DIFFUSE_H_ */ diff -r 038951bdc42a sys/netinet/ip_fw.h --- a/sys/netinet/ip_fw.h Thu Sep 29 14:08:06 2011 +1000 +++ b/sys/netinet/ip_fw.h Tue Oct 04 22:37:38 2011 +1100 @@ -205,6 +205,28 @@ O_FORWARD_IP6, /* fwd sockaddr_in6 */ + /* + * Actions for DIFFUSE. + */ + O_DI_FEATURES, /* feature list */ + O_DI_FEATURES_IMPLICIT, /* feature list (implicitely configured) */ + O_DI_FLOW_TABLE, /* check flow table and update features */ + O_DI_FEATURE_MATCH, /* feature match */ + O_DI_AFTER_EACH_RULE, /* pseudo opcode, used after match for rule was + decided */ + O_DI_CLASS_TAGS, /* tags used for classified packets */ + O_DI_ML_CLASSIFY, /* classify sub flow (action) */ + O_DI_ML_CLASSIFY_IMPLICIT, /* classify sub flow + (implicit -> non-action) */ + O_DI_MATCH_IF_CLASS, /* match if classified as class x */ + O_DI_EXPORT, /* export rules */ + O_DI_BEFORE_ALL_RULES, /* pseudo opcode, before matching rules, + _inside_ lock */ + O_DI_AFTER_ALL_RULES, /* pseudo opcode, after all rules, _outside_ + lock */ + O_DI_BEFORE_RULE_CHK, /* pseudo opcode, called before rule checks */ + O_DI_AFTER_RULE_CHK, /* pseudo opcode, called after rule checks */ + O_LAST_OPCODE /* not an opcode! */ }; diff -r 038951bdc42a sys/netinet/ipfw/diffuse_common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/netinet/ipfw/diffuse_common.h Tue Oct 04 22:37:38 2011 +1100 @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2010-2011 + * Swinburne University of Technology, Melbourne, Australia. + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _NETINET_IPFW_DIFFUSE_COMMON_H_ +#define _NETINET_IPFW_DIFFUSE_COMMON_H_ + +/* MSVC does not support designated initializers so we need this ugly macro. */ +#ifdef _WIN32 +#define _FI(fld) +#else +#define _FI(fld) fld +#endif + +/* Feature or classifier instance data. */ +struct di_cdata { + void *conf; /* Instance configuration ptr. */ +}; + +/* Flow data. */ +struct di_fdata { + void *data; /* Work data ptr. */ + int32_t *stats; /* Stats ptr. */ +}; + +/* + * Fast fixed point division with rounding for dividing by a number of 2. + * a is the divident and b is the power of the divisor. + */ +static inline uint64_t +fixp_div(uint64_t a, int b) +{ + uint64_t q, r; + + if (b <= 0) + return (a); + + q = a >> b; + r = a & (b - 1); + if ((r << 1) >= ((uint64_t)1 << b)) + return (q + 1); + else + return (q); +} + +static inline uint32_t +fixp_sqrt(uint64_t x) +{ + uint64_t rem_hi, rem_lo, test_div; + uint32_t root; + int count; + + rem_hi = 0; + rem_lo = x; + root = 0; + count = 31; + + do { + rem_hi = (rem_hi << 2) | (rem_lo >> 62); + rem_lo <<= 2; /* Get 2 bits of arg. */ + root <<= 1; /* Get ready for the next bit in the root. */ + test_div = (root << 1) + 1; /* Test radical. */ + if (rem_hi >= test_div) { + rem_hi -= test_div; + root++; + } + } while (count-- != 0); + + return (root); +} + +/* Similar to timevalsub, but ensures the timeval returned will be >= 0. */ +static inline struct timeval +tv_sub0(struct timeval *num, struct timeval *sub) +{ + struct timeval rv; + + rv.tv_sec = num->tv_sec - sub->tv_sec; + rv.tv_usec = num->tv_usec - sub->tv_usec; + + if (rv.tv_usec < 0) { + rv.tv_usec += 1000000; + rv.tv_sec--; + } + if (rv.tv_sec < 0) { + rv.tv_sec = 0; + rv.tv_usec = 0; + } + + return (rv); +} + +#endif /* _NETINET_IPFW_DIFFUSE_COMMON_H_ */ diff -r 038951bdc42a sys/netinet/ipfw/diffuse_feature.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/netinet/ipfw/diffuse_feature.h Tue Oct 04 22:37:38 2011 +1100 @@ -0,0 +1,149 @@ +/*- + * Copyright (c) 2010-2011 + * Swinburne University of Technology, Melbourne, Australia. + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +/* + * DIFFUSE feature computation module parts. + */ + +#ifndef _NETINET_IPFW_DIFFUSE_FEATURE_H_ +#define _NETINET_IPFW_DIFFUSE_FEATURE_H_ + +struct di_cdata; +struct di_fdata; +struct mbuf; + +/* + * Descriptor for a feature. A feature is actually a list of related + * statistics. Contains all function pointers for a given feature. This is + * typically created when a module is loaded, and stored in a global list of + * feature instances. + */ +struct di_feature_alg { + const char *name; /* Feature name. */ + const int type; /* Unidirectional or bidirectional. */ + volatile int ref_count; /* Number of instances in the system */ + + /* + * Init instance. + * param1: pointer to instance config + * param2: config from userspace + * return: non-zero on error + */ + int (*init_instance)(struct di_cdata *, struct di_oid *); + + /* + * Destroy instance. + * param1: pointer to instance config + * return: non-zero on error + */ + int (*destroy_instance)(struct di_cdata *); + + /* + * Init state for flow. + * param1: pointer to instance config + * param2: pointer to flow work and stats data + * return: non-zero on error + */ + int (*init_stats)(struct di_cdata *, struct di_fdata *); + + /* + * Destroy flow state. + * param1: pointer to instance config + * param2: pointer to flow work and stats data + * return: non-zero on error + */ + int (*destroy_stats)(struct di_cdata *, struct di_fdata *); + + /* + * Update feature, called for each packet. + * pre: the packet is an IPv4 or IPv6 packet and the caller has done a + * pullup on the mbuf prior to calling. + * param1: pointer to instance config + * param2: pointer to flow work and stats data + * param3: packet (mbuf chain) + * param4: protocol on top of IP + * param5: pointer to upper layer protocol (e.g. UDP, TCP) + * param6: direction of the packet (MATCH_FORWARD or MATCH_REVERSE) + * return: non-zero on error + */ + int (*update_stats)(struct di_cdata *, struct di_fdata *, struct mbuf *, + int proto, void *ulp, int dir); + + /* + * Reset stats. + * param1: pointer to instance config + * param2: pointer to flow work and stats data + * return: non-zero on error + */ + int (*reset_stats)(struct di_cdata *, struct di_fdata *); + + /* + * Get feature statistics. + * param1: pointer to instance config + * param2: pointer to flow work and stats data + * param3: pointer to stats + * return: non-zero on error + */ + int (*get_stats)(struct di_cdata *, struct di_fdata *, int32_t **); + + /* + * Get one feature statistics. + * param1: pointer to instance config + * param2: pointer to flow work and stats data + * param3: which one + * param4: pointer to stat + * return: non-zero on error + */ + int (*get_stat)(struct di_cdata *, struct di_fdata *, int, int32_t *); + + /* + * Get names of statistics. + * param1: array of names + * return: non-zero on error + */ + int (*get_stat_names)(char **[]); + + /* + * Get configuration data. + * param1: pointer to instance config + * param2: pointer to configuration + * param3: only compute size (if 1) + * return: non-zero on error + */ + int (*get_conf)(struct di_cdata *, struct di_oid *, int); + + SLIST_ENTRY(di_feature_alg) next; /* Next feature in the list. */ +}; + +#endif /* _NETINET_IPFW_DIFFUSE_FEATURE_H_ */ diff -r 038951bdc42a sys/netinet/ipfw/diffuse_private.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/netinet/ipfw/diffuse_private.h Tue Oct 04 22:37:38 2011 +1100 @@ -0,0 +1,307 @@ +/*- + * Copyright (c) 2010-2011 + * Swinburne University of Technology, Melbourne, Australia. + * 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. + * + * 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. + * + * $FreeBSD$ + */ + +/* + * Internal stuff for DIFFUSE. + */ + +#ifndef _NETINET_IPFW_DIFFUSE_PRIVATE_H_ +#define _NETINET_IPFW_DIFFUSE_PRIVATE_H_ + +/* + * For platforms that do not have SYSCTL support, we wrap the SYSCTL_* into a + * function (one per file) to collect the values into an array at module + * initialization. The wrapping macros, SYSBEGIN() and SYSEND, are empty in the + * default case. + */ +#ifndef SYSBEGIN +#define SYSBEGIN(x) +#endif +#ifndef SYSEND +#define SYSEND +#endif + +MALLOC_DECLARE(M_DIFFUSE); + +#define DI_LOCK_INIT() rw_init(&di_conf.mtx, "diffuse main lock") +#define DI_LOCK_DESTROY() rw_destroy(&di_conf.mtx) +#define DI_RLOCK() rw_rlock(&di_conf.mtx) +#define DI_WLOCK() rw_wlock(&di_conf.mtx) +#define DI_UNLOCK() rw_unlock(&di_conf.mtx) +#define DI_RLOCK_ASSERT() rw_assert(&di_conf.mtx, RA_RLOCKED) +#define DI_WLOCK_ASSERT() rw_assert(&di_conf.mtx, RA_WLOCKED) +#define DI_LOCK_ASSERT() rw_assert(&di_conf.mtx, RA_LOCKED) + +SLIST_HEAD(di_feature_alg_head, di_feature_alg); +LIST_HEAD(di_features_head, di_feature); +SLIST_HEAD(di_classifier_alg_head, di_classifier_alg); +LIST_HEAD(di_classifier_head, di_classifier); +LIST_HEAD(di_export_head, di_export); +/* One (big) list of export records (fifo) - double linked for fast removals. */ +TAILQ_HEAD(di_export_rec_head, di_export_rec); + +/* + * Configuration and global data for DIFFUSE. + * + * When a configuration is modified from userland, 'id' is incremented + * so we can use the value to check for stale pointers. + */ +struct di_parms { + uint32_t id; /* Configuration version. */ + + int debug; + int init_done; + + int an_rule_removal; /* Explicit remove messages or timeouts. */ + + /* Counters of objects -- used for reporting space. */ + int feature_count; /* Number of feature instances. */ + int classifier_count; /* Number of classifier instances. */ + int export_count; /* Number of exports. */ + int export_rec_count; /* Number of exports recs. */ + + /* List of feature algorithms. */ + struct di_feature_alg_head feature_list; + /* List of feature instances. */ + struct di_features_head feature_inst_list; + + /* List of classifier algorithms. */ + struct di_classifier_alg_head classifier_list; + /* List of classifier instances. */ + struct di_classifier_head classifier_inst_list; + + /* List of export instances. */ + struct di_export_head export_list; + + /* List of export records. */ + struct di_export_rec_head export_rec_list; + +#ifdef _KERNEL + /* + * This file is normally used in the kernel, unless we do some userland + * tests, in which case we do not need a mtx. + */ +#if defined( __linux__ ) || defined( _WIN32 ) + spinlock_t mtx; +#else + struct rwlock mtx; +#endif +#endif /* _KERNEL */ +}; + +/* List of flow table timeouts. */ +struct di_to_entry { + struct di_ft_entry *flow; + LIST_ENTRY(di_to_entry) next; +}; + +typedef struct di_export_rec *(*di_to_handler_fn_t) + (struct di_ft_entry *q, struct di_export *, int); + +/* List of flow classes. */ +struct di_flow_class { + char cname[DI_MAX_NAME_STR_LEN]; /* Classifier name. */ + uint16_t class; /* Class ID. */ + int16_t prev_class; /* Prev class id, -1 if no previous class. */ + uint16_t confirm; /* How many identical consecutive classifications. */ + SLIST_ENTRY(di_flow_class) next; +}; + +SLIST_HEAD(di_flow_class_head, di_flow_class); + +/* List of exporters. */ +struct di_exp { + struct di_export *ex; + SLIST_ENTRY(di_exp) next; +}; + +SLIST_HEAD(di_exp_list_head, di_exp); + +/* Flow table entry. */ +struct di_ft_entry { + struct di_ft_entry *next; /* Linked list of rules. */ + struct ip_fw *rule; /* Used to pass up the rule number. */ + struct ipfw_flow_id id; /* (masked) flow id. */ + uint64_t pcnt; /* Packet match counter. */ + uint64_t bcnt; /* Byte match counter. */ + uint32_t expire; /* Expire time. */ + uint32_t bucket; /* Which bucket in hash table. */ + uint32_t state; /* State of this rule (typically a + * combination of TCP flags). */ + uint8_t ftype; /* Bidir vs unidir, match limiting. */ + uint8_t fcnt; /* Number of features. */ + uint8_t tcnt; /* Number of class tags. */ + uint16_t sample_int; /* Sample interval. */ + uint32_t sample_prob; /* Sample probability. */ + uint16_t pkts_after_last; /* Match limiting: packets n */ + struct di_feature *features[DI_MAX_FEATURES]; /* Feature ptrs. */ + struct di_fdata fwd_data[DI_MAX_FEATURES]; + struct di_fdata bck_data[DI_MAX_FEATURES]; + struct timeval ex_time; /* Time last exported. */ + struct di_flow_class_head flow_classes; /* List of class tags. */ + struct di_to_entry *to; /* Timeout list entry ptr. */ + struct di_exp_list_head ex_list; /* Ptrs to exporters. */ +}; + +/* Export data record. */ +struct di_export_rec { + char ename[DI_MAX_NAME_STR_LEN]; /* Generating export instance. */ + struct ipfw_flow_id id; /* IPs, ports. */ + struct timeval time; /* Generation time. */ + struct timeval no_earlier; /* Don't send before. */ + /* XXX: Flow label, TOS missing */ + uint8_t mtype; /* Message type. */ + uint8_t fcnt; /* Number of features. */ + uint8_t tcnt; /* Number of tags. */ + uint8_t ftype; /* Bidir vs unidir. */ + uint8_t match_dir; + uint8_t action_dir; /* Bidir vs unidir. */ + uint8_t ttype; /* Timeout type. */ + uint16_t tval; /* Timeout value in seconds. */ + uint32_t pcnt; /* Flow packet counter. */ + uint64_t bcnt; /* Flow byte counter. */ + struct di_ft_flow_class class_tags[DI_MAX_CLASSES]; + char action[DI_MAX_NAME_STR_LEN]; + char act_params[DI_MAX_PARAM_STR_LEN]; + struct di_ft_entry *ft_rec; /* Flow entry ptr. */ + TAILQ_ENTRY(di_export_rec) next; +}; + +/* Feature. */ +struct di_feature { + char *name; /* Instance name. */ + volatile int ref_count; /* Num rules referencing feature. */ + struct di_cdata conf; + struct di_feature_alg *alg; /* Feature algorithm ptr. */ + LIST_ENTRY(di_feature) next; /* Next in list. */ +}; + +/* Classifier. */ +struct di_feature_stat_ptr { + struct di_feature *fptr; /* Pointer to feature. */ + uint8_t sidx; /* Statistic index. */ +}; + +struct di_classifier { + char *name; /* Instance name. */ + volatile int ref_count; /* Num rules referencing classifier. */ + struct di_classifier_alg *alg; /* Classifier algorithm ptr. */ + struct di_cdata conf; + int32_t id; /* Unique id used for tag cookie. */ + uint16_t *tags; + uint16_t confirm; /* How many identical consecutive + * classifications. */ + uint8_t fscnt; /* Number of features. */ + uint8_t ccnt; /* Number of classes. */ + uint8_t tcnt; /* Tag count (ipfw tags). */ + struct di_feature_stat *fstats; /* Features + classes. */ + struct di_feature_stat_ptr *fstats_ptr; /* Feature indices. */ + LIST_ENTRY(di_classifier) next; /* Next in list. */ +}; + +/* Export. */ +struct di_export { + char *name; /* Instance name. */ + volatile int ref_count; /* Num rules referencing exporter. */ + struct di_export_config conf; /* Config from userspace. */ + struct socket *sock; /* Protocol socket. */ + uint32_t seq_no; /* Sequence number. */ + struct timeval last_pkt_time; /* Most recently sent packet. */ + struct mbuf *mh; /* First mbuf of export packet chain. */ + struct mbuf *mt; /* Last mbuf of export packet chain. */ + LIST_ENTRY(di_export) next; /* Next in list. */ +}; + +/* For tagging mbufs (packets) with classes. */ +#define MTAG_DIFFUSE_CLASS 1243750889 + +struct di_class_tag { + struct m_tag tag; + int class; + int prev_class; + int confirm; +}; + +/* Global configuration. */ +extern struct di_parms di_conf; + +/* Function prototypes. */ + +/* ip_diffuse.c */ +int diffuse_get_feature_idx(const char *name); + +/* diffuse_flowtable.c */ +void diffuse_ft_attach(void); +void diffuse_ft_detach(void); +void diffuse_ft_init(void); +void diffuse_ft_uninit(void); +int diffuse_ft_len(int expired); +struct di_ft_entry * diffuse_ft_install_state(struct ip_fw *rule, + ipfw_insn_features *cmd, struct ip_fw_args *args, void *ulp, int pktlen); +struct di_ft_entry * diffuse_ft_lookup_entry(struct ipfw_flow_id *pkt, + struct ip_fw_args *args, void *ulp, int pktlen, int *match_direction); +void diffuse_get_ft(char **pbp, const char *ep, int expired); +int diffuse_ft_entries(void); +void diffuse_ft_remove_entries(struct ip_fw *rule); +void diffuse_ft_add_class(struct di_ft_entry *e, char *cname, int class, + int *prev_class, int *confirm); +int diffuse_ft_get_class(struct di_ft_entry *e, char *cname, int *prev_class, + int *confirm); +void diffuse_ft_remove_class(struct di_ft_entry *e, char *cname); +void diffuse_ft_unlock(void); +void diffuse_ft_check_timeouts(di_to_handler_fn_t f); +void diffuse_ft_add_export(struct di_ft_entry *e, + struct di_export_rec *ex_rec, struct di_export *nex); +void diffuse_ft_flush(int reset_counters_only); +int diffuse_ft_get_stat(struct di_ft_entry *q, int fdir, + struct di_feature *fptr, int sidx, int32_t *val); +int diffuse_ft_get_stats(struct di_ft_entry *q, int fscnt, + struct di_feature_stat *fstats, struct di_feature_stat_ptr *fstats_ptr, + int32_t *fvec); +int diffuse_ft_do_export(struct di_ft_entry *q, uint16_t confirm); +int diffuse_ft_update_features(struct di_ft_entry *q, + ipfw_insn_features *cmd, struct ip_fw_args *args, void *ulp); + +/* diffuse_export.c */ +int diffuse_export_remove_recs(char *ename); +struct di_export_rec * diffuse_export_add_rec(struct di_ft_entry *q, + struct di_export *ex, int add_command); +struct socket *diffuse_export_open(struct di_export_config *conf); +int diffuse_export_send(struct di_export *ex); +void diffuse_export_close(struct socket *s); +void diffuse_export_init(void); +void diffuse_export_uninit(void); +void diffuse_export_prune_recs(void); + +#endif /* _NETINET_IPFW_DIFFUSE_PRIVATE_H_ */ diff -r 038951bdc42a sys/netinet/ipfw/ip_fw2.c --- a/sys/netinet/ipfw/ip_fw2.c Thu Sep 29 14:08:06 2011 +1000 +++ b/sys/netinet/ipfw/ip_fw2.c Tue Oct 04 22:37:38 2011 +1100 @@ -73,7 +73,7 @@ #include #include #include -#include +#include #include #include #include @@ -89,12 +89,18 @@ #include #endif +#include +#include + #include /* XXX for in_cksum */ #ifdef MAC #include #endif +/* DIFFUSE */ +ipfw_ext_t *ipfw_diffuse_ext = NULL; + /* * static variables followed by global ones. * All ipfw global variables are here. @@ -914,6 +920,8 @@ * MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL) */ int dyn_dir = MATCH_UNKNOWN; + /* For DIFFUSE features. */ + int flow_dir = MATCH_UNKNOWN; ipfw_dyn_rule *q = NULL; struct ip_fw_chain *chain = &V_layer3_chain; @@ -935,6 +943,12 @@ int done = 0; /* flag to exit the outer loop */ + /* + * DIFFUSE data container required for persistent storage between + * separate threads and calls to diffuse_chk_pkt(). + */ + struct di_chk_pkt_args di_args; + if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready)) return (IP_FW_PASS); /* accept */ @@ -1204,6 +1218,17 @@ IPFW_RUNLOCK(chain); return (IP_FW_PASS); /* accept */ } + + /* Call DIFFUSE before we start matching. */ + if (ipfw_diffuse_ext != NULL && ipfw_diffuse_ext->chk_pkt_cmd != NULL) { + ipfw_diffuse_ext->chk_pkt_cmd(&di_args, NULL, + O_DI_BEFORE_ALL_RULES, NULL, NULL, args, ulp, pktlen, + &flow_dir, NULL, NULL, &done, &retval); + + if (done) + return (retval); + } + if (args->rule.slot) { /* * Packet has already been tagged as a result of a previous @@ -1243,6 +1268,7 @@ uint32_t tablearg = 0; int l, cmdlen, skip_or; /* skip rest of OR block */ struct ip_fw *f; + int match; f = chain->map[f_pos]; if (V_set_disable & (1 << f->set) ) @@ -1251,7 +1277,6 @@ skip_or = 0; for (l = f->cmd_len, cmd = f->cmd ; l > 0 ; l -= cmdlen, cmd += cmdlen) { - int match; /* * check_body is a jump target used when we find a @@ -2407,7 +2432,22 @@ } default: + { + int ret; + + if (ipfw_diffuse_ext != NULL && + ipfw_diffuse_ext->chk_pkt_cmd != NULL) { + ret = ipfw_diffuse_ext->chk_pkt_cmd( + &di_args, f, cmd->opcode, &cmd, + &cmdlen, args, ulp, pktlen, + &flow_dir, &match, &l, &done, + &retval); + if (ret == 0) + break; + } + panic("-- unknown opcode %d\n", cmd->opcode); + } } /* end of switch() on opcodes */ /* * if we get here with l=0, then match is irrelevant. @@ -2427,6 +2467,14 @@ } /* end of inner loop, scan opcodes */ #undef PULLUP_LEN + /* Call DIFFUSE after we have a match for current rule. */ + if (ipfw_diffuse_ext != NULL && + ipfw_diffuse_ext->chk_pkt_cmd != NULL) { + ipfw_diffuse_ext->chk_pkt_cmd(&di_args, f, + O_DI_AFTER_EACH_RULE, NULL, NULL, args, ulp, pktlen, + &flow_dir, &match, NULL, &done, &retval); + } + if (done) break; @@ -2445,6 +2493,14 @@ printf("ipfw: ouch!, skip past end of rules, denying packet\n"); } IPFW_RUNLOCK(chain); + + /* Call DIFFUSE after packet processing. */ + if (ipfw_diffuse_ext != NULL && ipfw_diffuse_ext->chk_pkt_cmd != NULL) { + ipfw_diffuse_ext->chk_pkt_cmd(&di_args, NULL, + O_DI_AFTER_ALL_RULES, NULL, NULL, args, ulp, pktlen, + &flow_dir, NULL, NULL, &done, &retval); + } + #ifdef __FreeBSD__ if (ucred_cache != NULL) crfree(ucred_cache); diff -r 038951bdc42a sys/netinet/ipfw/ip_fw_private.h --- a/sys/netinet/ipfw/ip_fw_private.h Thu Sep 29 14:08:06 2011 +1000 +++ b/sys/netinet/ipfw/ip_fw_private.h Tue Oct 04 22:37:38 2011 +1100 @@ -299,5 +299,50 @@ extern ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr; extern ipfw_nat_cfg_t *ipfw_nat_get_log_ptr; +/* + * IPFW DIFFUSE extension. + */ + +/* In diffuse_private.h */ +struct di_chk_pkt_args; +struct di_chk_rule_cmd_args; + +/* + * Rule checking prior to adding. + * params: instruction/command, have_action + */ +typedef int (*chk_rule_cmd_t)(struct di_chk_rule_cmd_args *, ipfw_insn *, + int *); + +/* + * Packet checking. + * params: args, rule, opcode, instruction, args, ulproto, pktlen, direction, + * match, l, done, retval + */ +typedef int (*chk_pkt_cmd_t)(struct di_chk_pkt_args *, struct ip_fw *, int, + ipfw_insn **, int *, struct ip_fw_args *, void *, int, int *, int *, int *, + int *, int *); + +/* + * Remove rule. + * params: rule + */ +typedef void (*remove_rule_t)(struct ip_fw*); + +/* + * Add rule. + * params: rule + */ +typedef int (*add_rule_t)(struct ip_fw*); + +typedef struct ipfw_ext { + chk_rule_cmd_t chk_rule_cmd; + chk_pkt_cmd_t chk_pkt_cmd; + remove_rule_t remove_rule; + add_rule_t add_rule; +} ipfw_ext_t; + +extern ipfw_ext_t *ipfw_diffuse_ext; + #endif /* _KERNEL */ #endif /* _IPFW2_PRIVATE_H */ diff -r 038951bdc42a sys/netinet/ipfw/ip_fw_sockopt.c --- a/sys/netinet/ipfw/ip_fw_sockopt.c Thu Sep 29 14:08:06 2011 +1000 +++ b/sys/netinet/ipfw/ip_fw_sockopt.c Tue Oct 04 22:37:38 2011 +1100 @@ -65,6 +65,9 @@ #include #include /* hooks */ #include +#include + +#include #include #ifdef MAC @@ -160,7 +163,7 @@ ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule) { struct ip_fw *rule; - int i, l, insert_before; + int i, l, insert_before, ret; struct ip_fw **map; /* the new array of pointers */ if (chain->rules == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE-1) @@ -177,6 +180,16 @@ return ENOSPC; } + if (ipfw_diffuse_ext != NULL && ipfw_diffuse_ext->add_rule != NULL) { + ret = ipfw_diffuse_ext->add_rule(input_rule); + if (ret) { + free(rule, M_IPFW); + free(map, M_IPFW); + IPFW_UH_WUNLOCK(chain); + return (ret); + } + } + bcopy(input_rule, rule, l); /* clear fields not settable from userland */ rule->x_next = NULL; @@ -389,6 +402,9 @@ l = RULESIZE(rule); chain->static_len -= l; ipfw_remove_dyn_children(rule); + if (ipfw_diffuse_ext != NULL && + ipfw_diffuse_ext->remove_rule != NULL) + ipfw_diffuse_ext->remove_rule(rule); rule->x_next = chain->reap; chain->reap = rule; } @@ -528,9 +544,10 @@ static int check_ipfw_struct(struct ip_fw *rule, int size) { - int l, cmdlen = 0; + struct di_chk_rule_cmd_args di_args; + ipfw_insn *cmd, pseudo; + int l, ret, cmdlen = 0; int have_action=0; - ipfw_insn *cmd; if (size < sizeof(*rule)) { printf("ipfw: rule too short\n"); @@ -547,6 +564,14 @@ rule->act_ofs, rule->cmd_len - 1); return (EINVAL); } + + if (ipfw_diffuse_ext != NULL && + ipfw_diffuse_ext->chk_rule_cmd != NULL) { + pseudo.opcode = O_DI_BEFORE_RULE_CHK; + pseudo.len = F_INSN_SIZE(ipfw_insn); + ipfw_diffuse_ext->chk_rule_cmd(&di_args, &pseudo, &have_action); + } + /* * Now go for the individual checks. Very simple ones, basically only * instruction sizes. @@ -825,12 +850,30 @@ return EPROTONOSUPPORT; #endif default: + if (ipfw_diffuse_ext != NULL && + ipfw_diffuse_ext->chk_rule_cmd != NULL) { + ret = ipfw_diffuse_ext->chk_rule_cmd( + &di_args, cmd, &have_action); + if (ret > 0) + return (ret); + else if (ret == 0) + break; + } + printf("ipfw: opcode %d, unknown opcode\n", cmd->opcode); return EINVAL; } } } + + if (ipfw_diffuse_ext != NULL && + ipfw_diffuse_ext->chk_rule_cmd != NULL) { + pseudo.opcode = O_DI_AFTER_RULE_CHK; + pseudo.len = F_INSN_SIZE(ipfw_insn); + ipfw_diffuse_ext->chk_rule_cmd(&di_args, &pseudo, &have_action); + } + if (have_action == 0) { printf("ipfw: missing action\n"); return EINVAL;