Subject: This commit includes 2 changes Origin: softflowd-0.9.9-6-g9478639 Upstream-Author: Hitoshi Irino Date: Mon Mar 17 13:10:54 2014 +0900 1. It enables metering and exporting vlanid when using NetFlow v9 and IPFIX. 2. When ICMP flow information are exported It uses the ICMP_TYPE field instead of the L4_DST_PORT field --- a/configure.ac +++ b/configure.ac @@ -31,6 +31,11 @@ [ --enable-gcc-warnings Enable verbose warnings (only for gcc)], [ if test "x$enableval" = "xyes" ; then CFLAGS="$CFLAGS $WFLAGS"; fi ] ) +AC_ARG_ENABLE(nf9-vlan, + AC_HELP_STRING([--enable-nf9-vlan], + [enable exporting VLAN (and ICMP_TYPE) field in NetFlow v9 (default is NO)]), + AC_DEFINE([ENABLE_NF9_VLAN], 1, [enable exporting VLAN (and ICMP_TYPE) field in NetFlow v9])) + AC_ARG_WITH(cflags, [ --with-cflags Specify additional compiler flags], [ if test "x$withval" != "xno" ; then CFLAGS="$CFLAGS $withval"; fi ] --- a/ipfix.c +++ b/ipfix.c @@ -85,8 +85,15 @@ #define IPFIX_sourceIPv6Address 27 #define IPFIX_destinationIPv6Address 28 /* ... */ +#define IPFIX_icmpTypeCodeIPv4 32 +/* ... */ +/* ... */ +#define IPFIX_vlanId 58 + #define IPFIX_ipVersion 60 /* ... */ +#define IPFIX_icmpTypeCodeIPv6 139 +/* ... */ #define IPFIX_meteringProcessId 143 /* ... */ #define IPFIX_flowStartSeconds 150 @@ -107,7 +114,7 @@ #define PSAMP_selectorAlgorithm_count 1 /* Stuff pertaining to the templates that softflowd uses */ -#define IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS 14 +#define IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS 16 struct IPFIX_SOFTFLOWD_TEMPLATE { struct IPFIX_TEMPLATE_SET_HEADER h; struct IPFIX_FIELD_SPECIFIER r[IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS]; @@ -127,6 +134,7 @@ u_int32_t ingressInterface, egressInterface; u_int16_t sourceTransportPort, destinationTransportPort; u_int8_t protocolIdentifier, tcpControlBits, ipVersion, ipClassOfService; + u_int16_t icmpTypeCode, vlanId; //u_int32_t flowEndSysUpTime, flowStartSysUpTime; } __packed; @@ -220,31 +228,35 @@ v4_template.r[10].length = htons(1); v4_template.r[11].ie = htons(IPFIX_ipClassOfService); v4_template.r[11].length = htons(1); + v4_template.r[12].ie = htons(IPFIX_icmpTypeCodeIPv4); + v4_template.r[12].length = htons(2); + v4_template.r[13].ie = htons(IPFIX_vlanId); + v4_template.r[13].length = htons(2); 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)); + v4_template.r[14].ie = htons(IPFIX_flowStartSeconds); + v4_template.r[14].length = htons(sizeof(u_int32_t)); + v4_template.r[15].ie = htons(IPFIX_flowEndSeconds); + v4_template.r[15].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)); + v4_template.r[14].ie = htons(IPFIX_flowStartMilliSeconds); + v4_template.r[14].length = htons(sizeof(u_int64_t)); + v4_template.r[15].ie = htons(IPFIX_flowEndMilliSeconds); + v4_template.r[15].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)); + v4_template.r[14].ie = htons(IPFIX_flowStartMicroSeconds); + v4_template.r[14].length = htons(sizeof(u_int64_t)); + v4_template.r[15].ie = htons(IPFIX_flowEndMicroSeconds); + v4_template.r[15].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)); + v4_template.r[14].ie = htons(IPFIX_flowStartNanoSeconds); + v4_template.r[14].length = htons(sizeof(u_int64_t)); + v4_template.r[15].ie = htons(IPFIX_flowEndNanoSeconds); + v4_template.r[15].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)); + v4_template.r[14].ie = htons(IPFIX_flowStartSysUpTime); + v4_template.r[14].length = htons(sizeof(u_int32_t)); + v4_template.r[15].ie = htons(IPFIX_flowEndSysUpTime); + v4_template.r[15].length = htons(sizeof(u_int32_t)); } bzero(&v6_template, sizeof(v6_template)); @@ -276,31 +288,35 @@ v6_template.r[10].length = htons(1); v6_template.r[11].ie = htons(IPFIX_ipClassOfService); v6_template.r[11].length = htons(1); + v6_template.r[12].ie = htons(IPFIX_icmpTypeCodeIPv6); + v6_template.r[12].length = htons(2); + v6_template.r[13].ie = htons(IPFIX_vlanId); + v6_template.r[13].length = htons(2); 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)); + v6_template.r[14].ie = htons(IPFIX_flowStartSeconds); + v6_template.r[14].length = htons(sizeof(u_int32_t)); + v6_template.r[15].ie = htons(IPFIX_flowEndSeconds); + v6_template.r[15].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)); + v6_template.r[14].ie = htons(IPFIX_flowStartMilliSeconds); + v6_template.r[14].length = htons(sizeof(u_int64_t)); + v6_template.r[15].ie = htons(IPFIX_flowEndMilliSeconds); + v6_template.r[15].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)); + v6_template.r[14].ie = htons(IPFIX_flowStartMicroSeconds); + v6_template.r[14].length = htons(sizeof(u_int64_t)); + v6_template.r[15].ie = htons(IPFIX_flowEndMicroSeconds); + v6_template.r[15].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)); + v6_template.r[14].ie = htons(IPFIX_flowStartNanoSeconds); + v6_template.r[14].length = htons(sizeof(u_int64_t)); + v6_template.r[15].ie = htons(IPFIX_flowEndNanoSeconds); + v6_template.r[15].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)); + v6_template.r[14].ie = htons(IPFIX_flowStartSysUpTime); + v6_template.r[14].length = htons(sizeof(u_int32_t)); + v6_template.r[15].ie = htons(IPFIX_flowEndSysUpTime); + v6_template.r[15].length = htons(sizeof(u_int32_t)); } } @@ -427,6 +443,11 @@ dc[1]->tcpControlBits = flow->tcp_flags[1]; dc[0]->ipClassOfService = flow->tos[0]; dc[1]->ipClassOfService = flow->tos[1]; + if (flow->protocol == IPPROTO_ICMP || flow->protocol == IPPROTO_ICMPV6) { + dc[0]->icmpTypeCode = dc[0]->destinationTransportPort; + dc[1]->icmpTypeCode = dc[1]->destinationTransportPort; + } + dc[0]->vlanId = dc[1]->vlanId = htons(flow->vlanid); if (flow->octets[0] > 0) { if (ret_len + freclen > len) --- a/netflow9.c +++ b/netflow9.c @@ -77,13 +77,21 @@ #define NF9_IPV6_SRC_ADDR 27 #define NF9_IPV6_DST_ADDR 28 /* ... */ +#define NF9_ICMP_TYPE 32 +/* ... */ #define NF9_SAMPLING_INTERVAL 34 #define NF9_SAMPLING_ALGORITHM 35 /* ... */ +#define NF9_SRC_VLAN 58 +/* ... */ #define NF9_IP_PROTOCOL_VERSION 60 /* Stuff pertaining to the templates that softflowd uses */ -#define NF9_SOFTFLOWD_TEMPLATE_NRECORDS 13 +#ifdef ENABLE_NF9_VLAN +#define NF9_SOFTFLOWD_TEMPLATE_NRECORDS 16 +#else /* ENABLE_NF9_VLAN */ +#define NF9_SOFTFLOWD_TEMPLATE_NRECORDS 14 +#endif /* ENABLE_NF9_VLAN */ struct NF9_SOFTFLOWD_TEMPLATE { struct NF9_TEMPLATE_FLOWSET_HEADER h; struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_TEMPLATE_NRECORDS]; @@ -104,6 +112,9 @@ u_int32_t if_index_in, if_index_out; u_int16_t src_port, dst_port; u_int8_t protocol, tcp_flags, ipproto, tos; +#ifdef ENABLE_NF9_VLAN + u_int16_t icmp_type, vlanid; +#endif /* ENABLE_NF9_VLAN */ } __packed; struct NF9_SOFTFLOWD_DATA_V4 { @@ -185,7 +196,12 @@ v4_template.r[12].length = htons(1); v4_template.r[13].type = htons(NF9_TOS); v4_template.r[13].length = htons(1); - +#ifdef ENABLE_NF9_VLAN + v4_template.r[14].type = htons(NF9_ICMP_TYPE); + v4_template.r[14].length = htons(2); + v4_template.r[15].type = htons(NF9_SRC_VLAN); + v4_template.r[15].length = htons(2); +#endif /* ENABLE_NF9_VLAN */ bzero(&v6_template, sizeof(v6_template)); v6_template.h.c.flowset_id = htons(NF9_TEMPLATE_FLOWSET_ID); v6_template.h.c.length = htons(sizeof(v6_template)); @@ -219,6 +235,12 @@ v6_template.r[12].length = htons(1); v6_template.r[13].type = htons(NF9_TOS); v6_template.r[13].length = htons(1); +#ifdef ENABLE_NF9_VLAN + v6_template.r[14].type = htons(NF9_ICMP_TYPE); + v6_template.r[14].length = htons(2); + v6_template.r[15].type = htons(NF9_SRC_VLAN); + v6_template.r[15].length = htons(2); +#endif /* ENABLE_NF9_VLAN */ } static void @@ -298,7 +320,13 @@ dc[1]->tcp_flags = flow->tcp_flags[1]; dc[0]->tos = flow->tos[0]; dc[1]->tos = flow->tos[1]; - +#ifdef ENABLE_NF9_VLAN + if (flow->protocol == IPPROTO_ICMP || flow->protocol == IPPROTO_ICMPV6) { + dc[0]->icmp_type = dc[0]->dst_port; + dc[1]->icmp_type = dc[1]->dst_port; + } + dc[0]->vlanid = dc[1]->vlanid = htons(flow->vlanid); +#endif /* ENABLE_NF9_VLAN */ if (flow->octets[0] > 0) { if (ret_len + freclen > len) return (-1); @@ -366,16 +394,20 @@ memcpy(packet + offset, &v4_template, sizeof(v4_template)); offset += sizeof(v4_template); + nf9->flows++; memcpy(packet + offset, &v6_template, sizeof(v6_template)); offset += sizeof(v6_template); + nf9->flows++; if (option != NULL && option->sample > 1){ memcpy(packet + offset, &option_template, sizeof(option_template)); offset += sizeof(option_template); + nf9->flows++; memcpy(packet + offset, &option_data, sizeof(option_data)); offset += sizeof(option_data); + nf9->flows++; } nf9_pkts_until_template = NF9_DEFAULT_TEMPLATE_INTERVAL; --- a/softflowd.c +++ b/softflowd.c @@ -140,6 +140,9 @@ /* Be careful to avoid signed vs unsigned issues here */ int r; + if (a->vlanid != b->vlanid) + return (a->vlanid > b->vlanid ? 1 : -1); + if (a->af != b->af) return (a->af > b->af ? 1 : -1); @@ -336,6 +339,7 @@ flow->port[ndx ^ 1] = udp->uh_dport; break; case IPPROTO_ICMP: + case IPPROTO_ICMPV6: /* * Encode ICMP type * 256 + code into dest port like * Cisco routers @@ -351,7 +355,7 @@ /* Convert a IPv4 packet to a partial flow record (used for comparison) */ static int ipv4_to_flowrec(struct FLOW *flow, const u_int8_t *pkt, size_t caplen, - size_t len, int *isfrag, int af) + size_t len, int *isfrag, int af, u_int16_t vlanid) { const struct ip *ip = (const struct ip *)pkt; int ndx; @@ -370,6 +374,7 @@ flow->protocol = ip->ip_p; flow->octets[ndx] = len; flow->packets[ndx] = 1; + flow->vlanid = vlanid; *isfrag = (ntohs(ip->ip_off) & (IP_OFFMASK|IP_MF)) ? 1 : 0; @@ -384,7 +389,7 @@ /* Convert a IPv6 packet to a partial flow record (used for comparison) */ static int ipv6_to_flowrec(struct FLOW *flow, const u_int8_t *pkt, size_t caplen, - size_t len, int *isfrag, int af) + size_t len, int *isfrag, int af, u_int16_t vlanid) { const struct ip6_hdr *ip6 = (const struct ip6_hdr *)pkt; const struct ip6_ext *eh6; @@ -407,6 +412,7 @@ flow->addr[ndx ^ 1].v6 = ip6->ip6_dst; flow->octets[ndx] = len; flow->packets[ndx] = 1; + flow->vlanid = vlanid; *isfrag = 0; nxt = ip6->ip6_nxt; @@ -545,7 +551,7 @@ */ static int process_packet(struct FLOWTRACK *ft, const u_int8_t *pkt, int af, - const u_int32_t caplen, const u_int32_t len, + const u_int32_t caplen, const u_int32_t len, u_int16_t vlanid, const struct timeval *received_time) { struct FLOW tmp, *flow; @@ -557,11 +563,11 @@ memset(&tmp, 0, sizeof(tmp)); switch (af) { case AF_INET: - if (ipv4_to_flowrec(&tmp, pkt, caplen, len, &frag, af) == -1) + if (ipv4_to_flowrec(&tmp, pkt, caplen, len, &frag, af, vlanid) == -1) goto bad; break; case AF_INET6: - if (ipv6_to_flowrec(&tmp, pkt, caplen, len, &frag, af) == -1) + if (ipv6_to_flowrec(&tmp, pkt, caplen, len, &frag, af, vlanid) == -1) goto bad; break; default: @@ -583,6 +589,8 @@ tmp.tcp_flags[0] = tmp.tcp_flags[1] = 0; /* FALLTHROUGH */ case TRACK_FULL: + tmp.vlanid = 0; + case TRACK_FULL_VLAN: break; } @@ -1073,10 +1081,12 @@ * packet should be skipped */ static int -datalink_check(int linktype, const u_int8_t *pkt, u_int32_t caplen, int *af) +datalink_check(int linktype, const u_int8_t *pkt, u_int32_t caplen, int *af, u_int16_t *vlanid) { int i, j; u_int32_t frametype; + int add_offset = 0; + static const struct DATALINK *dl = NULL; /* Try to cache last used linktype */ @@ -1092,15 +1102,24 @@ /* Suck out the frametype */ frametype = 0; + + /* Processing 802.1Q vlan in ethernet */ + if (linktype == DLT_EN10MB && ntohs(*(u_int16_t *)(pkt + dl->ft_off)) == 0x8100) { + add_offset = 4; + if (caplen <= dl->skiplen + add_offset) + return (-1); + *vlanid = ntohs(*(u_int16_t *)(pkt + dl->ft_off + 2)) & 0x0fff; + } + if (dl->ft_is_be) { for (j = 0; j < dl->ft_len; j++) { frametype <<= 8; - frametype |= pkt[j + dl->ft_off]; + frametype |= pkt[j + dl->ft_off + add_offset]; } } else { for (j = dl->ft_len - 1; j >= 0 ; j--) { frametype <<= 8; - frametype |= pkt[j + dl->ft_off]; + frametype |= pkt[j + dl->ft_off + add_offset]; } } frametype &= dl->ft_mask; @@ -1112,7 +1131,7 @@ else return (-1); - return (dl->skiplen); + return (dl->skiplen + add_offset); } /* @@ -1123,9 +1142,10 @@ flow_cb(u_char *user_data, const struct pcap_pkthdr* phdr, const u_char *pkt) { - int s, af; + int s, af = 0; struct CB_CTXT *cb_ctxt = (struct CB_CTXT *)user_data; struct timeval tv; + u_int16_t vlanid = 0; if (cb_ctxt->ft->param.option.sample && (cb_ctxt->ft->param.total_packets + @@ -1134,14 +1154,14 @@ cb_ctxt->ft->param.non_sampled_packets++; return; } - s = datalink_check(cb_ctxt->linktype, pkt, phdr->caplen, &af); + s = datalink_check(cb_ctxt->linktype, pkt, phdr->caplen, &af, &vlanid); if (s < 0 || (!cb_ctxt->want_v6 && af == AF_INET6)) { cb_ctxt->ft->param.non_ip_packets++; } else { tv.tv_sec = phdr->ts.tv_sec; tv.tv_usec = phdr->ts.tv_usec; - if (process_packet(cb_ctxt->ft, pkt + s, af, - phdr->caplen - s, phdr->len - s, &tv) == PP_MALLOC_FAIL) + if (process_packet(cb_ctxt->ft, pkt + s, af, phdr->caplen - s, + phdr->len - s, vlanid, &tv) == PP_MALLOC_FAIL) cb_ctxt->fatal = 1; } } @@ -1393,7 +1413,7 @@ bpf_net = bpf_mask = 0; } *linktype = pcap_datalink(*pcap); - if (datalink_check(*linktype, NULL, 0, NULL) == -1) { + if (datalink_check(*linktype, NULL, 0, NULL, NULL) == -1) { fprintf(stderr, "Unsupported datalink type %d\n", *linktype); exit(1); } @@ -1496,14 +1516,14 @@ " -c pidfile Location of control socket\n" " (default: %s)\n" " -v 1|5|9|10 NetFlow export packet version\n" -" (10 means IPFI)\n" +" (10 means IPFIX)\n" " -L hoplimit Set TTL/hoplimit for export datagrams\n" -" -T full|port|proto|ip Set flow tracking level (default: full)\n" +" -T full|port|proto|ip| Set flow tracking level (default: full)\n" +" vlan (\"vlan\" tracking means \"full\" tracking with vlanid)\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" @@ -1777,6 +1797,8 @@ flowtrack.param.track_level = TRACK_IP_PROTO; else if (strcasecmp(optarg, "ip") == 0) flowtrack.param.track_level = TRACK_IP_ONLY; + else if (strcasecmp(optarg, "vlan") == 0) + flowtrack.param.track_level = TRACK_FULL_VLAN; else { fprintf(stderr, "Unknown flow tracking " "level\n"); --- a/softflowd.h +++ b/softflowd.h @@ -73,6 +73,7 @@ #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 */ +#define TRACK_FULL_VLAN 5 /* src/dst/addr/port/proto/tos/vlanid 7-tuple */ /* * This structure contains optional information carried by Option Data @@ -187,6 +188,7 @@ u_int16_t port[2]; /* Endpoint ports */ u_int8_t tcp_flags[2]; /* Cumulative OR of flags */ u_int8_t tos[2]; /* Tos */ + u_int16_t vlanid; /* vlanid */ u_int8_t protocol; /* Protocol */ }; @@ -234,5 +236,6 @@ /* Force a resend of the flow template */ void netflow9_resend_template(void); +void ipfix_resend_template(void); #endif /* _SOFTFLOWD_H */