Subject: ToS field is metered and exported in NetFlow v1,v5,v9 and IPFIX in default settings Origin: softflowd-0.9.9-5-gb4a7a1c Upstream-Author: Hitoshi Irino Date: Sun Dec 23 23:44:10 2012 +0900 -A option is added for exporting absolute time field in IPFIX records to be able to receive with NFDUMP(nfcapd). -P option is added to select export transport protocol from UDP, TCP, and SCTP. (It is not tested yet.) --- a/configure.ac +++ b/configure.ac @@ -52,6 +52,7 @@ [ AC_DEFINE_UNQUOTED([PRIVDROP_CHROOT_DIR], ["${withval}"], [privdrop chroot directory]) ] ) +AC_DEFINE([_BSD_SOURCE], [], [Define BSD SOURCE for Linux]) AC_CHECK_HEADERS(net/bpf.h pcap.h pcap-bpf.h sys/endian.h endian.h) dnl AC_CHECK_HEADERS(netinet/in_systm.h netinet/tcp.h netinet/udp.h) --- a/ipfix.c +++ b/ipfix.c @@ -28,6 +28,11 @@ #include "treetype.h" #include "softflowd.h" +#if defined (HAVE_HTONLL) && !defined (HAVE_HTOBE64) +#define htobe64 htonll +#endif +#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ + /* IPFIX a.k.a. Netflow v.10 */ struct IPFIX_HEADER { u_int16_t version, length; @@ -62,6 +67,7 @@ #define IPFIX_packetDeltaCount 2 /* ... */ #define IPFIX_protocolIdentifier 4 +#define IPFIX_ipClassOfService 5 /* ... */ #define IPFIX_tcpControlBits 6 #define IPFIX_sourceTransportPort 7 @@ -77,12 +83,23 @@ #define IPFIX_flowStartSysUpTime 22 /* ... */ #define IPFIX_sourceIPv6Address 27 -#define IPFIX_destinationIPv6Address 28 +#define IPFIX_destinationIPv6Address 28 +/* ... */ +#define IPFIX_ipVersion 60 /* ... */ -#define IPFIX_ipVersion 60 - #define IPFIX_meteringProcessId 143 +/* ... */ +#define IPFIX_flowStartSeconds 150 +#define IPFIX_flowEndSeconds 151 +#define IPFIX_flowStartMilliSeconds 152 +#define IPFIX_flowEndMilliSeconds 153 +#define IPFIX_flowStartMicroSeconds 154 +#define IPFIX_flowEndMicroSeconds 155 +#define IPFIX_flowStartNanoSeconds 156 +#define IPFIX_flowEndNanoSeconds 157 +/* ... */ #define IPFIX_systemInitTimeMilliseconds 160 +/* ... */ #define PSAMP_selectorAlgorithm 304 #define PSAMP_samplingPacketInterval 305 #define PSAMP_samplingPacketSpace 306 @@ -90,7 +107,7 @@ #define PSAMP_selectorAlgorithm_count 1 /* Stuff pertaining to the templates that softflowd uses */ -#define IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS 13 +#define IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS 14 struct IPFIX_SOFTFLOWD_TEMPLATE { struct IPFIX_TEMPLATE_SET_HEADER h; struct IPFIX_FIELD_SPECIFIER r[IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS]; @@ -106,22 +123,35 @@ /* softflowd data set */ struct IPFIX_SOFTFLOWD_DATA_COMMON { - u_int32_t flowEndSysUpTime, flowStartSysUpTime; u_int32_t octetDeltaCount, packetDeltaCount; u_int32_t ingressInterface, egressInterface; u_int16_t sourceTransportPort, destinationTransportPort; - u_int8_t protocolIdentifier, tcpControlBits, ipVersion; + u_int8_t protocolIdentifier, tcpControlBits, ipVersion, ipClassOfService; + //u_int32_t flowEndSysUpTime, flowStartSysUpTime; } __packed; +union IPFIX_SOFTFLOWD_DATA_TIME { + struct { + u_int32_t start; + u_int32_t end; + } u32; + struct { + u_int64_t start; + u_int64_t end; + } u64; +}; + struct IPFIX_SOFTFLOWD_DATA_V4 { u_int32_t sourceIPv4Address, destinationIPv4Address; struct IPFIX_SOFTFLOWD_DATA_COMMON c; + union IPFIX_SOFTFLOWD_DATA_TIME t; } __packed; struct IPFIX_SOFTFLOWD_DATA_V6 { //u_int8_t src_addr[16], dst_addr[16]; struct in6_addr sourceIPv6Address, destinationIPv6Address; struct IPFIX_SOFTFLOWD_DATA_COMMON c; + union IPFIX_SOFTFLOWD_DATA_TIME t; } __packed; struct IPFIX_SOFTFLOWD_OPTION_DATA { @@ -159,7 +189,7 @@ static int ipfix_pkts_until_template = -1; static void -ipfix_init_template(void) +ipfix_init_template(struct FLOWTRACKPARAMETERS *param) { bzero(&v4_template, sizeof(v4_template)); v4_template.h.c.set_id = htons(IPFIX_TEMPLATE_SET_ID); @@ -170,28 +200,52 @@ v4_template.r[0].length = htons(4); v4_template.r[1].ie = htons(IPFIX_destinationIPv4Address); v4_template.r[1].length = htons(4); - v4_template.r[2].ie = htons(IPFIX_flowEndSysUpTime); + v4_template.r[2].ie = htons(IPFIX_octetDeltaCount); v4_template.r[2].length = htons(4); - v4_template.r[3].ie = htons(IPFIX_flowStartSysUpTime); + v4_template.r[3].ie = htons(IPFIX_packetDeltaCount); v4_template.r[3].length = htons(4); - v4_template.r[4].ie = htons(IPFIX_octetDeltaCount); + v4_template.r[4].ie = htons(IPFIX_ingressInterface); v4_template.r[4].length = htons(4); - v4_template.r[5].ie = htons(IPFIX_packetDeltaCount); + v4_template.r[5].ie = htons(IPFIX_egressInterface); v4_template.r[5].length = htons(4); - v4_template.r[6].ie = htons(IPFIX_ingressInterface); - v4_template.r[6].length = htons(4); - v4_template.r[7].ie = htons(IPFIX_egressInterface); - v4_template.r[7].length = htons(4); - v4_template.r[8].ie = htons(IPFIX_sourceTransportPort); - v4_template.r[8].length = htons(2); - v4_template.r[9].ie = htons(IPFIX_destinationTransportPort); - v4_template.r[9].length = htons(2); - v4_template.r[10].ie = htons(IPFIX_protocolIdentifier); + v4_template.r[6].ie = htons(IPFIX_sourceTransportPort); + v4_template.r[6].length = htons(2); + v4_template.r[7].ie = htons(IPFIX_destinationTransportPort); + v4_template.r[7].length = htons(2); + v4_template.r[8].ie = htons(IPFIX_protocolIdentifier); + v4_template.r[8].length = htons(1); + v4_template.r[9].ie = htons(IPFIX_tcpControlBits); + v4_template.r[9].length = htons(1); + v4_template.r[10].ie = htons(IPFIX_ipVersion); v4_template.r[10].length = htons(1); - v4_template.r[11].ie = htons(IPFIX_tcpControlBits); + v4_template.r[11].ie = htons(IPFIX_ipClassOfService); v4_template.r[11].length = htons(1); - v4_template.r[12].ie = htons(IPFIX_ipVersion); - v4_template.r[12].length = htons(1); + if (param->time_format == 's') { + v4_template.r[12].ie = htons(IPFIX_flowStartSeconds); + v4_template.r[12].length = htons(sizeof(u_int32_t)); + v4_template.r[13].ie = htons(IPFIX_flowEndSeconds); + v4_template.r[13].length = htons(sizeof(u_int32_t)); + } else if (param->time_format == 'm') { + v4_template.r[12].ie = htons(IPFIX_flowStartMilliSeconds); + v4_template.r[12].length = htons(sizeof(u_int64_t)); + v4_template.r[13].ie = htons(IPFIX_flowEndMilliSeconds); + v4_template.r[13].length = htons(sizeof(u_int64_t)); + } else if (param->time_format == 'M') { + v4_template.r[12].ie = htons(IPFIX_flowStartMicroSeconds); + v4_template.r[12].length = htons(sizeof(u_int64_t)); + v4_template.r[13].ie = htons(IPFIX_flowEndMicroSeconds); + v4_template.r[13].length = htons(sizeof(u_int64_t)); + } else if (param->time_format == 'n') { + v4_template.r[12].ie = htons(IPFIX_flowStartNanoSeconds); + v4_template.r[12].length = htons(sizeof(u_int64_t)); + v4_template.r[13].ie = htons(IPFIX_flowEndNanoSeconds); + v4_template.r[13].length = htons(sizeof(u_int64_t)); + } else { + v4_template.r[12].ie = htons(IPFIX_flowStartSysUpTime); + v4_template.r[12].length = htons(sizeof(u_int32_t)); + v4_template.r[13].ie = htons(IPFIX_flowEndSysUpTime); + v4_template.r[13].length = htons(sizeof(u_int32_t)); + } bzero(&v6_template, sizeof(v6_template)); v6_template.h.c.set_id = htons(IPFIX_TEMPLATE_SET_ID); @@ -202,28 +256,52 @@ v6_template.r[0].length = htons(16); v6_template.r[1].ie = htons(IPFIX_destinationIPv6Address); v6_template.r[1].length = htons(16); - v6_template.r[2].ie = htons(IPFIX_flowEndSysUpTime); + v6_template.r[2].ie = htons(IPFIX_octetDeltaCount); v6_template.r[2].length = htons(4); - v6_template.r[3].ie = htons(IPFIX_flowStartSysUpTime); + v6_template.r[3].ie = htons(IPFIX_packetDeltaCount); v6_template.r[3].length = htons(4); - v6_template.r[4].ie = htons(IPFIX_octetDeltaCount); + v6_template.r[4].ie = htons(IPFIX_ingressInterface); v6_template.r[4].length = htons(4); - v6_template.r[5].ie = htons(IPFIX_packetDeltaCount); + v6_template.r[5].ie = htons(IPFIX_egressInterface); v6_template.r[5].length = htons(4); - v6_template.r[6].ie = htons(IPFIX_ingressInterface); - v6_template.r[6].length = htons(4); - v6_template.r[7].ie = htons(IPFIX_egressInterface); - v6_template.r[7].length = htons(4); - v6_template.r[8].ie = htons(IPFIX_sourceTransportPort); - v6_template.r[8].length = htons(2); - v6_template.r[9].ie = htons(IPFIX_destinationTransportPort); - v6_template.r[9].length = htons(2); - v6_template.r[10].ie = htons(IPFIX_protocolIdentifier); + v6_template.r[6].ie = htons(IPFIX_sourceTransportPort); + v6_template.r[6].length = htons(2); + v6_template.r[7].ie = htons(IPFIX_destinationTransportPort); + v6_template.r[7].length = htons(2); + v6_template.r[8].ie = htons(IPFIX_protocolIdentifier); + v6_template.r[8].length = htons(1); + v6_template.r[9].ie = htons(IPFIX_tcpControlBits); + v6_template.r[9].length = htons(1); + v6_template.r[10].ie = htons(IPFIX_ipVersion); v6_template.r[10].length = htons(1); - v6_template.r[11].ie = htons(IPFIX_tcpControlBits); + v6_template.r[11].ie = htons(IPFIX_ipClassOfService); v6_template.r[11].length = htons(1); - v6_template.r[12].ie = htons(IPFIX_ipVersion); - v6_template.r[12].length = htons(1); + if (param->time_format == 's') { + v6_template.r[12].ie = htons(IPFIX_flowStartSeconds); + v6_template.r[12].length = htons(sizeof(u_int32_t)); + v6_template.r[13].ie = htons(IPFIX_flowEndSeconds); + v6_template.r[13].length = htons(sizeof(u_int32_t)); + } else if (param->time_format == 'm') { + v6_template.r[12].ie = htons(IPFIX_flowStartMilliSeconds); + v6_template.r[12].length = htons(sizeof(u_int64_t)); + v6_template.r[13].ie = htons(IPFIX_flowEndMilliSeconds); + v6_template.r[13].length = htons(sizeof(u_int64_t)); + } else if (param->time_format == 'M') { + v6_template.r[12].ie = htons(IPFIX_flowStartMicroSeconds); + v6_template.r[12].length = htons(sizeof(u_int64_t)); + v6_template.r[13].ie = htons(IPFIX_flowEndMicroSeconds); + v6_template.r[13].length = htons(sizeof(u_int64_t)); + } else if (param->time_format == 'n') { + v6_template.r[12].ie = htons(IPFIX_flowStartNanoSeconds); + v6_template.r[12].length = htons(sizeof(u_int64_t)); + v6_template.r[13].ie = htons(IPFIX_flowEndNanoSeconds); + v6_template.r[13].length = htons(sizeof(u_int64_t)); + } else { + v6_template.r[12].ie = htons(IPFIX_flowStartSysUpTime); + v6_template.r[12].length = htons(sizeof(u_int32_t)); + v6_template.r[13].ie = htons(IPFIX_flowEndSysUpTime); + v6_template.r[13].length = htons(sizeof(u_int32_t)); + } } static void @@ -249,10 +327,8 @@ option_data.c.set_id = htons(IPFIX_SOFTFLOWD_OPTION_TEMPLATE_ID); option_data.c.length = htons(sizeof(option_data)); option_data.scope_pid = htonl((u_int32_t)option->meteringProcessId); -#if defined HAVE_HTOBE64 +#if defined (_BSD_SOURCE) && defined (HAVE_ENDIAN_H) || defined (HAVE_HTOBE64) || defined (HAVE_HTONLL) option_data.systemInitTimeMilliseconds = htobe64((u_int64_t)system_boot_time->tv_sec * 1000 + (u_int64_t)system_boot_time->tv_usec / 1000); -#elif defined HAVE_HTONLL - option_data.systemInitTimeMilliseconds = htonll((u_int64_t)system_boot_time->tv_sec * 1000 + (u_int64_t)system_boot_time->tv_usec / 1000); #endif option_data.samplingAlgorithm = htons(PSAMP_selectorAlgorithm_count); option_data.samplingInterval = htons(1); @@ -260,13 +336,15 @@ } static int ipfix_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len, - u_int16_t ifidx, const struct timeval *system_boot_time, u_int *len_used) + u_int16_t ifidx, const struct timeval *system_boot_time, u_int *len_used, + struct FLOWTRACKPARAMETERS *param) { union { struct IPFIX_SOFTFLOWD_DATA_V4 d4; struct IPFIX_SOFTFLOWD_DATA_V6 d6; } d[2]; struct IPFIX_SOFTFLOWD_DATA_COMMON *dc[2]; + union IPFIX_SOFTFLOWD_DATA_TIME *dt[2]; u_int freclen, ret_len, nflows; bzero(d, sizeof(d)); @@ -274,32 +352,68 @@ switch (flow->af) { case AF_INET: freclen = sizeof(struct IPFIX_SOFTFLOWD_DATA_V4); + if (!(param->time_format == 'm' || param->time_format == 'M' || param->time_format == 'n')) { + freclen -= (sizeof(u_int64_t) - sizeof(u_int32_t)) * 2; + } memcpy(&d[0].d4.sourceIPv4Address, &flow->addr[0].v4, 4); memcpy(&d[0].d4.destinationIPv4Address, &flow->addr[1].v4, 4); memcpy(&d[1].d4.sourceIPv4Address, &flow->addr[1].v4, 4); memcpy(&d[1].d4.destinationIPv4Address, &flow->addr[0].v4, 4); dc[0] = &d[0].d4.c; dc[1] = &d[1].d4.c; + dt[0] = &d[0].d4.t; + dt[1] = &d[1].d4.t; dc[0]->ipVersion = dc[1]->ipVersion = 4; break; case AF_INET6: freclen = sizeof(struct IPFIX_SOFTFLOWD_DATA_V6); + if (!(param->time_format == 'm' || param->time_format == 'M' || param->time_format == 'n')) { + freclen -= (sizeof(u_int64_t) - sizeof(u_int32_t)) * 2; + } memcpy(&d[0].d6.sourceIPv6Address, &flow->addr[0].v6, 16); memcpy(&d[0].d6.destinationIPv6Address, &flow->addr[1].v6, 16); memcpy(&d[1].d6.sourceIPv6Address, &flow->addr[1].v6, 16); memcpy(&d[1].d6.destinationIPv6Address, &flow->addr[0].v6, 16); dc[0] = &d[0].d6.c; dc[1] = &d[1].d6.c; + dt[0] = &d[0].d6.t; + dt[1] = &d[1].d6.t; dc[0]->ipVersion = dc[1]->ipVersion = 6; break; default: return (-1); } - dc[0]->flowStartSysUpTime = dc[1]->flowStartSysUpTime = - htonl(timeval_sub_ms(&flow->flow_start, system_boot_time)); - dc[0]->flowEndSysUpTime = dc[1]->flowEndSysUpTime = - htonl(timeval_sub_ms(&flow->flow_last, system_boot_time)); + if (param->time_format == 's') { + dt[0]->u32.start = dt[1]->u32.start = + htonl(flow->flow_start.tv_sec); + dt[0]->u32.end = dt[1]->u32.end = + htonl(flow->flow_last.tv_sec); + } +#if defined (_BSD_SOURCE) && defined (HAVE_ENDIAN_H) || defined (HAVE_HTOBE64) || defined (HAVE_HTONLL) + else if (param->time_format == 'm') { /* milliseconds */ + dt[0]->u64.start = dt[1]->u64.start = + htobe64((u_int64_t)flow->flow_start.tv_sec * 1000 + (u_int64_t)flow->flow_start.tv_usec / 1000); + dt[0]->u64.end = dt[1]->u64.end = + htobe64((u_int64_t)flow->flow_last.tv_sec * 1000 + (u_int64_t)flow->flow_last.tv_usec / 1000); + } else if (param->time_format == 'M') { /* microseconds */ + dt[0]->u64.start = dt[1]->u64.start = + htobe64(((u_int64_t)(flow->flow_start.tv_sec + JAN_1970) << 32) + (u_int64_t)((flow->flow_start.tv_usec << 32)/ 1e6)); + dt[0]->u64.end = dt[1]->u64.end = + htobe64(((u_int64_t)(flow->flow_last.tv_sec + JAN_1970) << 32) + (u_int64_t)((flow->flow_start.tv_usec << 32)/ 1e6)); + } else if (param->time_format == 'n') { /* nanoseconds */ + dt[0]->u64.start = dt[1]->u64.start = + htobe64(((u_int64_t)(flow->flow_start.tv_sec + JAN_1970) << 32) + (u_int64_t)((flow->flow_start.tv_usec * 1000 << 32)/ 1e9)); + dt[0]->u64.end = dt[1]->u64.end = + htobe64(((u_int64_t)(flow->flow_last.tv_sec + JAN_1970) << 32) + (u_int64_t)((flow->flow_start.tv_usec * 1000 << 32)/ 1e9)); + } +#endif + else { + dt[0]->u32.start = dt[1]->u32.start = + htonl(timeval_sub_ms(&flow->flow_start, system_boot_time)); + dt[0]->u32.end = dt[1]->u32.end = + htonl(timeval_sub_ms(&flow->flow_last, system_boot_time)); + } dc[0]->octetDeltaCount = htonl(flow->octets[0]); dc[1]->octetDeltaCount = htonl(flow->octets[1]); dc[0]->packetDeltaCount = htonl(flow->packets[0]); @@ -311,6 +425,8 @@ dc[0]->protocolIdentifier = dc[1]->protocolIdentifier = flow->protocol; dc[0]->tcpControlBits = flow->tcp_flags[0]; dc[1]->tcpControlBits = flow->tcp_flags[1]; + dc[0]->ipClassOfService = flow->tos[0]; + dc[1]->ipClassOfService = flow->tos[1]; if (flow->octets[0] > 0) { if (ret_len + freclen > len) @@ -355,7 +471,7 @@ gettimeofday(&now, NULL); if (ipfix_pkts_until_template == -1) { - ipfix_init_template(); + ipfix_init_template(param); ipfix_pkts_until_template = 0; if (option != NULL){ ipfix_init_option(system_boot_time, option); @@ -424,7 +540,7 @@ } r = ipfix_flow_to_flowset(flows[i + j], packet + offset, - sizeof(packet) - offset, ifidx, system_boot_time, &inc); + sizeof(packet) - offset, ifidx, system_boot_time, &inc, param); if (r <= 0) { /* yank off data header, if we had to go back */ if (last_valid) --- a/netflow1.c +++ b/netflow1.c @@ -123,6 +123,7 @@ system_boot_time)); flw->protocol = flows[i]->protocol; flw->tcp_flags = flows[i]->tcp_flags[0]; + flw->tos = flows[i]->tos[0]; offset += sizeof(*flw); j++; hdr->flows++; @@ -145,6 +146,7 @@ system_boot_time)); flw->protocol = flows[i]->protocol; flw->tcp_flags = flows[i]->tcp_flags[1]; + flw->tos = flows[i]->tos[1]; offset += sizeof(*flw); j++; hdr->flows++; --- a/netflow5.c +++ b/netflow5.c @@ -128,6 +128,7 @@ system_boot_time)); flw->tcp_flags = flows[i]->tcp_flags[0]; flw->protocol = flows[i]->protocol; + flw->tos = flows[i]->tos[0]; offset += sizeof(*flw); j++; hdr->flows++; @@ -151,6 +152,7 @@ system_boot_time)); flw->tcp_flags = flows[i]->tcp_flags[1]; flw->protocol = flows[i]->protocol; + flw->tos = flows[i]->tos[1]; offset += sizeof(*flw); j++; hdr->flows++; --- a/netflow9.c +++ b/netflow9.c @@ -58,7 +58,8 @@ #define NF9_IN_BYTES 1 #define NF9_IN_PACKETS 2 /* ... */ -#define NF9_IN_PROTOCOL 4 +#define NF9_PROTOCOL 4 +#define NF9_TOS 5 /* ... */ #define NF9_TCP_FLAGS 6 #define NF9_L4_SRC_PORT 7 @@ -102,7 +103,7 @@ u_int32_t bytes, packets; u_int32_t if_index_in, if_index_out; u_int16_t src_port, dst_port; - u_int8_t protocol, tcp_flags, ipproto; + u_int8_t protocol, tcp_flags, ipproto, tos; } __packed; struct NF9_SOFTFLOWD_DATA_V4 { @@ -176,12 +177,14 @@ v4_template.r[8].length = htons(2); v4_template.r[9].type = htons(NF9_L4_DST_PORT); v4_template.r[9].length = htons(2); - v4_template.r[10].type = htons(NF9_IN_PROTOCOL); + v4_template.r[10].type = htons(NF9_PROTOCOL); v4_template.r[10].length = htons(1); v4_template.r[11].type = htons(NF9_TCP_FLAGS); v4_template.r[11].length = htons(1); v4_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION); v4_template.r[12].length = htons(1); + v4_template.r[13].type = htons(NF9_TOS); + v4_template.r[13].length = htons(1); bzero(&v6_template, sizeof(v6_template)); v6_template.h.c.flowset_id = htons(NF9_TEMPLATE_FLOWSET_ID); @@ -208,12 +211,14 @@ v6_template.r[8].length = htons(2); v6_template.r[9].type = htons(NF9_L4_DST_PORT); v6_template.r[9].length = htons(2); - v6_template.r[10].type = htons(NF9_IN_PROTOCOL); + v6_template.r[10].type = htons(NF9_PROTOCOL); v6_template.r[10].length = htons(1); v6_template.r[11].type = htons(NF9_TCP_FLAGS); v6_template.r[11].length = htons(1); v6_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION); v6_template.r[12].length = htons(1); + v6_template.r[13].type = htons(NF9_TOS); + v6_template.r[13].length = htons(1); } static void @@ -291,6 +296,8 @@ dc[0]->protocol = dc[1]->protocol = flow->protocol; dc[0]->tcp_flags = flow->tcp_flags[0]; dc[1]->tcp_flags = flow->tcp_flags[1]; + dc[0]->tos = flow->tos[0]; + dc[1]->tos = flow->tos[1]; if (flow->octets[0] > 0) { if (ret_len + freclen > len) --- a/softflowd.c +++ b/softflowd.c @@ -1271,7 +1271,7 @@ } static int -connsock(struct sockaddr_storage *addr, socklen_t len, int hoplimit) +connsock(struct sockaddr_storage *addr, socklen_t len, int hoplimit, int protocol) { int s; unsigned int h6; @@ -1279,7 +1279,7 @@ struct sockaddr_in *in4 = (struct sockaddr_in *)addr; struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; - if ((s = socket(addr->ss_family, SOCK_DGRAM, 0)) == -1) { + if ((s = socket(addr->ss_family, protocol == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM, protocol)) == -1) { fprintf(stderr, "socket() error: %s\n", strerror(errno)); exit(1); @@ -1486,24 +1486,28 @@ fprintf(stderr, "Usage: %s [options] [bpf_program]\n" "This is %s version %s. Valid commandline options:\n" -" -i [idx:]interface Specify interface to listen on\n" -" -r pcap_file Specify packet capture file to read\n" -" -t timeout=time Specify named timeout\n" -" -m max_flows Specify maximum number of flows to track (default %d)\n" -" -n host:port Send Cisco NetFlow(tm)-compatible packets to host:port\n" -" -p pidfile Record pid in specified file\n" -" (default: %s)\n" -" -c pidfile Location of control socket\n" -" (default: %s)\n" -" -v 1|5|9|10 NetFlow export packet version\n" -" -L hoplimit Set TTL/hoplimit for export datagrams\n" -" -T full|proto|ip Set flow tracking level (default: full)\n" -" -6 Track IPv6 flows, regardless of whether selected \n" -" NetFlow export protocol supports it\n" -" -d Don't daemonise (run in foreground)\n" -" -D Debug mode: foreground + verbosity + track v6 flows\n" -" -s sampling_rate Specify periodical sampling rate (denominator)\n" -" -h Display this help\n" +" -i [idx:]interface Specify interface to listen on\n" +" -r pcap_file Specify packet capture file to read\n" +" -t timeout=time Specify named timeout\n" +" -m max_flows Specify maximum number of flows to track (default %d)\n" +" -n host:port Send Cisco NetFlow(tm)-compatible packets to host:port\n" +" -p pidfile Record pid in specified file\n" +" (default: %s)\n" +" -c pidfile Location of control socket\n" +" (default: %s)\n" +" -v 1|5|9|10 NetFlow export packet version\n" +" (10 means IPFI)\n" +" -L hoplimit Set TTL/hoplimit for export datagrams\n" +" -T full|port|proto|ip Set flow tracking level (default: full)\n" +" -6 Track IPv6 flows, regardless of whether selected \n" +" NetFlow export protocol supports it\n" +" -d Don't daemonise (run in foreground)\n" +" -D Debug mode: foreground + verbosity + track v6 flows\n" +" -s sampling_rate Specify periodical sampling rate (denominator)\n" +" -P udp|tcp|sctp Specify transport layer protocol for exporting packets\n" +" -A sec|milli|micro|nano Specify absolute time format form exporting records\n" +" -s sampling_rate Specify periodical sampling rate (denominator)\n" +" -h Display this help\n" "\n" "Valid timeout names and default values:\n" " tcp (default %6d)" @@ -1695,6 +1699,7 @@ struct NETFLOW_TARGET target; struct CB_CTXT cb_ctxt; struct pollfd pl[2]; + int protocol = IPPROTO_UDP; closefrom(STDERR_FILENO + 1); @@ -1714,7 +1719,7 @@ dontfork_flag = 0; always_v6 = 0; - while ((ch = getopt(argc, argv, "6hdDL:T:i:r:f:t:n:m:p:c:v:s:")) != -1) { + while ((ch = getopt(argc, argv, "6hdDL:T:i:r:f:t:n:m:p:c:v:s:P:A:")) != -1) { switch (ch) { case '6': always_v6 = 1; @@ -1766,6 +1771,8 @@ case 'T': if (strcasecmp(optarg, "full") == 0) flowtrack.param.track_level = TRACK_FULL; + else if (strcasecmp(optarg, "port") == 0) + flowtrack.param.track_level = TRACK_IP_PROTO_PORT; else if (strcasecmp(optarg, "proto") == 0) flowtrack.param.track_level = TRACK_IP_PROTO; else if (strcasecmp(optarg, "ip") == 0) @@ -1824,6 +1831,38 @@ flowtrack.param.option.sample = 0; } break; + case 'P': + if (strcasecmp(optarg, "udp") == 0) + protocol = IPPROTO_UDP; + else if (strcasecmp(optarg, "tcp") == 0) + protocol = IPPROTO_TCP; +#ifdef IPPROTO_SCTP + else if (strcasecmp(optarg, "sctp") == 0) + protocol = IPPROTO_SCTP; +#endif + else { + fprintf(stderr, "Unknown transport layer protocol" + "\n"); + usage(); + exit(1); + } + break; + case 'A': + if (strcasecmp(optarg, "sec") == 0) + flowtrack.param.time_format = 's'; + else if (strcasecmp(optarg, "milli") == 0) + flowtrack.param.time_format = 'm'; + else if (strcasecmp(optarg, "micro") == 0) + flowtrack.param.time_format = 'M'; + else if (strcasecmp(optarg, "nano") == 0) + flowtrack.param.time_format = 'n'; + else { + fprintf(stderr, "Unknown time format" + "\n"); + usage(); + exit(1); + } + break; default: fprintf(stderr, "Invalid commandline option.\n"); usage(); @@ -1852,7 +1891,7 @@ fprintf(stderr, "getnameinfo: %d\n", err); exit(1); } - target.fd = connsock(&dest, dest_len, hoplimit); + target.fd = connsock(&dest, dest_len, hoplimit, protocol); } /* Control socket */ --- a/softflowd.h +++ b/softflowd.h @@ -69,9 +69,10 @@ }; /* Flow tracking levels */ -#define TRACK_FULL 1 /* src/dst/addr/port/proto 5-tuple */ -#define TRACK_IP_PROTO 2 /* src/dst/proto 3-tuple */ -#define TRACK_IP_ONLY 3 /* src/dst tuple */ +#define TRACK_FULL 1 /* src/dst/addr/port/proto/tos 6-tuple */ +#define TRACK_IP_PROTO_PORT 2 /* src/dst/addr/port/proto 5-tuple */ +#define TRACK_IP_PROTO 3 /* src/dst/proto 3-tuple */ +#define TRACK_IP_ONLY 4 /* src/dst tuple */ /* * This structure contains optional information carried by Option Data @@ -136,6 +137,7 @@ /* Optional information */ struct OPTION option; + char time_format; }; /* * This structure is the root of the flow tracking system. @@ -166,6 +168,15 @@ struct EXPIRY *expiry; /* Pointer to expiry record */ FLOW_ENTRY(FLOW) trp; /* Tree pointer */ + /* Per-flow statistics (all in _host_ byte order) */ + u_int64_t flow_seq; /* Flow ID */ + struct timeval flow_start; /* Time of creation */ + struct timeval flow_last; /* Time of last traffic */ + + /* Per-endpoint statistics (all in _host_ byte order) */ + u_int32_t octets[2]; /* Octets so far */ + u_int32_t packets[2]; /* Packets so far */ + /* Flow identity (all are in network byte order) */ int af; /* Address family of flow */ u_int32_t ip6_flowlabel[2]; /* IPv6 Flowlabel */ @@ -175,16 +186,8 @@ } addr[2]; /* Endpoint addresses */ u_int16_t port[2]; /* Endpoint ports */ u_int8_t tcp_flags[2]; /* Cumulative OR of flags */ + u_int8_t tos[2]; /* Tos */ u_int8_t protocol; /* Protocol */ - - /* Per-flow statistics (all in _host_ byte order) */ - u_int64_t flow_seq; /* Flow ID */ - struct timeval flow_start; /* Time of creation */ - struct timeval flow_last; /* Time of last traffic */ - - /* Per-endpoint statistics (all in _host_ byte order) */ - u_int32_t octets[2]; /* Octets so far */ - u_int32_t packets[2]; /* Packets so far */ }; /*