--- Add the diffuse_exporter sbin program, which can be used to receive export --- data from the DIFFUSE kernel module via UDP and propagate it out to one or --- more action nodes using UDP, TCP or SCTP. --- --- Sponsored by: FreeBSD Foundation --- Reviewed by: bz --- MFC after: 1 month --- diff -r ecebbec72578 sbin/ipfw/Makefile --- a/sbin/ipfw/Makefile Sun Oct 30 13:40:47 2011 +1100 +++ b/sbin/ipfw/Makefile Thu Nov 03 10:52:49 2011 +1100 @@ -13,4 +13,6 @@ LDADD= -lutil -lm MAN= ipfw.8 +SUBDIR= diffuse_exporter + .include diff -r ecebbec72578 sbin/ipfw/diffuse_exporter/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_exporter/Makefile Thu Nov 03 10:52:49 2011 +1100 @@ -0,0 +1,14 @@ +# $FreeBSD$ + +.include + +.PATH: ${.CURDIR}/.. +PROG= diffuse_exporter +SRCS= diffuse_exporter.c diffuse_proto.c +WARNS?= 2 +DPADD= ${LIBUTIL} +LDADD= -lutil +BINDIR= /sbin +MAN= + +.include diff -r ecebbec72578 sbin/ipfw/diffuse_exporter/diffuse_exporter.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_exporter/diffuse_exporter.c Thu Nov 03 10:52:49 2011 +1100 @@ -0,0 +1,622 @@ +/*- + * 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: + * Rule/flow exporter. + */ + +#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 + +#include "../diffuse_proto.h" +#include "../diffuse_ui.h" + +#define MAX_ERRORS_BEFORE_IGNORE 12 + +#define STRLEN_LITERAL(s) (sizeof((s)) - 1) + +const char *USAGE = "Usage: diffuse_exporter [-hq] [-c ] " + "[-a ,...]"; + +/* + * Could remove templ_id since we also have template, but we save + * a bit of memory since for each action node we only need lists of IDs. + */ +struct templ_id { + uint16_t id; + RB_ENTRY(templ_id) node; +}; + +static inline int +templ_id_compare(struct templ_id *a, struct templ_id *b) +{ + + return ((a->id != b->id) ? (a->id < b->id ? -1 : 1) : 0); +} + +RB_HEAD(templ_id_head, templ_id); +RB_PROTOTYPE(templ_id_head, templ_id, node, templ_id_compare); +RB_GENERATE(templ_id_head, templ_id, node, templ_id_compare); + +/* Represents an action node we will be exporting rules to. */ +struct action_node { + int proto; + uint32_t ip; + uint16_t port; + uint32_t seq_no; + int sock; + int closed; + int errors; + struct templ_id_head templ_ids; + SLIST_ENTRY(action_node) next; +}; + +/* List of action nodes. */ +SLIST_HEAD(action_node_head, action_node) anodes = + SLIST_HEAD_INITIALIZER(action_node_head); + +/* Template list, templates received from classifier. */ +RB_GENERATE(di_template_head, di_template, node, template_compare); +struct di_template_head templ_list; + +/* Global exit flag. */ +int stop; + +/* Sets the flag to terminate the main loop on receipt of a signal. */ +void +sigint_handler(int i) +{ + + stop = 1; +} + +/* Parse URL for a single action node. */ +void +parse_anode(char *s, struct action_node *an) +{ + struct hostent *h; + char *errptr, *ip, *p; + + p = strstr(s, "://"); + if (p == NULL) { + errx(EX_USAGE, + "target must be specified as ://:"); + } + + /* Parse protocol. */ + if (!strncmp(s, "udp", STRLEN_LITERAL("udp"))) + an->proto = IPPROTO_UDP; + else if (!strncmp(s, "tcp", STRLEN_LITERAL("tcp"))) + an->proto = IPPROTO_TCP; + else if (!strncmp(s, "sctp", STRLEN_LITERAL("sctp"))) + an->proto = IPPROTO_SCTP; + else + errx(EX_USAGE, "only udp/tcp/sctp are supported"); + + ip = p + 3; + p = strstr(ip, ":"); + if (p != NULL) { + *p = '\0'; + p++; + an->port = strtonum(p, 1, 65535, (const char **)&errptr); + if (errptr != NULL) + errx(EX_USAGE, "error target port '%s': %s", p, errptr); + } else { + an->port = DI_EXP_DEFAULT_PORT; + } + + /* Parse IP. */ + if ((h = gethostbyname(ip)) == NULL) + errx(EX_DATAERR, "can't resolve target address"); + + if (h->h_addrtype != AF_INET || h->h_length != sizeof(struct in_addr)) + errx(EX_DATAERR, "only IPv4 supported for now"); + + an->ip = ((struct in_addr *)h->h_addr_list[0])->s_addr; + an->sock = -1; + an->closed = 0; + an->errors = 0; + an->seq_no = 0; + RB_INIT(&an->templ_ids); +} + +void +parse_anodes(char *optarg) +{ + struct action_node *tmp_anode; + char *anode_str, *x; + char *sep = ","; + + x = strdup(optarg); + + for (anode_str = strtok(x, sep); anode_str; + anode_str = strtok(NULL, sep)) { + printf("%s\n", anode_str); + tmp_anode = malloc(sizeof(struct action_node)); + if (tmp_anode == NULL) + err(EX_OSERR, NULL); + parse_anode(anode_str, tmp_anode); + SLIST_INSERT_HEAD(&anodes, tmp_anode, next); + } + free(x); +} + +void +parse_class(char *optarg, uint32_t *class_ip, uint16_t *class_port) +{ + struct hostent *h; + char *errptr, *p; + + p = strstr(optarg, ":"); + + if (p != NULL) { + *p = '\0'; + p++; + *class_port = strtonum(p, 1, 65535, (const char **)&errptr); + if (errptr) + errx(EX_USAGE, "parse error port '%s': %s", p, errptr); + } + + h = gethostbyname(optarg); + if (h != NULL) { + if (h->h_addrtype != AF_INET || + h->h_length != sizeof(struct in_addr)) { + errx(EX_USAGE, "only IPv4 supported for now"); + } + *class_ip = ((struct in_addr *)h->h_addr_list[0])->s_addr; + } else { + *class_ip = 0; + } +} + +/* Close socket for action node. */ +void +close_anode_socket(struct action_node *an) +{ + struct sctp_sndrcvinfo sinfo; + + if (an->proto == IPPROTO_SCTP) { + sinfo.sinfo_flags = SCTP_EOF; + sctp_send(an->sock, NULL, 0, &sinfo, 0); + } + + close(an->sock); +} + +/* Open socket for action node. */ +int +open_anode_socket(struct action_node *an) +{ + struct sctp_initmsg initmsg; + struct sctp_status status; + struct sockaddr_in sin; + int type; + + if (an->proto == IPPROTO_UDP) + type = SOCK_DGRAM; + else + type = SOCK_STREAM; + + if ((an->sock = socket(AF_INET, type, an->proto)) < 0) { + errx(EX_OSERR, "create action node socket: %s", + strerror(errno)); + } + + if (an->proto == IPPROTO_SCTP) { + /* Must have two streams. */ + memset(&initmsg, 0, sizeof(initmsg)); + initmsg.sinit_max_instreams = 2; + initmsg.sinit_num_ostreams = 2; + + if (setsockopt(an->sock, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, + sizeof(initmsg))) { + errx(EX_OSERR, "set sock option initmsg"); + } + } + + if (an->proto != IPPROTO_UDP) { + memset(&sin, 0, sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = htons(an->port); + sin.sin_addr.s_addr = an->ip; + + if (connect(an->sock, (struct sockaddr *)&sin, + sizeof(sin)) < 0) { + errx(EX_OSERR, "connect action node socket: %s", + strerror(errno)); + } + } + + if (an->proto == IPPROTO_SCTP) { + socklen_t len; + memset(&status, 0, sizeof(status)); + len = sizeof(status); + + if (getsockopt(an->sock, IPPROTO_SCTP, SCTP_STATUS, &status, + &len) == -1) { + errx(EX_OSERR,"get sock option status: %s", + strerror(errno)); + } + if (status.sstat_instrms < 2 || status.sstat_outstrms < 2) + errx(EX_OSERR,"can't get two streams"); + } + + /* XXX: Use one-to-many association for SCTP. */ + + return (an->sock); +} + +/* Forward message to action node. */ +int +fwd_anode(struct action_node *an, char *buf, int len) +{ + struct dip_header *hdr; + struct dip_set_header *shdr; + struct dip_templ_header *thdr; + struct templ_id *r, s; + struct sockaddr_in sin; + struct sctp_sndrcvinfo sinfo; + char *databuf, *dstbuf, *templbuf; + char buf2[len * 2]; + int databuf_index, offs, ret, templbuf_index; + + hdr = (struct dip_header *)buf; + ret = 0; + + if (an->closed || an->errors > MAX_ERRORS_BEFORE_IGNORE) + return (0); + + /* + * XXX: We don't do PMTUD yet so if len > MTU of outgoing interface + * (likely in the common case where diffuse_exporter runs on classifier + * node and kernel sends packets via loopback which has a default MTU of + * 16384), we will fragment the outgoing datagrams. + */ + if (an->proto == IPPROTO_UDP) { + hdr->seq_no = htonl(an->seq_no++); + sin.sin_family = AF_INET; + sin.sin_port = htons(an->port); + sin.sin_addr.s_addr = an->ip; + + /* Forward unchanged except for seq no. */ + ret = sendto(an->sock, buf, len, 0, (struct sockaddr *)&sin, + sizeof(sin)); + } else { + /* TCP or SCTP. */ + templbuf = buf2; + databuf = buf2 + (sizeof(buf2) / 2); + + /* + * For TCP, we lump the templates and data all into a single + * buffer (buf2). For SCTP, we split the templates and data so that + * they can be sent over separate SCTP streams. + */ + if (an->proto == IPPROTO_TCP) { + memcpy(buf2, buf, sizeof(struct dip_header)); + databuf_index = sizeof(struct dip_header); + templbuf_index = 0; + } else if (an->proto == IPPROTO_SCTP) { + memcpy(templbuf, buf, sizeof(struct dip_header)); + memcpy(databuf, buf, sizeof(struct dip_header)); + templbuf_index = databuf_index = + sizeof(struct dip_header); + } + offs = sizeof(struct dip_header); + + while (offs < ntohs(hdr->msg_len)) { + shdr = (struct dip_set_header *)(buf + offs); + if (ntohs(shdr->set_id) <= DIP_SET_ID_FLOWRULE_TPL) { + /* Template. */ + thdr = (struct dip_templ_header *)(buf + + sizeof(struct dip_set_header)); + s.id = ntohs(thdr->templ_id); + r = RB_FIND(templ_id_head, &an->templ_ids, &s); + if (r == NULL) { + r = malloc(sizeof(struct templ_id)); + if (r == NULL) + continue; /* XXX: Or break? */ + r->id = s.id; + RB_INSERT(templ_id_head, &an->templ_ids, + r); + if (an->proto == IPPROTO_TCP) { + dstbuf = &buf2[templbuf_index + + databuf_index]; + } else if (an->proto == IPPROTO_SCTP) { + dstbuf = + &templbuf[templbuf_index]; + } + + memcpy(dstbuf, &buf[offs], + ntohs(shdr->set_len)); + templbuf_index += ntohs(shdr->set_len); + } + } else { + /* Data. */ + if (an->proto == IPPROTO_TCP) { + dstbuf = &buf2[templbuf_index + + databuf_index]; + } else if (an->proto == IPPROTO_SCTP) { + dstbuf = &databuf[databuf_index]; + } + + memcpy(dstbuf, &buf[offs], + ntohs(shdr->set_len)); + databuf_index += ntohs(shdr->set_len); + } + offs += ntohs(shdr->set_len); + } + + if (an->proto == IPPROTO_TCP) { + hdr = (struct dip_header *)buf2; + hdr->msg_len = htons(templbuf_index + databuf_index); + hdr->seq_no = htonl(an->seq_no++); + ret = send(an->sock, buf2, + templbuf_index + databuf_index, 0); + } else if (an->proto == IPPROTO_SCTP) { + /* Send templates and data over different streams. */ + if (templbuf_index > sizeof(struct dip_header)) { + hdr = (struct dip_header *)templbuf; + hdr->msg_len = htons(templbuf_index); + hdr->seq_no = htonl(an->seq_no++); + bzero(&sinfo, sizeof(sinfo)); + sinfo.sinfo_stream = 0; + ret = sctp_send(an->sock, templbuf, + templbuf_index, &sinfo, 0); + } + + if (!ret && databuf_index > sizeof(struct dip_header)) { + hdr = (struct dip_header *)databuf; + hdr->msg_len = htons(databuf_index); + hdr->seq_no = htonl(an->seq_no++); + bzero(&sinfo, sizeof(sinfo)); + sinfo.sinfo_stream = 1; +#ifdef __FREEBSD__ + /* Use SCTP PR if possible (man sctp_send). */ + sinfo.sinfo_flags |= SCTP_PR_SCTP_TTL; + /* Drop if can't send for this many ms. */ + sinfo.sinfo_timetolive = 200; +#endif + ret = sctp_send(an->sock, databuf, + databuf_index, &sinfo, 0); + } + } + } + + if (ret < 0) + an->errors++; + else + an->errors = 0; + + return (ret); +} + +/* Destroy all action nodes. */ +void +free_anodes() +{ + struct action_node *tmp_anode; + struct templ_id *n, *r; + + while (!SLIST_EMPTY(&anodes)) { + tmp_anode = SLIST_FIRST(&anodes); + SLIST_REMOVE_HEAD(&anodes, next); + close_anode_socket(tmp_anode); + for (r = RB_MIN(templ_id_head, &tmp_anode->templ_ids); r != NULL; + r = n) { + n = RB_NEXT(templ_id_head, &tmp_anode->templ_ids, r); + RB_REMOVE(templ_id_head, &tmp_anode->templ_ids, r); + free(r); + } + free(tmp_anode); + } +} + +/* Free templates received from classifier. */ +void +free_templates() +{ + struct di_template *n, *r; + + for (r = RB_MIN(di_template_head, &templ_list); r != NULL; r = n) { + n = RB_NEXT(di_template_head, &templ_list, r); + RB_REMOVE(di_template_head, &templ_list, r); + free(r); + } +} + +int +main(int argc, char *argv[]) +{ + struct action_node *tmp_anode; + uint32_t class_ip = INADDR_ANY; + uint16_t class_port = DI_EXP_DEFAULT_PORT; + struct sockaddr_in sin; + struct timeval tv; + char buf[IP_MAXPACKET]; + int clsock; + fd_set rset, wset, _rset, _wset; + int cnt, max_fd, n, quiet; + char ch; + + max_fd = quiet = 0; + tv.tv_sec = 1; + tv.tv_usec = 0; + RB_INIT(&templ_list); + + signal(SIGINT, sigint_handler); + signal(SIGTERM, sigint_handler); + + if (argc < 1) { + printf("%s\n", USAGE); + exit(-1); + } + + while ((ch = getopt(argc, argv, "a:c:hq")) != EOF) { + switch (ch) { + case 'a': + parse_anodes(optarg); + break; + + case 'c': + parse_class(optarg, &class_ip, &class_port); + break; + + case 'h': + printf("%s\n", USAGE); + exit(0); + + case 'q': + quiet++; + break; + + default: + printf("%s\n", USAGE); + exit(-1); + } + } + + /* Open a local socket to the kernel classifier. */ + if ((clsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + errx(EX_OSERR, "create class socket: %s", strerror(errno)); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(class_port); + sin.sin_addr.s_addr = class_ip; + + if (bind(clsock, (struct sockaddr *)&sin, sizeof(sin)) < 0) + errx(EX_OSERR, "bind class socket: %s", strerror(errno)); + + printf("listening %s:%d\n", inet_ntoa(*((struct in_addr *)&class_ip)), + class_port); + + /* Open sockets to action nodes. */ + SLIST_FOREACH(tmp_anode, &anodes, next) { + open_anode_socket(tmp_anode); + } + + max_fd = clsock; + SLIST_FOREACH(tmp_anode, &anodes, next) { + if (tmp_anode->sock > max_fd) + max_fd = tmp_anode->sock; + } + + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(clsock, &rset); + + SLIST_FOREACH(tmp_anode, &anodes, next) { + FD_SET(tmp_anode->sock, &rset); + } + + /* Packet processing. */ + do { + _rset = rset; + _wset = wset; + + if ((cnt = select(max_fd + 1, &_rset, &_wset, NULL, &tv)) < 0) { + if (errno != EINTR) + errx(EX_OSERR, "select error"); + } + + if (cnt > 0) { + if (FD_ISSET(clsock, &_rset)) { + n = recv(clsock, &buf, sizeof(buf), 0); + if (n < 0) + errx(EX_OSERR, "class sock read error"); + +#ifdef DIFFUSE_DEBUG2 + printf("message %u\n", n); + for (int j = 0; j < n; j++) + printf("%u ", (uint8_t)buf[j]); + printf("\n"); +#endif + + if (!quiet) { + diffuse_proto_print_msg(buf, + &templ_list); + } + + SLIST_FOREACH(tmp_anode, &anodes, next) { + fwd_anode(tmp_anode, buf, n); + } + } else { + SLIST_FOREACH(tmp_anode, &anodes, + next) { + if (FD_ISSET(tmp_anode->sock, &_rset)) { + if (read(tmp_anode->sock, &buf, + sizeof(buf)) == 0) { + close(tmp_anode->sock); + tmp_anode->closed = 1; + } + /* XXX: handle SCTP events? */ + } + } + } + } + + } while (!stop); + + close(clsock); + free_anodes(); + free_templates(); + + return (0); +} diff -r ecebbec72578 sbin/ipfw/diffuse_proto.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/ipfw/diffuse_proto.c Thu Nov 03 10:52:49 2011 +1100 @@ -0,0 +1,278 @@ +/*- + * 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 for control protocol. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include + +#include + +#include +#include +#include +#define WITH_DIP_INFO 1 +#include + +#include +#include +#include +#include + +#include "diffuse_proto.h" + +/* + * Print field data in val based on info element id and length. + * XXX: IPv6 support missing. + */ +void +print_field(int idx, int id, int len, char *val) +{ + char *c; + struct in_addr a; + + switch(idx) { + case DIP_IE_SRC_IPV4: + case DIP_IE_DST_IPV4: + { + /* XXX: Resolve to name. */ + a.s_addr = *((uint32_t *)val); + printf("%s", inet_ntoa(a)); + break; + } + + case DIP_IE_SRC_PORT: + case DIP_IE_DST_PORT: + /* XXX: Resolve to name. */ + printf("%u", ntohs(*((uint16_t *)val))); + break; + + case DIP_IE_PROTO: + case DIP_IE_MSG_TYPE: + case DIP_IE_TIMEOUT_TYPE: + case DIP_IE_IPV4_TOS: + /* XXX: Resolve to name. */ + printf("%u", *((uint8_t *)val)); + break; + + case DIP_IE_CLASS_LABEL: + case DIP_IE_ACTION_FLAGS: + case DIP_IE_TIMEOUT: + printf("%u", ntohs(*((uint16_t *)val))); + break; + + case DIP_IE_ACTION: + case DIP_IE_CLASSIFIER_NAME: + case DIP_IE_EXPORT_NAME: + case DIP_IE_ACTION_PARAMS: + printf("%s", val); + break; + + case DIP_IE_PCKT_CNT: + case DIP_IE_KBYTE_CNT: + printf("%u", ntohl(*((uint32_t *)val))); + break; + + case DIP_IE_CLASSES: + { + c = val; + while (c < val + len) { + printf("%s:", c); + c += strlen(val) + 1; + printf("%u", ntohs(*((uint16_t *)c))); + c += sizeof(uint16_t); + if (c < val + len) + printf(" "); + } + break; + } + + default: + printf("unknown info element %d\n", id); + break; + } +} + +/* XXX: Not very fast. */ +struct dip_info_descr +diffuse_proto_get_info(uint16_t id) +{ + int i; + + for (i = 0; i < sizeof(dip_info) / sizeof(struct dip_info_descr); i++) { + if (dip_info[i].id == id) + return (dip_info[i]); + } + + return (dip_info[sizeof(dip_info) / sizeof(struct dip_info_descr) - 1]); +} + +void +diffuse_proto_print_msg(char *buf, struct di_template_head *templ_list) +{ + struct di_template s, *r; + struct dip_header *hdr; + struct dip_info_descr info; + struct dip_set_header *shdr; + struct dip_templ_header *thdr; + char time_str[128]; + char *p; + time_t time; + int cnt, dlen, i, offs, toffs; + + hdr = (struct dip_header *)buf; + offs = 0; + time = ntohl(hdr->time); + strcpy(time_str, ctime(&time)); + p = strstr(time_str, "\n"); + if (p != NULL) + *p = '\0'; + + printf("ver %u\n", ntohs(hdr->version)); + printf("len %u\n", ntohs(hdr->msg_len)); + printf("seq %u\n", ntohl(hdr->seq_no)); + printf("time %u (%s)\n", hdr->time, time_str); + offs += sizeof(struct dip_header); + + while (offs < ntohs(hdr->msg_len)) { + shdr = (struct dip_set_header *)(buf + offs); + offs += sizeof(struct dip_set_header); + + printf("set %u len %u\n", ntohs(shdr->set_id), + ntohs(shdr->set_len)); + + if (ntohs(shdr->set_id) <= DIP_SET_ID_FLOWRULE_TPL) { + /* Process template. */ + thdr = (struct dip_templ_header *)(buf + offs); + offs += sizeof(struct dip_templ_header); + + s.id = ntohs(thdr->templ_id); + r = RB_FIND(di_template_head, templ_list, &s); + + if (r == NULL) { + /* Store template. */ + toffs = offs; + r = malloc(sizeof(struct di_template)); + if (r == NULL) + continue; /* XXX: Or break? */ + memset(r, 0, sizeof(struct di_template)); + r->id = s.id; + + while (offs - toffs < ntohs(shdr->set_len) - + sizeof(struct dip_set_header) - + sizeof(struct dip_templ_header)) { + r->fields[r->fcnt].id = + ntohs(*((uint16_t *)(buf + offs))); + offs += sizeof(uint16_t); + info = diffuse_proto_get_info( + r->fields[r->fcnt].id); + r->fields[r->fcnt].idx = info.idx; + r->fields[r->fcnt].len = info.len; + if (r->fields[r->fcnt].len == 0) { + r->fields[r->fcnt].len = + ntohs(*((uint16_t *) + (buf + offs))); + offs += sizeof(uint16_t); + } + r->fcnt++; + } + RB_INSERT(di_template_head, templ_list, r); + } else { + offs += ntohs(shdr->set_len) - + sizeof(struct dip_set_header) - + sizeof(struct dip_templ_header); + } + + for(i = 0; i < r->fcnt; i++) { + printf("%s(%d)", + diffuse_proto_get_info( + r->fields[i].id).name, + r->fields[i].len); + if (i < r->fcnt - 1) + printf(", "); + } + printf("\n"); + } else if (ntohs(shdr->set_id) >= DIP_SET_ID_DATA) { + /* Print data. */ + s.id = ntohs(shdr->set_id); + r = RB_FIND(di_template_head, templ_list, &s); + + if (r == NULL) { + printf("missing template %u!\n", s.id); + offs += ntohs(shdr->set_len) - + sizeof(struct dip_set_header); + } else { + toffs = offs; + cnt = 0; + + while (offs - toffs < ntohs(shdr->set_len) - + sizeof(struct dip_set_header)) { + if (r->fields[cnt].len == -1) { + /* Read dynamic length */ + dlen = + *((unsigned char *) + (buf + offs)); + print_field(r->fields[cnt].idx, + r->fields[cnt].id, dlen - 1, + buf + offs + 1); + offs += dlen; + } else { + print_field(r->fields[cnt].idx, + r->fields[cnt].id, + r->fields[cnt].len, + buf + offs); + offs += r->fields[cnt].len; + } + cnt++; + if (cnt == r->fcnt) { + cnt = 0; + printf("\n"); + } else { + printf(", "); + } + } + } + printf("\n"); + } else { + printf("unknown set type\n"); + offs += ntohs(shdr->set_len); + } + } +} diff -r ecebbec72578 sys/netinet/ip_diffuse_export.h --- a/sys/netinet/ip_diffuse_export.h Sun Oct 30 13:40:47 2011 +1100 +++ b/sys/netinet/ip_diffuse_export.h Thu Nov 03 10:52:49 2011 +1100 @@ -45,6 +45,10 @@ /* Used if querying MTU from routing table fails. */ #define DIP_DEFAULT_MTU 1500 +#define DIP_SET_ID_OPTS_TPL 0 +#define DIP_SET_ID_FLOWRULE_TPL 1 +#define DIP_SET_ID_DATA 256 + enum dip_msg_types { DIP_MSG_ADD = 0, DIP_MSG_REMOVE diff -r ecebbec72578 sys/netinet/ipfw/diffuse_export.c --- a/sys/netinet/ipfw/diffuse_export.c Sun Oct 30 13:40:47 2011 +1100 +++ b/sys/netinet/ipfw/diffuse_export.c Thu Nov 03 10:52:49 2011 +1100 @@ -364,10 +364,6 @@ static void prepare_header(void) { -#define SET_ID_OPTS_TPL 0 -#define SET_ID_FLOWRULE_TPL 1 -#define SET_ID_DATA 256 - struct dip_header *hdr; struct dip_set_header *shdr; struct dip_templ_header *thdr; @@ -393,12 +389,12 @@ offs += sizeof(struct dip_header); shdr = (struct dip_set_header *)(buf + offs); - shdr->set_id = htons((uint16_t)SET_ID_FLOWRULE_TPL); + shdr->set_id = htons((uint16_t)DIP_SET_ID_FLOWRULE_TPL); shdr->set_len = 0; offs += sizeof(struct dip_set_header); thdr = (struct dip_templ_header *)(buf + offs); - thdr->templ_id = htons((uint16_t)SET_ID_DATA); + thdr->templ_id = htons((uint16_t)DIP_SET_ID_DATA); thdr->flags = 0; offs += sizeof(struct dip_templ_header); @@ -422,7 +418,7 @@ shdr->set_len = htons(offs - sizeof(struct dip_header)); def_data_shdr_offs = offs; shdr = (struct dip_set_header *)(buf + offs); - shdr->set_id = htons((uint16_t)SET_ID_DATA); + shdr->set_id = htons((uint16_t)DIP_SET_ID_DATA); shdr->set_len = htons(sizeof(struct dip_set_header)); offs += sizeof(struct dip_set_header);