--- 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 f4d36d0ecb3f sys/netinet/ip_diffuse.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/netinet/ip_diffuse.h Sun Sep 25 17:47:10 2011 +1000 @@ -0,0 +1,315 @@ +/*- + * 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 MAX_NAME_STR_LEN 8 + +/* Max action parameter string length (including terminator). */ +#define MAX_PARAM_STR_LEN 16 + +/* Max number of features in list. */ +#define MAX_FEATURES 12 + +/* Max number of statistics/features used by classifier. */ +#define MAX_FEATURE_STATS 64 + +/* Max number of classes/tags used by classifier. */ +#define MAX_CLASSES 25 + +/* Char to indicate class numbers. */ +#define CLASS_NO_CHAR '#' + +#define DI_API_VERSION 1 +#define DI_MAX_ID 0x10000 + +/* 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", __FUNCTION__, \ + ## __VA_ARGS__) +#else +#define DID(fmt, ...) do {} while (0) +#endif + +#ifdef DIFFUSE_DEBUG2 +#define DID2(fmt, ...) printf("diffuse: %-10s: " fmt "\n", __FUNCTION__, \ + ## __VA_ARGS__) +#else +#define DID2(fmt, ...) do {} while (0) +#endif + +#define DIND(fmt, ...) do {} while (0) + +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_CNF, + DI_CLASSIFIER, + DI_CLASSIFIER_CNF, + DI_EXPORT, + DI_FLOW_TABLE, + + DI_LAST +}; + +/* Flow table export. */ +enum ft_get_flags { + DI_FT_GET_NONE = 0x00, + DI_FT_GET_EXPIRED = 0x01 +}; + +typedef struct diffuse_ft_flow_class { + char cname[MAX_NAME_STR_LEN]; + uint16_t class; +} diffuse_ft_flow_class_t; + +/* This is the data sent to userspace for a show command. */ +typedef struct diffuse_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. */ + diffuse_ft_flow_class_t class_tags[tcnt]; /* Class tags. */ +#endif +} diffuse_ft_export_entry_t; + +/* Feature related types. */ +struct di_feature +{ + struct di_oid oid; + char name[MAX_NAME_STR_LEN]; /* Feature name. */ + char mod_name[MAX_NAME_STR_LEN]; /* Algorithm name. */ +}; + +typedef struct feature_stat { + uint8_t fdir; /* Flow direction. */ + char sname[MAX_NAME_STR_LEN]; /* Stat name. */ + char fname[MAX_NAME_STR_LEN]; /* Feature name. */ +} feature_stat_t; + +/* Classifier related types. */ +struct di_classifier +{ + struct di_oid oid; + char name[MAX_NAME_STR_LEN]; /* Classifier name. */ + char mod_name[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. */ + feature_stat_t fstats[]; /* Features. */ +}; + +/* Exporter related types. */ +enum diffuse_act_types { + DIFFUSE_ACTION_TYPE_UNIDIRECTIONAL = 0x00, + DIFFUSE_ACTION_TYPE_BIDIRECTIONAL = 0x01 +}; + +typedef struct export_cnf { + uint8_t proto; /* Fixed to UDP for now. */ + uint8_t addr_type; /* 4=ip4, 6=ip6 */ + uint16_t port; /* Port exporter is listening. */ + uint32_t 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[MAX_NAME_STR_LEN]; /* Opaque action for action node. */ + char action_param[MAX_PARAM_STR_LEN]; /* Opaque action params. */ +} export_cnf_t; + +struct di_export +{ + struct di_oid oid; + char name[MAX_NAME_STR_LEN]; /* Export set identifier. */ + export_cnf_t cnf; +}; + +/* Feature list. */ +enum diffuse_ft_types { + DIFFUSE_FLOW_TYPE_UNIDIRECTIONAL = 0x00, + DIFFUSE_FLOW_TYPE_BIDIRECTIONAL = 0x01, + DIFFUSE_FLOW_TYPE_MATCH_ONCE = 0x02, + DIFFUSE_FLOW_TYPE_MATCH_SAMPLE_REG = 0x04, + DIFFUSE_FLOW_TYPE_MATCH_SAMPLE_RAND = 0x08, + DIFFUSE_FLOW_TYPE_MATCH_ONCE_CLASS = 0x10, + DIFFUSE_FLOW_TYPE_MATCH_ONCE_EXP = 0x20 +}; + +/* 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[MAX_FEATURES][MAX_NAME_STR_LEN]; /* Names. */ + struct diffuse_feature *fptrs[MAX_FEATURES]; /* Feature ptrs. */ +} ipfw_insn_features; + +/* Feature match instruction. */ +enum diffuse_match_dir_types { + DIFFUSE_MATCH_DIR_NONE = 0x00, + DIFFUSE_MATCH_DIR_FWD = 0x01, + DIFFUSE_MATCH_DIR_BCK = 0x02 +}; + +enum diffuse_comp_types { + DIFFUSE_COMP_LT = 0, + DIFFUSE_COMP_LE, + DIFFUSE_COMP_EQ, + DIFFUSE_COMP_GE, + DIFFUSE_COMP_GT +}; + +typedef struct _ipfw_insn_feature_match { + ipfw_insn o; + struct diffuse_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[MAX_NAME_STR_LEN]; /* Feature statistic. */ + char fname[MAX_NAME_STR_LEN]; /* Feature name. */ +} ipfw_insn_feature_match; + +/* Match if class instruction. */ + +/* Max number of classes in match-if. */ +#define 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[MAX_MATCH_CLASSES]; /* Class number of matching + classes. */ + struct diffuse_classifier *clptr; /* Classifier ptr. */ + char cname[MAX_NAME_STR_LEN]; /* Classifier name. */ + char clnames[][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[MAX_NAME_STR_LEN]; /* Classifier name. */ + struct diffuse_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[MAX_NAME_STR_LEN]; /* Classifier name. */ + struct diffuse_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[MAX_NAME_STR_LEN]; /* Export name. */ + struct diffuse_export *eptr; /* Export instance ptr. */ +} ipfw_insn_export; + +#endif /* _NETINET_IP_DIFFUSE_H_ */ diff -r f4d36d0ecb3f sys/netinet/ip_fw.h --- a/sys/netinet/ip_fw.h Sun Sep 25 16:11:02 2011 +1000 +++ b/sys/netinet/ip_fw.h Sun Sep 25 17:47:10 2011 +1000 @@ -198,20 +198,41 @@ enum ipfw_opcodes { /* arguments (4 byt O_SETFIB, /* arg1=FIB number */ O_FIB, /* arg1=FIB desired fib number */ O_SOCKARG, /* socket argument */ O_CALLRETURN, /* arg1=called rule number */ O_FORWARD_IP6, /* fwd sockaddr_in6 */ + /* + * Actions for DIFFUSE. + */ + O_FEATURES, /* feature list */ + O_FEATURES_IMPLICIT, /* feature list (implicitely configured) */ + O_FLOW_TABLE, /* check flow table and update features */ + O_FEATURE_MATCH, /* feature match */ + O_AFTER_EACH_RULE, /* pseudo opcode, used after match for rule was + decided */ + O_CLASS_TAGS, /* tags used for classified packets */ + O_ML_CLASSIFY, /* classify sub flow (action) */ + O_ML_CLASSIFY_IMPLICIT, /* classify sub flow (implicit -> non-action) */ + O_MATCH_IF_CLASS, /* match if classified as class x */ + O_EXPORT, /* export rules */ + O_BEFORE_ALL_RULES , /* pseudo opcode, before matching rules, + _inside_ lock */ + O_AFTER_ALL_RULES, /* pseudo opcode, after all rules, _outside_ + lock */ + O_BEFORE_RULE_CHK, /* pseudo opcode, called before rule checks */ + O_AFTER_RULE_CHK, /* pseudo opcode, called after rule checks */ + O_LAST_OPCODE /* not an opcode! */ }; /* * The extension header are filtered only for presence using a bit * vector with a flag for each header. */ #define EXT_FRAGMENT 0x1 #define EXT_HOPOPTS 0x2 diff -r f4d36d0ecb3f sys/netinet/ipfw/diffuse_common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/netinet/ipfw/diffuse_common.h Sun Sep 25 17:47:10 2011 +1000 @@ -0,0 +1,122 @@ +/*- + * 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 cdata { + void *conf; /* Instance configuration ptr. */ +}; + +/* Flow data. */ +struct 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); +} + +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 f4d36d0ecb3f sys/netinet/ipfw/diffuse_feature.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/netinet/ipfw/diffuse_feature.h Sun Sep 25 17:47:10 2011 +1000 @@ -0,0 +1,144 @@ +/*- + * 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_ + +/* Algorithm types. */ +enum difffuse_feature_alg_types { + FEATURE_ALG_UNIDIRECTIONAL = 0x0, + FEATURE_ALG_BIDIRECTIONAL = 0x1 +}; + +struct cdata; +struct 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 diffuse_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 + */ + int (*init_instance)(struct cdata *, struct di_oid *); + + /* + * Destroy instance. + * param1: pointer to instance config + */ + int (*destroy_instance)(struct cdata *); + + /* + * Init state for flow. + * param1: pointer to instance config + * param2: pointer to flow work and stats data + */ + int (*init_stats)(struct cdata *, struct fdata *); + + /* + * Destroy flow state. + * param1: pointer to instance config + * param2: pointer to flow work and stats data + */ + int (*destroy_stats)(struct cdata *, struct 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) + */ + int (*update_stats)(struct cdata *, struct fdata *, struct mbuf *, + int proto, void *ulp, int dir); + + /* + * Reset stats. + * param1: pointer to flow work and stats data + */ + int (*reset_stats)(struct cdata *, struct fdata *); + + /* + * Get feature statistics. + * param1: pointer to instance config + * param2: pointer to flow work and stats data + * param3: pointer to stats + */ + int (*get_stats)(struct cdata *, struct 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 + */ + int (*get_stat)(struct cdata *, struct fdata *, int, int32_t *); + + /* + * Get names of statistics. + * param1: array of names + */ + int (*get_stat_names)(char **[]); + + /* + * Get configuration data. + * param1: pointer to instance config + * param2: pointer to configuration + * param3: only compute size (if 1) + */ + int (*get_conf)(struct cdata *, struct di_oid *, int); + + SLIST_ENTRY(diffuse_feature_alg) next; /* Next feature in the list. */ +}; + +#endif /* _NETINET_IPFW_DIFFUSE_FEATURE_H_ */ diff -r f4d36d0ecb3f sys/netinet/ipfw/diffuse_private.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/netinet/ipfw/diffuse_private.h Sun Sep 25 17:47:10 2011 +1000 @@ -0,0 +1,339 @@ +/*- + * 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() do { \ + rw_init(&diffuse_cfg.mtx, "diffuse main lock"); \ +} while (0) + +#define DI_LOCK_DESTROY() do { \ + rw_destroy(&diffuse_cfg.mtx); \ +} while (0) + +#define DI_RLOCK() rw_rlock(&diffuse_cfg.mtx) +#define DI_WLOCK() rw_wlock(&diffuse_cfg.mtx) +#define DI_UNLOCK() rw_unlock(&diffuse_cfg.mtx) +#define DI_RLOCK_ASSERT() rw_assert(&diffuse_cfg.mtx, RA_RLOCKED) +#define DI_WLOCK_ASSERT() rw_assert(&diffuse_cfg.mtx, RA_WLOCKED) +#define DI_LOCK_ASSERT() rw_assert(&diffuse_cfg.mtx, RA_LOCKED) + + +SLIST_HEAD(diffuse_feature_alg_head, diffuse_feature_alg); +LIST_HEAD(diffuse_features_head, diffuse_feature); +SLIST_HEAD(diffuse_classifier_alg_head, diffuse_classifier_alg); +LIST_HEAD(diffuse_classifier_head, diffuse_classifier); +LIST_HEAD(diffuse_export_head, diffuse_export); +/* One (big) list of export records (fifo) - double linked for fast removals. */ +TAILQ_HEAD(diffuse_export_rec_head, diffuse_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 diffuse_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 diffuse_feature_alg_head feature_list; + /* List of feature instances. */ + struct diffuse_features_head feature_inst_list; + + /* List of classifier algorithms. */ + struct diffuse_classifier_alg_head classifier_list; + /* List of classifier instances. */ + struct diffuse_classifier_head classifier_inst_list; + + /* List of export instances. */ + struct diffuse_export_head export_list; + + /* List of export records. */ + struct diffuse_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. */ +typedef struct diffuse_to_entry { + struct diffuse_ft_entry *flow; + LIST_ENTRY(diffuse_to_entry) next; +} diffuse_to_entry_t; + +typedef struct diffuse_export_rec* (*to_handler_fct_t) + (struct diffuse_ft_entry *q, struct diffuse_export *, int); + +/* List of flow classes. */ +typedef struct flow_class { + char cname[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(flow_class) next; +} flow_class_t; + +SLIST_HEAD(flow_class_head, flow_class); + +/* List of exporters. */ +typedef struct exp { + struct diffuse_export *ex; + SLIST_ENTRY(exp) next; +} exp_t; + +SLIST_HEAD(exp_list_head, exp); + +/* Flow table entry. */ +typedef struct diffuse_ft_entry { + struct diffuse_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 diffuse_feature *features[MAX_FEATURES]; /* Feature ptrs. */ + struct fdata fwd_data[MAX_FEATURES]; + struct fdata bck_data[MAX_FEATURES]; + struct timeval ex_time; /* Time last exported. */ + struct flow_class_head flow_classes; /* List of class tags. */ + struct diffuse_to_entry *to; /* Timeout list entry ptr. */ + struct exp_list_head ex_list; /* Ptrs to exporters. */ +} diffuse_ft_entry_t; + +/* Export data record. */ +typedef struct diffuse_export_rec { + char ename[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 diffuse_ft_flow_class class_tags[MAX_CLASSES]; + char action[MAX_NAME_STR_LEN]; + char act_params[MAX_PARAM_STR_LEN]; + struct diffuse_ft_entry *ft_rec; /* Flow entry ptr. */ + TAILQ_ENTRY(diffuse_export_rec) next; +} diffuse_export_rec_t; + +/* Feature. */ +struct diffuse_feature { + char *name; /* Instance name. */ + volatile int ref_count; /* Num rules referencing this feature. */ + struct cdata cnf; + struct diffuse_feature_alg *alg; /* Feature algorithm ptr. */ + LIST_ENTRY(diffuse_feature) next; /* Next in list. */ +}; + +/* Classifier. */ +typedef struct feature_stat_ptr { + struct diffuse_feature *fptr; /* Pointer to feature. */ + uint8_t sidx; /* Statistic index. */ +} feature_stat_ptr_t; + +struct diffuse_classifier { + char *name; /* Instance name. */ + volatile int ref_count; /* Num rules referencing this classifier. */ + struct diffuse_classifier_alg *alg; /* Classifier algorithm ptr. */ + struct cdata cnf; + 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). */ + feature_stat_t *fstats; /* Features + classes. */ + feature_stat_ptr_t *fstats_ptr; /* Feature indices. */ + LIST_ENTRY(diffuse_classifier) next; /* Next in list. */ +}; + +/* Export. */ +struct diffuse_export { + char *name; /* Instance name. */ + volatile int ref_count; /* Num rules referencing this exporter. */ + export_cnf_t cnf; /* Config from userspace. */ + struct socket *sock; /* Protocol socket. */ + uint32_t seq_no; /* Sequence number. */ + struct timeval last_pkt_time; /* Time of most recently sent packet. */ + struct mbuf *mh; /* First mbuf of export packet chain. */ + struct mbuf *mt; /* Last mbuf of export packet chain. */ + LIST_ENTRY(diffuse_export) next; /* Next in list. */ +}; + +/* Mbuf queue (used for exporting). */ +typedef struct mb_queue_entry { + struct mbuf *mbuf; + TAILQ_ENTRY(mb_queue_entry) next; +} mb_queue_entry_t; + +TAILQ_HEAD(mb_queue_head, mb_queue_entry); + +/* For tagging mbufs (packets) with classes. */ +#define MTAG_DIFFUSE_CLASS 1243750889 + +typedef struct class_tag { + struct m_tag tag; + int class; + int prev_class; + int confirm; +} class_tag_t; + +/* + * Stores all the persistent data required across multiple calls to + * diffuse_chk_pkt() + */ +typedef struct diffuse_chk_pkt_args { + diffuse_ft_entry_t *q; + ipfw_insn_class_tags *tcmd; + int no_class; +} diffuse_chk_pkt_args_t; + +/* + * Stores all the persistent data required across multiple calls to + * diffuse_chk_rule_cmd() + */ +typedef struct diffuse_chk_rule_cmd_args { +} diffuse_chk_rule_cmd_args_t; + +/* Global configuration. */ +extern struct diffuse_parms diffuse_cfg; + +/* 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); +diffuse_ft_entry_t *diffuse_ft_install_state(struct ip_fw *rule, + ipfw_insn_features *cmd, struct ip_fw_args *args, void *ulp, int pktlen); +diffuse_ft_entry_t *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(diffuse_ft_entry_t *e, char *cname, int class, + int *prev_class, int *confirm); +int diffuse_ft_get_class(diffuse_ft_entry_t *e, char *cname, int *prev_class, + int *confirm); +void diffuse_ft_remove_class(diffuse_ft_entry_t *e, char *cname); +void diffuse_ft_unlock(void); +void diffuse_ft_check_timeouts(to_handler_fct_t f); +void diffuse_ft_add_export(diffuse_ft_entry_t *e, + struct diffuse_export_rec *ex_rec, struct diffuse_export *nex); +void diffuse_ft_flush(int reset_counters_only); +int diffuse_ft_get_stat(diffuse_ft_entry_t *q, int fdir, + struct diffuse_feature *fptr, int sidx, int32_t *val); +int diffuse_ft_get_stats(diffuse_ft_entry_t *q, int fscnt, + struct feature_stat *fstats, struct feature_stat_ptr *fstats_ptr, + int32_t *fvec); +int diffuse_ft_do_export(diffuse_ft_entry_t *q, uint16_t confirm); +int diffuse_ft_update_features(diffuse_ft_entry_t *q, ipfw_insn_features *cmd, + struct ip_fw_args *args, void *ulp); + +/* diffuse_export.c */ +int diffuse_export_remove_recs(char *ename); +struct diffuse_export_rec* diffuse_export_add_rec(diffuse_ft_entry_t *q, + struct diffuse_export *ex, int add_command); +struct socket *diffuse_export_open(export_cnf_t *cnf); +int diffuse_export_send(struct diffuse_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 f4d36d0ecb3f sys/netinet/ipfw/ip_fw2.c --- a/sys/netinet/ipfw/ip_fw2.c Sun Sep 25 16:11:02 2011 +1000 +++ b/sys/netinet/ipfw/ip_fw2.c Sun Sep 25 17:47:10 2011 +1000 @@ -66,42 +66,49 @@ __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #ifdef INET6 #include #include #include #endif +#include +#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. */ /* ipfw_vnet_ready controls when we are open for business */ static VNET_DEFINE(int, ipfw_vnet_ready) = 0; #define V_ipfw_vnet_ready VNET(ipfw_vnet_ready) static VNET_DEFINE(int, fw_deny_unknown_exthdrs); @@ -907,20 +914,22 @@ ipfw_chk(struct ip_fw_args *args) uint16_t iplen=0; int pktlen; uint16_t etype = 0; /* Host order stored ether type */ /* * dyn_dir = MATCH_UNKNOWN when rules unchecked, * MATCH_NONE when checked and not matched (q = NULL), * MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL) */ int dyn_dir = MATCH_UNKNOWN; + /* For features. */ + int flow_dir = MATCH_UNKNOWN; ipfw_dyn_rule *q = NULL; struct ip_fw_chain *chain = &V_layer3_chain; /* * We store in ulp a pointer to the upper layer protocol header. * In the ipv4 case this is easy to determine from the header, * but for ipv6 we might have some additional headers in the middle. * ulp is NULL if not found. */ void *ulp = NULL; /* upper layer protocol pointer. */ @@ -928,20 +937,26 @@ ipfw_chk(struct ip_fw_args *args) /* XXX ipv6 variables */ int is_ipv6 = 0; uint8_t icmp6_type = 0; uint16_t ext_hd = 0; /* bits vector for extension header filtering */ /* end of ipv6 variables */ int is_ipv4 = 0; 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(). + */ + diffuse_chk_pkt_args_t di_args; + if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready)) return (IP_FW_PASS); /* accept */ dst_ip.s_addr = 0; /* make sure it is initialized */ src_ip.s_addr = 0; /* make sure it is initialized */ pktlen = m->m_pkthdr.len; args->f_id.fib = M_GETFIB(m); /* note mbuf not altered) */ proto = args->f_id.proto = 0; /* mark f_id invalid */ /* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */ @@ -1197,20 +1212,31 @@ do { \ args->f_id.proto = proto; args->f_id.src_port = src_port = ntohs(src_port); args->f_id.dst_port = dst_port = ntohs(dst_port); } IPFW_RLOCK(chain); if (! V_ipfw_vnet_ready) { /* shutting down, leave NOW. */ 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(&di_args, NULL, + O_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 * match on rule args->rule aka args->rule_id (PIPE, QUEUE, * REASS, NETGRAPH, DIVERT/TEE...) * Validate the slot and continue from the next one * if still present, otherwise do a lookup. */ f_pos = (args->rule.chain_id == chain->id) ? args->rule.slot : @@ -1236,29 +1262,29 @@ do { \ * cmdlen=0 if we don't want to advance cmd. * We break the outer loop by setting done=1 * We can restart the inner loop by setting l>0 and f_pos, f, cmd * as needed. */ for (; f_pos < chain->n_rules; f_pos++) { ipfw_insn *cmd; 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) ) continue; 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 * CHECK_STATE, and need to jump to the body of * the target rule. */ /* check_body: */ cmdlen = F_LEN(cmd); /* @@ -2399,59 +2425,87 @@ do { \ ip->ip_sum = in_cksum_hdr(ip); else ip->ip_sum = in_cksum(m, hlen); retval = IP_FW_REASS; set_match(args, f_pos, chain); } done = 1; /* exit outer loop */ break; } - default: + default: { + int ret; + + if (ipfw_diffuse_ext != 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. */ if (cmd->len & F_NOT) match = !match; if (match) { if (cmd->len & F_OR) skip_or = 1; } else { if (!(cmd->len & F_OR)) /* not an OR block, */ break; /* try next rule */ } } /* 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(&di_args, f, + O_AFTER_EACH_RULE, NULL, NULL, args, ulp, pktlen, + &flow_dir, &match, NULL, &done, &retval); + } + if (done) break; /* next_rule:; */ /* try next rule */ } /* end of outer for, scan rules */ if (done) { struct ip_fw *rule = chain->map[f_pos]; /* Update statistics */ rule->pcnt++; rule->bcnt += pktlen; rule->timestamp = time_uptime; } else { retval = IP_FW_DENY; 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(&di_args, NULL, O_AFTER_ALL_RULES, + NULL, NULL, args, ulp, pktlen, &flow_dir, NULL, NULL, &done, + &retval); + } + #ifdef __FreeBSD__ if (ucred_cache != NULL) crfree(ucred_cache); #endif return (retval); pullup_failed: if (V_fw_verbose) printf("ipfw: pullup failed\n"); return (IP_FW_DENY); diff -r f4d36d0ecb3f sys/netinet/ipfw/ip_fw_private.h --- a/sys/netinet/ipfw/ip_fw_private.h Sun Sep 25 16:11:02 2011 +1000 +++ b/sys/netinet/ipfw/ip_fw_private.h Sun Sep 25 17:47:10 2011 +1000 @@ -292,12 +292,57 @@ typedef int ipfw_nat_t(struct ip_fw_args typedef int ipfw_nat_cfg_t(struct sockopt *); extern ipfw_nat_t *ipfw_nat_ptr; #define IPFW_NAT_LOADED (ipfw_nat_ptr != NULL) extern ipfw_nat_cfg_t *ipfw_nat_cfg_ptr; extern ipfw_nat_cfg_t *ipfw_nat_del_ptr; 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 diffuse_chk_pkt_args; +struct diffuse_chk_rule_cmd_args; + +/* + * Rule checking prior to adding. + * params: instruction/command, have_action + */ +typedef int (*chk_rule_cmd_t)(struct diffuse_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 diffuse_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 f4d36d0ecb3f sys/netinet/ipfw/ip_fw_sockopt.c --- a/sys/netinet/ipfw/ip_fw_sockopt.c Sun Sep 25 16:11:02 2011 +1000 +++ b/sys/netinet/ipfw/ip_fw_sockopt.c Sun Sep 25 17:47:10 2011 +1000 @@ -58,20 +58,24 @@ __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include /* hooks */ #include +#include + +#include +#include #include #ifdef MAC #include #endif MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); /* * static variables followed by global ones (none in this file) @@ -153,37 +157,47 @@ swap_map(struct ip_fw_chain *chain, stru * Add a new rule to the list. Copy the rule into a malloc'ed area, then * possibly create a rule number and add the rule to the list. * Update the rule_number in the input struct so the caller knows it as well. * XXX DO NOT USE FOR THE DEFAULT RULE. * Must be called without IPFW_UH held */ int 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) return (EINVAL); l = RULESIZE(input_rule); rule = malloc(l, M_IPFW, M_WAITOK | M_ZERO); if (rule == NULL) return (ENOSPC); /* get_map returns with IPFW_UH_WLOCK if successful */ map = get_map(chain, 1, 0 /* not locked */); if (map == NULL) { free(rule, M_IPFW); return ENOSPC; } + if (ipfw_diffuse_ext != 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; rule->next_rule = NULL; rule->pcnt = 0; rule->bcnt = 0; rule->timestamp = 0; if (V_autoinc_step < 1) V_autoinc_step = 1; @@ -382,20 +396,22 @@ del_entry(struct ip_fw_chain *chain, uin map = swap_map(chain, map, chain->n_rules - n); /* 5. now remove the rules deleted from the old map */ for (i = start; i < end; i++) { int l; rule = map[i]; if (keep_rule(rule, cmd, new_set, num)) continue; l = RULESIZE(rule); chain->static_len -= l; ipfw_remove_dyn_children(rule); + if (ipfw_diffuse_ext != NULL) + ipfw_diffuse_ext->remove_rule(rule); rule->x_next = chain->reap; chain->reap = rule; } break; /* * In the next 3 cases the loop stops at (n_rules - 1) * because the default rule is never eligible.. */ @@ -521,39 +537,47 @@ zero_entry(struct ip_fw_chain *chain, u_ return (0); } /* * Check validity of the structure before insert. * Rules are simple, so this mostly need to check rule sizes. */ static int check_ipfw_struct(struct ip_fw *rule, int size) { - int l, cmdlen = 0; + diffuse_chk_rule_cmd_args_t 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"); return (EINVAL); } /* first, check for valid size */ l = RULESIZE(rule); if (l != size) { printf("ipfw: size mismatch (have %d want %d)\n", size, l); return (EINVAL); } if (rule->act_ofs >= rule->cmd_len) { printf("ipfw: bogus action offset (%u > %u)\n", rule->act_ofs, rule->cmd_len - 1); return (EINVAL); } + + if (ipfw_diffuse_ext != NULL) { + pseudo.opcode = O_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. */ for (l = rule->cmd_len, cmd = rule->cmd ; l > 0 ; l -= cmdlen, cmd += cmdlen) { cmdlen = F_LEN(cmd); if (cmdlen > l) { printf("ipfw: opcode %d size truncated\n", cmd->opcode); @@ -818,26 +842,43 @@ check_action: case O_IP6_SRC: case O_IP6_DST: case O_FLOW6ID: case O_IP6_SRC_MASK: case O_IP6_DST_MASK: case O_ICMP6TYPE: printf("ipfw: no IPv6 support in kernel\n"); return EPROTONOSUPPORT; #endif default: + + if (ipfw_diffuse_ext != 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) { + pseudo.opcode = O_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; } return 0; bad_size: printf("ipfw: opcode %d size %d wrong\n", cmd->opcode, cmdlen); return EINVAL;