/* 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 <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <setjmp.h>
#ifdef WIN32
#include "winsock2.h"
#include "fcntl.h"
#else
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/poll.h>
#endif
#include "sflowtool.h"
#include "sflow.h" // sFlow v5
/*
#ifdef DARWIN
#include <architecture/byte_order.h>
#define bswap_16(x) NXSwapShort(x)
#define bswap_32(x) NXSwapInt(x)
#else
#include <byteswap.h>
#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 <jumbo>\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 <no_process_name>\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 <host>/<port>
#define MAX_HOSTANDPORT_LEN 100
char hoststr[MAX_HOSTANDPORT_LEN+1];
char *p;
if(hostandport == NULL) {
fprintf(stderr, "expected <host>/<port>\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 <vlans> - include vlans (e.g. +v 0-20,4091)\n");
fprintf(stderr, " -v <vlans> - 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;
}
syntax highlighted by Code2HTML, v. 0.9.1