/*
* Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
* Copyright (c) 2015 xFlow Research Inc. and others, All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.tsdr.netflow;
import java.io.IOException;
import java.net.BindException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.LinkedList;
import java.util.List;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.rev150219.DataCategory;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.collector.spi.rev150915.InsertTSDRLogRecordInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.collector.spi.rev150915.TsdrCollectorSpiService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.collector.spi.rev150915.inserttsdrlogrecord.input.TSDRLogRecord;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.collector.spi.rev150915.inserttsdrlogrecord.input.TSDRLogRecordBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* NetFlow Collector to receive netflow packets and store into TSDR data store.
*
* Currently only NetFlow Version 5 is supported.
*
* @author <a href="mailto:yuling_c@dell.com">YuLing Chen</a>
* @author <a href="mailto:muhammad.umair@xflowresearch.com">Umair Bhatti</a>
* @author <a href="mailto:saichler@xgmail.com">Sharon Aicler</a>
*
* Created: December 1, 2015
* Modified: Aug 02, 2016
*/
public class TSDRNetflowCollectorImpl extends Thread{
private static final long PERSIST_CHECK_INTERVAL_IN_MILLISECONDS = 5000;
private static final long INCOMING_QUEUE_WAIT_INTERVAL_IN_MILLISECONDS = 2000;
private static final int FLOW_SIZE_FOR_NETFLOW_PACKET = 48;
private static final Logger logger = LoggerFactory.getLogger(TSDRNetflowCollectorImpl.class);
private static byte packetsCountForTests = 0; //Just to test the counts of packet for test
private final TsdrCollectorSpiService collectorSPIService;
private DatagramSocket socket;
private boolean running = true;
private final LinkedList<DatagramPacket> incomingNetFlow = new LinkedList<>();
private long lastPersisted = System.currentTimeMillis();
private long lastTimeStamp = System.currentTimeMillis();
private int logRecordIndex = 0;
/**
* Constructor
* @param _collectorSPIService
*/
public TSDRNetflowCollectorImpl(TsdrCollectorSpiService _collectorSPIService) throws IOException{
super("TSDR NetFlow Listener");
this.setDaemon(true);
logRecordIndex = 0;
this.collectorSPIService = _collectorSPIService;
try{
this.socket = new DatagramSocket(2055);
}catch(Exception e){
logger.error("Collector service already running. Failed to bind it again on Port 2055.");
//Collector service already running, just passing the code for testing purpose.
//this.socket = new DatagramSocket();
shutdown();
}
this.start();
new NetFlowProcessor();
}
public long getIncomingNetflowSize(){
return incomingNetFlow.size();
}
public byte getPacketCount(){
return packetsCountForTests;
}
public void run(){
if(this.socket==null || this.socket.isClosed()){
shutdown();
}else {
while (running) {
byte data[] = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket.receive(packet);
handleNetFlow(packet);
} catch (IOException e) {
logger.error("Error while handleling netflow packets.", e);
shutdown();
}
}
}
}
public void shutdown(){
running = false;
if(socket!=null){
socket.close();
}
synchronized(incomingNetFlow){
incomingNetFlow.notifyAll();
}
}
public void handleNetFlow(DatagramPacket packet){
synchronized(incomingNetFlow){
incomingNetFlow.add(packet);
packetsCountForTests = (byte) (((int)packetsCountForTests) + 1);
incomingNetFlow.notifyAll();
}
}
private class NetFlowProcessor extends Thread{
private TSDRLogRecordBuilder recordbuilder;
public NetFlowProcessor(){
super("TSDR NetFlow Processor");
this.setDaemon(true);
this.start();
logger.debug("NetFlow Processor thread initialized");
}
public long getIncomingNetflowSize(){
return incomingNetFlow.size();
}
public byte getPacketCount(){
return packetsCountForTests;
}
public void run(){
DatagramPacket packet = null;
LinkedList<TSDRLogRecord> netFlowQueue = new LinkedList<TSDRLogRecord>();
while(running){
synchronized(incomingNetFlow) {
if (incomingNetFlow.isEmpty()) {
logger.debug("No Pkts in queue");
try {
incomingNetFlow.wait(INCOMING_QUEUE_WAIT_INTERVAL_IN_MILLISECONDS);
} catch (InterruptedException e) {
logger.error("Interrupted while waiting on incoming queue", e);
}
}
if (!incomingNetFlow.isEmpty()) {
packet = incomingNetFlow.removeFirst();
}
}
if(packet != null){
logger.debug("Pkts found");
byte[] buff = packet.getData();
String srcIp = packet.getAddress().getHostAddress().trim();
int netFlowVersion = new Integer(NetflowPacketParser.convert(buff, 0, 2)).intValue();
long currentTimeStamp = System.currentTimeMillis();
if(netFlowVersion == 9){
int flowCount = new Integer(NetflowPacketParser.convert(buff, 2, 2)).intValue();
int flowCounter = 1;
int dataBufferOffset = 20;
int flowsetid = Integer.parseInt(NetflowPacketParser.convert(buff, dataBufferOffset, 2));
int flowsetLength = Integer.parseInt(NetflowPacketParser.convert(buff, dataBufferOffset + 2, 2));
if(flowsetid == 0){
dataBufferOffset += 4;
NetflowPacketParser.fillFlowSetTemplateMap(buff, dataBufferOffset, flowCount);
dataBufferOffset += flowsetLength;
flowsetid = Integer.parseInt(NetflowPacketParser.convert(buff, dataBufferOffset, 2));
flowsetLength = Integer.parseInt(NetflowPacketParser.convert(buff, dataBufferOffset + 2, 2));
dataBufferOffset += 4;
}
int packetLength = (flowsetLength - 4) / flowCount;
while(flowCounter <= flowCount && (dataBufferOffset + packetLength) < buff.length){
recordbuilder = new TSDRLogRecordBuilder();
NetflowPacketParser parser = new NetflowPacketParser(buff);
parser.addFormat(buff, dataBufferOffset);
/*Fill up the RecordBuilder object*/
recordbuilder.setNodeID(srcIp);
recordbuilder.setTimeStamp(currentTimeStamp);
recordbuilder.setIndex(flowCounter);
recordbuilder.setTSDRDataCategory(DataCategory.NETFLOW);
recordbuilder.setRecordFullText(parser.toString());
logger.info(parser.toString());
if(logger.isDebugEnabled()) {
logger.debug(parser.toString());
}
recordbuilder.setRecordAttributes(parser.getRecordAttributes());
TSDRLogRecord logRecord = recordbuilder.build();
if(logRecord!=null){
netFlowQueue.add(logRecord);
}
dataBufferOffset += packetLength;
flowCounter += 1;
}
}else{
int flowCount = new Integer(NetflowPacketParser.convert(buff, 2, 2)).intValue();
int flowCounter = 1;
int dataBufferOffset = 0;
while(flowCounter <= flowCount && (dataBufferOffset + FLOW_SIZE_FOR_NETFLOW_PACKET) < buff.length){
recordbuilder = new TSDRLogRecordBuilder();
NetflowPacketParser parser = new NetflowPacketParser(buff);
parser.addFormat(buff, dataBufferOffset);
dataBufferOffset += FLOW_SIZE_FOR_NETFLOW_PACKET;
/*Fill up the RecordBuilder object*/
recordbuilder.setNodeID(srcIp);
recordbuilder.setTimeStamp(currentTimeStamp);
recordbuilder.setIndex(flowCounter);
recordbuilder.setTSDRDataCategory(DataCategory.NETFLOW);
recordbuilder.setRecordFullText(parser.toString());
if(logger.isDebugEnabled()){
logger.debug(parser.toString());
}
recordbuilder.setRecordAttributes(parser.getRecordAttributes());
TSDRLogRecord logRecord = recordbuilder.build();
if(logRecord!=null){
netFlowQueue.add(logRecord);
}
flowCounter += 1;
}
}
if(System.currentTimeMillis() - lastPersisted > PERSIST_CHECK_INTERVAL_IN_MILLISECONDS && !netFlowQueue.isEmpty() && packet != null){
LinkedList<TSDRLogRecord> queue = null;
if(System.currentTimeMillis() - lastPersisted > PERSIST_CHECK_INTERVAL_IN_MILLISECONDS && !netFlowQueue.isEmpty()){
lastPersisted = System.currentTimeMillis();
queue = netFlowQueue;
netFlowQueue = new LinkedList<TSDRLogRecord>();
}
if(queue!=null){
store(queue);
}
}
packet = null;
}
}
}
}
/**
* Store the data into TSDR data store
* @param queue
*/
private void store(List<TSDRLogRecord> queue){
InsertTSDRLogRecordInputBuilder input = new InsertTSDRLogRecordInputBuilder();
input.setTSDRLogRecord(queue);
input.setCollectorCodeName("TSDRNetFlowCollector");
collectorSPIService.insertTSDRLogRecord(input.build());
}
}