#pragma module NETFLOW_HOC "NETFLOW_HOC-2-D" #define __MODULE__ "NETFLOW_HOC" /* **++ ** FACILITY: NetFlow Host Collector ** ** MODULE DESCRIPTION: ** ** This program supposed to be used as Host Collector of the data exported by ** Cisco's devices with NetFlow protocol (version 5). ** ** AUTHORS: ** ** Ruslan R. Laishev ** ** CREATION DATE: 25-DEC-2002 ** ** ** USAGE: ** On Cisco Routers (example): ** ... ** (config)#ip flow-cache timeout active 1 ** (config)#ip flow-export version 5 ** (config)#ip flow-export destination 333.129.97.1 9001 ** (config)#interface GigabitEthernet0/0 ** (config-if)#ip route-cache flow ** (config-if)#exit ** ... ** ** MODIFICATION HISTORY: ** ** 2-SEP-2003 RRL Added automatic switching data files at daily basis. ** 3-SEP-2003 RRL Added storing system IP from where a packet was originated. ** 7-AUG-2009 RRL Create a data file by using FDL$ routines. ** 10-AUG-2009 RRL Move file/record operation off to NETFLOW_RMS.C/NETFLOW_RDB.C ** ** {@tbs@}... **-- */ /* ** ** INCLUDE FILES ** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netflowdef.h" #define __NEW_STARLET 1 #include int exit_flag = 0, debug_flag = 1; #define DEBUG if(debug_flag)printf("%-24.24s:%-08.0u ",__MODULE__,__LINE__);if(debug_flag)printf $DESCRIPTOR(dsc_sysnet, "SYS$NET"); $DESCRIPTOR(dsc_ucxdev, "UCX$DEVICE"); $DESCRIPTOR(dsc_tcpipdev,"TCPIP$DEVICE"); $DESCRIPTOR(dsc_db, "NETFLOW_IODB"); $DESCRIPTOR(dsc_dbinit, "NETFLOW_DBINIT"); $DESCRIPTOR(dsc_dbshut, "NETFLOW_DBSHUT"); $DESCRIPTOR(dsc_dbsave, "NETFLOW_DBSAVE"); int (*db_init)(...),(*db_shut)(...),(*db_save)(...); $DESCRIPTOR(iotmo_dsc, "0 00:00:30"); int iotmo [ 2 ]; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Canceling all queued and processed request due of expiration timer. ** ** FORMAL PARAMETERS: ** ** reqidt: pointer to a network channel ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** Cancel _all_ request for the given I/O channel. **-- */ void timer_ast ( int reqidt ) { sys$cancel(reqidt); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Close (deassign) a given channel to a network device, UDP or TCP. ** ** FORMAL PARAMETERS: ** ** chan: channel number, given from netio_open() or netio_connect() ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int netio_close ( short chan ) { return sys$dassgn (chan); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Open a UDP socket, try to assign a I/O channel to SYS$NET, ** bind it to given local addres and port. This routine is ** creates a UDP network device. ** ** FORMAL PARAMETERS: ** ** chan: channel number, an opened channel number ** home: a pointer to sockaddr_in structure contained address and port ** recvbfsz: an optional receive buffer size ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int netio_open ( short *chan, struct sockaddr_in *home, int recvbfsz ) { int status; struct { short proto; char type; char domain; } sck_parm = {INET$C_UDP,INET_PROTYP$C_DGRAM,INET$C_AF_INET}; iosb netiosb; ILE2 lhst_adrs = {sizeof(struct sockaddr_in),0,home}; ILE2 sock_opt = {sizeof(recvbfsz),INET$C_RCVBUF,&recvbfsz}; ILE2 itm_lst = {sizeof(ILE2),INET$C_SOCKOPT,&sock_opt}; *chan = 0; /* ** Open a network device */ if ( !(1 & (status = sys$assign( &dsc_sysnet,chan,0,0))) ) { if ( status != SS$_NOSUCHDEV ) return status; else if ( !(1 & (status = sys$assign( &dsc_ucxdev,chan,0,0))) ) { if ( status != SS$_NOSUCHDEV ) return status; if ( !(1 & (status = sys$assign( &dsc_tcpipdev,chan,0,0))) ) return status; } } else return status; /* ** Bind the network device a specific port and address, ** set a receive buffer size if it is not zero. */ home->sin_family = INET$C_AF_INET; if ( !(1 & (status = sys$qiow (EFN$C_ENF,*chan,IO$_SETMODE,&netiosb, 0,0,&sck_parm,0,&lhst_adrs,0,recvbfsz?&itm_lst:0,0))) ) { sys$dassgn (*chan); return status; } if ( !(netiosb.iosb$w_status & 1) ) { sys$dassgn (*chan); return netiosb.iosb$w_status; } return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Read a data from the specified network channel, if remhost parameter is took place ** the UDP-style reading is used. In other case is used a TCP-style reading for the ** speficied amount of data. In case of TCP-style receiving a timeout processing is ** performed. ** ** FORMAL PARAMETERS: ** ** chan: a channel number, given from netio_open() or netio_connect() ** buf: a buffer to receiving data ** buflen: the size of the buffer ** retlen: a length of a received data in the buffer ** remhost:what host is sent the data (UDP-style) ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int netio_read ( short chan, char *buf, short buflen, short *retlen, struct sockaddr_in *remhost ) { int status,remhostlen = 0,reqidt = chan; iosb netiosb; ILE3 rsck_adrs = {sizeof(struct sockaddr_in),0,remhost,(unsigned short*) &remhostlen}; *retlen = 0; /* ** Prepare to timeout processing */ if ( !(1 & (status = sys$setimr(EFN$C_ENF, &iotmo,timer_ast,reqidt,0))) ) return status; status = sys$qiow (EFN$C_ENF,chan,IO$_READVBLK,&netiosb,0,0, buf,buflen,&rsck_adrs,0,0,0); DEBUG("status = %u\n",status); DEBUG("netiosb.iosb$w_status = %u\n",netiosb.iosb$w_status); DEBUG("netiosb.iosb$w_bcnt = %u\n",netiosb.iosb$w_bcnt); sys$cantim(reqidt,0); *retlen = netiosb.iosb$w_bcnt; return (status & 1)?netiosb.iosb$w_status:status; } void ntoh_hdr ( void *hdr ) { int status,version = 0; version = ntohs( *((short *)hdr)); switch (version) { case 1: { struct nf_hdrv1 *h = (struct nf_hdrv1 *) hdr; h->nf_hdr$w_version = ntohs(h->nf_hdr$w_version); h->nf_hdr$w_count = ntohs(h->nf_hdr$w_count); h->nf_hdr$l_uptime = ntohl(h->nf_hdr$l_uptime); h->nf_hdr$l_secs = ntohl(h->nf_hdr$l_secs); h->nf_hdr$l_nsecs = ntohl(h->nf_hdr$l_nsecs); } break; case 5: case 7: case 8: { struct nf_hdrv5 *h = (struct nf_hdrv5 *) hdr; h->nf_hdr$w_version = ntohs(h->nf_hdr$w_version); h->nf_hdr$w_count = ntohs(h->nf_hdr$w_count); h->nf_hdr$l_uptime = ntohl(h->nf_hdr$l_uptime); h->nf_hdr$l_secs = ntohl(h->nf_hdr$l_secs); h->nf_hdr$l_nsecs = ntohl(h->nf_hdr$l_nsecs); h->nf_hdr$l_seq = ntohl(h->nf_hdr$l_seq); } break; } } void ntoh_rec ( short version, void *pkt, void *rec ) { switch (version) { case 5: { struct nf_pktv5 *p = (struct nf_pktv5 *) pkt; struct nf_recv5 *r = (struct nf_recv5 *) rec; struct in_addr *ip; ip = (struct in_addr *)&p->nf_pkt$l_srcaddr; memset(&r->nf_rec$t_srcaddr,0,sizeof(r->nf_rec$t_srcaddr)); sprintf (&r->nf_rec$t_srcaddr,"%s",inet_ntoa(*ip)); ip = (struct in_addr *)&p->nf_pkt$l_dstaddr; memset(&r->nf_rec$t_dstaddr,0,sizeof(r->nf_rec$t_dstaddr)); sprintf (&r->nf_rec$t_dstaddr,"%s",inet_ntoa(*ip)); ip = (struct in_addr *)&p->nf_pkt$l_nexthop; memset(&r->nf_rec$t_nexthop,0,sizeof(r->nf_rec$t_nexthop)); sprintf (&r->nf_rec$t_nexthop,"%s",inet_ntoa(*ip)); r->nf_rec$w_input = ntohs(p->nf_pkt$w_input); r->nf_rec$w_output = ntohs(p->nf_pkt$w_output); r->nf_rec$l_dpkts = ntohl(p->nf_pkt$l_dpkts); r->nf_rec$l_doctets = ntohl(p->nf_pkt$l_doctets); r->nf_rec$l_first = ntohl(p->nf_pkt$l_first); r->nf_rec$l_last = ntohl(p->nf_pkt$l_last); r->nf_rec$w_srcport = ntohs(p->nf_pkt$w_srcport); r->nf_rec$w_dstport = ntohs(p->nf_pkt$w_dstport); r->nf_rec$b_tcp_flags = p->nf_pkt$b_tcp_flags; r->nf_rec$b_prot = p->nf_pkt$b_prot; r->nf_rec$b_tos = p->nf_pkt$b_tos; r->nf_rec$w_dst_as = ntohs(p->nf_pkt$w_dst_as); r->nf_rec$w_src_as = ntohs(p->nf_pkt$w_src_as); r->nf_rec$b_dst_mask = p->nf_pkt$b_dst_mask; r->nf_rec$b_src_mask = p->nf_pkt$b_src_mask; DEBUG("Prot:%2.2u %15.15s:%5.5u -> %15.15s:%5.5u ->%3.3u/%3.3u-> %u(%u)\n", r->nf_rec$b_prot, r->nf_rec$t_srcaddr,r->nf_rec$w_srcport, r->nf_rec$t_dstaddr,r->nf_rec$w_dstport, r->nf_rec$w_input,r->nf_rec$w_output, r->nf_rec$l_dpkts,r->nf_rec$l_doctets); } break; } } struct sockaddr_in home,remhost; int main (void) { int status; short chan = 0,retlen = 0; char buf[32000],*prec; struct nf_hdrv5 *hdr = (struct nf_hdrv5 *) buf; struct nf_pktv5 *pkt; struct nf_recv5 rec; void * dbctx = NULL; debug_flag = (NULL != getenv("NETFLOW$DEBUG")); if ( !(1 & (status = lib$find_image_symbol(&dsc_db,&dsc_dbinit,&db_init,0,0))) ) lib$signal(status); if ( !(1 & (status = lib$find_image_symbol(&dsc_db,&dsc_dbshut,&db_shut,0,0))) ) lib$signal(status); if ( !(1 & (status = lib$find_image_symbol(&dsc_db,&dsc_dbsave,&db_save,0,0))) ) lib$signal(status); if ( !(1 & (status = db_init(&dbctx))) ) lib$signal(status); if ( !(1 & (status = sys$bintim (&iotmo_dsc,&iotmo))) ) sys$exit(status); /* ** Open network I/O channel */ home.sin_port = htons(9001); if ( !(1 & (status = netio_open(&chan,&home,32000))) ) lib$signal(status); /* ** Reading data from network */ while ( !exit_flag ) { /* ** Getting a next netflow packet */ if ( !(1 & (status = netio_read(chan,buf,sizeof(buf),&retlen,&remhost))) && status != SS$_CANCEL ) lib$signal(status); else if ( status == SS$_CANCEL ) break; else if ( !retlen ) continue; ntoh_hdr(hdr); DEBUG("nf_hdr$w_version = %u\n",hdr->nf_hdr$w_version); DEBUG("nf_hdr$w_count = %u\n",hdr->nf_hdr$w_count); DEBUG("nf_hdr$l_uptime = %u\n",hdr->nf_hdr$l_uptime); DEBUG("nf_hdr$l_secs = %u\n",hdr->nf_hdr$l_secs); DEBUG("nf_hdr$l_nsecs = %u\n",hdr->nf_hdr$l_nsecs); DEBUG("nf_hdr$l_seq = %u\n",hdr->nf_hdr$l_seq); memset(&rec,0, sizeof(struct nf_recv5)); /* ** Get a current system time to store it as time stamp of the data records */ if ( !(1 & (status = sys$gettim (&rec.nf_rec$q_timestamp))) ) lib$signal(status); sprintf (&rec.nf_rec$t_sysip,"%s",inet_ntoa(remhost.sin_addr)); /* ** Processing netflow data packet */ pkt = &buf[sizeof (struct nf_hdrv5)]; for (int i = 0;i < hdr->nf_hdr$w_count;i++) { ntoh_rec(hdr->nf_hdr$w_version,pkt,&rec); if ( !(1 & (status = db_save(dbctx,&rec))) ) lib$signal(status); (char *)pkt += sizeof(struct nf_pktv5); } } status = db_shut(dbctx); return netio_close(chan); }