--- Integrate DIFFUSE related functionality into the IPFW userspace control --- utility. The code extends sbin/ipfw to manage, parse and output DIFFUSE --- specific configuration exchanged with the DIFFUSE kernel module via the --- DIFFUSE control raw socket. The UI extension for features and classifiers is --- kept modular to minimise the overhead involved in adding new features and --- classifiers in future. --- --- Sponsored by: FreeBSD Foundation --- Reviewed by: bz --- MFC after: 1 month --- diff -r c7d5be203cfa etc/mtree/BSD.include.dist --- a/etc/mtree/BSD.include.dist Sat Oct 08 16:34:15 2011 +1100 +++ b/etc/mtree/BSD.include.dist Tue Oct 11 18:22:08 2011 +1100 @@ -249,20 +249,22 @@ atm .. bluetooth include .. .. netflow .. .. netinet + ipfw + .. .. netinet6 .. netipsec .. netipx .. netnatm api .. diff -r c7d5be203cfa include/Makefile --- a/include/Makefile Sat Oct 08 16:34:15 2011 +1100 +++ b/include/Makefile Tue Oct 11 18:22:08 2011 +1100 @@ -209,20 +209,30 @@ copies: cd ${.CURDIR}/../sys/${_MARCH}/include/pc; \ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 *.h \ ${DESTDIR}${INCLUDEDIR}/${_MARCH}/pc .endif .endif .endfor cd ${.CURDIR}/../sys/rpc; \ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 types.h \ ${DESTDIR}${INCLUDEDIR}/rpc + cd ${.CURDIR}/../sys/netinet/ipfw; \ + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 diffuse_feature*.h \ + ${DESTDIR}${INCLUDEDIR}/netinet/ipfw; \ + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 diffuse_classifier*.h \ + ${DESTDIR}${INCLUDEDIR}/netinet/ipfw; \ + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 diffuse_common.h \ + ${DESTDIR}${INCLUDEDIR}/netinet/ipfw; \ + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 diffuse_user_compat.h \ + ${DESTDIR}${INCLUDEDIR}/netinet/ipfw + symlinks: @${ECHO} "Setting up symlinks to kernel source tree..." .for i in ${LDIRS} cd ${.CURDIR}/../sys/$i; \ for h in *.h; do \ ln -fs ../../../sys/$i/$$h ${DESTDIR}${INCLUDEDIR}/$i; \ done .endfor .for i in ${LSUBDIRS:Ndev/acpica:Ndev/bktr} cd ${.CURDIR}/../sys/$i; \ @@ -308,10 +318,19 @@ symlinks: cd ${.CURDIR}/../sys/fs/cd9660; \ for h in *.h; do \ ln -fs ../../../../sys/fs/cd9660/$$h \ ${DESTDIR}${INCLUDEDIR}/isofs/cd9660; \ done cd ${.CURDIR}/../sys/rpc; \ for h in types.h; do \ ln -fs ../../../sys/rpc/$$h \ ${DESTDIR}${INCLUDEDIR}/rpc; \ done + cd ${.CURDIR}/../sys/netinet/ipfw; \ + for h in diffuse_classifier*.h diffuse_feature*.h; do \ + ln -fs ../../../../sys/netinet/ipfw/$$h \ + ${DESTDIR}${INCLUDEDIR}/netinet/ipfw; \ + done; \ + ln -fs ../../../../sys/netinet/ipfw/diffuse_common.h \ + ${DESTDIR}${INCLUDEDIR}/netinet/ipfw; \ + ln -fs ../../../../sys/netinet/ipfw/diffuse_user_compat.h \ + ${DESTDIR}${INCLUDEDIR}/netinet/ipfw diff -r c7d5be203cfa sbin/ipfw/Makefile --- a/sbin/ipfw/Makefile Sat Oct 08 16:34:15 2011 +1100 +++ b/sbin/ipfw/Makefile Tue Oct 11 18:22:08 2011 +1100 @@ -1,10 +1,15 @@ # $FreeBSD$ PROG= ipfw SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c altq.c +SRCS+= diffuse_ui.c diffuse_ui_classifier_c45.c diffuse_ui_classifier_nbayes.c +SRCS+= diffuse_ui_feature_iat.c diffuse_ui_feature_iatbd.c +SRCS+= diffuse_ui_feature_pcnt.c diffuse_ui_feature_plen.c +SRCS+= diffuse_ui_feature_plenbd.c diffuse_ui_feature_skype.c diffuse_modules.c WARNS?= 2 DPADD= ${LIBUTIL} -LDADD= -lutil +LDADD= -lutil -lm MAN= ipfw.8 +DEBUG_FLAGS= -g .include diff -r c7d5be203cfa sbin/ipfw/diffuse_modules.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_modules.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,165 @@ +/*- + * 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. + */ + +/* + * Description: + * Functions to manage classifier and feature UI modules. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "diffuse_ui.h" + +SLIST_HEAD(di_featuremods_head, di_feature_module); +SLIST_HEAD(di_classifiermods_head, di_classifier_module); + +/* List of feature modules. */ +static struct di_featuremods_head *featuremods = + SLIST_HEAD_INITIALIZER(featuremods); + +/* List of classifier modules. */ +static struct di_classifiermods_head *classifiermods = + SLIST_HEAD_INITIALIZER(classifiermods); + +struct di_classifier_module * +find_classifier_module(const char *name) +{ + struct di_classifier_module *tmp; + + tmp = NULL; + + SLIST_FOREACH(tmp, classifiermods, next) { + if (strcmp(tmp->name, name) == 0) + break; + } + + return (tmp); +} + +void +print_classifier_modules() +{ + struct di_classifier_module *tmp; + + SLIST_FOREACH(tmp, classifiermods, next) { + printf("%s ", tmp->name); + } + printf("\n"); +} + +struct di_feature_module * +find_feature_module(const char *name) +{ + struct di_feature_module *tmp; + + tmp = NULL; + + SLIST_FOREACH(tmp, featuremods, next) { + if (strcmp(tmp->name, name) == 0) + break; + } + + return (tmp); +} + +void +print_feature_modules() +{ + struct di_feature_module *tmp; + + SLIST_FOREACH(tmp, featuremods, next) { + printf("%s ", tmp->name); + } + printf("\n"); +} + +void +diffuse_classifier_modules_init() +{ + + SLIST_INIT(classifiermods); + SLIST_INSERT_HEAD(classifiermods, c45_module(), next); + SLIST_INSERT_HEAD(classifiermods, nbayes_module(), next); +} + +void +diffuse_feature_modules_init() +{ + + SLIST_INIT(featuremods); + SLIST_INSERT_HEAD(featuremods, iat_module(), next); + SLIST_INSERT_HEAD(featuremods, iatbd_module(), next); + SLIST_INSERT_HEAD(featuremods, pcnt_module(), next); + SLIST_INSERT_HEAD(featuremods, plen_module(), next); + SLIST_INSERT_HEAD(featuremods, plenbd_module(), next); + SLIST_INSERT_HEAD(featuremods, skype_module(), next); +} + +void +diffuse_modules_init() +{ + + diffuse_feature_modules_init(); + diffuse_classifier_modules_init(); +#ifdef DIFFFUSE_DEBUG + printf("Known features: "); + print_feature_modules(); + printf("Known classifiers: "); + print_classifier_modules(); +#endif +} diff -r c7d5be203cfa sbin/ipfw/diffuse_proto.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_proto.h Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,67 @@ +/*- + * 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$ + */ + +/* + * Description: + * Functions for control protocol. + */ + +#ifndef _SBIN_IPFW_DIFFUSE_PROTO_H_ +#define _SBIN_IPFW_DIFFUSE_PROTO_H_ + +#define DI_EXP_DEFAULT_PORT 3191 + +/* Template list. */ + +struct di_template { + uint16_t id; /* Template id. */ + struct dip_info_element fields[64]; /* Fields. */ + int fcnt; /* Number of template fields. */ + RB_ENTRY(di_template) node; +}; + +static inline int +template_compare(struct di_template *a, struct di_template *b) +{ + + return ((a->id != b->id) ? (a->id < b->id ? -1 : 1) : 0); +} + +RB_HEAD(di_template_head, di_template); +RB_PROTOTYPE(di_template_head, di_template, node, template_compare); + +struct dip_info_descr diffuse_proto_get_info(uint16_t id); +void diffuse_proto_print_msg(char *buf, struct di_template_head *templ_list); + +#endif /* _SBIN_IPFW_DIFFUSE_PROTO_H_ */ diff -r c7d5be203cfa sbin/ipfw/diffuse_ui.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,2245 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diffuse_ui.h" +#include "diffuse_proto.h" +#include "ipfw2.h" + +#define GENERATE_FEATURES() do { \ + if (!features) { \ + (*cmd)->opcode = O_DI_FEATURES_IMPLICIT; \ + fill_features((ipfw_insn_features *)*cmd, NULL); \ + features = (ipfw_insn_features *)*cmd; \ + *cmd = next_cmd(*cmd); \ + } \ +} while(0) + +/* New tokens used by DIFFUSE in rule sets. */ +static struct di_option diffuse_main_params[] = { + { "module", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_MODULE }, + { "algorithm", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_ALGORITHM }, + { "use-feature-stats", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_USE_FEATURE_STATS }, + { "class-names", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_CLASS_NAMES }, + { "expired", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_EXPIRED }, + { "target", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_EXP_TARGET }, + { "confirm", DI_OPTION_ARG_UINT, 1, 65535, + TOK_DI_CONFIRM }, + { "min-batch", DI_OPTION_ARG_UINT, 1, 65535, + TOK_DI_EXP_MIN_BATCH }, + { "max-batch", DI_OPTION_ARG_UINT, 1, 65535, + TOK_DI_EXP_MAX_BATCH }, + { "max-delay", DI_OPTION_ARG_UINT, 1, 65535, + TOK_DI_EXP_MAX_DELAY }, + { "flow-key", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_EXP_FLOW_KEY }, + { "features", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_EXP_FEATURES }, + { "action", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_EXP_ACTION }, + { "action-params", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_EXP_ACTION_PARAMS }, + { "unidirectional", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_EXP_ACTION_UNIDIR }, + { NULL, 0, 0, 0, 0 } +}; + +/* Main parameters plus parameters of registered features/classifiers. */ +static struct di_option *diffuse_all_params = NULL; + +/* Globals for parsing functions. */ +ipfw_insn_features *features = NULL; /* Ptr to features cmd. */ +ipfw_insn_ml_classify *classifier = NULL; /* Ptr to classify action. */ +int have_feature_match = 0; + +/* Merge feature module params (called from module). */ +int +diffuse_add_params(struct di_option *opts, int size) +{ + struct di_option *o; + int old; + + if (diffuse_all_params == NULL) { + diffuse_all_params = (struct di_option *)malloc(size); + memcpy(diffuse_all_params, opts, size); + } else { + old = sizeof(diffuse_main_params); + diffuse_all_params = + (struct di_option *)realloc(diffuse_all_params, old + size); + o = diffuse_all_params; + while (o->name != NULL) + o++; + + memcpy(o, opts, size); + } + + return (0); +} + +/* + * Helper stuff for building commands, etc. + */ + +#define O_NEXT(p, len) ((void *)(((char *)(p)) + (len))) + +void +diffuse_rec_fill(struct di_oid *oid, int len, int type, uintptr_t id) +{ + + oid->len = len; + oid->type = type; + oid->subtype = 0; + oid->id = id; +} + +/* Make room in the buffer and move the pointer forward. */ +void * +diffuse_rec_next(struct di_oid **o, int len, int type) +{ + struct di_oid *ret; + + ret = *o; + diffuse_rec_fill(ret, len, type, 0); + *o = O_NEXT(*o, len); + + return (ret); +} + +/* + * Takes a table and a string, returns the value associated + * with the string (NULL in case of failure). + */ +struct di_option * +diffuse_match_token(struct di_option *table, char *string) +{ + struct di_option *pt; + size_t i; + + pt = NULL; + i = strlen(string); + for (pt = table; i && pt->name != NULL; pt++) + if (strlen(pt->name) == i && !bcmp(string, pt->name, i)) + break; + + return (pt); +} + +void +print_tokens(void) +{ + struct di_option *pt; + + for (pt = diffuse_all_params; pt->name != NULL; pt++) + printf("t %s\n", pt->name); +} + +/* Test if string only contains alphanumeric characters. */ +int +str_isalnum(char *s) +{ + + while (isalnum(*s)) + ++s; + + return (*s == 0); +} + +/* + * List functions. + */ + +/* List flow table. */ +static void +list_ft(struct di_oid *oid, struct di_oid *end, + struct di_feature_arr_entry *features, int counters) +{ + struct di_feature_module *f; + struct di_ft_export_entry *p; + struct in_addr a; + struct protoent *pe; + char *cname, *cp, *fname, *mod_name; + char buf[INET6_ADDRSTRLEN]; + int i, j; + int32_t *bck_svals, *fwd_svals; + uint16_t *class; + uint8_t *fidx, *scnt; + +#ifdef DIFFUSE_DEBUG2 + printf("list_ft\n"); +#endif + if (oid->type == DI_FLOW_TABLE) { + cp = (char *)oid + sizeof(struct di_oid); + while (cp < (char *)end) { + p = (struct di_ft_export_entry *)cp; + + printf("%05d", p->ruleno); + printf(" %05d", p->bucket); + + if (counters > 0) { + printf(" "); + pr_u64(&p->pcnt, pr_u64(&p->pcnt, 0)); + pr_u64(&p->bcnt, pr_u64(&p->bcnt,0)); + printf("(%ds)", p->expire); + } + if ((pe = getprotobynumber(p->id.proto)) != NULL) + printf(" %s", pe->p_name); + else + printf(" proto %u", p->id.proto); + + if (p->id.addr_type == 4) { + a.s_addr = htonl(p->id.src_ip); + printf(" %s %d", inet_ntoa(a), p->id.src_port); + + a.s_addr = htonl(p->id.dst_ip); + if (p->ftype & DI_FLOW_TYPE_BIDIRECTIONAL) + printf(" <-> "); + else + printf(" -> "); + + printf("%s %d", inet_ntoa(a), p->id.dst_port); + } else if (p->id.addr_type == 6) { + printf(" %s %d", inet_ntop(AF_INET6, + &p->id.src_ip6, buf, sizeof(buf)), + p->id.src_port); + if (p->ftype & DI_FLOW_TYPE_BIDIRECTIONAL) + printf(" <-> "); + else + printf(" -> "); + + printf("%s %d", inet_ntop(AF_INET6, + &p->id.dst_ip6, buf, sizeof(buf)), + p->id.dst_port); + } else { + printf(" UNKNOWN <-> UNKNOWN\n"); + } + + cp += sizeof(struct di_ft_export_entry); + fidx = (uint8_t *)cp; + cp += p->fcnt*sizeof(uint8_t); + scnt = (uint8_t *)cp; + cp += p->fcnt * sizeof(uint8_t); + printf(" "); + + for (i = 0; i < p->fcnt; i++) { + /* + * Need to have feature name and algo name + * lookup stat names via algo. + */ + fname = features[fidx[i]].name; + mod_name = features[fidx[i]].mod_name; + f = find_feature_module(mod_name); + + if (!(f->type & DI_FEATURE_ALG_BIDIRECTIONAL) && + p->ftype & DI_FLOW_TYPE_BIDIRECTIONAL) { + fwd_svals = (int32_t *)cp; + cp += scnt[i] * sizeof(int32_t); + bck_svals = (int32_t * )cp; + cp += scnt[i] * sizeof(int32_t); + + for (j = 0; j < scnt[i]; j++) { + printf("fwd.%s.%s=%d ", + f->get_stat_name(j), fname, + fwd_svals[j]); + } + for (j = 0; j < scnt[i]; j++) { + printf("bck.%s.%s=%d ", + f->get_stat_name(j), fname, + bck_svals[j]); + } + } else { + fwd_svals = (int32_t *)cp; + cp += scnt[i] * sizeof(int32_t); + + for (j = 0; j < scnt[i]; j++) { + printf("%s.%s=%d ", + f->get_stat_name(j), fname, + fwd_svals[j]); + } + } + } + for (i = 0; i < p->tcnt; i++) { + cname = cp; + cp += DI_MAX_NAME_STR_LEN; + class = (uint16_t *)cp; + cp += sizeof(uint16_t); + printf("%s:%c%u ", cname, DI_CLASS_NO_CHAR, + *class); + } + printf("\n"); + } + } +} + +/* List features and flow table. */ +static void +list_features(struct di_oid *oid, struct di_oid *end) +{ + struct di_ctl_feature *f; + struct di_feature_module *last_mod; + struct di_feature_arr_entry feature_arr[DI_MAX_FEATURES]; + int fcnt; + + last_mod = NULL; + + for (fcnt = 0; oid != end; oid = O_NEXT(oid, oid->len)) { + if (oid->len < sizeof(*oid)) + errx(1, "invalid oid len %d", oid->len); + + switch (oid->type) { + case DI_CMD_GET: + if (co.verbose) + printf("answer for cmd %d, len %d\n", oid->type, + oid->id); + break; + + case DI_FEATURE: + { + f = (struct di_ctl_feature *)oid; + printf("feature %s (algorithm %s)\n", f->name, + f->mod_name); + last_mod = find_feature_module(f->mod_name); + + strcpy(feature_arr[fcnt].name, f->name); + strcpy(feature_arr[fcnt].mod_name, f->mod_name); + fcnt++; + break; + } + /* + * Assume that we have a set of + * { DI_FEATURE, DI_FEATURE_CONFIG }. + */ + + case DI_FEATURE_CONFIG: + if (last_mod != NULL) + last_mod->print_opts(oid); + else + printf(" unrecognized feature\n"); + break; + + case DI_FLOW_TABLE: + list_ft(oid, end, feature_arr, 1); + break; + + default: + printf("unrecognized object %d size %d\n", oid->type, + oid->len); + break; + } + } +} + +/* List classifiers. */ +static void +list_classifiers(struct di_oid *oid, struct di_oid *end) +{ + struct di_classifier_module *last_mod; + struct di_ctl_classifier *c; + int i; + + last_mod = NULL; + + for (; oid != end; oid = O_NEXT(oid, oid->len)) { + if (oid->len < sizeof(*oid)) + errx(1, "invalid oid len %d", oid->len); + + switch (oid->type) { + case DI_CMD_CONFIG: + break; + + case DI_CMD_GET: + if (co.verbose) + printf("answer for cmd %d, len %d\n", oid->type, + oid->id); + break; + + case DI_CLASSIFIER: + { + c = (struct di_ctl_classifier *)oid; + + printf("classifier %s (algorithm %s)\n", c->name, + c->mod_name); + printf(" features: %d\n", c->fscnt); + for (i = 0; i < c->fscnt; i++) { + printf(" %s%s.%s\n", + c->fstats[i].fdir == DI_MATCH_DIR_NONE ? + "" : c->fstats[i].fdir == DI_MATCH_DIR_FWD ? + "fwd." : "bck.", + c->fstats[i].sname, + c->fstats[i].fname); + } + printf(" classes: %d\n", c->ccnt); + for (i = 0; i < c->ccnt; i++) + printf(" %s\n", c->fstats[c->fscnt+i].fname); + + printf(" confirm: %d\n", c->confirm); + last_mod = find_classifier_module(c->mod_name); + break; + } + /* + * Assume that we have a set of + * { DI_CLASSIFIER, DI_CLASSIFIER_CONFIG }. + */ + + case DI_CLASSIFIER_CONFIG: + if (last_mod != NULL) + last_mod->print_opts(oid); + else + printf(" unrecognized feature\n"); + break; + + default: + printf("unrecognized object %d size %d\n", oid->type, + oid->len); + break; + } + } +} + +/* List exports. */ +static void +list_exports(struct di_oid *oid, struct di_oid *end) +{ + struct di_ctl_export *e; + struct in_addr a; + + for (; oid != end; oid = O_NEXT(oid, oid->len)) { + if (oid->len < sizeof(*oid)) + errx(1, "invalid oid len %d", oid->len); + + switch (oid->type) { + case DI_CMD_GET: + if (co.verbose) + printf("answer for cmd %d, len %d\n", oid->type, + oid->id); + break; + + case DI_EXPORT: + { + e = (struct di_ctl_export *)oid; + printf("export %s\n", e->name); + /* XXX: IPv6 missing */ + a = e->conf.ip; + printf(" target udp://%s:%d\n", inet_ntoa(a), + e->conf.port); + printf(" confirm %d\n", e->conf.confirm); + printf(" min_batch %d\n", e->conf.min_batch); + printf(" max_batch %d\n", e->conf.max_batch); + printf(" max_delay %d\n", e->conf.max_delay); + printf(" action %s %s\n", e->conf.action, + e->conf.action_param); /* XXX: Define types. */ + if (e->conf.atype & DI_ACTION_TYPE_BIDIRECTIONAL) + printf(" bidirectional\n"); + else + printf(" unidirectional\n"); + break; + } + default: + printf("unrecognized object %d size %d\n", oid->type, + oid->len); + break; + } + } +} + +/* + * Main functions called from ipfw. + */ + +/* XXX: Need to extend main ipfw help. */ + +void +print_feature_usage() +{ + + printf("ipfw feature config "); +} + +void +print_classifier_usage() +{ + + printf("ipfw mlclass config [confirm]"); +} + +void +print_export_usage() +{ + + printf("ipfw export config target ://: "); + printf("[confirm ] [action ] [action-params ] "); + printf("[min-batch ] [max-batch ] "); + printf("[max-delay ]\n"); +} + +void +diffuse_init() +{ + + diffuse_modules_init(); + diffuse_add_params(diffuse_main_params, sizeof(diffuse_main_params)); + /* XXX: Modules are not deregistered at the end. */ +} + +/* + * Only support one feature/classifier/export name now. + * XXX: extend to comma separated list. + */ +void +diffuse_delete(int ac, char *av[], int type) +{ + struct di_ctl_classifier *class; + struct di_ctl_export *exp; + struct di_ctl_feature *feature; + struct di_oid *buf, *base; + char *name; + int lmax; + + feature = NULL; + class = NULL; + exp = NULL; + +#ifdef DIFFFUSE_DEBUG2 + printf("diffuse delete\n"); +#endif + + lmax = sizeof(struct di_oid); /* Command header. */ + if (type == DI_FEATURE) + lmax += sizeof(struct di_ctl_feature); + else if (type == DI_CLASSIFIER) + lmax += sizeof(struct di_ctl_classifier); + else if (type == DI_EXPORT) + lmax += sizeof(struct di_ctl_export); + else + errx(1, "invalid DIFFUSE deletion type %d", type); + + base = buf = safe_calloc(1, lmax); + /* All commands start with a 'DELETE' and a version. */ + diffuse_rec_next(&buf, sizeof(struct di_oid), DI_CMD_DELETE); + base->id = DI_API_VERSION; + + if (type == DI_FEATURE) { + feature = diffuse_rec_next(&buf, sizeof(*feature), DI_FEATURE); + name = feature->name; + } else if (type == DI_CLASSIFIER) { + class = diffuse_rec_next(&buf, sizeof(*class), DI_CLASSIFIER); + name = class->name; + } else if (type == DI_EXPORT) { + exp = diffuse_rec_next(&buf, sizeof(*exp), DI_EXPORT); + name = exp->name; + } + + av++; ac--; + + /* Set name. */ + if (ac) { + strncpy(name, *av, DI_MAX_NAME_STR_LEN - 1); + name[DI_MAX_NAME_STR_LEN - 1] = '\0'; + av++; ac--; + } else { + errx(EX_USAGE, "need a feature/classifier/export name"); + } + + if (do_cmd(IP_DIFFUSE, base, (char *)buf - (char *)base)) + err(1, "%s: setsockopt(%s)", "IP_DIFFUSE_DELETE", name); +} + +static void +check_option_val(int ac, char **av, struct di_option *opt) +{ + double val; + char *endptr; + + if (opt == NULL) + errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); + + if (opt->arg_type != DI_OPTION_ARG_NOARG) { + if (ac > 0 && (*av)) { + if (opt->arg_type == DI_OPTION_ARG_UINT || + opt->arg_type == DI_OPTION_ARG_INT || + opt->arg_type == DI_OPTION_ARG_DOUBLE) { + if (opt->arg_type == DI_OPTION_ARG_UINT) + val = (double)strtoul(*av, &endptr, 0); + else if (opt->arg_type == DI_OPTION_ARG_INT) + val = (double)strtol(*av, &endptr, 0); + else + val = strtod(*av, &endptr); + + if (*endptr) { + errx(EX_DATAERR, "value of option %s " + "has wrong format at '%s'", + opt->name, endptr); + } + + if (val < opt->arg_min || val > opt->arg_max) { + errx(EX_DATAERR, "value of option %s " + "not in allowed range %.2f to %.2f", + opt->name, opt->arg_min, + opt->arg_max); + } + } + } else { + if (opt->arg_max > opt->arg_min) { + errx(EX_USAGE, "option %s needs an argument " + "%.2f ... %.2f", opt->name, opt->arg_min, + opt->arg_max); + } else { + errx(EX_USAGE, "option %s needs an argument", + opt->name); + } + } + } +} + +/* Configuration of features. */ +static void +feature_config(int ac, char **av) +{ + struct di_ctl_feature *feature; + struct di_feature_module *f; + struct di_oid *buf, *cmd, *base; + struct di_option *opt, *opts; + int fconf_len, len, size; + + feature = NULL; + len = 0; + +#ifdef DIFFUSE_DEBUG2 + printf("diffuse feature config\n"); +#endif + + /* Allocate space for 1 header + 1 feature. */ + len = sizeof(struct di_oid) + sizeof(struct di_feature); + base = buf = safe_calloc(1, len); + + /* All commands start with a CONFIG and a version. */ + diffuse_rec_next(&buf, sizeof(struct di_oid), DI_CMD_CONFIG); + base->id = DI_API_VERSION; + feature = diffuse_rec_next(&buf, sizeof(*feature), DI_FEATURE); + av++; ac--; + + /* Get feature name. */ + if (ac) { + if (!str_isalnum(*av)) { + errx(EX_USAGE, "feature name can only contain " + "alphanumeric characters"); + } + strncpy(feature->name, *av, DI_MAX_NAME_STR_LEN - 1); + feature->name[DI_MAX_NAME_STR_LEN - 1] = '\0'; + av++; ac--; + } else { + errx(EX_USAGE, "need a feature name"); + } + + /* Set feature configuration. */ + while (ac > 0) { + if (*av && !strcmp("-h", *av)) { + print_feature_usage(); + if (feature != NULL) { + f = find_feature_module(feature->mod_name); + if (f) + f->print_usage(); + else + printf("module \n"); + } + free(base); + exit(0); + } + + opt = diffuse_match_token(diffuse_all_params, *av); + ac--; av++; + check_option_val(ac, av, opt); + + switch(opt->token) { + case TOK_DI_MODULE: + NEED(feature, "module is only for feature config"); + NEED1("module name must be specified"); + strncpy(feature->mod_name, *av, + DI_MAX_NAME_STR_LEN - 1); + feature->mod_name[DI_MAX_NAME_STR_LEN - 1] = '\0'; + + f = find_feature_module(feature->mod_name); + if (f == NULL) { + errx(EX_DATAERR, "unrecognised module ``%s''", + feature->mod_name); + } + + /* Add options. */ + size = f->get_opts(&opts); + diffuse_add_params(opts, size); + + /* Add space for config record. */ + fconf_len = f->get_conf_size(); + len += fconf_len; + buf = base = realloc(base, len); + buf = O_NEXT(buf, sizeof(struct di_oid)); + + /* Restore pointer to feature. */ + feature = (struct di_ctl_feature *)buf; + buf = O_NEXT(buf, sizeof(struct di_feature)); + memset(buf, 0, fconf_len); + cmd = (struct di_oid *)buf; + cmd->type = DI_FEATURE_CONFIG; + cmd->subtype = 0; + cmd->len = fconf_len; + f->parse_opts(TOK_DI_OPTS_INIT, NULL, buf); + + ac--; av++; + break; + + default: + { + f = find_feature_module(feature->mod_name); + if (!f) { + errx(EX_DATAERR, "unrecognised option ``%s''", + av[-1]); + } else { + f->parse_opts(opt->token, *av, buf); + if (opt->arg_type != DI_OPTION_ARG_NOARG) { + ac--; av++; + } + } + } + } + } + + if (!strlen(feature->mod_name)) + errx(EX_DATAERR, "no feature module name specified"); + + if (do_cmd(IP_DIFFUSE, base, len)) + err(1, "setsockopt(%s)", "IP_DIFFUSE_FEATURE_CONFIGURE"); +} + +/* + * Parse list of feature stats. + * Max number of feature stats is DI_MAX_FEATURE_STATS (netinet/ip_fw.h). + */ +static int +fill_feature_stats(struct di_ctl_classifier *class, char *arg, int check) +{ + char *name, *tmp; + char *sep = ","; + char *s1, *s2, *s3, *p; + int len = 0; + + if (!check) + class->fscnt = 0; + + tmp = malloc(strlen(arg) + 1); + strcpy(tmp, arg); + + for (name = strtok(tmp, sep); name; name = strtok(NULL, sep)) + { + if (!check) { + s1 = s2 = s3 = NULL; + + if (class->fscnt + 1 > DI_MAX_FEATURE_STATS) { + errx(EX_DATAERR, "maximum number of features " + "limited to %d", DI_MAX_FEATURE_STATS); + } + + /* Parse the feature name. */ + s1 = name; + p = strstr(name, "."); + if (p == NULL) { + errx(EX_USAGE, "feature stat must be specified " + "as [fwd|bck.]stat_name.feature_name"); + } + s2 = p + 1; + *p = '\0'; + p = strstr(s2, "."); + if (p != NULL) { + s3 = p + 1; + *p = '\0'; + } + + class->fstats[class->fscnt].fdir = DI_MATCH_DIR_NONE; + if (s3 != NULL) { + /* First string must be direction. */ + if (!strcmp(s1, "fwd")) { + class->fstats[class->fscnt].fdir = + DI_MATCH_DIR_FWD; + } else if (!strcmp(s1, "bck")) { + class->fstats[class->fscnt].fdir = + DI_MATCH_DIR_BCK; + } else { + errx(EX_USAGE, "missing " + "feature/statistic name or " + "direction missing/mispelled"); + } + s1 = s2; + s2 = s3; + } + + /* Second string is statistics. */ + strncpy(class->fstats[class->fscnt].sname, s1, + DI_MAX_NAME_STR_LEN - 1); + class->fstats[class->fscnt].sname[DI_MAX_NAME_STR_LEN - + 1] = '\0'; + + /* Last string is feature name. */ + strncpy(class->fstats[class->fscnt].fname, s2, + DI_MAX_NAME_STR_LEN - 1); + class->fstats[class->fscnt].fname[DI_MAX_NAME_STR_LEN - + 1] = '\0'; + class->fscnt++; + } + len += sizeof(struct di_feature_stat); + } + + free(tmp); + +#ifdef DIFFUSE_DEBUG2 + printf("feature stats %d\n", class->fscnt); +#endif + + return (len); +} + +/* + * Parse list of classes. + * Max number of classes is DI_MAX_CLASSES (netinet/ip_fw.h). + */ +static int +fill_class_names(struct di_ctl_classifier *class, char *arg, int check) +{ + char *name, *tmp; + char *sep = ","; + int i, len; + + len = 0; + + if (!check) + class->ccnt = 0; + + tmp = malloc(strlen(arg) + 1); + strcpy(tmp, arg); + + for (name = strtok(tmp, sep); name; name = strtok(NULL, sep)) + { + if (!check) { + if (!str_isalnum(name)) { + errx(EX_USAGE, "class names can only contain " + "alphanumeric characters"); + } + + if (class->ccnt + 1 > DI_MAX_CLASSES) { + errx(EX_DATAERR, "maximum number of classes " + "limited to %d", DI_MAX_CLASSES); + } + + /* Do not allow duplicates. */ + for (i = 0; i < class->ccnt; i++) { + if (!strcmp(class->fstats[i].fname, name)) { + errx(EX_DATAERR, "duplicate class " + "name %s", name); + } + } + + /* Copy class in feature name. */ + strncpy(class->fstats[class->fscnt + class->ccnt].fname, + name, DI_MAX_NAME_STR_LEN - 1); + class->fstats[class->fscnt + class->ccnt].fname[ + DI_MAX_NAME_STR_LEN - 1] = '\0'; + class->ccnt++; + } + len += sizeof(struct di_feature_stat); + } + + free(tmp); + +#ifdef DIFFUSE_DEBUG2 + printf("classes %d\n", class->ccnt); +#endif + + return (len); +} + +/* Configuration of classifier. */ +static void +classifier_config(int ac, char **av) +{ + struct di_classifier_module *c; + struct di_ctl_classifier *class; + struct di_oid *buf, *base, *class_config, *cmd; + struct di_option *opt, *opts; + char *class_str, *feature_str, *model; + int cconf_len, clen, flen, have_class_names, have_feature_stats, i, len; + int mod_len, size; + + have_class_names = have_feature_stats = len = 0; + class_config = NULL; + class = NULL; + class_str = feature_str = model = NULL; + +#ifdef DIFFUSE_DEBUG2 + printf("diffuse classifier config\n"); +#endif + + /* Allocate space for 1 header + 1 classifier */ + len = sizeof(struct di_oid) + sizeof(struct di_ctl_classifier); + base = buf = safe_calloc(1, len); + + /* All commands start with a CONFIG and a version. */ + diffuse_rec_next(&buf, sizeof(struct di_oid), DI_CMD_CONFIG); + base->id = DI_API_VERSION; + class = diffuse_rec_next(&buf, sizeof(*class), DI_CLASSIFIER); + + av++; ac--; + /* Classifier name. */ + if (ac) { + if (!str_isalnum(*av)) { + errx(EX_USAGE, "classifier name can only contain " + "alphanumeric characters"); + } + strncpy(class->name, *av, DI_MAX_NAME_STR_LEN - 1); + class->name[DI_MAX_NAME_STR_LEN - 1] = '\0'; + av++; ac--; + } else { + errx(EX_USAGE, "need a classifier name"); + } + + /* Set feature configuration. */ + while (ac > 0) { + if (*av && !strcmp("-h", *av)) { + print_classifier_usage(); + if (class != NULL) { + c = find_classifier_module(class->mod_name); + if (c) + c->print_usage(); + else + printf("algorithm \n"); + } + free(base); + exit(0); + } + + opt = diffuse_match_token(diffuse_all_params, *av); + ac--; av++; + check_option_val(ac, av, opt); + + switch(opt->token) { + case TOK_DI_ALGORITHM: + { + NEED(class, "algorithm is only for classifier config"); + NEED1("algorithm name must be specified"); + strncpy(class->mod_name, *av, DI_MAX_NAME_STR_LEN - 1); + class->mod_name[DI_MAX_NAME_STR_LEN - 1] = '\0'; + class->confirm = 0; + + c = find_classifier_module(class->mod_name); + if (c == NULL) { + errx(EX_DATAERR, "unrecognised module ``%s''", + class->mod_name); + } + /* Add options. */ + size = c->get_opts(&opts); + diffuse_add_params(opts, size); + + /* Alloc space for config. */ + cconf_len = c->get_conf_size(); + class_config = safe_calloc(1, cconf_len); + class_config->type = DI_CLASSIFIER_CONFIG; + class_config->subtype = 0; + class_config->len = cconf_len; + c->parse_opts(TOK_DI_OPTS_INIT, NULL, class_config); + ac--; av++; + } + break; + + case TOK_DI_CONFIRM: + NEED(class, "confirm is only for classifier/export " + "config"); + NEED1("confirm number must be specified"); + class->confirm = atoi(*av); + ac--; av++; + break; + + case TOK_DI_USE_FEATURE_STATS: + { + NEED(class && class_config, + "use-feature-stats is only for classifier config"); + NEED1("feature statistics must be specified"); + + if (have_class_names) { + errx(EX_USAGE, "class-names must be specified " + "after use-feature-stats"); + } + + flen = fill_feature_stats(class, av[0], 1); + len += flen; + buf = base = realloc(base, len); + buf = O_NEXT(buf, sizeof(struct di_oid)); + class = (struct di_ctl_classifier *)buf; + class->oid.len += flen; + fill_feature_stats(class, av[0], 0); + have_feature_stats = flen; + ac--; av++; + } + break; + + case TOK_DI_CLASS_NAMES: + { + NEED(class && class_config, + "class-names is only for classifier config"); + NEED1("class names must be specified"); + clen = fill_class_names(class, av[0], 1); + len += clen; + buf = base = realloc(base, len); + buf = O_NEXT(buf, sizeof(struct di_oid)); + class = (struct di_ctl_classifier *)buf; + class->oid.len += clen; + fill_class_names(class, av[0], 0); + have_class_names = clen; + ac--; av++; + } + break; + + default: + { + c = find_classifier_module(class->mod_name); + if (!c) { + errx(EX_DATAERR, "unrecognised option ``%s''", + av[-1]); + } else { + c->parse_opts(opt->token, *av, class_config); + if (opt->arg_type != DI_OPTION_ARG_NOARG) { + ac--; av++; + } + } + } + } + } + + if (!strlen(class->mod_name)) + errx(EX_DATAERR, "no classifier algorithm name specified"); + + /* Now try loading the model. */ + c = find_classifier_module(class->mod_name); + if (c && (mod_len = c->load_model(class_config, &model, &feature_str, + &class_str)) >= 0) { + if (mod_len > 0) { + /* Merge the fixed length conf and the model. */ + class_config = realloc((char *)class_config, + class_config->len + mod_len); + /* Copy model. */ + memcpy((char *)class_config + class_config->len, model, + mod_len); + class_config->len += mod_len; + + /* + * If not previously specified use model features and + * classes. + */ + flen = fill_feature_stats(class, feature_str, 1); + clen = fill_class_names(class, class_str, 1); + + if (have_feature_stats && have_feature_stats != flen) { + errx(EX_USAGE, "number of feature stats " + "specified in use-feature-stats different " + "from number in model file"); + } + if (have_class_names && have_class_names != clen) { + errx(EX_USAGE, "number of class names " + "specified in class-names different from " + "number in model file"); + } + + if (!have_feature_stats) { + len += flen; + buf = base = realloc(base, len); + buf = O_NEXT(buf, sizeof(struct di_oid)); + class = (struct di_ctl_classifier *)buf; + cmd = (struct di_oid *)buf; + cmd->len += flen; + + if (have_class_names) { + /* + * Move specified class names to the + * back. + * XXX: Ugly, should really have + * separate list of class names. + */ + for (i = 0; i < class->ccnt; i++) { + strcpy(class->fstats[flen / + sizeof(struct di_feature_stat) + + i].fname, + class->fstats[i].fname); + } + } + fill_feature_stats(class, feature_str, 0); + } + if (!have_class_names) { + len += clen; + buf = base = realloc(base, len); + buf = O_NEXT(buf, sizeof(struct di_oid)); + class = (struct di_ctl_classifier *)buf; + class->oid.len += clen; + fill_class_names(class, class_str, 0); + } + } + } else { + errx(EX_DATAERR, "couldn't load classifier model"); + } + + /* Build message. */ + len += class_config->len; + buf = base = realloc(base, len); + buf = O_NEXT(buf, sizeof(struct di_oid)); + cmd = (struct di_oid *)buf; + buf = O_NEXT(buf, cmd->len); + memcpy(buf, class_config, class_config->len); + free(class_config); + class_config = (struct di_oid *)buf; + list_classifiers(base, O_NEXT(buf, class_config->len)); + + if (do_cmd(IP_DIFFUSE, base, len)) + err(1, "setsockopt(%s)", "IP_DIFFUSE_CLASSIFIER_CONFIGURE"); +} + +static void +export_config(int ac, char **av) +{ + struct di_ctl_export *exp; + struct di_oid *buf, *base; + struct di_option *opt; + struct hostent *h; + char *errptr, *ip, *p; + int len; + + exp = NULL; + len = 0; + +#ifdef DIFFUSE_DEBUG2 + printf("diffuse export config\n"); +#endif + + /* Allocate space for 1 header + 1 export */ + len = sizeof(struct di_oid) + sizeof(struct di_ctl_export); + base = buf = safe_calloc(1, len); + + /* All commands start with a CONFIG and a version. */ + diffuse_rec_next(&buf, sizeof(struct di_oid), DI_CMD_CONFIG); + base->id = DI_API_VERSION; + exp = diffuse_rec_next(&buf, sizeof(*exp), DI_EXPORT); + + av++; ac--; + /* Export name. */ + if (ac) { + if (!str_isalnum(*av)) { + errx(EX_USAGE, "export name can only contain " + "alphanumeric characters"); + } + strncpy(exp->name, *av, DI_MAX_NAME_STR_LEN - 1); + exp->name[DI_MAX_NAME_STR_LEN - 1] = '\0'; + av++; ac--; + } else { + errx(EX_USAGE, "need a export name"); + } + + /* Set defaults. */ + exp->conf.atype |= DI_ACTION_TYPE_BIDIRECTIONAL; + + /* Set feature configuration. */ + while (ac > 0) { + if (*av && !strcmp("-h", *av)) { + print_export_usage(); + free(base); + exit(0); + } + + opt = diffuse_match_token(diffuse_all_params, *av); + ac--; av++; + check_option_val(ac, av, opt); + + switch(opt->token) { + case TOK_DI_EXP_TARGET: + { + NEED(exp, "target is only for export config"); + NEED1("target name must be specified"); + p = strstr(*av, "://"); + if (p == NULL) { + errx(EX_USAGE, "target must be specified as " + "://:"); + } + /* Parse protocol. */ + if (strncmp(*av, "udp", 3)) + errx(EX_USAGE, "only udp is supported for now"); + exp->conf.proto = IPPROTO_UDP; + + ip = p + 3; + p = strstr(ip, ":"); + if (p != NULL) { + *p = '\0'; + p++; + exp->conf.port = strtonum(p, 1, 65535, + (const char **)&errptr); + if (errptr != NULL) { + errx(EX_USAGE, "error parsing target " + "port: %s", errptr); + } + } else { + exp->conf.port = DI_EXP_DEFAULT_PORT; + } + + /* Parse IP. */ + if ((h = gethostbyname(ip)) == NULL) + errx(EX_USAGE, "can't resolve target address"); + + if (h->h_addrtype != AF_INET || + h->h_length != sizeof(struct in_addr)) { + errx(EX_USAGE, "only IPv4 supported for now"); + } + exp->conf.ip = *((struct in_addr *)h->h_addr_list[0]); + ac--; av++; + break; + } + + case TOK_DI_CONFIRM: + NEED(exp, "confirm is only for classifier/export " + "config"); + NEED1("confirm number must be specified"); + exp->conf.confirm = atoi(*av); + ac--; av++; + break; + + case TOK_DI_EXP_MIN_BATCH: + NEED(exp, "min-batch is only for export config"); + NEED1("min-batch must be specified"); + exp->conf.min_batch = atoi(*av); + if (exp->conf.max_batch == 0) + exp->conf.max_batch = exp->conf.min_batch; + if (exp->conf.min_batch > exp->conf.max_batch) { + errx(EX_USAGE, + "min-batch must be <= max-batch"); + } + ac--; av++; + break; + + case TOK_DI_EXP_MAX_BATCH: + NEED(exp, "max-batch is only for export config"); + NEED1("max-batch must be specified"); + exp->conf.max_batch = atoi(*av); + if (exp->conf.min_batch == 0) + exp->conf.min_batch = exp->conf.max_batch; + if (exp->conf.max_batch < exp->conf.min_batch) { + errx(EX_USAGE, + "max-batch must be >= min-batch"); + } + ac--; av++; + break; + + case TOK_DI_EXP_MAX_DELAY: + NEED(exp, "max-delay is only for export config"); + NEED1("max-delay must be specified"); + exp->conf.max_delay = atoi(*av); + ac--; av++; + break; + +#if 0 + case TOK_DI_EXP_FLOW_KEY: + NEED(exp, "flow-key is only for export config"); + NEED1("flow-key name must be specified"); + /* XXX */ + ac--; av++; + break; + + case TOK_DI_EXP_FEATURES: + NEED(exp, "feature-key is only for export config"); + NEED1("feature-key name must be specified"); + /* XXX */ + ac--; av++; + break; +#endif + + case TOK_DI_EXP_ACTION: + NEED(exp, "action is only for export config"); + NEED1("action must be specified"); + /* + * XXX: Check whether action is valid ipfw action. + * XXX: As an optimisation use constants instead of + * strings to identify actions. + */ + strncpy(exp->conf.action, *av, DI_MAX_NAME_STR_LEN - 1); + exp->conf.action[DI_MAX_NAME_STR_LEN - 1] = '\0'; + ac--; av++; + break; + + case TOK_DI_EXP_ACTION_PARAMS: + NEED(exp, "action-param is only for export config"); + NEED1("action-param must be specified"); + strncpy(exp->conf.action_param, *av, + DI_MAX_PARAM_STR_LEN - 1); + exp->conf.action[DI_MAX_PARAM_STR_LEN - 1] = '\0'; + ac--; av++; + break; + + case TOK_DI_EXP_ACTION_UNIDIR: + NEED(exp, "unidirectional is only for export config"); + exp->conf.atype &= ~DI_ACTION_TYPE_BIDIRECTIONAL; + break; + + default: + errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); + break; + } + } + + if (do_cmd(IP_DIFFUSE, base, len)) + err(1, "setsockopt(%s)", "IP_DIFFUSE_EXPORT_CONFIGURE"); +} + +void +diffuse_config(int ac, char **av, int type) +{ + + switch(type) { + case DI_FEATURE: + feature_config(ac, av); + break; + + case DI_CLASSIFIER: + classifier_config(ac, av); + break; + + case DI_EXPORT: + export_config(ac, av); + break; + + default: + errx(EX_DATAERR, "unrecognised option"); + break; + } +} + +/* + * Entry point for list functions. Currently only supports either one name or + * 'all' (which returns all features, classifiers, exports). + * XXX: Add comma separated lists. + */ +void +diffuse_list(int ac, char *av[], int type, int show_counters) +{ + struct di_ctl_classifier *class; + struct di_ctl_export *exp; + struct di_ctl_feature *feature; + struct di_oid *buf, *base; + struct di_option *opt; + char *name; + int buflen, i, lmax, ret, total_len; + + class = NULL; + exp = NULL; + feature = NULL; + base = NULL; + name = NULL; + +#ifdef DIFFUSE_DEBUG2 + printf("diffuse_list\n"); +#endif + + ac--; av++; + + if (!ac) { + if (type == DI_FEATURE) + errx(EX_USAGE, "need a feature name"); + else if (type == DI_CLASSIFIER) + errx(EX_USAGE, "need a classifier name"); + else if (type == DI_EXPORT) + errx(EX_USAGE, "need an export name"); + } + + lmax = sizeof(struct di_oid); + if (type == DI_FEATURE) + lmax += sizeof(struct di_ctl_feature); + else if (type == DI_CLASSIFIER) + lmax += sizeof(struct di_ctl_classifier); + else if (type == DI_EXPORT) + lmax += sizeof(struct di_ctl_export); + + base = buf = safe_calloc(1, lmax); + diffuse_rec_next(&buf, sizeof(struct di_oid), DI_CMD_GET); + base->id = DI_API_VERSION; + + if (type == DI_FEATURE) { + base->subtype = DI_FEATURE; + feature = diffuse_rec_next(&buf, sizeof(*feature), DI_FEATURE); + name = feature->name; + } else if (type == DI_CLASSIFIER) { + base->subtype = DI_CLASSIFIER; + class = diffuse_rec_next(&buf, sizeof(*class), DI_CLASSIFIER); + name = class->name; + } else if (type == DI_EXPORT) { + base->subtype = DI_EXPORT; + exp = diffuse_rec_next(&buf, sizeof(*exp), DI_EXPORT); + name = exp->name; + } else { /* Flow table. */ + base->subtype = DI_FLOW_TABLE; + base->flags = DI_FT_GET_NONE; + } + + if (name != NULL) { + strncpy(name, *av, DI_MAX_NAME_STR_LEN - 1); + name[DI_MAX_NAME_STR_LEN - 1] = '\0'; + av++; ac--; + } + + while (ac > 0) { + opt = diffuse_match_token(diffuse_all_params, *av); + ac--; av++; + check_option_val(ac, av, opt); + + switch(opt->token) { + case TOK_DI_EXPIRED: + if (type != DI_FLOW_TABLE) { + errx(EX_USAGE, + "expired is only for flowtable show"); + } + base->flags |= DI_FT_GET_EXPIRED; + break; + + default: + errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); + break; + } + } + + /* + * Ask the kernel an estimate of the required space (result in oid.id). + * In any case, space might grow in the meantime due to the creation of + * new features, so we must be prepared to retry. + */ + total_len = (char *)buf - (char *)base; + /* Arg 1 being -ve implies getsockopt instead of setsockopt. */ + ret = do_cmd(-IP_DIFFUSE, base, (uintptr_t)&total_len); + if (ret != 0 || base->id < total_len) + goto done; + + if (base->id > total_len) { + buflen = base->id * 2; + /* Try a few times, until the buffer fits. */ + for (i = 0; i < 20; i++) { + total_len = buflen; + base = safe_realloc(base, total_len); + ret = do_cmd(-IP_DIFFUSE, base, (uintptr_t)&total_len); + if (ret != 0 || base->id < total_len) + goto done; + + if (total_len >= base->id) + break; /* ok */ + + buflen *= 2; /* Double for next attempt. */ + } + } + + if (type == DI_FEATURE || type == DI_FLOW_TABLE) + list_features(base, O_NEXT(base, total_len)); + else if (type == DI_CLASSIFIER) + list_classifiers(base, O_NEXT(base, total_len)); + else + list_exports(base, O_NEXT(base, total_len)); + +done: + free(base); + if (ret) + err(1, "setsockopt(%s)", "IP_DIFFUSE_SHOW"); +} + +void +diffuse_flush(int ac, char *av[], int reset_counters_only) +{ + struct di_oid *buf, *base; + +#ifdef DIFFUSE_DEBUG2 + printf("diffuse_ft_flush\n"); +#endif + + ac--; av++; + base = buf = safe_calloc(1, sizeof(struct di_oid)); + + if (reset_counters_only) + diffuse_rec_next(&buf, sizeof(struct di_oid), DI_CMD_ZERO); + else + diffuse_rec_next(&buf, sizeof(struct di_oid), DI_CMD_FLUSH); + + base->id = DI_API_VERSION; + base->subtype = DI_FLOW_TABLE; + + if (ac) + errx(EX_DATAERR, "unrecognised option ``%s''", *av); + + if (do_cmd(IP_DIFFUSE, base, sizeof(struct di_oid))) + err(1, "setsockopt(%s)", "IP_DIFFUSE_FLUSH"); +} + +/* + * Functions to print out DIFFUSE rule instructions, called from ipfw. + * + * XXX: All the print methods should print token string from token list to + * ensure easy consistency if tokens are changed. + */ + +void +print_features(ipfw_insn_features *f, int implicit) +{ + int i; + + if (implicit && + !(f->ftype & DI_MATCH_ONCE) && + !(f->ftype & DI_MATCH_ONCE_CLASS) && + !(f->ftype & DI_MATCH_ONCE_EXP) && + !(f->ftype & DI_MATCH_SAMPLE_REG) && + !(f->ftype & DI_MATCH_SAMPLE_RAND)) { + return; + } + + printf(" features "); + for(i = 0; i < f->fcnt; i++) { + printf("%s", f->fnames[i]); + if (i < f->fcnt - 1) + printf(","); + } + if (f->ftype & DI_FLOW_TYPE_BIDIRECTIONAL) + printf(" bidirectional"); + else + printf(" unidirectional"); + + if (f->ftype & DI_MATCH_ONCE) + printf(" once"); + else if (f->ftype & DI_MATCH_SAMPLE_REG) + printf(" sample %d", f->sample_int); + else if (f->ftype & DI_MATCH_SAMPLE_RAND) + printf(" rnd-sample %.3f", (double)f->sample_prob / 0xFFFFFFFF); + else if (f->ftype & DI_MATCH_ONCE_CLASS) + printf(" once-classified"); + else if (f->ftype & DI_MATCH_ONCE_EXP) + printf(" once-exported"); +} + +void +print_feature_match(ipfw_insn_feature_match *fm) +{ + + printf(" "); + if (fm->fdir == DI_MATCH_DIR_BCK) + printf("bck."); + else if (fm->fdir == DI_MATCH_DIR_FWD) + printf("fwd."); + + printf("%s.%s", fm->sname, fm->fname); + + switch(fm->comp) { + case DI_COMP_LT: + printf("<"); + break; + + case DI_COMP_LE: + printf("<="); + break; + + case DI_COMP_EQ: + printf("="); + break; + + case DI_COMP_GE: + printf(">="); + break; + + case DI_COMP_GT: + printf(">"); + break; + } + + printf("%d", fm->thresh); +} + +void +print_mlclass(ipfw_insn_ml_classify *cl) +{ + + printf("mlclass %s", cl->cname); +} + +void +print_ctags(ipfw_insn_class_tags *ct) +{ + int i; + + if (ct->tcnt > 0) { + printf(" class-tags "); + for (i = 0; i < ct->tcnt; i++) { + printf("%d", ct->tags[i]); + if (i < ct->tcnt - 1) + printf(","); + } + } +} + +void +print_match_if(ipfw_insn_match_if_class *mic) +{ + int i; + + printf(" match-if-class %s:", mic->cname); + for (i = 0; i < mic->mcnt; i++) { +#ifdef DIFFUSE_DEBUG2 + printf("%s(%d)", mic->clnames[i], mic->match_classes[i]); +#else + printf("%s", mic->clnames[i]); +#endif + if (i < mic->mcnt - 1) + printf(","); + } +} + +void +print_export(ipfw_insn_export *ex) +{ + + printf("export %s", ex->ename); +} + +int +diffuse_show_cmd(ipfw_insn *cmd) +{ + + switch(cmd->opcode) { + case O_DI_FEATURES: + print_features((ipfw_insn_features *)cmd, 0); + break; + + case O_DI_FEATURES_IMPLICIT: + print_features((ipfw_insn_features *)cmd, 1); + break; + + case O_DI_FEATURE_MATCH: + print_feature_match((ipfw_insn_feature_match *)cmd); + break; + + case O_DI_ML_CLASSIFY: + print_mlclass((ipfw_insn_ml_classify *)cmd); + break; + + case O_DI_MATCH_IF_CLASS: + print_match_if((ipfw_insn_match_if_class *)cmd); + break; + + case O_DI_EXPORT: + print_export((ipfw_insn_export *)cmd); + break; + + case O_DI_CLASS_TAGS: + print_ctags((ipfw_insn_class_tags *)cmd); + break; + + case O_DI_FLOW_TABLE: + case O_DI_ML_CLASSIFY_IMPLICIT: + /* Ignore. */ + break; + + default: + return (1); + } + + return (0); +} + +/* + * Functions to parse diffuse instructions, called from ipfw. + */ + +/* + * Parse list of features and fill instructions. Max number of features is + * DI_MAX_FEATURES (netinet/ip_fw.h). + */ +static void +fill_features(ipfw_insn_features *cmd, char *arg) +{ + char *name, *tmp; + char *sep = ","; + int slen; + + cmd->ftype = 0; + cmd->ftype |= DI_FLOW_TYPE_BIDIRECTIONAL; + cmd->fcnt = 0; + cmd->sample_int = 0; + cmd->o.len |= F_INSN_SIZE(ipfw_insn_features); + + if (!arg) + return; + + tmp = malloc(strlen(arg) + 1); + strcpy(tmp, arg); + + for (name = strtok(tmp, sep); name; name = strtok(NULL, sep)) { + if (cmd->fcnt + 1 > DI_MAX_FEATURES) { + errx(EX_DATAERR, + "maximum number of features limited to %d", + DI_MAX_FEATURES); + } + + slen = strlen(name); + if (slen > DI_MAX_NAME_STR_LEN - 1) + slen = DI_MAX_NAME_STR_LEN - 1; + + strncpy(cmd->fnames[cmd->fcnt], name, slen); + cmd->fcnt++; + } + + free(tmp); + DID2("features %d", cmd->fcnt); + DID2("command len %d", cmd->o.len); +} + +static void +fill_feature_match(ipfw_insn_feature_match *cmd, char *arg) +{ + char *endptr, *p, *s1, *s2, *s3; + char tmp[256]; + + s1 = s2 = s3 = NULL; + + cmd->o.len |= F_INSN_SIZE(ipfw_insn_feature_match); + cmd->comp = 0; + cmd->thresh = 0; + cmd->fdir = DI_MATCH_DIR_NONE; + + strncpy(tmp, arg, sizeof(tmp)); + + /* Parse comparision op. */ + p = strstr(tmp, "<"); + if (p != NULL) { + if (p[1] == '=') + cmd->comp = DI_COMP_LE; + else + cmd->comp = DI_COMP_LT; + } else { + p = strstr(tmp, ">"); + if (p != NULL) { + if (p[1] == '=') + cmd->comp = DI_COMP_GE; + else + cmd->comp = DI_COMP_GT; + } else { + p = strstr(tmp, "="); + if (p != NULL) { + cmd->comp = DI_COMP_EQ; + } else { + errx(EX_USAGE, + "feature match has no comparison"); + } + } + } + + *p = '\0'; + p++; + if (cmd->comp == DI_COMP_LE || cmd->comp == DI_COMP_GE) + p++; + + /* Parse the threshold value. */ + cmd->thresh = strtoul(p, &endptr, 10); + if (*endptr) { + errx(EX_USAGE, "feature match has invalid threshold value %s", + endptr); + } + + /* Parse the feature name. */ + s1 = tmp; + p = strstr(tmp, "."); + if (p == NULL) { + /* Should never happen. */ + errx(EX_USAGE, "feature match has invalid format"); + } + s2 = p + 1; + *p = '\0'; + p = strstr(s2, "."); + if (p != NULL) { + s3 = p + 1; + *p = '\0'; + } + + if (s3 != NULL) { + /* First string must be direction. */ + if (!strcmp(s1, "fwd")) { + cmd->fdir = DI_MATCH_DIR_FWD; + } else if (!strcmp(s1, "bck")) { + cmd->fdir = DI_MATCH_DIR_BCK; + } else { + errx(EX_USAGE, "missing feature/statistic name or " + "direction mispelled"); + } + s1 = s2; + s2 = s3; + } + + /* Second string is statistics. */ + strncpy(cmd->sname, s1, DI_MAX_NAME_STR_LEN - 1); + cmd->sname[DI_MAX_NAME_STR_LEN - 1] = '\0'; + + /* Last string is feature name. */ + strncpy(cmd->fname, s2, DI_MAX_NAME_STR_LEN - 1); + cmd->fname[DI_MAX_NAME_STR_LEN - 1] = '\0'; + + DID2("dir %d", cmd->fdir); + DID2("stat %s", cmd->sname); + DID2("feat %s", cmd->fname); + DID2("comp %d", cmd->comp); + DID2("thre %d", cmd->thresh); +} + +static void +fill_class_tags(ipfw_insn_class_tags *cmd, ipfw_insn_ml_classify *classifier, + char *arg) +{ + char *errptr, *tag, *tmp; + char *sep = ","; + int len; + uint16_t v; + + strcpy(cmd->cname, classifier->cname); + cmd->tcnt = 0; + len = sizeof(ipfw_insn_class_tags); + tmp = malloc(strlen(arg) + 1); + strcpy(tmp, arg); + + for (tag = strtok(tmp, sep); tag; tag = strtok(NULL, sep)) { + if (cmd->tcnt + 1 > DI_MAX_CLASSES) { + errx(EX_DATAERR, "maximum number of tags limited to %d", + DI_MAX_CLASSES); + } + + v = strtonum(tag, 0, 65535, (const char **)&errptr); + if (errptr != NULL) + errx(EX_USAGE, "tag number: %s", errptr); + + cmd->tags[cmd->tcnt++] = v; + len += sizeof(uint16_t); + } + + free(tmp); + cmd->o.len |= (len + 3) / sizeof(uint32_t); + DID2("classifier %s", cmd->cname); + DID2("tags %d", cmd->tcnt); +} + +/* Parse the match-if-class match. */ +static void +fill_match_if_class(ipfw_insn_match_if_class *cmd, + ipfw_insn_ml_classify *classifier, char *arg) +{ + char *sep = ","; + char *class, *classes, *errptr, *p, *tmp; + int len; + uint16_t v; + + cmd->mcnt = 0; + len = sizeof(ipfw_insn_match_if_class); + tmp = malloc(strlen(arg) + 1); + strcpy(tmp, arg); + + p = strstr(tmp, ":"); + if (!p || p - tmp >= strlen(arg)) { + errx(EX_USAGE, + "match must be specified as classifier:class[,class,...]"); + } + classes = p + 1; + *p = '\0'; + + strncpy(cmd->cname, tmp, DI_MAX_NAME_STR_LEN - 1); + cmd->cname[DI_MAX_NAME_STR_LEN - 1] = '\0'; + strncpy(classifier->cname, tmp, DI_MAX_NAME_STR_LEN - 1); + classifier->cname[DI_MAX_NAME_STR_LEN - 1] = '\0'; + + /* Parse classes. */ + for (class = strtok(classes, sep); class; class = strtok(NULL, sep)) + { + if (cmd->mcnt + 1 > DI_MAX_MATCH_CLASSES) { + errx(EX_DATAERR, + "maximum number of classes limited to %d", + DI_MAX_MATCH_CLASSES); + } + + if (class[0] == DI_CLASS_NO_CHAR) { + v = strtonum(&class[1], 0, 65535, + (const char **)&errptr); + if (errptr != NULL) { + errx(EX_USAGE, "invalid class number: %s", + errptr); + } + cmd->match_classes[cmd->mcnt] = v; + } + else if (!str_isalnum(class)) { + errx(EX_USAGE, "class names can only contain " + "alphanumeric characters"); + } + + strncpy(cmd->clnames[cmd->mcnt], class, DI_MAX_NAME_STR_LEN); + cmd->clnames[cmd->mcnt][DI_MAX_NAME_STR_LEN - 1] = '\0'; + cmd->mcnt++; + len += DI_MAX_NAME_STR_LEN; + } + + free(tmp); + cmd->o.len |= (len + 3) / sizeof(uint32_t); + DID2("classifier %s", cmd->cname); + DID2("classes %d", cmd->mcnt); +} + +/* + * Helper functions (from ipfw2.c). + */ + +static void +fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg) +{ + + cmd->opcode = opcode; + cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1; + cmd->arg1 = arg; +} + +static ipfw_insn * +next_cmd(ipfw_insn *cmd) +{ + + cmd += F_LEN(cmd); + bzero(cmd, sizeof(*cmd)); + + return (cmd); +} + +int +diffuse_parse_action(int token, ipfw_insn *action, char **avp[]) +{ + char **av; + + av = *avp; + + switch(token) { + case TOK_DI_ML_CLASSIFY: + classifier = (ipfw_insn_ml_classify *)action; + NEED1("missing classifier name"); + action->opcode = O_DI_ML_CLASSIFY; + action->len = F_INSN_SIZE(ipfw_insn_ml_classify); + strncpy(classifier->cname, av[0], DI_MAX_NAME_STR_LEN - 1); + classifier->cname[DI_MAX_NAME_STR_LEN - 1] = '\0'; + (*avp)++; + break; + + case TOK_DI_EXPORT: + NEED1("missing export name"); + action->opcode = O_DI_EXPORT; + action->len = F_INSN_SIZE(ipfw_insn_export); + ipfw_insn_export *exp = (ipfw_insn_export *)action; + strncpy(exp->ename, av[0], DI_MAX_NAME_STR_LEN - 1); + exp->ename[DI_MAX_NAME_STR_LEN - 1] = '\0'; + (*avp)++; + break; + + default: + /* Don't know. */ + return (1); + } + + return (0); +} + +int +diffuse_parse_cmd(int token, int open_par, ipfw_insn **cmd, char **avp[]) +{ + ipfw_insn_ml_classify *cl; + char **av; + char *errptr; + double prob; + + av = *avp; /* av is used by the NEED macro. */ + + switch(token) { + case TOK_DI_FEATURES: + NEED1("features, missing feature list"); + if (open_par) { + errx(EX_USAGE, "features cannot be part of an or " + "block"); + } + if (features) { + errx(EX_USAGE, "only one features command allowed, " + "which must be defined at the start"); + } + (*cmd)->opcode = O_DI_FEATURES; + fill_features((ipfw_insn_features *)*cmd, av[0]); + features = (ipfw_insn_features *)*cmd; + (*avp)++; + break; + + case TOK_DI_UNIDIRECTIONAL: + if (open_par) { + errx(EX_USAGE, "unidirectional cannot be part of an or " + "block"); + } + GENERATE_FEATURES(); + features->ftype &= ~DI_FLOW_TYPE_BIDIRECTIONAL; + break; + + case TOK_DI_FEATURE_MATCH: + (*avp)--; + av = *avp; + GENERATE_FEATURES(); + (*cmd)->opcode = O_DI_FEATURE_MATCH; + fill_feature_match((ipfw_insn_feature_match *)*cmd, av[0]); + have_feature_match = 1; + (*avp)++; + break; + + case TOK_DI_EVERY: + /* Default behaviour, nothing to do here. */ + break; + + case TOK_DI_ONCE: + if (open_par) { + errx(EX_USAGE, "once cannot be part of an or block"); + } + GENERATE_FEATURES(); + features->ftype |= DI_MATCH_ONCE; + break; + + case TOK_DI_ONCE_CLASS: + if (open_par) { + errx(EX_USAGE, "once-classified cannot be part of an " + "or block"); + } + GENERATE_FEATURES(); + features->ftype |= DI_MATCH_ONCE_CLASS; + break; + + case TOK_DI_ONCE_EXP: + if (open_par) { + errx(EX_USAGE, "once-classified cannot be part of an " + "or block"); + } + GENERATE_FEATURES(); + features->ftype |= DI_MATCH_ONCE_EXP; + break; + + case TOK_DI_SAMPLE_REG: + NEED1("sample, missing number of packets"); + if (open_par) { + errx(EX_USAGE, "sample cannot be part of an or block"); + } + GENERATE_FEATURES(); + features->ftype |= DI_MATCH_SAMPLE_REG; + { + errptr = NULL; + features->sample_int = strtonum(av[0], 1, 65535, + (const char **)&errptr); + if (errptr) { + errx(EX_USAGE, + "sample interval '%s' invalid, %s", av[0], + errptr); + } + } + (*avp)++; + break; + + case TOK_DI_SAMPLE_RAND: + NEED1("rnd-sample, missing probability"); + if (open_par) { + errx(EX_USAGE, "rnd-sample cannot be part of an or " + "block"); + } + GENERATE_FEATURES(); + features->ftype |= DI_MATCH_SAMPLE_RAND; + { + errptr = NULL; + prob = strtod(av[0], &errptr); + if (*errptr) { + errx(EX_USAGE, + "sample probability invalid at '%s'", + errptr); + } + features->sample_prob = (uint32_t)floor(prob * + 0xFFFFFFFF); + } + (*avp)++; + break; + + case TOK_DI_MATCH_IF_CLASS: + NEED1("match-if-class, missing classifier:class[,class...]"); + GENERATE_FEATURES(); + /* + * If somebody (unnecessarily) uses multiple match-ifs on same + * classifier, we get multiple redundant + * O_DI_ML_CLASSIFY_IMPLICIT. + */ + { + (*cmd)->opcode = O_DI_ML_CLASSIFY_IMPLICIT; + (*cmd)->len = F_INSN_SIZE(ipfw_insn_ml_classify); + cl = (ipfw_insn_ml_classify *)*cmd; + cl->cname[0] = '\0'; + *cmd = next_cmd(*cmd); + (*cmd)->opcode = O_DI_MATCH_IF_CLASS; + fill_match_if_class((ipfw_insn_match_if_class *)*cmd, + cl, av[0]); + } + (*avp)++; + break; + + case TOK_DI_CLASS_TAGS: + NEED(classifier, + "class-tags can be only used with an mlclass action"); + NEED1("class-tags, missing list of class tags"); + if (open_par) { + errx(EX_USAGE, "class-tags cannot be part of an or " + "block"); + } + (*cmd)->opcode = O_DI_CLASS_TAGS; + fill_class_tags((ipfw_insn_class_tags *)*cmd, classifier, + av[0]); + (*avp)++; + break; + + default: + /* Don't know. */ + return (1); + } + + return (0); +} + +/* Called at the start before any other opcodes, except O_PROB. */ +void +diffuse_rule_build_1(uint32_t cmdbuf[], ipfw_insn *cmd, ipfw_insn **dst) +{ + ipfw_insn *src; + int i, j; + + /* Generate an O_DI_FLOW_TABLE at the start if rule uses features. */ + if (features || classifier) { + fill_cmd(*dst, O_DI_FLOW_TABLE, 0, 0); + *dst = next_cmd(*dst); + } + + /* + * ML classifiers are linked to features in kernel because features are + * specified during config, so we only need to handle feature matches + * here. + */ + + if (!have_feature_match) + return; + + /* + * Make sure O_DI_FEATURES contains all features needed in matches. It + * can contain more if specified by user. + */ + for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { + i = F_LEN(src); + + if (src->opcode == O_DI_FEATURE_MATCH) { + ipfw_insn_feature_match *fm = + (ipfw_insn_feature_match *)src; + for(j = 0; j < features->fcnt; j++) { + if (strcmp(fm->fname, features->fnames[j]) == + 0) { + if (fm->fdir == DI_MATCH_DIR_BCK || + fm->fdir == DI_MATCH_DIR_FWD) { + features->ftype |= + DI_FLOW_TYPE_BIDIRECTIONAL; + } + break; + } + } + if (j >= features->fcnt) { + if (j >= DI_MAX_FEATURES) { + errx(EX_DATAERR, "maximum number of " + "features limited to %d", + DI_MAX_FEATURES); + } + strcpy(features->fnames[features->fcnt++], + fm->fname); + } + if (fm->fdir == DI_MATCH_DIR_BCK || + fm->fdir == DI_MATCH_DIR_FWD) { + features->ftype |= DI_FLOW_TYPE_BIDIRECTIONAL; + } + } + } +} + +/* + * Called after all non-action opcodes except O_CHECK_STATE, or in other words + * before options. + */ +void +diffuse_rule_build_2(uint32_t cmdbuf[], ipfw_insn *cmd, ipfw_insn **dst) +{ + + /* Generate an O_DI_FEATURES_IMPLICIT if we have an mlclass action. */ + if (classifier && !features) { + (*dst)->opcode = O_DI_FEATURES_IMPLICIT; + fill_features((ipfw_insn_features *)*dst, NULL); + *dst = next_cmd(*dst); + } +} diff -r c7d5be203cfa sbin/ipfw/diffuse_ui.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui.h Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,183 @@ +/*- + * 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 _SBIN_IPFW_DIFFUSE_UI_H_ +#define _SBIN_IPFW_DIFFUSE_UI_H_ + +#define TOK_DI_FEATURE_MOD_START 256 +/* Tokens used by the basic diffuse rules. */ +enum diffuse_tokens { + /* TOK_NULL = 0, in ipfw2.h */ + TOK_DI_MODULE = 1, + TOK_DI_ALGORITHM, + TOK_DI_USE_FEATURE_STATS, + TOK_DI_CLASS_NAMES, + TOK_DI_EXPIRED, + TOK_DI_EXP_TARGET, + TOK_DI_EXP_LIMIT, + TOK_DI_EXP_FLOW_LIMIT, + TOK_DI_CONFIRM, + TOK_DI_EXP_FLOW_KEY, + TOK_DI_EXP_FEATURES, + TOK_DI_EXP_ACTION, + TOK_DI_EXP_ACTION_PARAMS, + TOK_DI_EXP_MIN_BATCH, + TOK_DI_EXP_MAX_BATCH, + TOK_DI_EXP_MAX_DELAY, + TOK_DI_EXP_ACTION_UNIDIR, + TOK_DI_OPTS_INIT /* Used for initialising module options. */ +}; + +/* Argument types. */ +typedef enum diffuse_option_arg_types { + DI_OPTION_ARG_NOARG = 0, + DI_OPTION_ARG_STR, + DI_OPTION_ARG_CHAR, + DI_OPTION_ARG_INT, + DI_OPTION_ARG_UINT, + DI_OPTION_ARG_DOUBLE +} di_option_arg_types_t; + +/* Definition of an option. */ +struct di_option { + char *name; + di_option_arg_types_t arg_type; + double arg_min; + double arg_max; + int token; +}; + +/* + * Load a model. + * param1: pointer to config buffer + * param2: pointer to model loaded + * param3: pointer to feature name string + * param4: pointer to class name string + * return: model size, -ve if error + */ +typedef int (*load_model_fn_t)(struct di_oid *, char **, char **, char **); + +/* + * External init function. + * param1: pointer to pointer to options + * return: size of returned options in byte + */ +typedef int (*get_options_fn_t)(struct di_option **opts); + +/* + * External parse function. + * param1: token + * param2: arg value (can be NULL) + * param3: pointer to record buffer + */ +typedef int (*parse_opts_fn_t)(int, char*, struct di_oid *); + +/* + * Returns size of config record. + */ +typedef int (*get_config_size_fn_t)(void); + +/* + * Prints options. + * param1: pointer to option record + */ +typedef void (*print_opts_fn_t)(struct di_oid *); + +/* + * Print usage. + */ +typedef void (*print_usage_fn_t)(void); + +/* + * Get stat name. + * param1: stat number + * return: stat name + */ +typedef char * (*get_stat_name_fn_t)(int); + +/* For listing flow table. */ +struct di_feature_arr_entry { + char name [DI_MAX_NAME_STR_LEN]; + char mod_name [DI_MAX_NAME_STR_LEN]; +}; + +struct di_feature_module { + char name[DI_MAX_NAME_STR_LEN]; + int type; + get_config_size_fn_t get_conf_size; + get_options_fn_t get_opts; + parse_opts_fn_t parse_opts; + print_opts_fn_t print_opts; + print_usage_fn_t print_usage; + get_stat_name_fn_t get_stat_name; + SLIST_ENTRY(di_feature_module) next; +}; + +struct di_classifier_module { + char name[DI_MAX_NAME_STR_LEN]; + get_config_size_fn_t get_conf_size; + get_options_fn_t get_opts; + parse_opts_fn_t parse_opts; + print_opts_fn_t print_opts; + print_usage_fn_t print_usage; + load_model_fn_t load_model; + SLIST_ENTRY(di_classifier_module) next; +}; + +void diffuse_config(int ac, char **av, int type); +void diffuse_show(int ac, char **av, int type, int counters); +void diffuse_list(int ac, char *av[], int type, int show_counters); +void diffuse_flush(int ac, char *av[], int reset_counters_only); +void diffuse_delete(int ac, char *av[], int type); +/* Called by ipfw for unknown opcodes. */ +int diffuse_show_cmd(ipfw_insn *cmd); + +/* Parse methods for rule extensions. */ +int diffuse_parse_action(int token, ipfw_insn *action, char **av[]); +int diffuse_parse_cmd(int token, int open_par, ipfw_insn **cmd, char **av[]); + +/* Called when building the rule. */ +void diffuse_rule_build_1(uint32_t cmdbuf[], ipfw_insn *cmd, ipfw_insn **dst); +void diffuse_rule_build_2(uint32_t cmdbuf[], ipfw_insn *cmd, ipfw_insn **dst); + +/* diffuse_modules.c */ +void diffuse_modules_init(void); +void diffuse_classifier_modules_init(void); +struct di_classifier_module * find_classifier_module(const char *name); +void print_classifier_modules(void); +void diffuse_feature_modules_init(void); +struct di_feature_module * find_feature_module(const char *name); +void print_feature_modules(void); + +#endif /* _SBIN_IPFW_DIFFUSE_UI_H_ */ diff -r c7d5be203cfa sbin/ipfw/diffuse_ui_classifier_c45.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui_classifier_c45.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,438 @@ +/*- + * 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. + */ + +/* + * Description: + * C4.5 classifier. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diffuse_ui.h" +#include "ipfw2.h" + +enum classifier_c45_tokens { + TOK_DI_MODEL_FILE = TOK_DI_FEATURE_MOD_START, +}; + +static struct di_option classifier_c45_params[] = { + { "model", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_MODEL_FILE }, + { NULL, 0, 0 } /* Terminator. */ +}; + +struct c45_id { + uint16_t val; + uint8_t type; +}; + +int +c45_get_conf_size(void) +{ + + return (sizeof(struct di_classifier_c45_config)); +} + +int +c45_get_opts(struct di_option **opts) +{ + *opts = classifier_c45_params; + + return (sizeof(classifier_c45_params)); +} + +int +c45_parse_opts(int token, char *arg_val, struct di_oid *buf) +{ + static struct di_classifier_c45_config *conf = NULL; + + if (conf == NULL) { + conf = (struct di_classifier_c45_config *)buf; + conf->model_name[0] = '\0'; + conf->class_cnt = 0; + conf->feature_cnt = 0; + } + + switch(token) { + case TOK_DI_OPTS_INIT: + break; + + case TOK_DI_MODEL_FILE: + strncpy(conf->model_name, arg_val, DI_MAX_MODEL_STR_LEN - 1); + conf->model_name[DI_MAX_MODEL_STR_LEN - 1] = '\0'; + break; + + default: + /* This should never happen. */ + errx(EX_DATAERR, "invalid option, fix source"); + } + + return (0); +} + +char * +print_id(int id, int type) +{ + static char buf[16]; + + if (type & DI_C45_CLASS) + sprintf(buf, "c_%u", id); + else if (type & DI_C45_NODE) + sprintf(buf, "n_%u", id); + else + sprintf(buf, "a_%u", id); + + return (buf); +} + +void +c45_print_model(struct di_oid *opts, char *mod_data) +{ + struct di_classifier_c45_config *conf; + struct di_c45_node_real *nodes; + int i; + + conf = (struct di_classifier_c45_config *)opts; + + if (mod_data != NULL) + nodes = (struct di_c45_node_real *)mod_data; + else + nodes = (struct di_c45_node_real *)conf->nodes; + + for(i = 0; i < conf->tree_len / sizeof(struct di_c45_node_real); i++) { + printf(" n_%u a_%u %s c_%u ", i, nodes[i].nid.feature, + (nodes[i].nid.type & DI_C45_REAL) ? "r" : "n", + nodes[i].nid.missing_class); + + if (nodes[i].nid.type == DI_C45_BNOM) { + /* XXX: No support for non-binary nominal yet. */ + } else { + printf("%.2f ", (double)nodes[i].val / (1 << conf->multi)); + printf("%s ", print_id(nodes[i].le_id, nodes[i].le_type)); + printf("%s\n", print_id(nodes[i].gt_id, nodes[i].gt_type)); + } + } +} + +void +c45_print_opts(struct di_oid *opts) +{ + struct di_classifier_c45_config *conf; + + conf = (struct di_classifier_c45_config *)opts; + + printf(" model name: %s\n", conf->model_name); + printf(" model:\n"); + c45_print_model(opts, NULL); +} + +void +c45_print_usage() +{ + + printf("algorithm c45 model \n"); +} + +struct c45_id parse_id(unsigned int line_no, char *s, int type) +{ + char *p, *endptr; + struct c45_id ret = {0, 0}; + uint16_t max_id; + + p = NULL; + + if (type & DI_C45_CLASS) { + p = strstr(s, "c_"); + ret.type = DI_C45_CLASS; + } + if (p == NULL && (type & DI_C45_NODE)) { + p = strstr(s, "n_"); + ret.type = DI_C45_NODE; + } + if (p == NULL && (type & DI_C45_FEAT)) { + p = strstr(s, "a_"); + ret.type = DI_C45_FEAT; + } + + if (p == NULL) { + errx(EX_DATAERR, "model line %u: invalid class/node/feature " + "id specification %s", line_no, s); + } + + if (ret.type == DI_C45_NODE) + max_id = 4095; + else + max_id = 255; + + p += 2; + ret.val = strtonum(p, 0, max_id, (const char **)&endptr); + if (endptr != NULL) { + errx(EX_DATAERR, "model line %u: invalid or out of range " + "(0--%d) class/node id %s", line_no, max_id, p); + } + + return (ret); +} + +int +c45_load_model(struct di_oid *buf, char **model, char **feature_str, + char **class_str) +{ + struct di_classifier_c45_config *conf; + struct di_c45_node_real *node, *nodes; + struct c45_id x; + /* Allocate these statically for now. */ + static char mod_data[65535], fstr[1024], clstr[1024]; + FILE *f; + char *sep = " \t"; + char *endptr, *p, *word; + char line[1024], tmp[1024]; + double v; + unsigned int line_no; + uint16_t max_nodes, multi; + + conf = (struct di_classifier_c45_config *)buf; + line_no = 0; + multi = 16; /* 2^multi */ + max_nodes = sizeof(mod_data) / sizeof(struct di_c45_node_real); + + if (conf->model_name == NULL) + errx(EX_DATAERR, "no classifier model specified"); + + fstr[0] = '\0'; + clstr[0] = '\0'; + + /* The precision. */ + conf->multi = multi; + + /* Pointer to list of dists. */ + conf->tree_len = 0; + nodes = (struct di_c45_node_real *)mod_data; + + if ((f = fopen(conf->model_name, "r")) != NULL) { + while (fgets(line, sizeof(line), f) != NULL) { + line_no++; + + /* + * Trim leading ws and check that we actually have at + * least 1 char. + */ + p = line; + while (isspace(*p)) + p++; + + if (!strlen(p) || !sscanf(p, "%[^\r\n]\n", tmp)) + continue; + + word = strtok(tmp, sep); + + if (!strncmp(word, "#", 1)) { + /* Ignore comments. */ + continue; + } else if (!strcmp(word, "classes")) { + word = strtok(NULL, sep); + for (; word; word = strtok(NULL, sep)) { + conf->class_cnt++; + strcat(clstr, word); + strcat(clstr, ","); + } + /* Get rid of last comma. */ + clstr[strlen(clstr) - 1] = '\0'; + continue; + } else if (!strcmp(word, "attributes")) { + word = strtok(NULL, sep); + for (; word; word = strtok(NULL, sep)) { + conf->feature_cnt++; + strcat(fstr, word); + strcat(fstr, ","); + } + /* Get rid of last comma. */ + fstr[strlen(fstr) - 1] = '\0'; + continue; + } else { + /* Tree node description. */ + endptr = NULL; + node = NULL; + + x = parse_id(line_no, word, DI_C45_NODE); + if (x.val > max_nodes) { + errx(EX_DATAERR, "classifier model too " + "large (max %u nodes)\n", + max_nodes); + } + node = (struct di_c45_node_real *)&nodes[x.val]; + conf->tree_len = (x.val + 1) * + sizeof(struct di_c45_node_real); + + word=strtok(NULL, sep); + if (!word) + goto bad; + + x = parse_id(line_no, word, DI_C45_FEAT); + node->nid.feature = x.val; + word=strtok(NULL, sep); + if (!word) + goto bad; + + if (!strcmp(word, "r")) { + node->nid.type = DI_C45_REAL; + } else if (!strcmp(word, "n")) { + node->nid.type = DI_C45_BNOM; + /* XXX: Check if non-binary. */ + } else { + errx(EX_DATAERR, "model line %u: " + "unknown node type %s", line_no, + word); + } + + word=strtok(NULL, sep); + if (!word) + goto bad; + + x = parse_id(line_no, word, DI_C45_CLASS); + node->nid.missing_class = x.val; + + /* Parse split values and classes. */ + word = strtok(NULL, sep); + if (!word) + goto bad; + + if (node->nid.type == DI_C45_NOM) { + /* + * XXX: Non-binary nominal not supported + * yet. + */ + errx(EX_DATAERR, "model line %u: " + "non-binary nominal splits not " + "supported yet", line_no); + } else { + /* + * Binary nominal has the same + * structures as real. + */ + v = strtod(word, &endptr); + if (endptr == NULL) { + errx(EX_DATAERR, "model line " + "%u: split value not a " + "number %s", line_no, word); + } + node->val = (int64_t)round(v * + (1 << conf->multi)); + + word = strtok(NULL, sep); + if (!word) + goto bad; + + x = parse_id(line_no, word, + DI_C45_NODE | DI_C45_CLASS); + node->le_id = x.val; + node->le_type = x.type; + + word = strtok(NULL, sep); + if (!word) + goto bad; + + x = parse_id(line_no, word, + DI_C45_NODE | DI_C45_CLASS); + node->gt_id = x.val; + node->gt_type = x.type; + } + } +bad: + if (!word) { + errx(EX_DATAERR, + "model line %u: missing value(s)", line_no); + } + } + fclose(f); + } else { + errx(EX_DATAERR, "could not open classifier model %s", + conf->model_name); + } + + if (conf->tree_len == 0) + errx(EX_DATAERR, "empty classifier model %s", conf->model_name); + +#ifdef DIFFUSE_DEBUG2 + printf("model file: %s\n", conf->model_name); + printf("classes: %d\n", conf->class_cnt); + printf("attributes: %d\n", conf->feature_cnt); + printf("multi: %d\n", (1 << conf->multi)); + printf("tree_len: %d\n", conf->tree_len); + c45_print_model(buf, mod_data); +#endif + + *model = mod_data; + *feature_str = fstr; + *class_str = clstr; + + return (conf->tree_len); +} + +static struct di_classifier_module c45_classifier_module = { + .name = "c4.5", + .get_conf_size = c45_get_conf_size, + .get_opts = c45_get_opts, + .parse_opts = c45_parse_opts, + .print_opts = c45_print_opts, + .print_usage = c45_print_usage, + .load_model = c45_load_model +}; + +struct di_classifier_module * +c45_module(void) +{ + + return (&c45_classifier_module); +} diff -r c7d5be203cfa sbin/ipfw/diffuse_ui_classifier_nbayes.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui_classifier_nbayes.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,554 @@ +/*- + * 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. + */ + +/* + * Description: + * Naive bayes classifier. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diffuse_ui.h" +#include "ipfw2.h" + +enum classifier_nbayes_tokens { + TOK_DI_MODEL_FILE = TOK_DI_FEATURE_MOD_START, +}; + +static struct di_option classifier_nbayes_params[] = { + { "model", DI_OPTION_ARG_STR, 0, 0, + TOK_DI_MODEL_FILE }, + { NULL, 0, 0 } /* Terminator. */ +}; + +int +nbayes_get_conf_size(void) +{ + + return (sizeof(struct di_classifier_nbayes_config)); +} + +int +nbayes_get_opts(struct di_option **opts) +{ + + *opts = classifier_nbayes_params; + + return (sizeof(classifier_nbayes_params)); +} + +int +nbayes_parse_opts(int token, char *arg_val, struct di_oid *buf) +{ + static struct di_classifier_nbayes_config *conf = NULL; + + if (conf == NULL) { + conf = (struct di_classifier_nbayes_config *)buf; + conf->model_name[0] = '\0'; + conf->class_cnt = 0; + conf->feature_cnt = 0; + } + + switch(token) { + case TOK_DI_OPTS_INIT: + break; + + case TOK_DI_MODEL_FILE: + strncpy(conf->model_name, arg_val, DI_MAX_MODEL_STR_LEN - 1); + conf->model_name[DI_MAX_MODEL_STR_LEN - 1] = '\0'; + break; + + default: + /* This should never happen. */ + errx(EX_DATAERR, "invalid option, fix source"); + } + + return (0); +} + +void +nbayes_print_model(struct di_oid *opts, char *mod_data) +{ + struct di_classifier_nbayes_config *conf; + struct di_nbayes_attr_disc *disc; + struct di_nbayes_attr_disc_val *val; + struct di_nbayes_attr_id *id; + struct di_nbayes_attr_norm *norm; + struct di_nbayes_attr_prior *prior; + char *dst; + int fcnt, i, j, l, len; + + conf = (struct di_classifier_nbayes_config *)opts; + fcnt = 0; + + if (mod_data != NULL) + dst = mod_data; + else + dst = (char *)conf->fdist; + + for (l = conf->fdist_len; l > 0; dst += len, l -= len) { + id = (struct di_nbayes_attr_id *)dst; + len = id->len; +#ifdef DIFFUSE_DEBUG2 + printf(" type %d(%d)\n", id->type, id->len); +#endif + switch(id->type) { + case DI_NBAYES_ATTR_PRIOR: + { + prior = (struct di_nbayes_attr_prior *)dst; + if (id->len < sizeof(struct di_nbayes_attr_prior)) { + errx(EX_DATAERR, "invalid classifier model"); + goto done; + } + printf(" prior "); + for (i = 0; i < conf->class_cnt; i++) + printf("%d ", prior->prior_p[i]); + printf("\n"); + break; + } + + case DI_NBAYES_ATTR_DISC: + { + disc = (struct di_nbayes_attr_disc *)dst; + if (id->len < sizeof(struct di_nbayes_attr_disc)) { + errx(EX_DATAERR, "invalid classifier model"); + goto done; + } + for (i = 0; i < disc->val_cnt; i++) { + val = (struct di_nbayes_attr_disc_val *) + (((char *)disc->val) + + (sizeof(struct di_nbayes_attr_disc_val) + + conf->class_cnt * sizeof(uint32_t)) * i); + + printf(" a_%d %d ", fcnt, val->high_val); + for (j = 0; j < conf->class_cnt; j++) + printf("%u ", val->cond_p[j]); + printf("\n"); + } + fcnt++; + break; + } + + case DI_NBAYES_ATTR_NORM: + { + norm = (struct di_nbayes_attr_norm *)dst; + if (id->len < sizeof(struct di_nbayes_attr_norm)) { + errx(EX_DATAERR, "invalid classifier model"); + goto done; + } + printf(" a_%d mean ", fcnt); + for (i = 0; i < conf->class_cnt; i++) + printf("%d ", norm->class[i].mean); + + printf("\n"); + printf(" a_%d stddev ", fcnt); + for (i = 0; i < conf->class_cnt; i++) + printf("%u ", norm->class[i].stddev); + + printf("\n"); + /* Weight, precision missing. */ + fcnt++; + break; + } + + default: + errx(EX_DATAERR, "unknown feature dist type"); + } + } +done: + return; +} + +void +nbayes_print_opts(struct di_oid *opts) +{ + struct di_classifier_nbayes_config *conf; + + conf = (struct di_classifier_nbayes_config *)opts; + + printf(" model name: %s\n", conf->model_name); + printf(" model:\n"); + nbayes_print_model(opts, NULL); +} + +void +nbayes_print_usage() +{ + + printf("algorithm nbayes model \n"); +} + +int +nbayes_load_model(struct di_oid *buf, char **model, char **feature_str, + char **class_str) +{ + struct di_classifier_nbayes_config *conf; + struct di_nbayes_attr_disc *disc; + struct di_nbayes_attr_disc_val *val; + struct di_nbayes_attr_id *id; + struct di_nbayes_attr_norm *norm; + struct di_nbayes_attr_prior *prior; + /* Allocate these statically for now. */ + static char mod_data[65535], fstr[1024], clstr[1024]; + FILE *f; + char *sep = " \t"; + char *dst, *endptr, *first, *p, *second, *word; + char line[1024], tmp[1024], last_attr[128]; + double v; + int i; + unsigned int line_no; + uint16_t multi; + + conf = (struct di_classifier_nbayes_config *)buf; + line_no = 0; + multi = 16; /* 2^multi. */ + + if (conf->model_name == NULL) + errx(EX_DATAERR, "no classifier model specified"); + + fstr[0] = '\0'; + clstr[0] = '\0'; + + /* The precision. */ + conf->multi = multi; + + /* Pointer to list of dists. */ + conf->fdist_len = 0; + dst = (char *)mod_data; + + if ((f = fopen(conf->model_name, "r")) != NULL) { + last_attr[0] = '\0'; + while (fgets(line, sizeof(line), f) != NULL) { + line_no++; + + /* + * Trim leading ws and check that we actually have at + * least 1 char. + */ + p = line; + while (isspace(*p)) + p++; + + if (!strlen(p) || !sscanf(p, "%[^\r\n]\n", tmp)) + continue; + + first = strtok(tmp, sep); + if (!strncmp(first, "#", 1)) { + /* Ignore comments. */ + continue; + } else if (!strcmp(first, "classes")) { + word = strtok(NULL, sep); + for (; word; word = strtok(NULL, sep)) { + conf->class_cnt++; + strcat(clstr, word); + strcat(clstr, ","); + } + /* Get rid of last comma. */ + clstr[strlen(clstr)-1] = '\0'; + } else if (!strcmp(first, "attributes")) { + word = strtok(NULL, sep); + for (; word; word = strtok(NULL, sep)) { + conf->feature_cnt++; + strcat(fstr, word); + strcat(fstr, ","); + } + /* Get rid of last comma. */ + fstr[strlen(fstr) - 1] = '\0'; + } else if (!strcmp(first, "prior")) { + prior = (struct di_nbayes_attr_prior *)dst; + prior->id.type = DI_NBAYES_ATTR_PRIOR; + prior->id.len = + sizeof(struct di_nbayes_attr_id) + + conf->class_cnt * sizeof(uint32_t); + + for (word = strtok(NULL, sep), i = 0; word; + word = strtok(NULL, sep), i++) { + endptr = NULL; + v = strtod(word, &endptr); + if (endptr == NULL) { + errx(EX_DATAERR, "model line " + "%u: prior probability not " + "a number %s", line_no, + word); + } + prior->prior_p[i] = (uint32_t)round(v * + (1 << multi)); + } + conf->fdist_len += prior->id.len; + dst += prior->id.len; + if (dst > mod_data + sizeof(mod_data)) { + errx(EX_DATAERR, "classifier model too " + "large (max 64kB)\n"); + } + } else { + /* Must be an attribute line. */ + if (strcmp(first, last_attr)) { + /* Attribute complete. */ + id = (struct di_nbayes_attr_id *)dst; + conf->fdist_len += id->len; + dst += id->len; + if (dst > mod_data + sizeof(mod_data)) { + errx(EX_DATAERR, + "classifier model too " + "large (max 64kB)\n"); + } + } + second = word = strtok(NULL, sep); + + if (!strcmp(word, "mean") || + !strcmp(word, "stddev") || + !strcmp(word, "weightsum") || + !strcmp(word, "precision")) { + /* Normal. */ + norm = (struct di_nbayes_attr_norm *)dst; + if (strcmp(first, last_attr)) { + norm->id.type = + DI_NBAYES_ATTR_NORM; + norm->id.len = + sizeof( + struct di_nbayes_attr_norm); + } + for (word = strtok(NULL, sep), i = 0; + word; + word = strtok(NULL, sep), i++) { + v = strtod(word, &endptr); + if (endptr == NULL) { + errx(EX_DATAERR, + "model line %u: %s " + "value not a " + "number %s", + line_no, second, + word); + } + + if (!strcmp(second, "mean")) { + norm->class[i].mean = + (int32_t)round(v * + (1 << multi)); + } else if (!strcmp(second, + "stddev")) { + norm->class[i].stddev = + (uint32_t)round(v * + (1 << multi)); + } else if (!strcmp(second, + "weightsum")) { + norm->class[i].wsum = + (int32_t)round(v * + (1 << multi)); + } else { + norm->class[i].prec = + (uint32_t)round(v * + (1 << multi)); + } + } + + if (i != conf->class_cnt) { + errx(EX_DATAERR, + "model line %u: %s %s has " + "less values than classes", + line_no, first, second); + } + norm->id.len += conf->class_cnt * + sizeof(uint32_t); + + } else { + /* Discrete. */ + disc = (struct di_nbayes_attr_disc *)dst; + if (strcmp(first, last_attr)) { + disc->id.type = + DI_NBAYES_ATTR_DISC; + disc->id.len = + sizeof(struct + di_nbayes_attr_disc); + disc->val_cnt = 1; + } else { + disc->val_cnt++; + } + + val = (struct di_nbayes_attr_disc_val *) + (((char *)disc->val) + + (sizeof( + struct di_nbayes_attr_disc_val) + + conf->class_cnt * + sizeof(uint32_t)) * + (disc->val_cnt - 1)); + + /* Parse interval value. + * Interval: "low-high" || "All", + * values can be negative/positive + * numbers or "-inf" or "inf". + * + * XXX: Implement single nominal value? + */ + + /* + * Find "-" after first "(-)inf" or + * number. + */ + p = strchr(&word[1], '-'); + if (p != NULL) + p++; + + /* Else assume single nominal value. */ + if (!p || !strncmp(p, "inf", 3)) { + /* + * !p catches the case when + * interval = "All". + */ + val->high_val = 0x7FFFFFFF; + } else { + v = strtod(p, &endptr); + if (endptr == NULL) { + errx(EX_DATAERR, + "model line %u: " + "interval high val " + "not a number %s", + line_no, p); + } + /* + * We test if high_val >= + * feature and with integer + * features, intervals will be + * on .5, e.g. if the value is + * 40 we have an interval of + * 39.5-40.5. So, for >= 0 use + * floor and for <0 use ceil. + */ + if (v >= 0) { + val->high_val = + (int32_t)floor(v); + } else { + val->high_val = + (int32_t)ceil(v); + } + } + + /* Parse probabilities. */ + for (word = strtok(NULL, sep), i = 0; + word; + word = strtok(NULL, sep), i++) { + v = strtod(word, &endptr); + if (endptr == NULL) { + errx(EX_DATAERR, + "model line %u: " + "conditional " + "probability not a " + "number %s", + line_no, word); + } + val->cond_p[i] = + (uint32_t)round(v * + (1 << multi)); + } + if (i != conf->class_cnt) { + errx(EX_DATAERR, + "model line %u: %s %s has " + "less values than classes", + line_no, first, second); + } + + disc->id.len += sizeof(int32_t) + + conf->class_cnt * sizeof(uint32_t); + } + + strncpy(last_attr, first, + sizeof(last_attr) - 1); + last_attr[sizeof(last_attr) - 1] = '\0'; + } + } + id = (struct di_nbayes_attr_id *)dst; + conf->fdist_len += id->len; + + fclose(f); + } else { + errx(EX_DATAERR, "could not open classifier model %s", + conf->model_name); + } + + if (conf->fdist_len == 0) + errx(EX_DATAERR, "empty classifier model %s", conf->model_name); + +#ifdef DIFFUSE_DEBUG2 + printf("model: %s\n", conf->model_name); + printf("classes: %d\n", conf->class_cnt); + printf("attributes: %d\n", conf->feature_cnt); + printf("multi: %d\n", (1 << conf->multi)); + printf("fdist_len: %d\n", conf->fdist_len); + nbayes_print_model(buf, mod_data); +#endif + + *model = mod_data; + *feature_str = fstr; + *class_str = clstr; + + return (conf->fdist_len); +} + +static struct di_classifier_module nbayes_classifier_module = { + .name = "nbayes", + .get_conf_size = nbayes_get_conf_size, + .get_opts = nbayes_get_opts, + .parse_opts = nbayes_parse_opts, + .print_opts = nbayes_print_opts, + .print_usage = nbayes_print_usage, + .load_model = nbayes_load_model +}; + +struct di_classifier_module * +nbayes_module(void) +{ + + return (&nbayes_classifier_module); +} diff -r c7d5be203cfa sbin/ipfw/diffuse_ui_feature_iat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui_feature_iat.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,206 @@ +/*- + * 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. + */ + +/* + * Description: + * Inter-arrival times feature. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diffuse_ui.h" +#include "ipfw2.h" + +enum feature_iat_tokens { + TOK_DI_WINDOW = TOK_DI_FEATURE_MOD_START, + TOK_DI_PARTIAL_WINDOWS, + TOK_DI_ACCURATE_TIME, + TOK_DI_JUMP_WINDOWS, + TOK_DI_PRECISION +}; + +static struct di_option feature_iat_params[] = { + /* Min window of 2 because we need n packets for n-1 iats. */ + { "window", DI_OPTION_ARG_UINT, 2, 1000, + TOK_DI_WINDOW }, + { "partial-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_PARTIAL_WINDOWS }, + { "accurate-time", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_ACCURATE_TIME }, + { "jump-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_JUMP_WINDOWS }, + /* Configurable precision from 1us to 1ms. */ + { "precision", DI_OPTION_ARG_UINT, 1, 10000, + TOK_DI_PRECISION }, + { NULL, 0, 0 } /* Terminator. */ +}; + +int +iat_get_conf_size(void) +{ + + return (sizeof(struct di_feature_iat_config)); +} + +int +iat_get_opts(struct di_option **opts) +{ + + *opts = feature_iat_params; + + return (sizeof(feature_iat_params)); +} + +int +iat_parse_opts(int token, char *arg_val, struct di_oid *buf) +{ + static struct di_feature_iat_config *conf = NULL; + char *end; + + if (conf == NULL) { + conf = (struct di_feature_iat_config *)buf; + conf->iat_window = -1; + conf->iat_partial_window = -1; + conf->iat_ts_acc = -1; + conf->iat_jump_window = -1; + conf->iat_prec = -1; + } + + switch(token) { + case TOK_DI_OPTS_INIT: + break; + + case TOK_DI_WINDOW: + end = NULL; + conf->iat_window = strtoul(arg_val, &end, 0); + if (*end == 'K' || *end == 'k') + conf->iat_window *= 1024; + break; + + case TOK_DI_PARTIAL_WINDOWS: + conf->iat_partial_window = 1; + break; + + case TOK_DI_ACCURATE_TIME: + conf->iat_ts_acc = 1; + break; + + case TOK_DI_JUMP_WINDOWS: + conf->iat_jump_window = 1; + break; + + case TOK_DI_PRECISION: + conf->iat_prec = strtoul(arg_val, &end, 0); + break; + + default: + /* This should never happen. */ + errx(EX_DATAERR, "invalid option, fix source"); + } + + return (0); +} + +void +iat_print_opts(struct di_oid *opts) +{ + struct di_feature_iat_config *conf; + + conf = (struct di_feature_iat_config *)opts; + + printf(" window: %d\n", conf->iat_window); + printf(" partial windows: %s\n", + (conf->iat_partial_window == 1) ? "yes" : "no"); + printf(" accurate time: %s\n", (conf->iat_ts_acc == 1) ? "yes" : "no"); + printf(" jump window: %s\n", + (conf->iat_jump_window == 1) ? "yes" : "no"); + printf(" precision: %d\n", conf->iat_prec); +} + +void +iat_print_usage() +{ + + printf("module iat [window ] [partial-windows] [jump-windows] " + "[accurate-time] [precision ]\n"); +} + +DI_IAT_STAT_NAMES; /* Stat name array in diffuse_feature_iat.h. */ +char * +iat_get_stat_name(int i) +{ + + return (di_iat_stat_names[i]); +} + +static struct di_feature_module iat_feature_module = { + .name = DI_IAT_NAME, + .type = DI_IAT_TYPE, + .get_conf_size = iat_get_conf_size, + .get_opts = iat_get_opts, + .parse_opts = iat_parse_opts, + .print_opts = iat_print_opts, + .print_usage = iat_print_usage, + .get_stat_name = iat_get_stat_name +}; + +struct di_feature_module * +iat_module(void) +{ + + return (&iat_feature_module); +} diff -r c7d5be203cfa sbin/ipfw/diffuse_ui_feature_iatbd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui_feature_iatbd.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,205 @@ +/*- + * 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. + */ + +/* + * Description: + * Bidirectional inter-arrival times feature. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diffuse_ui.h" +#include "ipfw2.h" + +enum feature_iatbd_tokens { + TOK_DI_WINDOW = TOK_DI_FEATURE_MOD_START, + TOK_DI_PARTIAL_WINDOWS, + TOK_DI_ACCURATE_TIME, + TOK_DI_JUMP_WINDOWS, + TOK_DI_PRECISION +}; + +static struct di_option feature_iatbd_params[] = { + /* Min window of 2 because we need n packets for n-1 iats. */ + { "window", DI_OPTION_ARG_UINT, 2, 1000, + TOK_DI_WINDOW }, + { "partial-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_PARTIAL_WINDOWS }, + { "accurate-time", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_ACCURATE_TIME }, + { "jump-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_JUMP_WINDOWS }, + /* Configurable precision from 1us to 1ms. */ + { "precision", DI_OPTION_ARG_UINT, 1, 10000, + TOK_DI_PRECISION }, + { NULL, 0, 0 } /* Terminator. */ +}; + +int +iatbd_get_conf_size(void) +{ + + return (sizeof(struct di_feature_iatbd_config)); +} + +int +iatbd_get_opts(struct di_option **opts) +{ + + *opts = feature_iatbd_params; + + return (sizeof(feature_iatbd_params)); +} + +int +iatbd_parse_opts(int token, char *arg_val, struct di_oid *buf) +{ + static struct di_feature_iatbd_config *conf = NULL; + char *end; + + if (conf == NULL) { + conf = (struct di_feature_iatbd_config *)buf; + conf->iat_window = -1; + conf->iat_partial_window = -1; + conf->iat_ts_acc = -1; + conf->iat_jump_window = -1; + conf->iat_prec = -1; + } + + switch(token) { + case TOK_DI_OPTS_INIT: + break; + + case TOK_DI_WINDOW: + end = NULL; + conf->iat_window = strtoul(arg_val, &end, 0); + if (*end == 'K' || *end == 'k') + conf->iat_window *= 1024; + break; + + case TOK_DI_PARTIAL_WINDOWS: + conf->iat_partial_window = 1; + break; + + case TOK_DI_ACCURATE_TIME: + conf->iat_ts_acc = 1; + break; + + case TOK_DI_JUMP_WINDOWS: + conf->iat_jump_window = 1; + break; + + case TOK_DI_PRECISION: + conf->iat_prec = strtoul(arg_val, &end, 0); + break; + + default: + /* This should never happen. */ + errx(EX_DATAERR, "invalid option, fix source"); + } + + return (0); +} + +void +iatbd_print_opts(struct di_oid *opts) +{ + struct di_feature_iatbd_config *conf; + + conf = (struct di_feature_iatbd_config *)opts; + + printf(" window: %d\n", conf->iat_window); + printf(" partial windows: %s\n", + (conf->iat_partial_window == 1) ? "yes" : "no"); + printf(" accurate time: %s\n", (conf->iat_ts_acc == 1) ? "yes" : "no"); + printf(" jump window: %s\n", + (conf->iat_jump_window == 1) ? "yes" : "no"); + printf(" precision: %d\n", conf->iat_prec); +} + +void +iatbd_print_usage() +{ + + printf("module iatbd [window ] [partial-windows] " + "[jump-windows] [accurate-time] [precision ]\n"); +} + +DI_IATBD_STAT_NAMES; /* Stat name array in diffuse_feature_iatbd.h. */ +char * +iatbd_get_stat_name(int i) +{ + + return (di_iatbd_stat_names[i]); +} + +static struct di_feature_module iatbd_feature_module = { + .name = DI_IATBD_NAME, + .type = DI_IATBD_TYPE, + .get_conf_size = iatbd_get_conf_size, + .get_opts = iatbd_get_opts, + .parse_opts = iatbd_parse_opts, + .print_opts = iatbd_print_opts, + .print_usage = iatbd_print_usage, + .get_stat_name = iatbd_get_stat_name +}; + +struct di_feature_module *iatbd_module(void) +{ + + return (&iatbd_feature_module); +} diff -r c7d5be203cfa sbin/ipfw/diffuse_ui_feature_pcnt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui_feature_pcnt.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,185 @@ +/*- + * 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. + */ + +/* + * Description: + * Packet count feature. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diffuse_ui.h" +#include "ipfw2.h" + +enum feature_pcnt_tokens { + TOK_DI_WINDOW = TOK_DI_FEATURE_MOD_START, + TOK_DI_PARTIAL_WINDOWS, + TOK_DI_JUMP_WINDOWS +}; + +static struct di_option feature_pcnt_params[] = { + { "window", DI_OPTION_ARG_UINT, 1, 1000, + TOK_DI_WINDOW }, + { "partial-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_PARTIAL_WINDOWS }, + { "jump-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_JUMP_WINDOWS }, + { NULL, 0, 0 } /* Terminator. */ +}; + +int +pcnt_get_conf_size(void) +{ + + return (sizeof(struct di_feature_pcnt_config)); +} + +int +pcnt_get_opts(struct di_option **opts) +{ + + *opts = feature_pcnt_params; + + return (sizeof(feature_pcnt_params)); +} + +int +pcnt_parse_opts(int token, char *arg_val, struct di_oid *buf) +{ + static struct di_feature_pcnt_config *conf = NULL; + char *end; + + if (conf == NULL) { + conf = (struct di_feature_pcnt_config *)buf; + conf->pcnt_window = -1; + conf->pcnt_partial_window = -1; + conf->pcnt_jump_window = -1; + } + + switch(token) { + case TOK_DI_OPTS_INIT: + break; + + case TOK_DI_WINDOW: + end = NULL; + conf->pcnt_window = strtoul(arg_val, &end, 0); + if (*end == 'K' || *end == 'k') + conf->pcnt_window *= 1024; + break; + + case TOK_DI_PARTIAL_WINDOWS: + conf->pcnt_partial_window = 1; + break; + + case TOK_DI_JUMP_WINDOWS: + conf->pcnt_jump_window = 1; + break; + + default: + /* This should never happen. */ + errx(EX_DATAERR, "invalid option, fix source"); + } + + return (0); +} + +void +pcnt_print_opts(struct di_oid *opts) +{ + struct di_feature_pcnt_config *conf; + + conf = (struct di_feature_pcnt_config *)opts; + + printf(" window: %d\n", conf->pcnt_window); + printf(" partial windows: %s\n", + (conf->pcnt_partial_window == 1) ? "yes" : "no"); + printf(" jump window: %s\n", + (conf->pcnt_jump_window == 1) ? "yes" : "no"); +} + +void +pcnt_print_usage() +{ + + printf("module pcnt [window ] [partial-windows] " + "[jump-windows]\n"); +} + +DI_PCNT_STAT_NAMES; /* Stat name array in diffuse_feature_pcnt.h. */ +char * +pcnt_get_stat_name(int i) +{ + + return (di_pcnt_stat_names[i]); +} + +static struct di_feature_module pcnt_feature_module = { + .name = DI_PCNT_NAME, + .type = DI_PCNT_TYPE, + .get_conf_size = pcnt_get_conf_size, + .get_opts = pcnt_get_opts, + .parse_opts = pcnt_parse_opts, + .print_opts = pcnt_print_opts, + .print_usage = pcnt_print_usage, + .get_stat_name = pcnt_get_stat_name +}; + +struct di_feature_module * +pcnt_module(void) +{ + + return (&pcnt_feature_module); +} diff -r c7d5be203cfa sbin/ipfw/diffuse_ui_feature_plen.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui_feature_plen.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,205 @@ +/*- + * 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. + */ + +/* + * Description: + * Packet length feature. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diffuse_ui.h" +#include "ipfw2.h" + +enum feature_plen_tokens { + TOK_DI_WINDOW = TOK_DI_FEATURE_MOD_START, + TOK_DI_PARTIAL_WINDOWS, + TOK_DI_PAYLOAD_LEN, + TOK_DI_IPDATA_LEN, + TOK_DI_JUMP_WINDOWS +}; + + +static struct di_option feature_plen_params[] = { + { "window", DI_OPTION_ARG_UINT, 1, 1000, + TOK_DI_WINDOW }, + { "partial-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_PARTIAL_WINDOWS }, + { "payload-len", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_PAYLOAD_LEN }, + { "ipdata-len", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_IPDATA_LEN }, + { "jump-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_JUMP_WINDOWS }, + { NULL, 0, 0 } /* Terminator. */ +}; + +int +plen_get_conf_size(void) +{ + + return (sizeof(struct di_feature_plen_config)); +} + +int +plen_get_opts(struct di_option **opts) +{ + + *opts = feature_plen_params; + + return (sizeof(feature_plen_params)); +} + +int +plen_parse_opts(int token, char *arg_val, struct di_oid *buf) +{ + static struct di_feature_plen_config *conf = NULL; + char *end; + + if (conf == NULL) { + conf = (struct di_feature_plen_config *)buf; + conf->plen_window = -1; + conf->plen_partial_window = -1; + conf->plen_len_type = -1; + conf->plen_jump_window = -1; + } + + switch(token) { + case TOK_DI_OPTS_INIT: + break; + + case TOK_DI_WINDOW: + end = NULL; + conf->plen_window = strtoul(arg_val, &end, 0); + if (*end == 'K' || *end == 'k') + conf->plen_window *= 1024; + break; + + case TOK_DI_PARTIAL_WINDOWS: + conf->plen_partial_window = 1; + break; + + case TOK_DI_PAYLOAD_LEN: + conf->plen_len_type = DI_PLEN_LEN_PAYLOAD; + break; + + case TOK_DI_IPDATA_LEN: + conf->plen_len_type = DI_PLEN_LEN_IPDATA; + break; + + case TOK_DI_JUMP_WINDOWS: + conf->plen_jump_window = 1; + break; + + default: + /* This should never happen. */ + errx(EX_DATAERR, "invalid option, fix source"); + } + + return (0); +} + +void +plen_print_opts(struct di_oid *opts) +{ + struct di_feature_plen_config *conf; + + conf = (struct di_feature_plen_config *)opts; + + printf(" window: %d\n", conf->plen_window); + printf(" partial windows: %s\n", + (conf->plen_partial_window == 1) ? "yes" : "no"); + printf(" length type: %s\n", + (conf->plen_len_type == DI_PLEN_LEN_FULL) ? "ip" : + (conf->plen_len_type == DI_PLEN_LEN_IPDATA) ? "ipdata" : "payload"); + printf(" jump window: %s\n", + (conf->plen_jump_window == 1) ? "yes" : "no"); +} + +void +plen_print_usage() +{ + + printf("module plen [window ] [partial-windows] " + "[jump-windows] [payload-len]\n"); +} + +DI_PLEN_STAT_NAMES; /* Stat name array in diffuse_feature_plen.h. */ +char * +plen_get_stat_name(int i) +{ + + return (di_plen_stat_names[i]); +} + +static struct di_feature_module plen_feature_module = { + .name = DI_PLEN_NAME, + .type = DI_PLEN_TYPE, + .get_conf_size = plen_get_conf_size, + .get_opts = plen_get_opts, + .parse_opts = plen_parse_opts, + .print_opts = plen_print_opts, + .print_usage = plen_print_usage, + .get_stat_name = plen_get_stat_name +}; + +struct di_feature_module * +plen_module(void) +{ + + return (&plen_feature_module); +} diff -r c7d5be203cfa sbin/ipfw/diffuse_ui_feature_plenbd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui_feature_plenbd.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,203 @@ +/*- + * 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. + */ + +/* + * Description: + * Bidirectional packet length feature. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diffuse_ui.h" +#include "ipfw2.h" + +enum feature_plenbd_tokens { + TOK_DI_WINDOW = TOK_DI_FEATURE_MOD_START, + TOK_DI_PARTIAL_WINDOWS, + TOK_DI_PAYLOAD_LEN, + TOK_DI_IPDATA_LEN, + TOK_DI_JUMP_WINDOWS +}; + +static struct di_option feature_plenbd_params[] = { + { "window", DI_OPTION_ARG_UINT, 1, 1000, + TOK_DI_WINDOW }, + { "partial-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_PARTIAL_WINDOWS }, + { "payload-len", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_PAYLOAD_LEN }, + { "ipdata-len", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_IPDATA_LEN }, + { "jump-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_JUMP_WINDOWS }, + { NULL, 0, 0 } /* Terminator. */ +}; + +int +plenbd_get_conf_size(void) +{ + + return (sizeof(struct di_feature_plenbd_config)); +} + +int +plenbd_get_opts(struct di_option **opts) +{ + *opts = feature_plenbd_params; + + return (sizeof(feature_plenbd_params)); +} + +int +plenbd_parse_opts(int token, char *arg_val, struct di_oid *buf) +{ + static struct di_feature_plenbd_config *conf = NULL; + char *end; + + if (conf == NULL) { + conf = (struct di_feature_plenbd_config *)buf; + conf->plen_window = -1; + conf->plen_partial_window = -1; + conf->plen_len_type = -1; + conf->plen_jump_window = -1; + } + + switch(token) { + case TOK_DI_OPTS_INIT: + break; + + case TOK_DI_WINDOW: + end = NULL; + conf->plen_window = strtoul(arg_val, &end, 0); + if (*end == 'K' || *end == 'k') + conf->plen_window *= 1024; + break; + + case TOK_DI_PARTIAL_WINDOWS: + conf->plen_partial_window = 1; + break; + + case TOK_DI_PAYLOAD_LEN: + conf->plen_len_type = DI_PLEN_LEN_PAYLOAD; + break; + + case TOK_DI_IPDATA_LEN: + conf->plen_len_type = DI_PLEN_LEN_IPDATA; + break; + + case TOK_DI_JUMP_WINDOWS: + conf->plen_jump_window = 1; + break; + + default: + /* This should never happen. */ + errx(EX_DATAERR, "invalid option, fix source"); + } + + return (0); +} + +void +plenbd_print_opts(struct di_oid *opts) +{ + struct di_feature_plenbd_config *conf; + + conf = (struct di_feature_plenbd_config *) opts; + + printf(" window: %d\n", conf->plen_window); + printf(" partial windows: %s\n", + (conf->plen_partial_window == 1) ? "yes" : "no"); + printf(" length type: %s\n", + (conf->plen_len_type == DI_PLEN_LEN_FULL) ? "ip" : + (conf->plen_len_type == DI_PLEN_LEN_IPDATA) ? "ipdata" : "payload"); + printf(" jump window: %s\n", + (conf->plen_jump_window == 1) ? "yes" : "no"); +} + +void +plenbd_print_usage() +{ + + printf("module plenbd [window ] [partial-windows] " + "[jump-windows] [payload-len]\n"); +} + +DI_PLENBD_STAT_NAMES; /* Stat name array in diffuse_feature_plenbd.h. */ +char * +plenbd_get_stat_name(int i) +{ + + return (di_plenbd_stat_names[i]); +} + +static struct di_feature_module plenbd_feature_module = { + .name = DI_PLENBD_NAME, + .type = DI_PLENBD_TYPE, + .get_conf_size = plenbd_get_conf_size, + .get_opts = plenbd_get_opts, + .parse_opts = plenbd_parse_opts, + .print_opts = plenbd_print_opts, + .print_usage = plenbd_print_usage, + .get_stat_name = plenbd_get_stat_name +}; + +struct di_feature_module * +plenbd_module(void) +{ + + return (&plenbd_feature_module); +} diff -r c7d5be203cfa sbin/ipfw/diffuse_ui_feature_skype.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_ui_feature_skype.c Tue Oct 11 18:22:08 2011 +1100 @@ -0,0 +1,201 @@ +/*- + * 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 Rozanna Jesudasan. + * + * 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: + * Skype feature. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diffuse_ui.h" +#include "ipfw2.h" + +enum feature_skype_tokens { + TOK_DI_WINDOW = TOK_DI_FEATURE_MOD_START, + TOK_DI_PARTIAL_WINDOWS, + TOK_DI_PAYLOAD_LEN, + TOK_DI_IPDATA_LEN, + TOK_DI_JUMP_WINDOWS +}; + +static struct di_option feature_skype_params[] = { + { "window", DI_OPTION_ARG_UINT, 1, 1000, + TOK_DI_WINDOW }, + { "partial-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_PARTIAL_WINDOWS }, + { "payload-len", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_PAYLOAD_LEN }, + { "ipdata-len", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_IPDATA_LEN }, + { "jump-windows", DI_OPTION_ARG_NOARG, 0, 0, + TOK_DI_JUMP_WINDOWS}, + { NULL, 0, 0 } /* Terminator. */ +}; + +int +skype_get_conf_size(void) +{ + return (sizeof(struct di_feature_skype_config)); +} + +int +skype_get_opts(struct di_option **opts) +{ + + *opts = feature_skype_params; + + return (sizeof(feature_skype_params)); +} + +int +skype_parse_opts(int token, char *arg_val, struct di_oid *buf) +{ + static struct di_feature_skype_config *conf = NULL; + char *end; + + if (conf == NULL) { + conf = (struct di_feature_skype_config *)buf; + conf->plen_window = -1; + conf->plen_partial_window = -1; + conf->plen_len_type = -1; + conf->plen_jump_window = -1; + } + + switch(token) { + case TOK_DI_OPTS_INIT: + break; + + case TOK_DI_WINDOW: + end = NULL; + conf->plen_window = strtoul(arg_val, &end, 0); + if (*end == 'K' || *end == 'k') + conf->plen_window *= 1024; + break; + + case TOK_DI_PARTIAL_WINDOWS: + conf->plen_partial_window = 1; + break; + + case TOK_DI_PAYLOAD_LEN: + conf->plen_len_type = DI_PLEN_LEN_PAYLOAD; + break; + + case TOK_DI_IPDATA_LEN: + conf->plen_len_type = DI_PLEN_LEN_IPDATA; + break; + + case TOK_DI_JUMP_WINDOWS: + conf->plen_jump_window = 1; + break; + + default: + /* This should never happen. */ + errx(EX_DATAERR, "invalid option, fix source"); + } + + return (0); +} + +void +skype_print_opts(struct di_oid *opts) +{ + struct di_feature_skype_config *conf; + + conf = (struct di_feature_skype_config *)opts; + + printf(" window: %d\n", conf->plen_window); + printf(" partial windows: %s\n", + (conf->plen_partial_window == 1) ? "yes" : "no"); + printf(" length type: %s\n", + (conf->plen_len_type == DI_PLEN_LEN_FULL) ? "ip" : + (conf->plen_len_type == DI_PLEN_LEN_IPDATA) ? "ipdata" : "payload"); + printf(" jump window: %s\n", + (conf->plen_jump_window == 1) ? "yes" : "no"); +} + +void +skype_print_usage() +{ + + printf("module skype [window ] [partial-windows] " + "[jump-windows] [payload-len]\n"); +} + +DI_SKYPE_STAT_NAMES; /* Stat name array in diffuse_feature_skype.h. */ +char * +skype_get_stat_name(int i) +{ + + return (di_skype_stat_names[i]); +} + +static struct di_feature_module skype_feature_module = { + .name = DI_SKYPE_NAME, + .type = DI_SKYPE_TYPE, + .get_conf_size = skype_get_conf_size, + .get_opts = skype_get_opts, + .parse_opts = skype_parse_opts, + .print_opts = skype_print_opts, + .print_usage = skype_print_usage, + .get_stat_name = skype_get_stat_name +}; + +struct di_feature_module * +skype_module(void) +{ + + return (&skype_feature_module); +} diff -r c7d5be203cfa sbin/ipfw/ipfw2.c --- a/sbin/ipfw/ipfw2.c Sat Oct 08 16:34:15 2011 +1100 +++ b/sbin/ipfw/ipfw2.c Tue Oct 11 18:22:08 2011 +1100 @@ -14,51 +14,54 @@ * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * NEW command line interface for IP firewall facility * * $FreeBSD$ */ #include -#include #include #include #include -#include "ipfw2.h" +#include + +#include +#include /* only IFNAMSIZ */ + +#include +#include /* only n_short, n_long */ +#include +#include +#include +#include /* Must come after ip_fw.h */ +#include #include #include #include +#include #include #include #include #include #include #include #include #include /* ctime */ #include /* _long_to_time */ #include -#include - -#include -#include /* only IFNAMSIZ */ -#include -#include /* only n_short, n_long */ -#include -#include -#include -#include -#include + +#include "ipfw2.h" +#include "diffuse_ui.h" struct cmdline_opts co; /* global options */ int resvd_set_number = RESVD_SET; #define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ if (!av[0]) \ errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ if (_substrcmp(*av, "tablearg") == 0) { \ arg = IP_FW_TABLEARG; \ @@ -207,20 +210,22 @@ static struct _s_x rule_actions[] = { { "reject", TOK_REJECT }, { "reset6", TOK_RESET6 }, { "reset", TOK_RESET }, { "unreach6", TOK_UNREACH6 }, { "unreach", TOK_UNREACH }, { "check-state", TOK_CHECKSTATE }, { "//", TOK_COMMENT }, { "nat", TOK_NAT }, { "reass", TOK_REASS }, { "setfib", TOK_SETFIB }, + { "mlclass", TOK_DI_ML_CLASSIFY }, /* DIFFUSE. */ + { "export", TOK_DI_EXPORT }, /* DIFFUSE. */ { "call", TOK_CALL }, { "return", TOK_RETURN }, { NULL, 0 } /* terminator */ }; static struct _s_x rule_action_params[] = { { "altq", TOK_ALTQ }, { "log", TOK_LOG }, { "tag", TOK_TAG }, { "untag", TOK_UNTAG }, @@ -308,20 +313,33 @@ static struct _s_x rule_options[] = { { "//", TOK_COMMENT }, { "not", TOK_NOT }, /* pseudo option */ { "!", /* escape ? */ TOK_NOT }, /* pseudo option */ { "or", TOK_OR }, /* pseudo option */ { "|", /* escape */ TOK_OR }, /* pseudo option */ { "{", TOK_STARTBRACE }, /* pseudo option */ { "(", TOK_STARTBRACE }, /* pseudo option */ { "}", TOK_ENDBRACE }, /* pseudo option */ { ")", TOK_ENDBRACE }, /* pseudo option */ + + /* DIFFUSE tokens. */ + { "features", TOK_DI_FEATURES }, /* list of features to be computed */ + { "unidirectional", TOK_DI_UNIDIRECTIONAL },/* bidirectional vs. unidirectional flows */ + { "every", TOK_DI_EVERY }, /* make decision for every packet */ + { "once", TOK_DI_ONCE }, /* process only first packet */ + { "sample", TOK_DI_SAMPLE_REG }, /* process every n-th packet */ + { "rnd-sample", TOK_DI_SAMPLE_RAND }, /* process randomly sampled packets */ + { "once-classified", TOK_DI_ONCE_CLASS }, /* process until first classified */ + { "once-exported", TOK_DI_ONCE_EXP }, /* process until first exported */ + { "class-tags", TOK_DI_CLASS_TAGS }, /* tags for tagging based on classes */ + { "match-if-class", TOK_DI_MATCH_IF_CLASS },/* match if any of the specified classes */ + { NULL, 0 } /* terminator */ }; /* * Helper routine to print a possibly unaligned uint64_t on * various platform. If width > 0, print the value with * the desired width, followed by a space; * otherwise, return the required width. */ int @@ -400,20 +418,24 @@ do_cmd(int optname, void *optval, uintpt /** * match_token takes a table and a string, returns the value associated * with the string (-1 in case of failure). */ int match_token(struct _s_x *table, char *string) { struct _s_x *pt; uint i = strlen(string); + /* XXX: This is a bit hacky. */ + if (table == rule_options && strstr(string, ".") != NULL) + return TOK_DI_FEATURE_MATCH; + for (pt = table ; i && pt->s != NULL ; pt++) if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) return pt->x; return -1; } /** * match_value takes a table and a value, returns the string associated * with the value (NULL in case of failure). */ @@ -1151,20 +1173,23 @@ show_ipfw(struct ip_fw *rule, int pcwidt break; case O_CALLRETURN: if (cmd->len & F_NOT) printf("return"); else PRINT_UINT_ARG("call ", cmd->arg1); break; default: + if (!diffuse_show_cmd(cmd)) + break; + printf("** unrecognized action %d len %d ", cmd->opcode, cmd->len); } } if (logptr) { if (logptr->max_log > 0) printf(" log logamount %d", logptr->max_log); else printf(" log"); } @@ -1576,20 +1601,23 @@ show_ipfw(struct ip_fw *rule, int pcwidt case O_TAGGED: if (F_LEN(cmd) == 1) PRINT_UINT_ARG(" tagged ", cmd->arg1); else print_newports((ipfw_insn_u16 *)cmd, 0, O_TAGGED); break; default: + if (!diffuse_show_cmd(cmd)) + break; + printf(" [opcode %d len %d]", cmd->opcode, cmd->len); } } if (cmd->len & F_OR) { printf(" or"); or_block = 1; } else if (or_block) { printf(" }"); or_block = 0; @@ -2647,21 +2675,21 @@ add_dst(ipfw_insn *cmd, char *av, u_char */ void ipfw_add(char *av[]) { /* * rules are added into the 'rulebuf' and then copied in * the correct order into the actual rule. * Some things that need to go out of order (prob, action etc.) * go into actbuf[]. */ - static uint32_t rulebuf[255], actbuf[255], cmdbuf[255]; + static uint32_t rulebuf[1024], actbuf[255], cmdbuf[1024]; ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; ipfw_insn *first_cmd; /* first match pattern */ struct ip_fw *rule; /* * various flags used to record that we entered some fields. */ ipfw_insn *have_state = NULL; /* check-state or keep-state */ @@ -2943,20 +2971,23 @@ chkarg: case TOK_REASS: action->opcode = O_REASS; break; case TOK_RETURN: fill_cmd(action, O_CALLRETURN, F_NOT, 0); break; default: + if (!diffuse_parse_action(i, action, &av)) + break; + errx(EX_DATAERR, "invalid action %s\n", av[-1]); } action = next_cmd(action); /* * [altq queuename] -- altq tag, optional * [log [logamount N]] -- log, optional * * If they exist, it go first in the cmdbuf, but then it is * skipped in the copy section to the end of the buffer. @@ -3657,20 +3688,23 @@ read_options: __PAST_END(c->d, 1) = j; // i converted to option av++; cmd->arg1 = strtoul(*av, &p, 0); if (p && *p) errx(EX_USAGE, "format: lookup argument tablenum"); av++; } break; default: + if (!diffuse_parse_cmd(i, open_par, &cmd, &av)) + break; + errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); } if (F_LEN(cmd) > 0) { /* prepare to advance */ prev = cmd; cmd = next_cmd(cmd); } } done: /* @@ -3685,20 +3719,23 @@ done: /* * First thing to write into the command stream is the match probability. */ if (match_prob != 1) { /* 1 means always match */ dst->opcode = O_PROB; dst->len = 2; *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff); dst += dst->len; } + /* Allow DIFFUSE to manipulate the rule building. */ + diffuse_rule_build_1(cmdbuf, cmd, &dst); + /* * generate O_PROBE_STATE if necessary */ if (have_state && have_state->opcode != O_CHECK_STATE) { fill_cmd(dst, O_PROBE_STATE, 0, 0); dst = next_cmd(dst); } /* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */ for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { @@ -3710,20 +3747,23 @@ done: case O_LIMIT: case O_ALTQ: case O_TAG: break; default: bcopy(src, dst, i * sizeof(uint32_t)); dst += i; } } + /* Allow DIFFUSE to manipulate the rule building. */ + diffuse_rule_build_2(cmdbuf, cmd, &dst); + /* * put back the have_state command as last opcode */ if (have_state && have_state->opcode != O_CHECK_STATE) { i = F_LEN(have_state); bcopy(have_state, dst, i * sizeof(uint32_t)); dst += i; } /* * start action section diff -r c7d5be203cfa sbin/ipfw/ipfw2.h --- a/sbin/ipfw/ipfw2.h Sat Oct 08 16:34:15 2011 +1100 +++ b/sbin/ipfw/ipfw2.h Tue Oct 11 18:22:08 2011 +1100 @@ -30,20 +30,21 @@ */ struct cmdline_opts { /* boolean options: */ int do_value_as_ip; /* show table value as IP */ int do_resolv; /* try to resolve all ip to names */ int do_time; /* Show time stamps */ int do_quiet; /* Be quiet in add and flush */ int do_pipe; /* this cmd refers to a pipe/queue/sched */ int do_nat; /* this cmd refers to a nat config */ + int do_diffuse; /* DIFFUSE */ int do_dynamic; /* display dynamic rules */ int do_expired; /* display expired dynamic rules */ int do_compact; /* show rules in compact mode */ int do_force; /* do not ask for confirmation */ int show_sets; /* display the set each rule belongs to */ int test_only; /* only check syntax */ int comment_only; /* only print action and comment */ int verbose; /* be verbose on some commands */ /* The options below can have multiple values. */ @@ -196,20 +197,35 @@ enum tokens { TOK_SRCIP6, TOK_IPV4, TOK_UNREACH6, TOK_RESET6, TOK_FIB, TOK_SETFIB, TOK_LOOKUP, TOK_SOCKARG, + + /* DIFFUSE tokens. */ + TOK_DI_FEATURES, + TOK_DI_UNIDIRECTIONAL, + TOK_DI_FEATURE_MATCH, + TOK_DI_EVERY, + TOK_DI_ONCE, + TOK_DI_ONCE_CLASS, + TOK_DI_ONCE_EXP, + TOK_DI_SAMPLE_REG, + TOK_DI_SAMPLE_RAND, + TOK_DI_CLASS_TAGS, + TOK_DI_ML_CLASSIFY, + TOK_DI_MATCH_IF_CLASS, + TOK_DI_EXPORT, }; /* * the following macro returns an error message if we run out of * arguments. */ #define NEED(_p, msg) {if (!_p) errx(EX_USAGE, msg);} #define NEED1(msg) {if (!(*av)) errx(EX_USAGE, msg);} int pr_u64(uint64_t *pd, int width); diff -r c7d5be203cfa sbin/ipfw/main.c --- a/sbin/ipfw/main.c Sat Oct 08 16:34:15 2011 +1100 +++ b/sbin/ipfw/main.c Tue Oct 11 18:22:08 2011 +1100 @@ -13,33 +13,47 @@ * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * Command line interface for IP firewall facility * * $FreeBSD$ */ +#include +#include #include + +#include + +#include +#include +#include +#include +#include /* Must come after ip_fw.h */ + #include #include #include #include #include #include #include #include #include +#include "diffuse_ui.h" #include "ipfw2.h" +/* XXX: Extend synopsis. */ + static void help(void) { fprintf(stderr, "ipfw syntax summary (but please do read the ipfw(8) manpage):\n\n" "\tipfw [-abcdefhnNqStTv] \n\n" "where is one of the following:\n\n" "add [num] [set N] [prob x] RULE-BODY\n" "{pipe|queue} N config PIPE-BODY\n" "[pipe|queue] {zero|delete|show} [N{,N}]\n" @@ -354,94 +368,118 @@ ipfw_main(int oldac, char **oldav) av[0] = av[1]; av[1] = p; } /* * Optional: pipe, queue or nat. */ co.do_nat = 0; co.do_pipe = 0; co.use_set = 0; + co.do_diffuse = 0; if (!strncmp(*av, "nat", strlen(*av))) co.do_nat = 1; else if (!strncmp(*av, "pipe", strlen(*av))) co.do_pipe = 1; else if (_substrcmp(*av, "queue") == 0) co.do_pipe = 2; else if (_substrcmp(*av, "flowset") == 0) co.do_pipe = 2; else if (_substrcmp(*av, "sched") == 0) co.do_pipe = 3; else if (!strncmp(*av, "set", strlen(*av))) { if (ac > 1 && isdigit(av[1][0])) { co.use_set = strtonum(av[1], 0, resvd_set_number, &errstr); if (errstr) errx(EX_DATAERR, "invalid set number %s\n", av[1]); ac -= 2; av += 2; co.use_set++; } } + else if (!strncmp(*av, "feature", strlen(*av))) + co.do_diffuse = DI_FEATURE; + else if (!strncmp(*av, "mlclass", strlen(*av))) + co.do_diffuse = DI_CLASSIFIER; + else if (!strncmp(*av, "export", strlen(*av))) + co.do_diffuse = DI_EXPORT; + else if (!strncmp(*av, "flowtable", strlen(*av))) + co.do_diffuse = DI_FLOW_TABLE; - if (co.do_pipe || co.do_nat) { + if (co.do_pipe || co.do_nat || co.do_diffuse) { ac--; av++; } NEED1("missing command"); /* * For pipes, queues and nats we normally say 'nat|pipe NN config' * but the code is easier to parse as 'nat|pipe config NN' * so we swap the two arguments. + * Same syntax for DIFFUSE, make sure we only swap if config. */ - if ((co.do_pipe || co.do_nat) && ac > 1 && isdigit(*av[0])) { + if (((co.do_pipe || co.do_nat) && ac > 1 && isdigit(*av[0])) || + (co.do_diffuse && ac > 1 && !_substrcmp(av[1], "config"))) { char *p = av[0]; av[0] = av[1]; av[1] = p; } + if (co.do_diffuse) + diffuse_init(); + if (co.use_set == 0) { if (_substrcmp(*av, "add") == 0) ipfw_add(av); else if (co.do_nat && _substrcmp(*av, "show") == 0) ipfw_show_nat(ac, av); else if (co.do_pipe && _substrcmp(*av, "config") == 0) ipfw_config_pipe(ac, av); else if (co.do_nat && _substrcmp(*av, "config") == 0) ipfw_config_nat(ac, av); else if (_substrcmp(*av, "set") == 0) ipfw_sets_handler(av); else if (_substrcmp(*av, "table") == 0) ipfw_table_handler(ac, av); else if (_substrcmp(*av, "enable") == 0) ipfw_sysctl_handler(av, 1); else if (_substrcmp(*av, "disable") == 0) ipfw_sysctl_handler(av, 0); + else if (co.do_diffuse && _substrcmp(*av, "config") == 0) + diffuse_config(ac, av, co.do_diffuse); else try_next = 1; } if (co.use_set || try_next) { - if (_substrcmp(*av, "delete") == 0) + if (!co.do_diffuse && _substrcmp(*av, "delete") == 0) ipfw_delete(av); - else if (_substrcmp(*av, "flush") == 0) + else if (!co.do_diffuse && _substrcmp(*av, "flush") == 0) ipfw_flush(co.do_force); - else if (_substrcmp(*av, "zero") == 0) + else if (!co.do_diffuse && _substrcmp(*av, "zero") == 0) ipfw_zero(ac, av, 0 /* IP_FW_ZERO */); else if (_substrcmp(*av, "resetlog") == 0) ipfw_zero(ac, av, 1 /* IP_FW_RESETLOG */); else if (_substrcmp(*av, "print") == 0 || _substrcmp(*av, "list") == 0) ipfw_list(ac, av, do_acct); - else if (_substrcmp(*av, "show") == 0) + else if (!co.do_diffuse && _substrcmp(*av, "show") == 0) ipfw_list(ac, av, 1 /* show counters */); + else if (co.do_diffuse && _substrcmp(*av, "delete") == 0) + diffuse_delete(ac, av, co.do_diffuse); + else if (co.do_diffuse && _substrcmp(*av, "show") == 0) + diffuse_list(ac, av, co.do_diffuse, 1); + else if (co.do_diffuse == DI_FLOW_TABLE && _substrcmp(*av, "flush") == 0) + diffuse_flush(ac, av, 0); + else if (co.do_diffuse == DI_FLOW_TABLE && _substrcmp(*av, "zero") == 0) + diffuse_flush(ac, av, 1); else errx(EX_USAGE, "bad command `%s'", *av); } /* Free memory allocated in the argument parsing. */ free(save_av); return 0; } @@ -598,23 +636,25 @@ main(int ac, char *av[]) /* Tell the user that we could not find a usable */ /* Winsock DLL. */ printf("WSAStartup failed with error: %d\n", ret); return 1; } } #endif /* * If the last argument is an absolute pathname, interpret it * as a file to be preprocessed. + * Make sure we don't mistake a DIFFUSE model for such a file. */ - if (ac > 1 && av[ac - 1][0] == '/') { + if (ac > 1 && av[ac - 1][0] == '/' + && (ac < 3 || strcmp(av[ac - 2], "model"))) { if (access(av[ac - 1], R_OK) == 0) ipfw_readfile(ac, av); else err(EX_USAGE, "pathname: %s", av[ac - 1]); } else { if (ipfw_main(ac, av)) { errx(EX_USAGE, "usage: ipfw [options]\n" "do \"ipfw -h\" or \"man ipfw\" for details"); }