/* Copyright (c) 2002-2006 InMon Corp. Licensed under the terms of the InMon sFlow licence: */ /* http://www.inmon.com/technology/sflowlicense.txt */ #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef WIN32 #include "winsock2.h" #include "fcntl.h" #else #include #include #include #include #include #endif #include "sflowtool.h" #include "sflow.h" // sFlow v5 /* #ifdef DARWIN #include #define bswap_16(x) NXSwapShort(x) #define bswap_32(x) NXSwapInt(x) #else #include #endif */ /* just do it in a portable way... */ static u_int32_t MyByteSwap32(u_int32_t n) { return (((n & 0x000000FF)<<24) + ((n & 0x0000FF00)<<8) + ((n & 0x00FF0000)>>8) + ((n & 0xFF000000)>>24)); } static u_int16_t MyByteSwap16(u_int16_t n) { return ((n >> 8) | (n << 8)); } #define YES 1 #define NO 0 /* define my own IP header struct - to ease portability */ struct myiphdr { u_int8_t version_and_headerLen; u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; }; /* same for tcp */ struct mytcphdr { u_int16_t th_sport; /* source port */ u_int16_t th_dport; /* destination port */ u_int32_t th_seq; /* sequence number */ u_int32_t th_ack; /* acknowledgement number */ u_int8_t th_off_and_unused; u_int8_t th_flags; u_int16_t th_win; /* window */ u_int16_t th_sum; /* checksum */ u_int16_t th_urp; /* urgent pointer */ }; /* and UDP */ struct myudphdr { u_int16_t uh_sport; /* source port */ u_int16_t uh_dport; /* destination port */ u_int16_t uh_ulen; /* udp length */ u_int16_t uh_sum; /* udp checksum */ }; /* and ICMP */ struct myicmphdr { u_int8_t type; /* message type */ u_int8_t code; /* type sub-code */ /* ignore the rest */ }; #ifdef SPOOFSOURCE #define SPOOFSOURCE_SENDPACKET_SIZE 2000 struct mySendPacket { struct myiphdr ip; struct myudphdr udp; u_char data[SPOOFSOURCE_SENDPACKET_SIZE]; }; #endif /* tcpdump file format */ struct pcap_file_header { u_int32_t magic; u_int16_t version_major; u_int16_t version_minor; u_int32_t thiszone; /* gmt to local correction */ u_int32_t sigfigs; /* accuracy of timestamps */ u_int32_t snaplen; /* max length saved portion of each pkt */ u_int32_t linktype; /* data link type (DLT_*) */ }; struct pcap_pkthdr { u_int32_t ts_sec; /* time stamp - used to be struct timeval, but time_t can be 64 bits now */ u_int32_t ts_usec; u_int32_t caplen; /* length of portion present */ u_int32_t len; /* length this packet (off wire) */ /* some systems expect to see more information here. For example, * on some versions of RedHat Linux, there are three extra fields: * int index; * unsigned short protocol; * unsigned char pkt_type; * To pad the header with zeros, use the tcpdumpHdrPad option. */ }; typedef struct _SFForwardingTarget { struct _SFForwardingTarget *nxt; struct in_addr host; u_int32_t port; struct sockaddr_in addr; int sock; } SFForwardingTarget; typedef enum { SFLFMT_FULL=0, SFLFMT_PCAP, SFLFMT_LINE, SFLFMT_NETFLOW, SFLFMT_FWD } EnumSFLFormat; typedef struct _SFConfig { /* sflow(R) options */ u_int16_t sFlowInputPort; /* netflow(TM) options */ u_int16_t netFlowOutputPort; struct in_addr netFlowOutputIP; int netFlowOutputSocket; u_int16_t netFlowPeerAS; int disableNetFlowScale; /* tcpdump options */ char *readPcapFileName; FILE *readPcapFile; struct pcap_file_header readPcapHdr; char *writePcapFile; EnumSFLFormat outputFormat; u_int32_t tcpdumpHdrPad; u_char zeroPad[100]; int pcapSwap; #ifdef SPOOFSOURCE int spoofSource; u_int16_t ipid; struct mySendPacket sendPkt; u_int32_t packetLen; #endif SFForwardingTarget *forwardingTargets; /* vlan filtering */ int gotVlanFilter; #define FILTER_MAX_VLAN 4096 u_char vlanFilter[FILTER_MAX_VLAN + 1]; /* content stripping */ int removeContent; } SFConfig; /* make the options structure global to the program */ static SFConfig sfConfig; typedef struct _SFSample { struct in_addr sourceIP; SFLAddress agent_addr; u_int32_t agentSubId; /* the raw pdu */ u_char *rawSample; u_int32_t rawSampleLen; u_char *endp; /* decode cursor */ u_int32_t *datap; u_int32_t datagramVersion; u_int32_t sampleType; u_int32_t ds_class; u_int32_t ds_index; /* generic interface counter sample */ SFLIf_counters ifCounters; /* sample stream info */ u_int32_t sysUpTime; u_int32_t sequenceNo; u_int32_t sampledPacketSize; u_int32_t samplesGenerated; u_int32_t meanSkipCount; u_int32_t samplePool; u_int32_t dropEvents; /* the sampled header */ u_int32_t packet_data_tag; u_int32_t headerProtocol; u_char *header; int headerLen; u_int32_t stripped; /* header decode */ int gotIPV4; int offsetToIPV4; int gotIPV6; int offsetToIPV6; int offsetToPayload; SFLAddress ipsrc; SFLAddress ipdst; u_int32_t dcd_ipProtocol; u_int32_t dcd_ipTos; u_int32_t dcd_ipTTL; u_int32_t dcd_sport; u_int32_t dcd_dport; u_int32_t dcd_tcpFlags; u_int32_t ip_fragmentOffset; u_int32_t udp_pduLen; /* ports */ u_int32_t inputPortFormat; u_int32_t outputPortFormat; u_int32_t inputPort; u_int32_t outputPort; /* ethernet */ u_int32_t eth_type; u_int32_t eth_len; u_char eth_src[8]; u_char eth_dst[8]; /* vlan */ u_int32_t in_vlan; u_int32_t in_priority; u_int32_t internalPriority; u_int32_t out_vlan; u_int32_t out_priority; int vlanFilterReject; /* extended data fields */ u_int32_t num_extended; u_int32_t extended_data_tag; #define SASAMPLE_EXTENDED_DATA_SWITCH 1 #define SASAMPLE_EXTENDED_DATA_ROUTER 4 #define SASAMPLE_EXTENDED_DATA_GATEWAY 8 #define SASAMPLE_EXTENDED_DATA_USER 16 #define SASAMPLE_EXTENDED_DATA_URL 32 #define SASAMPLE_EXTENDED_DATA_MPLS 64 #define SASAMPLE_EXTENDED_DATA_NAT 128 #define SASAMPLE_EXTENDED_DATA_MPLS_TUNNEL 256 #define SASAMPLE_EXTENDED_DATA_MPLS_VC 512 #define SASAMPLE_EXTENDED_DATA_MPLS_FTN 1024 #define SASAMPLE_EXTENDED_DATA_MPLS_LDP_FEC 2048 #define SASAMPLE_EXTENDED_DATA_VLAN_TUNNEL 4096 /* IP forwarding info */ SFLAddress nextHop; u_int32_t srcMask; u_int32_t dstMask; /* BGP info */ SFLAddress bgp_nextHop; u_int32_t my_as; u_int32_t src_as; u_int32_t src_peer_as; u_int32_t dst_as_path_len; u_int32_t *dst_as_path; /* note: version 4 dst as path segments just get printed, not stored here, however * the dst_peer and dst_as are filled in, since those are used for netflow encoding */ u_int32_t dst_peer_as; u_int32_t dst_as; u_int32_t communities_len; u_int32_t *communities; u_int32_t localpref; /* user id */ #define SA_MAX_EXTENDED_USER_LEN 200 u_int32_t src_user_charset; u_int32_t src_user_len; char src_user[SA_MAX_EXTENDED_USER_LEN+1]; u_int32_t dst_user_charset; u_int32_t dst_user_len; char dst_user[SA_MAX_EXTENDED_USER_LEN+1]; /* url */ #define SA_MAX_EXTENDED_URL_LEN 200 #define SA_MAX_EXTENDED_HOST_LEN 200 u_int32_t url_direction; u_int32_t url_len; char url[SA_MAX_EXTENDED_URL_LEN+1]; u_int32_t host_len; char host[SA_MAX_EXTENDED_HOST_LEN+1]; /* mpls */ SFLAddress mpls_nextHop; /* nat */ SFLAddress nat_src; SFLAddress nat_dst; /* counter blocks */ u_int32_t statsSamplingInterval; u_int32_t counterBlockVersion; /* exception handler context */ jmp_buf env; #define SFABORT(s, r) longjmp((s)->env, (r)) #define SF_ABORT_EOS 1 #define SF_ABORT_DECODE_ERROR 2 #define SF_ABORT_LENGTH_ERROR 3 } SFSample; /* Cisco netflow version 5 record format */ typedef struct _NFFlow5 { u_int32_t srcIP; u_int32_t dstIP; u_int32_t nextHop; u_int16_t if_in; u_int16_t if_out; u_int32_t frames; u_int32_t bytes; u_int32_t firstTime; u_int32_t lastTime; u_int16_t srcPort; u_int16_t dstPort; u_int8_t pad1; u_int8_t tcpFlags; u_int8_t ipProto; u_int8_t ipTos; u_int16_t srcAS; u_int16_t dstAS; u_int8_t srcMask; /* No. bits */ u_int8_t dstMask; /* No. bits */ u_int16_t pad2; } NFFlow5; typedef struct _NFFlowHdr5 { u_int16_t version; u_int16_t count; u_int32_t sysUpTime; u_int32_t unixSeconds; u_int32_t unixNanoSeconds; u_int32_t flowSequence; u_int8_t engineType; u_int8_t engineId; u_int16_t sampling_interval; } NFFlowHdr5; typedef struct _NFFlowPkt5 { NFFlowHdr5 hdr; NFFlow5 flow; /* normally an array, but here we always send just 1 at a time */ } NFFlowPkt5; /*_________________---------------------------__________________ _________________ sf_log __________________ -----------------___________________________------------------ */ void sf_log(char *fmt, ...) { /* don't print anything if we are exporting tcpdump format or tabular format instead */ if(sfConfig.outputFormat == SFLFMT_FULL) { va_list args; va_start(args, fmt); vprintf(fmt, args); } } /*_________________---------------------------__________________ _________________ printHex __________________ -----------------___________________________------------------ */ static u_char bin2hex(int nib) { return (nib < 10) ? ('0' + nib) : ('A' - 10 + nib); } int printHex(const u_char *a, int len, u_char *buf, int bufLen, int marker, int bytesPerOutputLine) { int b = 0, i = 0; for(; i < len; i++) { u_char byte; if(b > (bufLen - 10)) break; if(marker > 0 && i == marker) { buf[b++] = '<'; buf[b++] = '*'; buf[b++] = '>'; buf[b++] = '-'; } byte = a[i]; buf[b++] = bin2hex(byte >> 4); buf[b++] = bin2hex(byte & 0x0f); if(i > 0 && (i % bytesPerOutputLine) == 0) buf[b++] = '\n'; else { // separate the bytes with a dash if (i < (len - 1)) buf[b++] = '-'; } } buf[b] = '\0'; return b; } /*_________________---------------------------__________________ _________________ IP_to_a __________________ -----------------___________________________------------------ */ char *IP_to_a(u_int32_t ipaddr, char *buf) { u_char *ip = (u_char *)&ipaddr; sprintf(buf, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); return buf; } static char *printAddress(SFLAddress *address, char *buf, int bufLen) { if(address->type == SFLADDRESSTYPE_IP_V4) IP_to_a(address->address.ip_v4.s_addr, buf); else { u_char *b = address->address.ip_v6.s6_addr; // should really be: snprintf(buf, buflen,...) but snprintf() is not always available sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7],b[8],b[9],b[10],b[11],b[12],b[13],b[14],b[15],b[16]); } return buf; } /*_________________---------------------------__________________ _________________ sampleFilterOK __________________ -----------------___________________________------------------ */ int sampleFilterOK(SFSample *sample) { // the vlan filter will only reject a sample if both in_vlan and out_vlan are rejected. If the // vlan was not reported in an SFLExtended_Switch struct, but was only picked up from the 802.1q header // then the out_vlan will be 0, so to be sure you are rejecting vlan 1, you may need to reject both // vlan 0 and vlan 1. return(sfConfig.gotVlanFilter == NO || sfConfig.vlanFilter[sample->in_vlan] || sfConfig.vlanFilter[sample->out_vlan]); } /*_________________---------------------------__________________ _________________ writeFlowLine __________________ -----------------___________________________------------------ */ static void writeFlowLine(SFSample *sample) { char agentIP[51], srcIP[51], dstIP[51]; // source printf("FLOW,%s,%d,%d,", printAddress(&sample->agent_addr, agentIP, 50), sample->inputPort, sample->outputPort); // layer 2 printf("%02x%02x%02x%02x%02x%02x,%02x%02x%02x%02x%02x%02x,0x%04x,%d,%d", sample->eth_src[0], sample->eth_src[1], sample->eth_src[2], sample->eth_src[3], sample->eth_src[4], sample->eth_src[5], sample->eth_dst[0], sample->eth_dst[1], sample->eth_dst[2], sample->eth_dst[3], sample->eth_dst[4], sample->eth_dst[5], sample->eth_type, sample->in_vlan, sample->out_vlan); // layer 3/4 printf(",%s,%s,%d,0x%02x,%d,%d,%d,0x%02x", printAddress(&sample->ipsrc, srcIP, 50), printAddress(&sample->ipdst, dstIP, 50), sample->dcd_ipProtocol, sample->dcd_ipTos, sample->dcd_ipTTL, sample->dcd_sport, sample->dcd_dport, sample->dcd_tcpFlags); // bytes printf(",%d,%d,%d\n", sample->sampledPacketSize, sample->sampledPacketSize - sample->stripped - sample->offsetToIPV4, sample->meanSkipCount); } /*_________________---------------------------__________________ _________________ writeCountersLine __________________ -----------------___________________________------------------ */ static void writeCountersLine(SFSample *sample) { // source char agentIP[51]; printf("CNTR,%s,", printAddress(&sample->agent_addr, agentIP, 50)); #ifdef WIN32 printf("%lu,%lu,%I64u,%lu,%lu,%I64u,%lu,%lu,%lu,%lu,%lu,%lu,%I64u,%lu,%lu,%lu,%lu,%lu,%lu\n", #else printf("%lu,%lu,%llu,%lu,%lu,%llu,%lu,%lu,%lu,%lu,%lu,%lu,%llu,%lu,%lu,%lu,%lu,%lu,%lu\n", #endif sample->ifCounters.ifIndex, sample->ifCounters.ifType, sample->ifCounters.ifSpeed, sample->ifCounters.ifDirection, sample->ifCounters.ifStatus, sample->ifCounters.ifInOctets, sample->ifCounters.ifInUcastPkts, sample->ifCounters.ifInMulticastPkts, sample->ifCounters.ifInBroadcastPkts, sample->ifCounters.ifInDiscards, sample->ifCounters.ifInErrors, sample->ifCounters.ifInUnknownProtos, sample->ifCounters.ifOutOctets, sample->ifCounters.ifOutUcastPkts, sample->ifCounters.ifOutMulticastPkts, sample->ifCounters.ifOutBroadcastPkts, sample->ifCounters.ifOutDiscards, sample->ifCounters.ifOutErrors, sample->ifCounters.ifPromiscuousMode); } /*_________________---------------------------__________________ _________________ receiveError __________________ -----------------___________________________------------------ */ static void receiveError(SFSample *sample, char *errm, int hexdump) { char ipbuf[51]; u_char scratch[6000]; char *msg = ""; char *hex = ""; u_int32_t markOffset = (u_char *)sample->datap - sample->rawSample; if(errm) msg = errm; if(hexdump) { printHex(sample->rawSample, sample->rawSampleLen, scratch, 6000, markOffset, 16); hex = scratch; } fprintf(stderr, "%s (source IP = %s) %s\n", msg, IP_to_a(sample->sourceIP.s_addr, ipbuf), hex); SFABORT(sample, SF_ABORT_DECODE_ERROR); } /*_________________---------------------------__________________ _________________ lengthCheck __________________ -----------------___________________________------------------ */ static void lengthCheck(SFSample *sample, char *description, u_char *start, int len) { u_int32_t actualLen = (u_char *)sample->datap - start; if(actualLen != len) { fprintf(stderr, "%s length error (expected %d, found %d)\n", description, len, actualLen); SFABORT(sample, SF_ABORT_LENGTH_ERROR); } } /*_________________---------------------------__________________ _________________ decodeLinkLayer __________________ -----------------___________________________------------------ store the offset to the start of the ipv4 header in the sequence_number field or -1 if not found. Decode the 802.1d if it's there. */ #define NFT_ETHHDR_SIZ 14 #define NFT_8022_SIZ 3 #define NFT_MAX_8023_LEN 1500 #define NFT_MIN_SIZ (NFT_ETHHDR_SIZ + sizeof(struct myiphdr)) static void decodeLinkLayer(SFSample *sample) { u_char *start = (u_char *)sample->header; u_char *end = start + sample->headerLen; u_char *ptr = start; u_int16_t type_len; /* assume not found */ sample->gotIPV4 = NO; sample->gotIPV6 = NO; if(sample->headerLen < NFT_ETHHDR_SIZ) return; /* not enough for an Ethernet header */ sf_log("dstMAC %02x%02x%02x%02x%02x%02x\n", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); memcpy(sample->eth_dst, ptr, 6); ptr += 6; sf_log("srcMAC %02x%02x%02x%02x%02x%02x\n", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); memcpy(sample->eth_src, ptr, 6); ptr += 6; type_len = (ptr[0] << 8) + ptr[1]; ptr += 2; if(type_len == 0x8100) { /* VLAN - next two bytes */ u_int32_t vlanData = (ptr[0] << 8) + ptr[1]; u_int32_t vlan = vlanData & 0x0fff; u_int32_t priority = vlanData >> 13; ptr += 2; /* _____________________________________ */ /* | pri | c | vlan-id | */ /* ------------------------------------- */ /* [priority = 3bits] [Canonical Format Flag = 1bit] [vlan-id = 12 bits] */ sf_log("decodedVLAN %lu\n", vlan); sf_log("decodedPriority %lu\n", priority); sample->in_vlan = vlan; /* now get the type_len again (next two bytes) */ type_len = (ptr[0] << 8) + ptr[1]; ptr += 2; } /* now we're just looking for IP */ if(sample->headerLen < NFT_MIN_SIZ) return; /* not enough for an IPv4 header */ /* peek for IPX */ if(type_len == 0x0200 || type_len == 0x0201 || type_len == 0x0600) { #define IPX_HDR_LEN 30 #define IPX_MAX_DATA 546 int ipxChecksum = (ptr[0] == 0xff && ptr[1] == 0xff); int ipxLen = (ptr[2] << 8) + ptr[3]; if(ipxChecksum && ipxLen >= IPX_HDR_LEN && ipxLen <= (IPX_HDR_LEN + IPX_MAX_DATA)) /* we don't do anything with IPX here */ return; } if(type_len <= NFT_MAX_8023_LEN) { /* assume 802.3+802.2 header */ /* check for SNAP */ if(ptr[0] == 0xAA && ptr[1] == 0xAA && ptr[2] == 0x03) { ptr += 3; if(ptr[0] != 0 || ptr[1] != 0 || ptr[2] != 0) { sf_log("VSNAP_OUI %02X-%02X-%02X\n", ptr[0], ptr[1], ptr[2]); return; /* no further decode for vendor-specific protocol */ } ptr += 3; /* OUI == 00-00-00 means the next two bytes are the ethernet type (RFC 2895) */ type_len = (ptr[0] << 8) + ptr[1]; ptr += 2; } else { if (ptr[0] == 0x06 && ptr[1] == 0x06 && (ptr[2] & 0x01)) { /* IP over 8022 */ ptr += 3; /* force the type_len to be IP so we can inline the IP decode below */ type_len = 0x0800; } else return; } } /* assume type_len is an ethernet-type now */ sample->eth_type = type_len; if(type_len == 0x0800) { /* IPV4 */ if((end - ptr) < sizeof(struct myiphdr)) return; /* look at first byte of header.... */ /* ___________________________ */ /* | version | hdrlen | */ /* --------------------------- */ if((*ptr >> 4) != 4) return; /* not version 4 */ if((*ptr & 15) < 5) return; /* not IP (hdr len must be 5 quads or more) */ /* survived all the tests - store the offset to the start of the ip header */ sample->gotIPV4 = YES; sample->offsetToIPV4 = (ptr - start); } if(type_len == 0x86DD) { /* IPV6 */ /* look at first byte of header.... */ if((*ptr >> 4) != 6) return; /* not version 6 */ /* survived all the tests - store the offset to the start of the ip6 header */ sample->gotIPV6 = YES; sample->offsetToIPV6 = (ptr - start); } } /*_________________---------------------------__________________ _________________ decodeIPLayer4 __________________ -----------------___________________________------------------ */ static void decodeIPLayer4(SFSample *sample, u_char *ptr, u_int32_t ipProtocol) { u_char *end = sample->header + sample->headerLen; if(ptr > (end - 8)) return; // not enough header bytes left switch(ipProtocol) { case 1: /* ICMP */ { struct myicmphdr icmp; memcpy(&icmp, ptr, sizeof(icmp)); sf_log("ICMPType %u\n", icmp.type); sf_log("ICMPCode %u\n", icmp.code); sample->dcd_sport = icmp.type; sample->dcd_dport = icmp.code; sample->offsetToPayload = ptr + sizeof(icmp) - sample->header; } break; case 6: /* TCP */ { struct mytcphdr tcp; int headerBytes; memcpy(&tcp, ptr, sizeof(tcp)); sample->dcd_sport = ntohs(tcp.th_sport); sample->dcd_dport = ntohs(tcp.th_dport); sample->dcd_tcpFlags = tcp.th_flags; sf_log("TCPSrcPort %u\n", sample->dcd_sport); sf_log("TCPDstPort %u\n",sample->dcd_dport); sf_log("TCPFlags %u\n", sample->dcd_tcpFlags); headerBytes = (tcp.th_off_and_unused >> 4) * 4; sample->offsetToPayload = ptr + headerBytes - sample->header; } break; case 17: /* UDP */ { struct myudphdr udp; memcpy(&udp, ptr, sizeof(udp)); sample->dcd_sport = ntohs(udp.uh_sport); sample->dcd_dport = ntohs(udp.uh_dport); sample->udp_pduLen = ntohs(udp.uh_ulen); sf_log("UDPSrcPort %u\n", sample->dcd_sport); sf_log("UDPDstPort %u\n", sample->dcd_dport); sf_log("UDPBytes %u\n", sample->udp_pduLen); sample->offsetToPayload = ptr + sizeof(udp) - sample->header; } break; default: /* some other protcol */ sample->offsetToPayload = ptr - sample->header; break; } } /*_________________---------------------------__________________ _________________ decodeIPV4 __________________ -----------------___________________________------------------ */ static void decodeIPV4(SFSample *sample) { if(sample->gotIPV4) { char buf[51]; u_char *ptr = sample->header + sample->offsetToIPV4; /* Create a local copy of the IP header (cannot overlay structure in case it is not quad-aligned...some platforms would core-dump if we tried that). It's OK coz this probably performs just as well anyway. */ struct myiphdr ip; memcpy(&ip, ptr, sizeof(ip)); /* Value copy all ip elements into sample */ sample->ipsrc.type = SFLADDRESSTYPE_IP_V4; sample->ipsrc.address.ip_v4.s_addr = ip.saddr; sample->ipdst.type = SFLADDRESSTYPE_IP_V4; sample->ipdst.address.ip_v4.s_addr = ip.daddr; sample->dcd_ipProtocol = ip.protocol; sample->dcd_ipTos = ip.tos; sample->dcd_ipTTL = ip.ttl; sf_log("ip.tot_len %d\n", ntohs(ip.tot_len)); /* Log out the decoded IP fields */ sf_log("srcIP %s\n", printAddress(&sample->ipsrc, buf, 50)); sf_log("dstIP %s\n", printAddress(&sample->ipdst, buf, 50)); sf_log("IPProtocol %u\n", sample->dcd_ipProtocol); sf_log("IPTOS %u\n", sample->dcd_ipTos); sf_log("IPTTL %u\n", sample->dcd_ipTTL); /* check for fragments */ sample->ip_fragmentOffset = ntohs(ip.frag_off) & 0x1FFF; if(sample->ip_fragmentOffset > 0) { sf_log("IPFragmentOffset %u\n", sample->ip_fragmentOffset); } else { /* advance the pointer to the next protocol layer */ /* ip headerLen is expressed as a number of quads */ ptr += (ip.version_and_headerLen & 0x0f) * 4; decodeIPLayer4(sample, ptr, ip.protocol); } } } /*_________________---------------------------__________________ _________________ decodeIPV6 __________________ -----------------___________________________------------------ */ static void decodeIPV6(SFSample *sample) { u_int16_t payloadLen; u_int32_t label; u_int32_t nextHeader; u_char *end = sample->header + sample->headerLen; if(sample->gotIPV6) { char buf[51]; u_char *ptr = sample->header + sample->offsetToIPV6; // check the version { int ipVersion = (*ptr >> 4); if(ipVersion != 6) { sf_log("header decode error: unexpected IP version: %d\n", ipVersion); return; } } // get the tos (priority) sample->dcd_ipTos = *ptr++ & 15; sf_log("IPTOS %u\n", sample->dcd_ipTos); // 24-bit label label = *ptr++; label <<= 8; label += *ptr++; label <<= 8; label += *ptr++; sf_log("IP6_label 0x%lx\n", label); // payload payloadLen = (ptr[0] << 8) + ptr[1]; ptr += 2; // if payload is zero, that implies a jumbo payload if(payloadLen == 0) sf_log("IPV6_payloadLen \n"); else sf_log("IPV6_payloadLen %u\n", payloadLen); // next header nextHeader = *ptr++; // TTL sample->dcd_ipTTL = *ptr++; sf_log("IPTTL %u\n", sample->dcd_ipTTL); {// src and dst address char buf[101]; sample->ipsrc.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipsrc.address, ptr, 16); ptr +=16; sf_log("srcIP6 %s\n", printAddress(&sample->ipsrc, buf, 100)); sample->ipdst.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipdst.address, ptr, 16); ptr +=16; sf_log("dstIP6 %s\n", printAddress(&sample->ipdst, buf, 100)); } // skip over some common header extensions... // http://searchnetworking.techtarget.com/originalContent/0,289142,sid7_gci870277,00.html while(nextHeader == 0 || // hop nextHeader == 43 || // routing nextHeader == 44 || // fragment // nextHeader == 50 || // encryption - don't bother coz we'll not be able to read any further nextHeader == 51 || // auth nextHeader == 60) { // destination options u_int32_t optionLen, skip; sf_log("IP6HeaderExtension: %d\n", nextHeader); nextHeader = ptr[0]; optionLen = 8 * (ptr[1] + 1); // second byte gives option len in 8-byte chunks, not counting first 8 skip = optionLen - 2; ptr += skip; if(ptr > end) return; // ran off the end of the header } // now that we have eliminated the extension headers, nextHeader should have what we want to // remember as the ip protocol... sample->dcd_ipProtocol = nextHeader; sf_log("IPProtocol %u\n", sample->dcd_ipProtocol); decodeIPLayer4(sample, ptr, sample->dcd_ipProtocol); } } /*_________________---------------------------__________________ _________________ readPcapHeader __________________ -----------------___________________________------------------ */ #define TCPDUMP_MAGIC 0xa1b2c3d4 /* from libpcap-0.5: savefile.c */ #define DLT_EN10MB 1 /* from libpcap-0.5: net/bpf.h */ #define PCAP_VERSION_MAJOR 2 /* from libpcap-0.5: pcap.h */ #define PCAP_VERSION_MINOR 4 /* from libpcap-0.5: pcap.h */ static void readPcapHeader() { struct pcap_file_header hdr; if(fread(&hdr, sizeof(hdr), 1, sfConfig.readPcapFile) != 1) { fprintf(stderr, "unable to read pcap header from %s : %s\n", sfConfig.readPcapFileName, strerror(errno)); exit(-30); } if(hdr.magic != TCPDUMP_MAGIC) { if(hdr.magic == MyByteSwap32(TCPDUMP_MAGIC)) { sfConfig.pcapSwap = YES; hdr.version_major = MyByteSwap16(hdr.version_major); hdr.version_minor = MyByteSwap16(hdr.version_minor); hdr.thiszone = MyByteSwap32(hdr.thiszone); hdr.sigfigs = MyByteSwap32(hdr.sigfigs); hdr.snaplen = MyByteSwap32(hdr.snaplen); hdr.linktype = MyByteSwap32(hdr.linktype); } else { fprintf(stderr, "%s not recognized as a tcpdump file\n(magic number = %08x instead of %08x)\n", sfConfig.readPcapFileName, hdr.magic, TCPDUMP_MAGIC); exit(-31); } } fprintf(stderr, "pcap version=%d.%d snaplen=%d linktype=%d \n", hdr.version_major, hdr.version_minor, hdr.snaplen, hdr.linktype); sfConfig.readPcapHdr = hdr; } /*_________________---------------------------__________________ _________________ writePcapHeader __________________ -----------------___________________________------------------ */ #define DLT_EN10MB 1 /* from libpcap-0.5: net/bpf.h */ #define PCAP_VERSION_MAJOR 2 /* from libpcap-0.5: pcap.h */ #define PCAP_VERSION_MINOR 4 /* from libpcap-0.5: pcap.h */ static void writePcapHeader() { struct pcap_file_header hdr; memset(&hdr, 0, sizeof(hdr)); hdr.magic = TCPDUMP_MAGIC; hdr.version_major = PCAP_VERSION_MAJOR; hdr.version_minor = PCAP_VERSION_MINOR; hdr.thiszone = 0; hdr.snaplen = 128; hdr.sigfigs = 0; hdr.linktype = DLT_EN10MB; if (fwrite((char *)&hdr, sizeof(hdr), 1, stdout) != 1) { fprintf(stderr, "failed to write tcpdump header: %s\n", strerror(errno)); exit(-1); } fflush(stdout); } /*_________________---------------------------__________________ _________________ writePcapPacket __________________ -----------------___________________________------------------ */ static void writePcapPacket(SFSample *sample) { char buf[2048]; int bytes = 0; struct pcap_pkthdr hdr; hdr.ts_sec = time(NULL); hdr.ts_usec = 0; hdr.len = sample->sampledPacketSize; hdr.caplen = sample->headerLen; if(sfConfig.removeContent && sample->offsetToPayload) { // shorten the captured header to ensure no payload bytes are included hdr.caplen = sample->offsetToPayload; } // prepare the whole thing in a buffer first, in case we are piping the output // to another process and the reader expects it all to appear at once... memcpy(buf, &hdr, sizeof(hdr)); bytes = sizeof(hdr); if(sfConfig.tcpdumpHdrPad > 0) { memcpy(buf+bytes, sfConfig.zeroPad, sfConfig.tcpdumpHdrPad); bytes += sfConfig.tcpdumpHdrPad; } memcpy(buf+bytes, sample->header, hdr.caplen); bytes += hdr.caplen; if(fwrite(buf, bytes, 1, stdout) != 1) { fprintf(stderr, "writePcapPacket: packet write failed: %s\n", strerror(errno)); exit(-3); } fflush(stdout); } #ifdef SPOOFSOURCE /*_________________---------------------------__________________ _________________ in_checksum __________________ -----------------___________________________------------------ */ static u_int16_t in_checksum(u_int16_t *addr, int len) { int nleft = len; u_short *w = addr; u_short answer; int sum = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) sum += *(u_char *)w; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return (answer); } /*_________________---------------------------__________________ _________________ openNetFlowSocket_spoof __________________ -----------------___________________________------------------ */ static void openNetFlowSocket_spoof() { int on; if((sfConfig.netFlowOutputSocket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1) { fprintf(stderr, "netflow output raw socket open failed\n"); exit(-11); } on = 1; if(setsockopt(sfConfig.netFlowOutputSocket, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) { fprintf(stderr, "setsockopt( IP_HDRINCL ) failed\n"); exit(-13); } on = 1; if(setsockopt(sfConfig.netFlowOutputSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { fprintf(stderr, "setsockopt( SO_REUSEADDR ) failed\n"); exit(-14); } memset(&sfConfig.sendPkt, 0, sizeof(sfConfig.sendPkt)); sfConfig.sendPkt.ip.version_and_headerLen = 0x45; sfConfig.sendPkt.ip.protocol = IPPROTO_UDP; sfConfig.sendPkt.ip.ttl = 64; // IPDEFTTL sfConfig.ipid = 12000; // start counting from 12000 (just an arbitrary number) // sfConfig.ip->frag_off = htons(0x4000); // don't fragment // can't set the source address yet, but the dest address is known sfConfig.sendPkt.ip.daddr = sfConfig.netFlowOutputIP.s_addr; // can't do the ip_len and checksum until we know the size of the packet sfConfig.sendPkt.udp.uh_dport = htons(sfConfig.netFlowOutputPort); // might as well set the source port to be the same sfConfig.sendPkt.udp.uh_sport = htons(sfConfig.netFlowOutputPort); // can't do the udp_len or udp_checksum until we know the size of the packet } /*_________________---------------------------__________________ _________________ sendNetFlowDatagram_spoof __________________ -----------------___________________________------------------ */ static void sendNetFlowDatagram_spoof(SFSample *sample, NFFlowPkt5 *pkt) { u_int16_t packetLen = sizeof(*pkt) + sizeof(struct myiphdr) + sizeof(struct myudphdr); // copy the data into the send packet memcpy(sfConfig.sendPkt.data, (char *)pkt, sizeof(*pkt)); // increment the ip-id sfConfig.sendPkt.ip.id = htons(++sfConfig.ipid); // set the length fields in the ip and udp headers sfConfig.sendPkt.ip.tot_len = htons(packetLen); sfConfig.sendPkt.udp.uh_ulen = htons(packetLen - sizeof(struct myiphdr)); // set the source address to the source address of the input event sfConfig.sendPkt.ip.saddr = sample->agent_addr.address.ip_v4.s_addr; // IP header checksum sfConfig.sendPkt.ip.check = in_checksum((u_int16_t *)&sfConfig.sendPkt.ip, sizeof(struct myiphdr)); if (sfConfig.sendPkt.ip.check == 0) sfConfig.sendPkt.ip.check = 0xffff; // UDP Checksum // copy out those parts of the IP header that are supposed to be in the UDP checksum, // and blat them in front of the udp header (after saving what was there before). // Then compute the udp checksum. Then patch the saved data back again. { char *ptr; struct udpmagichdr { u_int32_t src; u_int32_t dst; u_char zero; u_char proto; u_short len; } h, saved; h.src = sfConfig.sendPkt.ip.saddr; h.dst = sfConfig.sendPkt.ip.daddr; h.zero = 0; h.proto = IPPROTO_UDP; h.len = sfConfig.sendPkt.udp.uh_ulen; // set the pointer to 12 bytes before the start of the udp header ptr = (char *)&sfConfig.sendPkt.udp; ptr -= sizeof(struct udpmagichdr); // save what's there memcpy(&saved, ptr, sizeof(struct udpmagichdr)); // blat in the replacement bytes memcpy(ptr, &h, sizeof(struct udpmagichdr)); // compute the checksum sfConfig.sendPkt.udp.uh_sum = 0; sfConfig.sendPkt.udp.uh_sum = in_checksum((u_int16_t *)ptr, ntohs(sfConfig.sendPkt.udp.uh_ulen) + sizeof(struct udpmagichdr)); if (sfConfig.sendPkt.udp.uh_sum == 0) sfConfig.sendPkt.udp.uh_sum = 0xffff; // copy the save bytes back again memcpy(ptr, &saved, sizeof(struct udpmagichdr)); { // now send the packet int bytesSent; struct sockaddr dest; struct sockaddr_in *to = (struct sockaddr_in *)&dest; memset(&dest, 0, sizeof(dest)); to->sin_family = AF_INET; to->sin_addr.s_addr = sfConfig.sendPkt.ip.daddr; if((bytesSent = sendto(sfConfig.netFlowOutputSocket, &sfConfig.sendPkt, packetLen, 0, &dest, sizeof(dest))) != packetLen) { fprintf(stderr, "sendto returned %d (expected %d): %s\n", bytesSent, packetLen, strerror(errno)); } } } } #endif /* SPOOFSOURCE */ /*_________________---------------------------__________________ _________________ openNetFlowSocket __________________ -----------------___________________________------------------ */ static void openNetFlowSocket() { #ifdef SPOOFSOURCE if(sfConfig.spoofSource) return openNetFlowSocket_spoof(); #endif { struct sockaddr_in addr; memset((char *)&addr,0,sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = ntohs(sfConfig.netFlowOutputPort); addr.sin_addr.s_addr = sfConfig.netFlowOutputIP.s_addr; // open an ordinary UDP socket if((sfConfig.netFlowOutputSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { fprintf(stderr, "netflow output socket open failed\n"); exit(-4); } /* connect to it so we can just use send() or write() to send on it */ if(connect(sfConfig.netFlowOutputSocket, (struct sockaddr *)&addr, sizeof(addr)) != 0) { fprintf(stderr, "connect() to netflow output socket failed\n"); exit(-5); } } } /*_________________---------------------------__________________ _________________ sendNetFlowDatagram __________________ -----------------___________________________------------------ */ static int NFFlowSequenceNo = 0; static void sendNetFlowDatagram(SFSample *sample) { NFFlowPkt5 pkt; time_t now = time(NULL); u_int32_t bytes; // ignore fragments if(sample->ip_fragmentOffset > 0) return; // count the bytes from the start of IP header, with the exception that // for udp packets we use the udp_pduLen. This is because the udp_pduLen // can be up tp 65535 bytes, which causes fragmentation at the IP layer. // Since the sampled fragments are discarded, we have to use this field // to get the total bytes estimates right. if(sample->udp_pduLen > 0) bytes = sample->udp_pduLen; else bytes = sample->sampledPacketSize - sample->stripped - sample->offsetToIPV4; memset(&pkt, 0, sizeof(pkt)); pkt.hdr.version = htons(5); pkt.hdr.count = htons(1); pkt.hdr.sysUpTime = htonl(now % (3600 * 24)) * 1000; /* pretend we started at midnight (milliseconds) */ pkt.hdr.unixSeconds = htonl(now); pkt.hdr.unixNanoSeconds = 0; /* no need to be more accurate than 1 second */ pkt.hdr.flowSequence = htonl(NFFlowSequenceNo++); pkt.flow.srcIP = sample->ipsrc.address.ip_v4.s_addr; pkt.flow.dstIP = sample->ipdst.address.ip_v4.s_addr; pkt.flow.nextHop = sample->nextHop.address.ip_v4.s_addr; pkt.flow.if_in = htons((u_int16_t)sample->inputPort); pkt.flow.if_out= htons((u_int16_t)sample->outputPort); if(!sfConfig.disableNetFlowScale) { pkt.flow.frames = htonl(sample->meanSkipCount); pkt.flow.bytes = htonl(sample->meanSkipCount * bytes); } else { /* set the sampling_interval header field too (used to be a 16-bit reserved field) */ u_int16_t samp_ival = (u_int16_t)sample->meanSkipCount; pkt.hdr.sampling_interval = htons(samp_ival & 0x4000); pkt.flow.frames = htonl(1); pkt.flow.bytes = htonl(bytes); } pkt.flow.firstTime = pkt.hdr.sysUpTime; /* set the start and end time to be now (in milliseconds since last boot) */ pkt.flow.lastTime = pkt.hdr.sysUpTime; pkt.flow.srcPort = htons((u_int16_t)sample->dcd_sport); pkt.flow.dstPort = htons((u_int16_t)sample->dcd_dport); pkt.flow.tcpFlags = sample->dcd_tcpFlags; pkt.flow.ipProto = sample->dcd_ipProtocol; pkt.flow.ipTos = sample->dcd_ipTos; if(sfConfig.netFlowPeerAS) { pkt.flow.srcAS = htons((u_int16_t)sample->src_peer_as); pkt.flow.dstAS = htons((u_int16_t)sample->dst_peer_as); } else { pkt.flow.srcAS = htons((u_int16_t)sample->src_as); pkt.flow.dstAS = htons((u_int16_t)sample->dst_as); } pkt.flow.srcMask = (u_int8_t)sample->srcMask; pkt.flow.dstMask = (u_int8_t)sample->dstMask; #ifdef SPOOFSOURCE if(sfConfig.spoofSource) { sendNetFlowDatagram_spoof(sample, &pkt); return; } #endif /* SPOOFSOURCE */ /* send non-blocking */ send(sfConfig.netFlowOutputSocket, (char *)&pkt, sizeof(pkt), 0); } /*_________________---------------------------__________________ _________________ read data fns __________________ -----------------___________________________------------------ */ static u_int32_t getData32(SFSample *sample) { if((u_char *)sample->datap > sample->endp) SFABORT(sample, SF_ABORT_EOS); return ntohl(*(sample->datap)++); } static u_int32_t getData32_nobswap(SFSample *sample) { if((u_char *)sample->datap > sample->endp) SFABORT(sample, SF_ABORT_EOS); return *(sample->datap)++; } static u_int64_t getData64(SFSample *sample) { u_int64_t tmpLo, tmpHi; tmpHi = getData32(sample); tmpLo = getData32(sample); return (tmpHi << 32) + tmpLo; } static void skipBytes(SFSample *sample, int skip) { int quads = (skip + 3) / 4; sample->datap += quads; if((u_char *)sample->datap > sample->endp) SFABORT(sample, SF_ABORT_EOS); } static u_int32_t sf_log_next32(SFSample *sample, char *fieldName) { u_int32_t val = getData32(sample); sf_log("%s %lu\n", fieldName, val); return val; } static u_int64_t sf_log_next64(SFSample *sample, char *fieldName) { u_int64_t val64 = getData64(sample); #ifdef WIN32 sf_log("%s %I64u\n", fieldName, val64); #else sf_log("%s %llu\n", fieldName, val64); #endif return val64; } void sf_log_percentage(SFSample *sample, char *fieldName) { u_int32_t hundredths = getData32(sample); if(hundredths == (u_int32_t)-1) sf_log("%s unknown\n", fieldName); else { float percent = (float)hundredths / 10.0; sf_log("%s %.1f\n", fieldName, percent); } } static u_int32_t getString(SFSample *sample, char *buf, int bufLen) { u_int32_t len, read_len; len = getData32(sample); // truncate if too long read_len = (len >= bufLen) ? (bufLen - 1) : len; memcpy(buf, sample->datap, read_len); buf[read_len] = '\0'; // null terminate skipBytes(sample, len); return len; } static u_int32_t getAddress(SFSample *sample, SFLAddress *address) { address->type = getData32(sample); if(address->type == SFLADDRESSTYPE_IP_V4) address->address.ip_v4.s_addr = getData32_nobswap(sample); else { memcpy(&address->address.ip_v6.s6_addr, sample->datap, 16); skipBytes(sample, 16); } return address->type; } static char *printTag(u_int32_t tag, char *buf, int bufLen) { // should really be: snprintf(buf, buflen,...) but snprintf() is not always available sprintf(buf, "%lu:%lu", (tag >> 12), (tag & 0x00000FFF)); return buf; } static void skipTLVRecord(SFSample *sample, u_int32_t tag, u_int32_t len, char *description) { char buf[51]; sf_log("skipping unknown %s: %s len=%d\n", description, printTag(tag, buf, 50), len); skipBytes(sample, len); } /*_________________---------------------------__________________ _________________ readExtendedSwitch __________________ -----------------___________________________------------------ */ static void readExtendedSwitch(SFSample *sample) { sf_log("extendedType SWITCH\n"); sample->in_vlan = getData32(sample); sample->in_priority = getData32(sample); sample->out_vlan = getData32(sample); sample->out_priority = getData32(sample); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_SWITCH; sf_log("in_vlan %lu\n", sample->in_vlan); sf_log("in_priority %lu\n", sample->in_priority); sf_log("out_vlan %lu\n", sample->out_vlan); sf_log("out_priority %lu\n", sample->out_priority); } /*_________________---------------------------__________________ _________________ readExtendedRouter __________________ -----------------___________________________------------------ */ static void readExtendedRouter(SFSample *sample) { u_int32_t addrType; char buf[51]; sf_log("extendedType ROUTER\n"); getAddress(sample, &sample->nextHop); sample->srcMask = getData32(sample); sample->dstMask = getData32(sample); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_ROUTER; sf_log("nextHop %s\n", printAddress(&sample->nextHop, buf, 50)); sf_log("srcSubnetMask %lu\n", sample->srcMask); sf_log("dstSubnetMask %lu\n", sample->dstMask); } /*_________________---------------------------__________________ _________________ readExtendedGateway_v2 __________________ -----------------___________________________------------------ */ static void readExtendedGateway_v2(SFSample *sample) { sf_log("extendedType GATEWAY\n"); sample->my_as = getData32(sample); sample->src_as = getData32(sample); sample->src_peer_as = getData32(sample); sample->dst_as_path_len = getData32(sample); /* just point at the dst_as_path array */ if(sample->dst_as_path_len > 0) { sample->dst_as_path = sample->datap; /* and skip over it in the input */ skipBytes(sample, sample->dst_as_path_len * 4); // fill in the dst and dst_peer fields too sample->dst_peer_as = ntohl(sample->dst_as_path[0]); sample->dst_as = ntohl(sample->dst_as_path[sample->dst_as_path_len - 1]); } sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_GATEWAY; sf_log("my_as %lu\n", sample->my_as); sf_log("src_as %lu\n", sample->src_as); sf_log("src_peer_as %lu\n", sample->src_peer_as); sf_log("dst_as %lu\n", sample->dst_as); sf_log("dst_peer_as %lu\n", sample->dst_peer_as); sf_log("dst_as_path_len %lu\n", sample->dst_as_path_len); if(sample->dst_as_path_len > 0) { u_int32_t i = 0; for(; i < sample->dst_as_path_len; i++) { if(i == 0) sf_log("dst_as_path "); else sf_log("-"); sf_log("%lu", ntohl(sample->dst_as_path[i])); } sf_log("\n"); } } /*_________________---------------------------__________________ _________________ readExtendedGateway __________________ -----------------___________________________------------------ */ static void readExtendedGateway(SFSample *sample) { u_int32_t segments; int seg; char buf[51]; sf_log("extendedType GATEWAY\n"); if(sample->datagramVersion >= 5) { getAddress(sample, &sample->bgp_nextHop); sf_log("bgp_nexthop %s\n", printAddress(&sample->bgp_nextHop, buf, 50)); } sample->my_as = getData32(sample); sample->src_as = getData32(sample); sample->src_peer_as = getData32(sample); sf_log("my_as %lu\n", sample->my_as); sf_log("src_as %lu\n", sample->src_as); sf_log("src_peer_as %lu\n", sample->src_peer_as); segments = getData32(sample); if(segments > 0) { sf_log("dst_as_path "); for(seg = 0; seg < segments; seg++) { u_int32_t seg_type; u_int32_t seg_len; int i; seg_type = getData32(sample); seg_len = getData32(sample); for(i = 0; i < seg_len; i++) { u_int32_t asNumber; asNumber = getData32(sample); /* mark the first one as the dst_peer_as */ if(i == 0 && seg == 0) sample->dst_peer_as = asNumber; else sf_log("-"); /* make sure the AS sets are in parentheses */ if(i == 0 && seg_type == SFLEXTENDED_AS_SET) sf_log("("); sf_log("%lu", asNumber); /* mark the last one as the dst_as */ if(seg == (segments - 1) && i == (seg_len - 1)) sample->dst_as = asNumber; } if(seg_type == SFLEXTENDED_AS_SET) sf_log(")"); } sf_log("\n"); } sf_log("dst_as %lu\n", sample->dst_as); sf_log("dst_peer_as %lu\n", sample->dst_peer_as); sample->communities_len = getData32(sample); /* just point at the communities array */ if(sample->communities_len > 0) sample->communities = sample->datap; /* and skip over it in the input */ skipBytes(sample, sample->communities_len * 4); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_GATEWAY; if(sample->communities_len > 0) { int j = 0; for(; j < sample->communities_len; j++) { if(j == 0) sf_log("BGP_communities "); else sf_log("-"); sf_log("%lu", ntohl(sample->communities[j])); } sf_log("\n"); } sample->localpref = getData32(sample); sf_log("BGP_localpref %lu\n", sample->localpref); } /*_________________---------------------------__________________ _________________ readExtendedUser __________________ -----------------___________________________------------------ */ static void readExtendedUser(SFSample *sample) { sf_log("extendedType USER\n"); if(sample->datagramVersion >= 5) { sample->src_user_charset = getData32(sample); sf_log("src_user_charset %d\n", sample->src_user_charset); } sample->src_user_len = getString(sample, sample->src_user, SA_MAX_EXTENDED_USER_LEN); if(sample->datagramVersion >= 5) { sample->dst_user_charset = getData32(sample); sf_log("dst_user_charset %d\n", sample->dst_user_charset); } sample->dst_user_len = getString(sample, sample->dst_user, SA_MAX_EXTENDED_USER_LEN); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_USER; sf_log("src_user %s\n", sample->src_user); sf_log("dst_user %s\n", sample->dst_user); } /*_________________---------------------------__________________ _________________ readExtendedUrl __________________ -----------------___________________________------------------ */ static void readExtendedUrl(SFSample *sample) { sf_log("extendedType URL\n"); sample->url_direction = getData32(sample); sf_log("url_direction %lu\n", sample->url_direction); sample->url_len = getString(sample, sample->url, SA_MAX_EXTENDED_URL_LEN); sf_log("url %s\n", sample->url); if(sample->datagramVersion >= 5) { sample->host_len = getString(sample, sample->host, SA_MAX_EXTENDED_HOST_LEN); sf_log("host %s\n", sample->host); } sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_URL; } /*_________________---------------------------__________________ _________________ mplsLabelStack __________________ -----------------___________________________------------------ */ static void mplsLabelStack(SFSample *sample, char *fieldName) { SFLLabelStack lstk; u_int32_t lab; lstk.depth = getData32(sample); /* just point at the lablelstack array */ if(lstk.depth > 0) lstk.stack = (u_int32_t *)sample->datap; /* and skip over it in the input */ skipBytes(sample, lstk.depth * 4); if(lstk.depth > 0) { int j = 0; for(; j < lstk.depth; j++) { if(j == 0) sf_log("%s ", fieldName); else sf_log("-"); lab = ntohl(lstk.stack[j]); sf_log("%lu.%lu.%lu.%lu", (lab >> 12), // label (lab >> 9) & 7, // experimental (lab >> 8) & 1, // bottom of stack (lab & 255)); // TTL } sf_log("\n"); } } /*_________________---------------------------__________________ _________________ readExtendedMpls __________________ -----------------___________________________------------------ */ static void readExtendedMpls(SFSample *sample) { char buf[51]; sf_log("extendedType MPLS\n"); getAddress(sample, &sample->mpls_nextHop); sf_log("mpls_nexthop %s\n", printAddress(&sample->mpls_nextHop, buf, 50)); mplsLabelStack(sample, "mpls_input_stack"); mplsLabelStack(sample, "mpls_output_stack"); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS; } /*_________________---------------------------__________________ _________________ readExtendedNat __________________ -----------------___________________________------------------ */ static void readExtendedNat(SFSample *sample) { char buf[51]; sf_log("extendedType NAT\n"); getAddress(sample, &sample->nat_src); sf_log("nat_src %s\n", printAddress(&sample->nat_src, buf, 50)); getAddress(sample, &sample->nat_dst); sf_log("nat_dst %s\n", printAddress(&sample->nat_dst, buf, 50)); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_NAT; } /*_________________---------------------------__________________ _________________ readExtendedMplsTunnel __________________ -----------------___________________________------------------ */ static void readExtendedMplsTunnel(SFSample *sample) { #define SA_MAX_TUNNELNAME_LEN 100 char tunnel_name[SA_MAX_TUNNELNAME_LEN+1]; u_int32_t tunnel_id, tunnel_cos; if(getString(sample, tunnel_name, SA_MAX_TUNNELNAME_LEN) > 0) sf_log("mpls_tunnel_lsp_name %s\n", tunnel_name); tunnel_id = getData32(sample); sf_log("mpls_tunnel_id %lu\n", tunnel_id); tunnel_cos = getData32(sample); sf_log("mpls_tunnel_cos %lu\n", tunnel_cos); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS_TUNNEL; } /*_________________---------------------------__________________ _________________ readExtendedMplsVC __________________ -----------------___________________________------------------ */ static void readExtendedMplsVC(SFSample *sample) { #define SA_MAX_VCNAME_LEN 100 char vc_name[SA_MAX_VCNAME_LEN+1]; u_int32_t vll_vc_id, vc_cos; if(getString(sample, vc_name, SA_MAX_VCNAME_LEN) > 0) sf_log("mpls_vc_name %s\n", vc_name); vll_vc_id = getData32(sample); sf_log("mpls_vll_vc_id %lu\n", vll_vc_id); vc_cos = getData32(sample); sf_log("mpls_vc_cos %lu\n", vc_cos); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS_VC; } /*_________________---------------------------__________________ _________________ readExtendedMplsFTN __________________ -----------------___________________________------------------ */ static void readExtendedMplsFTN(SFSample *sample) { #define SA_MAX_FTN_LEN 100 char ftn_descr[SA_MAX_FTN_LEN+1]; u_int32_t ftn_mask; if(getString(sample, ftn_descr, SA_MAX_FTN_LEN) > 0) sf_log("mpls_ftn_descr %s\n", ftn_descr); ftn_mask = getData32(sample); sf_log("mpls_ftn_mask %lu\n", ftn_mask); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS_FTN; } /*_________________---------------------------__________________ _________________ readExtendedMplsLDP_FEC __________________ -----------------___________________________------------------ */ static void readExtendedMplsLDP_FEC(SFSample *sample) { u_int32_t fec_addr_prefix_len = getData32(sample); sf_log("mpls_fec_addr_prefix_len %lu\n", fec_addr_prefix_len); sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_MPLS_LDP_FEC; } /*_________________---------------------------__________________ _________________ readExtendedVlanTunnel __________________ -----------------___________________________------------------ */ static void readExtendedVlanTunnel(SFSample *sample) { u_int32_t lab; SFLLabelStack lstk; lstk.depth = getData32(sample); /* just point at the lablelstack array */ if(lstk.depth > 0) lstk.stack = (u_int32_t *)sample->datap; /* and skip over it in the input */ skipBytes(sample, lstk.depth * 4); if(lstk.depth > 0) { int j = 0; for(; j < lstk.depth; j++) { if(j == 0) sf_log("vlan_tunnel "); else sf_log("-"); lab = ntohl(lstk.stack[j]); sf_log("0x%04x.%lu.%lu.%lu", (lab >> 16), // TPI (lab >> 13) & 7, // priority (lab >> 12) & 1, // CFI (lab & 4095)); // VLAN } sf_log("\n"); } sample->extended_data_tag |= SASAMPLE_EXTENDED_DATA_VLAN_TUNNEL; } /*_________________---------------------------__________________ _________________ readExtendedProcess __________________ -----------------___________________________------------------ */ static void readExtendedProcess(SFSample *sample) { char pname[51]; u_int32_t num_processes, i; sf_log("extendedType process\n"); num_processes = getData32(sample); for(i = 0; i < num_processes; i++) { u_int32_t pid = getData32(sample); if(getString(sample, pname, 50) > 0) sf_log("pid %lu %s\n", pid, pname); else sf_log("pid %lu \n", pid); } } /*_________________---------------------------__________________ _________________ readFlowSample_header __________________ -----------------___________________________------------------ */ static void readFlowSample_header(SFSample *sample) { sf_log("flowSampleType HEADER\n"); sample->headerProtocol = getData32(sample); sf_log("headerProtocol %lu\n", sample->headerProtocol); sample->sampledPacketSize = getData32(sample); sf_log("sampledPacketSize %lu\n", sample->sampledPacketSize); if(sample->datagramVersion > 4) { // stripped count introduced in sFlow version 5 sample->stripped = getData32(sample); sf_log("strippedBytes %lu\n", sample->stripped); } sample->headerLen = getData32(sample); sf_log("headerLen %lu\n", sample->headerLen); sample->header = (u_char *)sample->datap; /* just point at the header */ skipBytes(sample, sample->headerLen); { char scratch[2000]; printHex(sample->header, sample->headerLen, scratch, 2000, 0, 2000); sf_log("headerBytes %s\n", scratch); } switch(sample->headerProtocol) { /* the header protocol tells us where to jump into the decode */ case SFLHEADER_ETHERNET_ISO8023: decodeLinkLayer(sample); break; case SFLHEADER_IPv4: sample->gotIPV4 = YES; sample->offsetToIPV4 = 0; break; case SFLHEADER_IPv6: sample->gotIPV6 = YES; sample->offsetToIPV6 = 0; break; case SFLHEADER_ISO88024_TOKENBUS: case SFLHEADER_ISO88025_TOKENRING: case SFLHEADER_FDDI: case SFLHEADER_FRAME_RELAY: case SFLHEADER_X25: case SFLHEADER_PPP: case SFLHEADER_SMDS: case SFLHEADER_AAL5: case SFLHEADER_AAL5_IP: case SFLHEADER_MPLS: sf_log("NO_DECODE headerProtocol=%d\n", sample->headerProtocol); break; default: fprintf(stderr, "undefined headerProtocol = %d\n", sample->headerProtocol); exit(-12); } if(sample->gotIPV4) { // report the size of the original IPPdu (including the IP header) sf_log("IPSize %d\n", sample->sampledPacketSize - sample->stripped - sample->offsetToIPV4); decodeIPV4(sample); } else if(sample->gotIPV6) { // report the size of the original IPPdu (including the IP header) sf_log("IPSize %d\n", sample->sampledPacketSize - sample->stripped - sample->offsetToIPV6); decodeIPV6(sample); } } /*_________________---------------------------__________________ _________________ readFlowSample_ethernet __________________ -----------------___________________________------------------ */ static void readFlowSample_ethernet(SFSample *sample) { u_char *p; sf_log("flowSampleType ETHERNET\n"); sample->eth_len = getData32(sample); memcpy(sample->eth_src, sample->datap, 6); skipBytes(sample, 6); memcpy(sample->eth_dst, sample->datap, 6); skipBytes(sample, 6); sample->eth_type = getData32(sample); sf_log("ethernet_type %lu\n", sample->eth_type); sf_log("ethernet_len %lu\n", sample->eth_len); p = sample->eth_src; sf_log("ethernet_src %02x%02x%02x%02x%02x%02x\n", p[0], p[1], p[2], p[3], p[4], p[5]); p = sample->eth_dst; sf_log("ethernet_dst %02x%02x%02x%02x%02x%02x\n", p[0], p[1], p[2], p[3], p[4], p[5]); } /*_________________---------------------------__________________ _________________ readFlowSample_IPv4 __________________ -----------------___________________________------------------ */ static void readFlowSample_IPv4(SFSample *sample) { sf_log("flowSampleType IPV4\n"); sample->headerLen = sizeof(SFLSampled_ipv4); sample->header = (u_char *)sample->datap; /* just point at the header */ skipBytes(sample, sample->headerLen); { char buf[51]; SFLSampled_ipv4 nfKey; memcpy(&nfKey, sample->header, sizeof(nfKey)); sample->sampledPacketSize = ntohl(nfKey.length); sf_log("sampledPacketSize %lu\n", sample->sampledPacketSize); sf_log("IPSize %lu\n", sample->sampledPacketSize); sample->ipsrc.type = SFLADDRESSTYPE_IP_V4; sample->ipsrc.address.ip_v4 = nfKey.src_ip; sample->ipdst.type = SFLADDRESSTYPE_IP_V4; sample->ipdst.address.ip_v4 = nfKey.dst_ip; sample->dcd_ipProtocol = ntohl(nfKey.protocol); sample->dcd_ipTos = ntohl(nfKey.tos); sf_log("srcIP %s\n", printAddress(&sample->ipsrc, buf, 50)); sf_log("dstIP %s\n", printAddress(&sample->ipdst, buf, 50)); sf_log("IPProtocol %u\n", sample->dcd_ipProtocol); sf_log("IPTOS %u\n", sample->dcd_ipTos); sample->dcd_sport = ntohl(nfKey.src_port); sample->dcd_dport = ntohl(nfKey.dst_port); switch(sample->dcd_ipProtocol) { case 1: /* ICMP */ sf_log("ICMPType %u\n", sample->dcd_dport); /* not sure about the dest port being icmp type - might be that src port is icmp type and dest port is icmp code. Still, have seen some implementations where src port is 0 and dst port is the type, so it may be safer to assume that the destination port has the type */ break; case 6: /* TCP */ sf_log("TCPSrcPort %u\n", sample->dcd_sport); sf_log("TCPDstPort %u\n", sample->dcd_dport); sample->dcd_tcpFlags = ntohl(nfKey.tcp_flags); sf_log("TCPFlags %u\n", sample->dcd_tcpFlags); break; case 17: /* UDP */ sf_log("UDPSrcPort %u\n", sample->dcd_sport); sf_log("UDPDstPort %u\n", sample->dcd_dport); break; default: /* some other protcol */ break; } } } /*_________________---------------------------__________________ _________________ readFlowSample_IPv6 __________________ -----------------___________________________------------------ */ static void readFlowSample_IPv6(SFSample *sample) { sf_log("flowSampleType IPV6\n"); sample->header = (u_char *)sample->datap; /* just point at the header */ sample->headerLen = sizeof(SFLSampled_ipv6); skipBytes(sample, sample->headerLen); { char buf[51]; SFLSampled_ipv6 nfKey6; memcpy(&nfKey6, sample->header, sizeof(nfKey6)); sample->sampledPacketSize = ntohl(nfKey6.length); sf_log("sampledPacketSize %lu\n", sample->sampledPacketSize); sf_log("IPSize %lu\n", sample->sampledPacketSize); sample->ipsrc.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipsrc.address.ip_v6, &nfKey6.src_ip, 16); sample->ipdst.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipdst.address.ip_v6, &nfKey6.dst_ip, 16); sample->dcd_ipProtocol = ntohl(nfKey6.protocol); sf_log("srcIP6 %s\n", printAddress(&sample->ipsrc, buf, 50)); sf_log("dstIP6 %s\n", printAddress(&sample->ipdst, buf, 50)); sf_log("IPProtocol %lu\n", sample->dcd_ipProtocol); sf_log("priority %lu\n", ntohl(nfKey6.priority)); sample->dcd_sport = ntohl(nfKey6.src_port); sample->dcd_dport = ntohl(nfKey6.dst_port); switch(sample->dcd_ipProtocol) { case 1: /* ICMP */ sf_log("ICMPType %u\n", sample->dcd_dport); /* not sure about the dest port being icmp type - might be that src port is icmp type and dest port is icmp code. Still, have seen some implementations where src port is 0 and dst port is the type, so it may be safer to assume that the destination port has the type */ break; case 6: /* TCP */ sf_log("TCPSrcPort %u\n", sample->dcd_sport); sf_log("TCPDstPort %u\n", sample->dcd_dport); sample->dcd_tcpFlags = ntohl(nfKey6.tcp_flags); sf_log("TCPFlags %u\n", sample->dcd_tcpFlags); break; case 17: /* UDP */ sf_log("UDPSrcPort %u\n", sample->dcd_sport); sf_log("UDPDstPort %u\n", sample->dcd_dport); break; default: /* some other protcol */ break; } } } /*_________________---------------------------__________________ _________________ readFlowSample_v2v4 __________________ -----------------___________________________------------------ */ static void readFlowSample_v2v4(SFSample *sample) { sf_log("sampleType FLOWSAMPLE\n"); sample->samplesGenerated = getData32(sample); sf_log("sampleSequenceNo %lu\n", sample->samplesGenerated); { u_int32_t samplerId = getData32(sample); sample->ds_class = samplerId >> 24; sample->ds_index = samplerId & 0x00ffffff; sf_log("sourceId %lu:%lu\n", sample->ds_class, sample->ds_index); } sample->meanSkipCount = getData32(sample); sample->samplePool = getData32(sample); sample->dropEvents = getData32(sample); sample->inputPort = getData32(sample); sample->outputPort = getData32(sample); sf_log("meanSkipCount %lu\n", sample->meanSkipCount); sf_log("samplePool %lu\n", sample->samplePool); sf_log("dropEvents %lu\n", sample->dropEvents); sf_log("inputPort %lu\n", sample->inputPort); if(sample->outputPort & 0x80000000) { u_int32_t numOutputs = sample->outputPort & 0x7fffffff; if(numOutputs > 0) sf_log("outputPort multiple %d\n", numOutputs); else sf_log("outputPort multiple >1\n"); } else sf_log("outputPort %lu\n", sample->outputPort); sample->packet_data_tag = getData32(sample); switch(sample->packet_data_tag) { case INMPACKETTYPE_HEADER: readFlowSample_header(sample); break; case INMPACKETTYPE_IPV4: readFlowSample_IPv4(sample); break; case INMPACKETTYPE_IPV6: readFlowSample_IPv6(sample); break; default: receiveError(sample, "unexpected packet_data_tag", YES); break; } sample->extended_data_tag = 0; { u_int32_t x; sample->num_extended = getData32(sample); for(x = 0; x < sample->num_extended; x++) { u_int32_t extended_tag; extended_tag = getData32(sample); switch(extended_tag) { case INMEXTENDED_SWITCH: readExtendedSwitch(sample); break; case INMEXTENDED_ROUTER: readExtendedRouter(sample); break; case INMEXTENDED_GATEWAY: if(sample->datagramVersion == 2) readExtendedGateway_v2(sample); else readExtendedGateway(sample); break; case INMEXTENDED_USER: readExtendedUser(sample); break; case INMEXTENDED_URL: readExtendedUrl(sample); break; default: receiveError(sample, "unrecognized extended data tag", YES); break; } } } if(sampleFilterOK(sample)) { switch(sfConfig.outputFormat) { case SFLFMT_NETFLOW: /* if we are exporting netflow and we have an IPv4 layer, compose the datagram now */ if(sfConfig.netFlowOutputSocket && sample->gotIPV4) sendNetFlowDatagram(sample); break; case SFLFMT_PCAP: /* if we are writing tcpdump format, write the next packet record now */ writePcapPacket(sample); break; case SFLFMT_LINE: /* or line-by-line output... */ writeFlowLine(sample); break; case SFLFMT_FULL: default: /* if it was full-detail output then it was done as we went along */ break; } } } /*_________________---------------------------__________________ _________________ readFlowSample __________________ -----------------___________________________------------------ */ static void readFlowSample(SFSample *sample, int expanded) { u_int32_t num_elements, sampleLength, actualSampleLength; u_char *sampleStart; sf_log("sampleType FLOWSAMPLE\n"); sampleLength = getData32(sample); sampleStart = (u_char *)sample->datap; sample->samplesGenerated = getData32(sample); sf_log("sampleSequenceNo %lu\n", sample->samplesGenerated); if(expanded) { sample->ds_class = getData32(sample); sample->ds_index = getData32(sample); } else { u_int32_t samplerId = getData32(sample); sample->ds_class = samplerId >> 24; sample->ds_index = samplerId & 0x00ffffff; } sf_log("sourceId %lu:%lu\n", sample->ds_class, sample->ds_index); sample->meanSkipCount = getData32(sample); sample->samplePool = getData32(sample); sample->dropEvents = getData32(sample); sf_log("meanSkipCount %lu\n", sample->meanSkipCount); sf_log("samplePool %lu\n", sample->samplePool); sf_log("dropEvents %lu\n", sample->dropEvents); if(expanded) { sample->inputPortFormat = getData32(sample); sample->inputPort = getData32(sample); sample->outputPortFormat = getData32(sample); sample->outputPort = getData32(sample); } else { u_int32_t inp, outp; inp = getData32(sample); outp = getData32(sample); sample->inputPortFormat = inp >> 30; sample->outputPortFormat = outp >> 30; sample->inputPort = inp & 0x3fffffff; sample->outputPort = outp & 0x3fffffff; } switch(sample->inputPortFormat) { case 3: sf_log("inputPort format==3 %lu\n", sample->inputPort); break; case 2: sf_log("inputPort multiple %lu\n", sample->inputPort); break; case 1: sf_log("inputPort dropCode %lu\n", sample->inputPort); break; case 0: sf_log("inputPort %lu\n", sample->inputPort); break; } switch(sample->outputPortFormat) { case 3: sf_log("outputPort format==3 %lu\n", sample->outputPort); break; case 2: sf_log("outputPort multiple %lu\n", sample->outputPort); break; case 1: sf_log("outputPort dropCode %lu\n", sample->outputPort); break; case 0: sf_log("outputPort %lu\n", sample->outputPort); break; } num_elements = getData32(sample); { int el; for(el = 0; el < num_elements; el++) { u_int32_t tag, length; u_char *start; char buf[51]; tag = getData32(sample); sf_log("flowBlock_tag %s\n", printTag(tag, buf, 50)); length = getData32(sample); start = (u_char *)sample->datap; switch(tag) { case SFLFLOW_HEADER: readFlowSample_header(sample); break; case SFLFLOW_ETHERNET: readFlowSample_ethernet(sample); break; case SFLFLOW_IPV4: readFlowSample_IPv4(sample); break; case SFLFLOW_IPV6: readFlowSample_IPv6(sample); break; case SFLFLOW_EX_SWITCH: readExtendedSwitch(sample); break; case SFLFLOW_EX_ROUTER: readExtendedRouter(sample); break; case SFLFLOW_EX_GATEWAY: readExtendedGateway(sample); break; case SFLFLOW_EX_USER: readExtendedUser(sample); break; case SFLFLOW_EX_URL: readExtendedUrl(sample); break; case SFLFLOW_EX_MPLS: readExtendedMpls(sample); break; case SFLFLOW_EX_NAT: readExtendedNat(sample); break; case SFLFLOW_EX_MPLS_TUNNEL: readExtendedMplsTunnel(sample); break; case SFLFLOW_EX_MPLS_VC: readExtendedMplsVC(sample); break; case SFLFLOW_EX_MPLS_FTN: readExtendedMplsFTN(sample); break; case SFLFLOW_EX_MPLS_LDP_FEC: readExtendedMplsLDP_FEC(sample); break; case SFLFLOW_EX_VLAN_TUNNEL: readExtendedVlanTunnel(sample); break; case SFLFLOW_EX_PROCESS: readExtendedProcess(sample); break; default: skipTLVRecord(sample, tag, length, "flow_sample_element"); break; } lengthCheck(sample, "flow_sample_element", start, length); } } lengthCheck(sample, "flow_sample", sampleStart, sampleLength); if(sampleFilterOK(sample)) { switch(sfConfig.outputFormat) { case SFLFMT_NETFLOW: /* if we are exporting netflow and we have an IPv4 layer, compose the datagram now */ if(sfConfig.netFlowOutputSocket && sample->gotIPV4) sendNetFlowDatagram(sample); break; case SFLFMT_PCAP: /* if we are writing tcpdump format, write the next packet record now */ writePcapPacket(sample); break; case SFLFMT_LINE: /* or line-by-line output... */ writeFlowLine(sample); break; case SFLFMT_FULL: default: /* if it was full-detail output then it was done as we went along */ break; } } } /*_________________---------------------------__________________ _________________ readCounters_generic __________________ -----------------___________________________------------------ */ static void readCounters_generic(SFSample *sample) { /* the first part of the generic counters block is really just more info about the interface. */ sample->ifCounters.ifIndex = sf_log_next32(sample, "ifIndex"); sample->ifCounters.ifType = sf_log_next32(sample, "networkType"); sample->ifCounters.ifSpeed = sf_log_next64(sample, "ifSpeed"); sample->ifCounters.ifDirection = sf_log_next32(sample, "ifDirection"); sample->ifCounters.ifStatus = sf_log_next32(sample, "ifStatus"); /* the generic counters always come first */ sample->ifCounters.ifInOctets = sf_log_next64(sample, "ifInOctets"); sample->ifCounters.ifInUcastPkts = sf_log_next32(sample, "ifInUcastPkts"); sample->ifCounters.ifInMulticastPkts = sf_log_next32(sample, "ifInMulticastPkts"); sample->ifCounters.ifInBroadcastPkts = sf_log_next32(sample, "ifInBroadcastPkts"); sample->ifCounters.ifInDiscards = sf_log_next32(sample, "ifInDiscards"); sample->ifCounters.ifInErrors = sf_log_next32(sample, "ifInErrors"); sample->ifCounters.ifInUnknownProtos = sf_log_next32(sample, "ifInUnknownProtos"); sample->ifCounters.ifOutOctets = sf_log_next64(sample, "ifOutOctets"); sample->ifCounters.ifOutUcastPkts = sf_log_next32(sample, "ifOutUcastPkts"); sample->ifCounters.ifOutMulticastPkts = sf_log_next32(sample, "ifOutMulticastPkts"); sample->ifCounters.ifOutBroadcastPkts = sf_log_next32(sample, "ifOutBroadcastPkts"); sample->ifCounters.ifOutDiscards = sf_log_next32(sample, "ifOutDiscards"); sample->ifCounters.ifOutErrors = sf_log_next32(sample, "ifOutErrors"); sample->ifCounters.ifPromiscuousMode = sf_log_next32(sample, "ifPromiscuousMode"); } /*_________________---------------------------__________________ _________________ readCounters_ethernet __________________ -----------------___________________________------------------ */ static void readCounters_ethernet(SFSample *sample) { sf_log_next32(sample, "dot3StatsAlignmentErrors"); sf_log_next32(sample, "dot3StatsFCSErrors"); sf_log_next32(sample, "dot3StatsSingleCollisionFrames"); sf_log_next32(sample, "dot3StatsMultipleCollisionFrames"); sf_log_next32(sample, "dot3StatsSQETestErrors"); sf_log_next32(sample, "dot3StatsDeferredTransmissions"); sf_log_next32(sample, "dot3StatsLateCollisions"); sf_log_next32(sample, "dot3StatsExcessiveCollisions"); sf_log_next32(sample, "dot3StatsInternalMacTransmitErrors"); sf_log_next32(sample, "dot3StatsCarrierSenseErrors"); sf_log_next32(sample, "dot3StatsFrameTooLongs"); sf_log_next32(sample, "dot3StatsInternalMacReceiveErrors"); sf_log_next32(sample, "dot3StatsSymbolErrors"); } /*_________________---------------------------__________________ _________________ readCounters_tokenring __________________ -----------------___________________________------------------ */ static void readCounters_tokenring(SFSample *sample) { sf_log_next32(sample, "dot5StatsLineErrors"); sf_log_next32(sample, "dot5StatsBurstErrors"); sf_log_next32(sample, "dot5StatsACErrors"); sf_log_next32(sample, "dot5StatsAbortTransErrors"); sf_log_next32(sample, "dot5StatsInternalErrors"); sf_log_next32(sample, "dot5StatsLostFrameErrors"); sf_log_next32(sample, "dot5StatsReceiveCongestions"); sf_log_next32(sample, "dot5StatsFrameCopiedErrors"); sf_log_next32(sample, "dot5StatsTokenErrors"); sf_log_next32(sample, "dot5StatsSoftErrors"); sf_log_next32(sample, "dot5StatsHardErrors"); sf_log_next32(sample, "dot5StatsSignalLoss"); sf_log_next32(sample, "dot5StatsTransmitBeacons"); sf_log_next32(sample, "dot5StatsRecoverys"); sf_log_next32(sample, "dot5StatsLobeWires"); sf_log_next32(sample, "dot5StatsRemoves"); sf_log_next32(sample, "dot5StatsSingles"); sf_log_next32(sample, "dot5StatsFreqErrors"); } /*_________________---------------------------__________________ _________________ readCounters_vg __________________ -----------------___________________________------------------ */ static void readCounters_vg(SFSample *sample) { sf_log_next32(sample, "dot12InHighPriorityFrames"); sf_log_next64(sample, "dot12InHighPriorityOctets"); sf_log_next32(sample, "dot12InNormPriorityFrames"); sf_log_next64(sample, "dot12InNormPriorityOctets"); sf_log_next32(sample, "dot12InIPMErrors"); sf_log_next32(sample, "dot12InOversizeFrameErrors"); sf_log_next32(sample, "dot12InDataErrors"); sf_log_next32(sample, "dot12InNullAddressedFrames"); sf_log_next32(sample, "dot12OutHighPriorityFrames"); sf_log_next64(sample, "dot12OutHighPriorityOctets"); sf_log_next32(sample, "dot12TransitionIntoTrainings"); sf_log_next64(sample, "dot12HCInHighPriorityOctets"); sf_log_next64(sample, "dot12HCInNormPriorityOctets"); sf_log_next64(sample, "dot12HCOutHighPriorityOctets"); } /*_________________---------------------------__________________ _________________ readCounters_vlan __________________ -----------------___________________________------------------ */ static void readCounters_vlan(SFSample *sample) { sample->in_vlan = getData32(sample); sf_log("in_vlan %lu\n", sample->in_vlan); sf_log_next64(sample, "octets"); sf_log_next32(sample, "ucastPkts"); sf_log_next32(sample, "multicastPkts"); sf_log_next32(sample, "broadcastPkts"); sf_log_next32(sample, "discards"); } /*_________________---------------------------__________________ _________________ readCounters_processor __________________ -----------------___________________________------------------ */ static void readCounters_processor(SFSample *sample) { sf_log_percentage(sample, "5s_cpu"); sf_log_percentage(sample, "1m_cpu"); sf_log_percentage(sample, "5m_cpu"); sf_log_next64(sample, "total_memory_bytes"); sf_log_next64(sample, "free_memory_bytes"); } /*_________________---------------------------__________________ _________________ readCountersSample_v2v4 __________________ -----------------___________________________------------------ */ static void readCountersSample_v2v4(SFSample *sample) { sf_log("sampleType COUNTERSSAMPLE\n"); sample->samplesGenerated = getData32(sample); sf_log("sampleSequenceNo %lu\n", sample->samplesGenerated); { u_int32_t samplerId = getData32(sample); sample->ds_class = samplerId >> 24; sample->ds_index = samplerId & 0x00ffffff; } sf_log("sourceId %lu:%lu\n", sample->ds_class, sample->ds_index); sample->statsSamplingInterval = getData32(sample); sf_log("statsSamplingInterval %lu\n", sample->statsSamplingInterval); /* now find out what sort of counter blocks we have here... */ sample->counterBlockVersion = getData32(sample); sf_log("counterBlockVersion %lu\n", sample->counterBlockVersion); /* first see if we should read the generic stats */ switch(sample->counterBlockVersion) { case INMCOUNTERSVERSION_GENERIC: case INMCOUNTERSVERSION_ETHERNET: case INMCOUNTERSVERSION_TOKENRING: case INMCOUNTERSVERSION_FDDI: case INMCOUNTERSVERSION_VG: case INMCOUNTERSVERSION_WAN: readCounters_generic(sample); break; case INMCOUNTERSVERSION_VLAN: break; default: receiveError(sample, "unknown stats version", YES); break; } /* now see if there are any specific counter blocks to add */ switch(sample->counterBlockVersion) { case INMCOUNTERSVERSION_GENERIC: /* nothing more */ break; case INMCOUNTERSVERSION_ETHERNET: readCounters_ethernet(sample); break; case INMCOUNTERSVERSION_TOKENRING:readCounters_tokenring(sample); break; case INMCOUNTERSVERSION_FDDI: break; case INMCOUNTERSVERSION_VG: readCounters_vg(sample); break; case INMCOUNTERSVERSION_WAN: break; case INMCOUNTERSVERSION_VLAN: readCounters_vlan(sample); break; default: receiveError(sample, "unknown INMCOUNTERSVERSION", YES); break; } /* line-by-line output... */ if(sfConfig.outputFormat == SFLFMT_LINE) writeCountersLine(sample); } /*_________________---------------------------__________________ _________________ readCountersSample __________________ -----------------___________________________------------------ */ static void readCountersSample(SFSample *sample, int expanded) { u_int32_t sampleLength; u_int32_t num_elements; char *sampleStart; sf_log("sampleType COUNTERSSAMPLE\n"); sampleLength = getData32(sample); sampleStart = (u_char *)sample->datap; sample->samplesGenerated = getData32(sample); sf_log("sampleSequenceNo %lu\n", sample->samplesGenerated); if(expanded) { sample->ds_class = getData32(sample); sample->ds_index = getData32(sample); } else { u_int32_t samplerId = getData32(sample); sample->ds_class = samplerId >> 24; sample->ds_index = samplerId & 0x00ffffff; } sf_log("sourceId %lu:%lu\n", sample->ds_class, sample->ds_index); num_elements = getData32(sample); { int el; for(el = 0; el < num_elements; el++) { u_int32_t tag, length; char *start; char buf[51]; tag = getData32(sample); sf_log("counterBlock_tag %s\n", printTag(tag, buf, 50)); length = getData32(sample); start = (u_char *)sample->datap; switch(tag) { case SFLCOUNTERS_GENERIC: readCounters_generic(sample); break; case SFLCOUNTERS_ETHERNET: readCounters_ethernet(sample); break; case SFLCOUNTERS_TOKENRING:readCounters_tokenring(sample); break; case SFLCOUNTERS_VG: readCounters_vg(sample); break; case SFLCOUNTERS_VLAN: readCounters_vlan(sample); break; case SFLCOUNTERS_PROCESSOR: readCounters_processor(sample); break; default: skipTLVRecord(sample, tag, length, "counters_sample_element"); break; } lengthCheck(sample, "counters_sample_element", start, length); } } lengthCheck(sample, "counters_sample", sampleStart, sampleLength); /* line-by-line output... */ if(sfConfig.outputFormat == SFLFMT_LINE) writeCountersLine(sample); } /*_________________---------------------------__________________ _________________ readSFlowDatagram __________________ -----------------___________________________------------------ */ static void readSFlowDatagram(SFSample *sample) { u_int32_t addressType; u_int32_t samplesInPacket; struct timeval now; char buf[51]; /* log some datagram info */ now.tv_sec = time(NULL); now.tv_usec = 0; sf_log("datagramSourceIP %s\n", IP_to_a(sample->sourceIP.s_addr, buf)); sf_log("datagramSize %lu\n", sample->rawSampleLen); sf_log("unixSecondsUTC %lu\n", now.tv_sec); /* check the version */ sample->datagramVersion = getData32(sample); sf_log("datagramVersion %d\n", sample->datagramVersion); if(sample->datagramVersion != 2 && sample->datagramVersion != 4 && sample->datagramVersion != 5) { receiveError(sample, "unexpected datagram version number\n", YES); } /* get the agent address */ getAddress(sample, &sample->agent_addr); /* version 5 has an agent sub-id as well */ if(sample->datagramVersion >= 5) { sample->agentSubId = getData32(sample); sf_log("agentSubId %lu\n", sample->agentSubId); } sample->sequenceNo = getData32(sample); /* this is the packet sequence number */ sample->sysUpTime = getData32(sample); samplesInPacket = getData32(sample); sf_log("agent %s\n", printAddress(&sample->agent_addr, buf, 50)); sf_log("packetSequenceNo %lu\n", sample->sequenceNo); sf_log("sysUpTime %lu\n", sample->sysUpTime); sf_log("samplesInPacket %lu\n", samplesInPacket); /* now iterate and pull out the flows and counters samples */ { u_int32_t samp = 0; for(; samp < samplesInPacket; samp++) { // just read the tag, then call the approriate decode fn sample->sampleType = getData32(sample); sf_log("startSample ----------------------\n"); sf_log("sampleType_tag %s\n", printTag(sample->sampleType, buf, 50)); if(sample->datagramVersion >= 5) { switch(sample->sampleType) { case SFLFLOW_SAMPLE: readFlowSample(sample, NO); break; case SFLCOUNTERS_SAMPLE: readCountersSample(sample, NO); break; case SFLFLOW_SAMPLE_EXPANDED: readFlowSample(sample, YES); break; case SFLCOUNTERS_SAMPLE_EXPANDED: readCountersSample(sample, YES); break; default: skipTLVRecord(sample, sample->sampleType, getData32(sample), "sample"); break; } } else { switch(sample->sampleType) { case FLOWSAMPLE: readFlowSample_v2v4(sample); break; case COUNTERSSAMPLE: readCountersSample_v2v4(sample); break; default: receiveError(sample, "unexpected sample type", YES); break; } } sf_log("endSample ----------------------\n"); } } } /*_________________---------------------------__________________ _________________ receiveSFlowDatagram __________________ -----------------___________________________------------------ */ static void receiveSFlowDatagram(SFSample *sample) { int exceptionVal; sf_log("startDatagram =================================\n"); if((exceptionVal = setjmp(sample->env)) == 0) { // TRY sample->datap = (u_int32_t *)sample->rawSample; sample->endp = (u_char *)sample->rawSample + sample->rawSampleLen; readSFlowDatagram(sample); } else { // CATCH fprintf(stderr, "caught exception: %d\n", exceptionVal); } sf_log("endDatagram =================================\n"); } /*__________________-----------------------------__________________ _________________ openInputUDPSocket __________________ -----------------_____________________________------------------ */ static int openInputUDPSocket(u_int16_t port) { int soc; struct sockaddr_in myaddr_in; long save_fd; /* Create socket */ memset((char *)&myaddr_in, 0, sizeof(struct sockaddr_in)); myaddr_in.sin_family = AF_INET; myaddr_in.sin_addr.s_addr = INADDR_ANY; myaddr_in.sin_port = htons(port); if ((soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { fprintf(stderr, "socket() failed, %s\n", strerror(errno)); exit(-6); } /* make socket non-blocking */ save_fd = fcntl(soc, F_GETFL); save_fd |= O_NONBLOCK; fcntl(soc, F_SETFL, save_fd); /* Bind the socket */ if (bind(soc, (struct sockaddr *)&myaddr_in, sizeof(struct sockaddr_in)) == -1) { fprintf(stderr, "bind() failed, port = %d : %s\n", port, strerror(errno)); exit(-7); } return soc; } /*_________________---------------------------__________________ _________________ readPacket __________________ -----------------___________________________------------------ */ static void readPacket(int soc) { struct sockaddr_in peer; int alen, cc; #define MAX_PKT_SIZ 65536 u_char buf[MAX_PKT_SIZ]; alen = sizeof(peer); memset(&peer, 0, sizeof(peer)); cc = recvfrom(soc, buf, MAX_PKT_SIZ, 0, (struct sockaddr *)&peer, &alen); if(cc <= 0) { fprintf(stderr, "recvfrom() failed, %s\n", strerror(errno)); return; } if(sfConfig.forwardingTargets) { // if we are forwarding, then do nothing else (it might // be important from a performance point of view). SFForwardingTarget *tgt = sfConfig.forwardingTargets; for( ; tgt != NULL; tgt = tgt->nxt) { int bytesSent; if((bytesSent = sendto(tgt->sock, buf, cc, 0, (struct sockaddr *)(&tgt->addr), sizeof(tgt->addr))) != cc) { fprintf(stderr, "sendto returned %d (expected %d): %s\n", bytesSent, cc, strerror(errno)); } } } else { SFSample sample; memset(&sample, 0, sizeof(sample)); sample.rawSample = buf; sample.rawSampleLen = cc; sample.sourceIP = peer.sin_addr; receiveSFlowDatagram(&sample); fflush(stdout); } } /*_________________---------------------------__________________ _________________ readPcapPacket __________________ -----------------___________________________------------------ */ static int readPcapPacket(FILE *file) { int cc; u_char buf[2048]; struct pcap_pkthdr hdr; SFSample sample; int skipBytes = 0; time_t tim; if(fread(&hdr, sizeof(hdr), 1, file) != 1) { if(feof(file)) return 0; fprintf(stderr, "unable to read pcap packet header from %s : %s\n", sfConfig.readPcapFileName, strerror(errno)); exit(-32); } if(sfConfig.tcpdumpHdrPad > 0) { if(fread(buf, sfConfig.tcpdumpHdrPad, 1, file) != 1) { fprintf(stderr, "unable to read pcap header pad (%d bytes)\n", sfConfig.tcpdumpHdrPad); exit(-33); } } if(sfConfig.pcapSwap) { hdr.ts_sec = MyByteSwap32(hdr.ts_sec); hdr.ts_usec = MyByteSwap32(hdr.ts_usec); hdr.caplen = MyByteSwap32(hdr.caplen); hdr.len = MyByteSwap32(hdr.len); } if(fread(buf, hdr.caplen, 1, file) != 1) { fprintf(stderr, "unable to read pcap packet from %s : %s\n", sfConfig.readPcapFileName, strerror(errno)); exit(-34); } tim = hdr.ts_sec; fprintf(stderr, "\npcap_timestamp = %s\n", ctime(&tim)); // thanks to Richard Clayton for this bugfix if(hdr.caplen < hdr.len) { fprintf(stderr, "incomplete datagram (pcap snaplen too short)\n"); } else { // need to skip over the encapsulation in the captured packet. // -- should really do this by checking for 802.2, IP options etc. but // for now we just assume ethernet + IP + UDP if(sfConfig.readPcapHdr.linktype == DLT_EN10MB) skipBytes = 14 + 20 + 8; memset(&sample, 0, sizeof(sample)); sample.rawSample = buf + skipBytes; sample.rawSampleLen = hdr.caplen - skipBytes; receiveSFlowDatagram(&sample); fflush(stdout); } return 1; } /*_________________---------------------------__________________ _________________ parseVlanFilter __________________ -----------------___________________________------------------ */ static void peekForNumber(char *p) { if(*p < '0' || *p > '9') { fprintf(stderr, "error parsing vlan filter ranges (next char = <%c>)\n", *p); exit(-19); } } static void testVlan(u_int32_t num) { if(num > FILTER_MAX_VLAN) { fprintf(stderr, "error parsing vlan filter (vlan = <%d> out of range)\n", num); exit(-20); } } static void parseVlanFilter(u_char *array, u_char flag, char *start) { char *p = start; char *sep = " ,"; do { p += strspn(p, sep); // skip separators peekForNumber(p); u_int32_t first = strtol(p, &p, 0); // read an integer testVlan(first); array[first] = flag; if(*p == '-') { // a range. skip the '-' (so it doesn't get interpreted as unary minus) p++; // and read the second integer peekForNumber(p); u_int32_t last = strtol(p, &p, 0); testVlan(last); if(last > first) { int i; // iterate over the range for(i = first; i <= last; i++) array[i] = flag; } } } while(*p != '\0'); } /*_________________---------------------------__________________ _________________ addForwardingTarget __________________ -----------------___________________________------------------ return boolean for success or failure */ static int addForwardingTarget(char *hostandport) { SFForwardingTarget *tgt = calloc(1, sizeof(SFForwardingTarget)); // expect / #define MAX_HOSTANDPORT_LEN 100 char hoststr[MAX_HOSTANDPORT_LEN+1]; char *p; if(hostandport == NULL) { fprintf(stderr, "expected /\n"); return NO; } if(strlen(hostandport) > MAX_HOSTANDPORT_LEN) return NO; // take a copy strcpy(hoststr, hostandport); // find the '/' for(p = hoststr; *p != '\0'; p++) if(*p == '/') break; if(*p == '\0') { // not found fprintf(stderr, "host/port - no '/' found\n"); return NO; } (*p) = '\0'; // blat in a zero p++; // now p points to port string, and hoststr is just the hostname or IP { struct hostent *ent = gethostbyname(hoststr); if(ent == NULL) { fprintf(stderr, "hostname %s lookup failed\n", hoststr); return NO; } else tgt->host.s_addr = ((struct in_addr *)(ent->h_addr))->s_addr; } sscanf(p, "%lu", &tgt->port); if(tgt->port <= 0 || tgt->port >= 65535) { fprintf(stderr, "invalid port: %lu\n", tgt->port); return NO; } /* set up the destination socket-address */ tgt->addr.sin_family = AF_INET; tgt->addr.sin_port = ntohs(tgt->port); tgt->addr.sin_addr = tgt->host; /* and open the socket */ if((tgt->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { fprintf(stderr, "socket open (for %s) failed: %s", hostandport, strerror(errno)); return NO; } /* got this far, so must be OK */ tgt->nxt = sfConfig.forwardingTargets; sfConfig.forwardingTargets = tgt; return YES; } /*_________________---------------------------__________________ _________________ instructions __________________ -----------------___________________________------------------ */ static void instructions(char *command) { fprintf(stderr,"Copyright (c) InMon Corporation 2000-2006 ALL RIGHTS RESERVED\n"); fprintf(stderr,"This software provided with NO WARRANTY WHATSOEVER\n"); fprintf(stderr,"\n"); fprintf(stderr,"Usage: %s [-p port]\n", command); fprintf(stderr,"\n"); fprintf(stderr,"%s version: %s\n", command, VERSION); fprintf(stderr,"\n"); fprintf(stderr,"forwarding:\n"); fprintf(stderr, " -f host/port - (forward sflow to another collector\n"); fprintf(stderr, " - ...repeat for multiple collectors)\n"); fprintf(stderr,"\n"); fprintf(stderr,"csv output:\n"); fprintf(stderr, " -l - (output in line-by-line format)\n"); fprintf(stderr,"\n"); fprintf(stderr,"tcpdump output:\n"); fprintf(stderr, " -t - (output in binary tcpdump(1) format)\n"); fprintf(stderr, " -r file - (read binary tcpdump(1) format)\n"); fprintf(stderr, " -x - (remove all IPV4 content)\n"); fprintf(stderr, " -z pad - (extend tcpdump pkthdr with this many zeros\n"); fprintf(stderr, " e.g. try -z 8 for tcpdump on Red Hat Linux 6.2)\n"); fprintf(stderr,"\n"); fprintf(stderr,"NetFlow output:\n"); fprintf(stderr, " -c hostname_or_IP - (netflow collector host)\n"); fprintf(stderr, " -d port - (netflow collector UDP port)\n"); fprintf(stderr, " -e - (netflow collector peer_as (default = origin_as))\n"); fprintf(stderr, " -s - (disable scaling of netflow output by sampling rate)\n"); #ifdef SPOOFSOURCE fprintf(stderr, " -S - spoof source of netflow packets to input agent IP\n"); #endif fprintf(stderr,"\n"); fprintf(stderr,"Filters:\n"); fprintf(stderr, " +v - include vlans (e.g. +v 0-20,4091)\n"); fprintf(stderr, " -v - exclude vlans\n"); fprintf(stderr, "\n"); fprintf(stderr, "=============== Advanced Tools ==============================================\n"); fprintf(stderr, "| sFlowTrend (FREE) - http://www.inmon.com/products/sFlowTrend.php |\n"); fprintf(stderr, "| Traffic Sentinel - http://www.inmon.com/support/sentinel_release.php |\n"); fprintf(stderr, "=============================================================================\n"); exit(1); } /*_________________---------------------------__________________ _________________ process_command_line __________________ -----------------___________________________------------------ */ static void process_command_line(int argc, char *argv[]) { int arg = 1, in = 0; int i; int plus,minus; /* set defaults */ sfConfig.sFlowInputPort = 6343; /* walk though the args */ while (arg < argc) { plus = (argv[arg][0] == '+'); minus = (argv[arg][0] == '-'); if(plus == NO && minus == NO) instructions(*argv); in = argv[arg++][1]; switch(in) { case 'p': sfConfig.sFlowInputPort = atoi(argv[arg++]); break; case 't': sfConfig.outputFormat = SFLFMT_PCAP; break; case 'l': sfConfig.outputFormat = SFLFMT_LINE; break; case 'r': sfConfig.readPcapFileName = strdup(argv[arg++]); break; case 'x': sfConfig.removeContent = YES; break; case 'z': sfConfig.tcpdumpHdrPad = atoi(argv[arg++]); break; case 'c': { struct hostent *ent = gethostbyname(argv[arg++]); if(ent == NULL) { fprintf(stderr, "netflow collector hostname lookup failed\n"); exit(-8); } sfConfig.netFlowOutputIP.s_addr = ((struct in_addr *)(ent->h_addr))->s_addr; sfConfig.outputFormat = SFLFMT_NETFLOW; } break; case 'd': sfConfig.netFlowOutputPort = atoi(argv[arg++]); sfConfig.outputFormat = SFLFMT_NETFLOW; break; case 'e': sfConfig.netFlowPeerAS = YES; break; case 's': sfConfig.disableNetFlowScale = YES; break; #ifdef SPOOFSOURCE case 'S': sfConfig.spoofSource = YES; break; #endif case 'f': if(addForwardingTarget(argv[arg++]) == NO) exit(-35); sfConfig.outputFormat = SFLFMT_FWD; break; case 'v': if(plus) { // +v => include vlans sfConfig.gotVlanFilter = YES; parseVlanFilter(sfConfig.vlanFilter, YES, argv[arg++]); } else { // -v => exclude vlans if(! sfConfig.gotVlanFilter) { // when we start with an exclude list, that means the default should be YES for(i = 0; i < FILTER_MAX_VLAN; i++) sfConfig.vlanFilter[i] = YES; sfConfig.gotVlanFilter = YES; } parseVlanFilter(sfConfig.vlanFilter, NO, argv[arg++]); } break; case '?': case 'h': default: instructions(*argv); } } } /*_________________---------------------------__________________ _________________ main __________________ -----------------___________________________------------------ */ int main(int argc, char *argv[]) { u_int32_t soc; #ifdef WIN32 WSADATA wsadata; WSAStartup(0xffff, &wsadata); /* TODO: supposed to call WSACleanup() on termination */ #endif /* read the command line */ process_command_line(argc, argv); #ifdef WIN32 // on windows we need to tell stdout if we want it to be binary if(sfConfig.outputFormat == SFLFMT_PCAP) setmode(1, O_BINARY); #endif /* reading from file or socket? */ if(sfConfig.readPcapFileName) { if(strcmp(sfConfig.readPcapFileName, "-") == 0) sfConfig.readPcapFile = stdin; else sfConfig.readPcapFile = fopen(sfConfig.readPcapFileName, "rb"); if(sfConfig.readPcapFile == NULL) { fprintf(stderr, "cannot open %s : %s\n", sfConfig.readPcapFileName, strerror(errno)); exit(-1); } readPcapHeader(); } else { /* open the input socket */ soc = openInputUDPSocket(sfConfig.sFlowInputPort); } /* possible open an output socket for netflow */ if(sfConfig.netFlowOutputPort != 0 && sfConfig.netFlowOutputIP.s_addr != 0) openNetFlowSocket(); /* if tcpdump format, write the header */ if(sfConfig.outputFormat == SFLFMT_PCAP) writePcapHeader(); if(sfConfig.readPcapFile) { /* just use a blocking read */ while(readPcapPacket(sfConfig.readPcapFile)); } else { /* loop reading packets */ for(;;) { struct pollfd my_pollfd = { soc, POLLIN, 0 }; int nfds = poll(&my_pollfd, 1, 100); // we may return prematurely if a signal was caught, in which case nfds will // be -1 and errno will be set to EINTR. If we get any other error, abort. if(nfds < 0 && errno != EINTR) { fprintf(stderr, "select() returned %d\n", nfds); exit(-9); } if ((nfds > 0) && (my_pollfd.revents & POLLIN)) readPacket(soc); } } return 0; }