|
@@ -0,0 +1,377 @@
|
|
|
+/*
|
|
|
+ * Copyright 2002 Damien Miller <djm@mindrot.org> All rights reserved.
|
|
|
+ *
|
|
|
+ * 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 ``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 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.
|
|
|
+ */
|
|
|
+
|
|
|
+/* $Id: netflow9.c,v 1.9 2006/03/16 08:23:13 djm Exp $ */
|
|
|
+
|
|
|
+#include "common.h"
|
|
|
+#include "log.h"
|
|
|
+#include "treetype.h"
|
|
|
+#include "softflowd.h"
|
|
|
+
|
|
|
+RCSID("$Id: netflow9.c,v 1.9 2006/03/16 08:23:13 djm Exp $");
|
|
|
+
|
|
|
+/* Netflow v.9 */
|
|
|
+struct NF9_HEADER {
|
|
|
+ u_int16_t version, flows;
|
|
|
+ u_int32_t uptime_ms, time_sec;
|
|
|
+ u_int32_t package_sequence, source_id;
|
|
|
+} __packed;
|
|
|
+struct NF9_FLOWSET_HEADER_COMMON {
|
|
|
+ u_int16_t flowset_id, length;
|
|
|
+} __packed;
|
|
|
+struct NF9_TEMPLATE_FLOWSET_HEADER {
|
|
|
+ struct NF9_FLOWSET_HEADER_COMMON c;
|
|
|
+ u_int16_t template_id, count;
|
|
|
+} __packed;
|
|
|
+struct NF9_TEMPLATE_FLOWSET_RECORD {
|
|
|
+ u_int16_t type, length;
|
|
|
+} __packed;
|
|
|
+struct NF9_DATA_FLOWSET_HEADER {
|
|
|
+ struct NF9_FLOWSET_HEADER_COMMON c;
|
|
|
+} __packed;
|
|
|
+#define NF9_TEMPLATE_FLOWSET_ID 0
|
|
|
+#define NF9_OPTIONS_FLOWSET_ID 1
|
|
|
+#define NF9_MIN_RECORD_FLOWSET_ID 256
|
|
|
+
|
|
|
+/* Flowset record types the we care about */
|
|
|
+#define NF9_IN_BYTES 1
|
|
|
+#define NF9_IN_PACKETS 2
|
|
|
+/* ... */
|
|
|
+#define NF9_IN_PROTOCOL 4
|
|
|
+/* ... */
|
|
|
+#define NF9_TCP_FLAGS 6
|
|
|
+#define NF9_L4_SRC_PORT 7
|
|
|
+#define NF9_IPV4_SRC_ADDR 8
|
|
|
+/* ... */
|
|
|
+#define NF9_L4_DST_PORT 11
|
|
|
+#define NF9_IPV4_DST_ADDR 12
|
|
|
+/* ... */
|
|
|
+#define NF9_LAST_SWITCHED 21
|
|
|
+#define NF9_FIRST_SWITCHED 22
|
|
|
+/* ... */
|
|
|
+#define NF9_IPV6_SRC_ADDR 27
|
|
|
+#define NF9_IPV6_DST_ADDR 28
|
|
|
+/* ... */
|
|
|
+#define NF9_IP_PROTOCOL_VERSION 60
|
|
|
+
|
|
|
+/* Stuff pertaining to the templates that softflowd uses */
|
|
|
+#define NF9_SOFTFLOWD_TEMPLATE_NRECORDS 11
|
|
|
+struct NF9_SOFTFLOWD_TEMPLATE {
|
|
|
+ struct NF9_TEMPLATE_FLOWSET_HEADER h;
|
|
|
+ struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_TEMPLATE_NRECORDS];
|
|
|
+} __packed;
|
|
|
+
|
|
|
+/* softflowd data flowset types */
|
|
|
+struct NF9_SOFTFLOWD_DATA_COMMON {
|
|
|
+ u_int32_t first_switched, last_switched;
|
|
|
+ u_int32_t bytes, packets;
|
|
|
+ u_int16_t src_port, dst_port;
|
|
|
+ u_int8_t protocol, tcp_flags, ipproto;
|
|
|
+} __packed;
|
|
|
+
|
|
|
+struct NF9_SOFTFLOWD_DATA_V4 {
|
|
|
+ u_int32_t src_addr, dst_addr;
|
|
|
+ struct NF9_SOFTFLOWD_DATA_COMMON c;
|
|
|
+} __packed;
|
|
|
+
|
|
|
+struct NF9_SOFTFLOWD_DATA_V6 {
|
|
|
+ u_int8_t src_addr[16], dst_addr[16];
|
|
|
+ struct NF9_SOFTFLOWD_DATA_COMMON c;
|
|
|
+} __packed;
|
|
|
+
|
|
|
+/* Local data: templates and counters */
|
|
|
+#define NF9_SOFTFLOWD_MAX_PACKET_SIZE 512
|
|
|
+#define NF9_SOFTFLOWD_V4_TEMPLATE_ID 1024
|
|
|
+#define NF9_SOFTFLOWD_V6_TEMPLATE_ID 2048
|
|
|
+
|
|
|
+#define NF9_DEFAULT_TEMPLATE_INTERVAL 16
|
|
|
+
|
|
|
+static struct NF9_SOFTFLOWD_TEMPLATE v4_template;
|
|
|
+static struct NF9_SOFTFLOWD_TEMPLATE v6_template;
|
|
|
+static int nf9_pkts_until_template = -1;
|
|
|
+
|
|
|
+static void
|
|
|
+nf9_init_template(void)
|
|
|
+{
|
|
|
+ bzero(&v4_template, sizeof(v4_template));
|
|
|
+ v4_template.h.c.flowset_id = htons(0);
|
|
|
+ v4_template.h.c.length = htons(sizeof(v4_template));
|
|
|
+ v4_template.h.template_id = htons(NF9_SOFTFLOWD_V4_TEMPLATE_ID);
|
|
|
+ v4_template.h.count = htons(NF9_SOFTFLOWD_TEMPLATE_NRECORDS);
|
|
|
+ v4_template.r[0].type = htons(NF9_IPV4_SRC_ADDR);
|
|
|
+ v4_template.r[0].length = htons(4);
|
|
|
+ v4_template.r[1].type = htons(NF9_IPV4_DST_ADDR);
|
|
|
+ v4_template.r[1].length = htons(4);
|
|
|
+ v4_template.r[2].type = htons(NF9_LAST_SWITCHED);
|
|
|
+ v4_template.r[2].length = htons(4);
|
|
|
+ v4_template.r[3].type = htons(NF9_FIRST_SWITCHED);
|
|
|
+ v4_template.r[3].length = htons(4);
|
|
|
+ v4_template.r[4].type = htons(NF9_IN_BYTES);
|
|
|
+ v4_template.r[4].length = htons(4);
|
|
|
+ v4_template.r[5].type = htons(NF9_IN_PACKETS);
|
|
|
+ v4_template.r[5].length = htons(4);
|
|
|
+ v4_template.r[6].type = htons(NF9_L4_SRC_PORT);
|
|
|
+ v4_template.r[6].length = htons(2);
|
|
|
+ v4_template.r[7].type = htons(NF9_L4_DST_PORT);
|
|
|
+ v4_template.r[7].length = htons(2);
|
|
|
+ v4_template.r[8].type = htons(NF9_IN_PROTOCOL);
|
|
|
+ v4_template.r[8].length = htons(1);
|
|
|
+ v4_template.r[9].type = htons(NF9_TCP_FLAGS);
|
|
|
+ v4_template.r[9].length = htons(1);
|
|
|
+ v4_template.r[10].type = htons(NF9_IP_PROTOCOL_VERSION);
|
|
|
+ v4_template.r[10].length = htons(1);
|
|
|
+
|
|
|
+ bzero(&v6_template, sizeof(v6_template));
|
|
|
+ v6_template.h.c.flowset_id = htons(0);
|
|
|
+ v6_template.h.c.length = htons(sizeof(v6_template));
|
|
|
+ v6_template.h.template_id = htons(NF9_SOFTFLOWD_V6_TEMPLATE_ID);
|
|
|
+ v6_template.h.count = htons(NF9_SOFTFLOWD_TEMPLATE_NRECORDS);
|
|
|
+ v6_template.r[0].type = htons(NF9_IPV6_SRC_ADDR);
|
|
|
+ v6_template.r[0].length = htons(16);
|
|
|
+ v6_template.r[1].type = htons(NF9_IPV6_DST_ADDR);
|
|
|
+ v6_template.r[1].length = htons(16);
|
|
|
+ v6_template.r[2].type = htons(NF9_LAST_SWITCHED);
|
|
|
+ v6_template.r[2].length = htons(4);
|
|
|
+ v6_template.r[3].type = htons(NF9_FIRST_SWITCHED);
|
|
|
+ v6_template.r[3].length = htons(4);
|
|
|
+ v6_template.r[4].type = htons(NF9_IN_BYTES);
|
|
|
+ v6_template.r[4].length = htons(4);
|
|
|
+ v6_template.r[5].type = htons(NF9_IN_PACKETS);
|
|
|
+ v6_template.r[5].length = htons(4);
|
|
|
+ v6_template.r[6].type = htons(NF9_L4_SRC_PORT);
|
|
|
+ v6_template.r[6].length = htons(2);
|
|
|
+ v6_template.r[7].type = htons(NF9_L4_DST_PORT);
|
|
|
+ v6_template.r[7].length = htons(2);
|
|
|
+ v6_template.r[8].type = htons(NF9_IN_PROTOCOL);
|
|
|
+ v6_template.r[8].length = htons(1);
|
|
|
+ v6_template.r[9].type = htons(NF9_TCP_FLAGS);
|
|
|
+ v6_template.r[9].length = htons(1);
|
|
|
+ v6_template.r[10].type = htons(NF9_IP_PROTOCOL_VERSION);
|
|
|
+ v6_template.r[10].length = htons(1);
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+nf_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len,
|
|
|
+ const struct timeval *system_boot_time, u_int *len_used)
|
|
|
+{
|
|
|
+ union {
|
|
|
+ struct NF9_SOFTFLOWD_DATA_V4 d4;
|
|
|
+ struct NF9_SOFTFLOWD_DATA_V6 d6;
|
|
|
+ } d[2];
|
|
|
+ struct NF9_SOFTFLOWD_DATA_COMMON *dc[2];
|
|
|
+ u_int freclen, ret_len, nflows;
|
|
|
+
|
|
|
+ bzero(d, sizeof(d));
|
|
|
+ *len_used = nflows = ret_len = 0;
|
|
|
+ switch (flow->af) {
|
|
|
+ case AF_INET:
|
|
|
+ freclen = sizeof(struct NF9_SOFTFLOWD_DATA_V4);
|
|
|
+ memcpy(&d[0].d4.src_addr, &flow->addr[0].v4, 4);
|
|
|
+ memcpy(&d[0].d4.dst_addr, &flow->addr[1].v4, 4);
|
|
|
+ memcpy(&d[1].d4.src_addr, &flow->addr[1].v4, 4);
|
|
|
+ memcpy(&d[1].d4.dst_addr, &flow->addr[0].v4, 4);
|
|
|
+ dc[0] = &d[0].d4.c;
|
|
|
+ dc[1] = &d[1].d4.c;
|
|
|
+ dc[0]->ipproto = dc[1]->ipproto = 4;
|
|
|
+ break;
|
|
|
+ case AF_INET6:
|
|
|
+ freclen = sizeof(struct NF9_SOFTFLOWD_DATA_V6);
|
|
|
+ memcpy(&d[0].d6.src_addr, &flow->addr[0].v6, 16);
|
|
|
+ memcpy(&d[0].d6.dst_addr, &flow->addr[1].v6, 16);
|
|
|
+ memcpy(&d[1].d6.src_addr, &flow->addr[1].v6, 16);
|
|
|
+ memcpy(&d[1].d6.dst_addr, &flow->addr[0].v6, 16);
|
|
|
+ dc[0] = &d[0].d6.c;
|
|
|
+ dc[1] = &d[1].d6.c;
|
|
|
+ dc[0]->ipproto = dc[1]->ipproto = 6;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return (-1);
|
|
|
+ }
|
|
|
+
|
|
|
+ dc[0]->first_switched = dc[1]->first_switched =
|
|
|
+ htonl(timeval_sub_ms(&flow->flow_start, system_boot_time));
|
|
|
+ dc[0]->last_switched = dc[1]->last_switched =
|
|
|
+ htonl(timeval_sub_ms(&flow->flow_last, system_boot_time));
|
|
|
+ dc[0]->bytes = htonl(flow->octets[0]);
|
|
|
+ dc[1]->bytes = htonl(flow->octets[1]);
|
|
|
+ dc[0]->packets = htonl(flow->packets[0]);
|
|
|
+ dc[1]->packets = htonl(flow->packets[1]);
|
|
|
+ dc[0]->src_port = dc[1]->dst_port = flow->port[0];
|
|
|
+ dc[1]->src_port = dc[0]->dst_port = flow->port[1];
|
|
|
+ dc[0]->protocol = dc[1]->protocol = flow->protocol;
|
|
|
+ dc[0]->tcp_flags = flow->tcp_flags[0];
|
|
|
+ dc[1]->tcp_flags = flow->tcp_flags[1];
|
|
|
+
|
|
|
+ if (flow->octets[0] > 0) {
|
|
|
+ if (ret_len + freclen > len)
|
|
|
+ return (-1);
|
|
|
+ memcpy(packet + ret_len, &d[0], freclen);
|
|
|
+ ret_len += freclen;
|
|
|
+ nflows++;
|
|
|
+ }
|
|
|
+ if (flow->octets[1] > 0) {
|
|
|
+ if (ret_len + freclen > len)
|
|
|
+ return (-1);
|
|
|
+ memcpy(packet + ret_len, &d[1], freclen);
|
|
|
+ ret_len += freclen;
|
|
|
+ nflows++;
|
|
|
+ }
|
|
|
+
|
|
|
+ *len_used = ret_len;
|
|
|
+ return (nflows);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Given an array of expired flows, send netflow v9 report packets
|
|
|
+ * Returns number of packets sent or -1 on error
|
|
|
+ */
|
|
|
+int
|
|
|
+send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock,
|
|
|
+ u_int64_t *flows_exported, struct timeval *system_boot_time,
|
|
|
+ int verbose_flag)
|
|
|
+{
|
|
|
+ struct NF9_HEADER *nf9;
|
|
|
+ struct NF9_DATA_FLOWSET_HEADER *dh;
|
|
|
+ struct timeval now;
|
|
|
+ u_int offset, last_af, i, j, num_packets, inc, last_valid;
|
|
|
+ socklen_t errsz;
|
|
|
+ int err, r;
|
|
|
+ u_char packet[NF9_SOFTFLOWD_MAX_PACKET_SIZE];
|
|
|
+
|
|
|
+ gettimeofday(&now, NULL);
|
|
|
+
|
|
|
+ if (nf9_pkts_until_template == -1) {
|
|
|
+ nf9_init_template();
|
|
|
+ nf9_pkts_until_template = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ last_valid = num_packets = 0;
|
|
|
+ for (j = 0; j < num_flows;) {
|
|
|
+ bzero(packet, sizeof(packet));
|
|
|
+ nf9 = (struct NF9_HEADER *)packet;
|
|
|
+
|
|
|
+ nf9->version = htons(9);
|
|
|
+ nf9->flows = 0; /* Filled as we go, htons at end */
|
|
|
+ nf9->uptime_ms = htonl(timeval_sub_ms(&now, system_boot_time));
|
|
|
+ nf9->time_sec = htonl(time(NULL));
|
|
|
+ nf9->package_sequence = htonl(*flows_exported + j);
|
|
|
+ nf9->source_id = 0;
|
|
|
+ offset = sizeof(*nf9);
|
|
|
+
|
|
|
+ /* Refresh template headers if we need to */
|
|
|
+ if (nf9_pkts_until_template <= 0) {
|
|
|
+ memcpy(packet + offset, &v4_template,
|
|
|
+ sizeof(v4_template));
|
|
|
+ offset += sizeof(v4_template);
|
|
|
+ memcpy(packet + offset, &v6_template,
|
|
|
+ sizeof(v6_template));
|
|
|
+ offset += sizeof(v6_template);
|
|
|
+ nf9_pkts_until_template = NF9_DEFAULT_TEMPLATE_INTERVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ dh = NULL;
|
|
|
+ last_af = 0;
|
|
|
+ for (i = 0; i + j < num_flows; i++) {
|
|
|
+ if (dh == NULL || flows[i + j]->af != last_af) {
|
|
|
+ if (dh != NULL) {
|
|
|
+ if (offset % 4 != 0) {
|
|
|
+ /* Pad to multiple of 4 */
|
|
|
+ dh->c.length += 4 - (offset % 4);
|
|
|
+ offset += 4 - (offset % 4);
|
|
|
+ }
|
|
|
+ /* Finalise last header */
|
|
|
+ dh->c.length = htons(dh->c.length);
|
|
|
+ }
|
|
|
+ if (offset + sizeof(*dh) > sizeof(packet)) {
|
|
|
+ /* Mark header is finished */
|
|
|
+ dh = NULL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ dh = (struct NF9_DATA_FLOWSET_HEADER *)
|
|
|
+ (packet + offset);
|
|
|
+ dh->c.flowset_id =
|
|
|
+ (flows[i + j]->af == AF_INET) ?
|
|
|
+ v4_template.h.template_id :
|
|
|
+ v6_template.h.template_id;
|
|
|
+ last_af = flows[i + j]->af;
|
|
|
+ last_valid = offset;
|
|
|
+ dh->c.length = sizeof(*dh); /* Filled as we go */
|
|
|
+ offset += sizeof(*dh);
|
|
|
+ }
|
|
|
+
|
|
|
+ r = nf_flow_to_flowset(flows[i + j], packet + offset,
|
|
|
+ sizeof(packet) - offset, system_boot_time, &inc);
|
|
|
+ if (r <= 0) {
|
|
|
+ /* yank off data header, if we had to go back */
|
|
|
+ if (last_valid)
|
|
|
+ offset = last_valid;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ offset += inc;
|
|
|
+ dh->c.length += inc;
|
|
|
+ nf9->flows += r;
|
|
|
+ last_valid = 0; /* Don't clobber this header now */
|
|
|
+ if (verbose_flag) {
|
|
|
+ logit(LOG_DEBUG, "Flow %d/%d: "
|
|
|
+ "r %d offset %d type %04x len %d(0x%04x) "
|
|
|
+ "flows %d", r, i, j, offset,
|
|
|
+ dh->c.flowset_id, dh->c.length,
|
|
|
+ dh->c.length, nf9->flows);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* Don't finish header if it has already been done */
|
|
|
+ if (dh != NULL) {
|
|
|
+ if (offset % 4 != 0) {
|
|
|
+ /* Pad to multiple of 4 */
|
|
|
+ dh->c.length += 4 - (offset % 4);
|
|
|
+ offset += 4 - (offset % 4);
|
|
|
+ }
|
|
|
+ /* Finalise last header */
|
|
|
+ dh->c.length = htons(dh->c.length);
|
|
|
+ }
|
|
|
+ nf9->flows = htons(nf9->flows);
|
|
|
+
|
|
|
+ if (verbose_flag)
|
|
|
+ logit(LOG_DEBUG, "Sending flow packet len = %d", offset);
|
|
|
+ errsz = sizeof(err);
|
|
|
+ /* Clear ICMP errors */
|
|
|
+ getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz);
|
|
|
+ if (send(nfsock, packet, (size_t)offset, 0) == -1)
|
|
|
+ return (-1);
|
|
|
+ num_packets++;
|
|
|
+ nf9_pkts_until_template--;
|
|
|
+
|
|
|
+ j += i;
|
|
|
+ }
|
|
|
+
|
|
|
+ *flows_exported += j;
|
|
|
+ return (num_packets);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+netflow9_resend_template(void)
|
|
|
+{
|
|
|
+ if (nf9_pkts_until_template > 0)
|
|
|
+ nf9_pkts_until_template = 0;
|
|
|
+}
|