Index: Config.cpp
===================================================================
--- Config.cpp	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
+++ Config.cpp	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -0,0 +1,174 @@
+#include "Config.h"
+
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+using namespace std;
+
+#include "Log.h"
+
+Config::Config(string name, string parentDebugInfo) {
+	debugInfo = parentDebugInfo + ", " + name;
+}
+
+Config::Config(string configFile, char** envp) {
+	while (*envp) {
+		string envEntry = *envp;
+		size_t pos = envEntry.find('=');
+		if (pos != string::npos) {
+			string name = envEntry.substr(0, pos);
+			string value = envEntry.substr(pos+1, string::npos);
+			envSymbols[name] = value;
+			logDebug(cout << "environment symbol: '" << name << "' = '" << value << "'" << endl);
+		}
+		++envp;
+	}
+
+	debugInfo = configFile;
+	groupStack.push_front(this);
+
+	FILE* in = fopen(configFile.c_str(), "r");
+	if (!in) {
+		cerr << "cannot open input file '" << configFile << "'" << endl;
+		exit(2);
+	}
+
+	char buff[1024];
+	while (fgets(buff, 1024, in)) {
+
+		string line=buff;
+		if ( (line.length() > 2) && (line[0] != '#') && (line.find(')') == string::npos) ) {
+			string name;
+			string value;
+			split(line, name, value, '=');
+
+			if (value == "(") {
+				logDebug(cout << "   config: new group '" << name << "'" << endl);
+				Config* newGroup = new Config(name, debugInfo);
+				groupStack.front()->groups[name] = newGroup;
+				groupStack.push_front(newGroup);
+			} else {
+				for (list<Config*>::reverse_iterator i = groupStack.rbegin(); i != groupStack.rend(); ++i) {
+					(*i)->symbolExpand(value);
+				}
+				envSymbolExpand(value);
+				logDebug(cout << "   config: name = '" << name << "', value = '" << value << "'" << endl);
+				groupStack.front()->add(name, value);
+			}
+		}
+		if ( (line.length() > 0) && (line[0] != '#') && (line.find(')') != string::npos) ) {
+			logDebug(cout << "   end of group" << endl);
+			groupStack.pop_front();
+		}
+	}
+
+	fclose(in);
+}
+
+Config::~Config() {
+	for (map<string, Config*>::iterator i = groups.begin(); i != groups.end(); ++i) {
+		delete i->second;
+	}
+}
+
+void Config::add(string name, string value) {
+	symbols[name] = value;
+}
+
+void Config::split(string in, string& left, string& right, char c) {
+	size_t pos = in.find_first_of(c);
+	if(pos == string::npos) {
+		left = in;
+		trim(left);
+		right = "";
+	} else if (pos <= 1) {
+		left = "";
+		right = in.substr(pos+1, string::npos);
+		trim(right);
+	} else {
+		left = in.substr(0, pos-1);
+		trim(left);
+		right = in.substr(pos+1, string::npos);
+		trim(right);
+	}
+}
+
+void Config::trim(string& s) {
+	while ( (s.length() > 1) && ( (s[0] == ' ') || (s[0] =='\t') ) ) {
+		s = s.substr(1, string::npos);
+	}
+	while ( (s.length() > 1) &&
+			( (s[s.length()-1] == ' ') ||
+			  (s[s.length()-1] == '\t') || 
+			  (s[s.length()-1] == '\n') || 
+			  (s[s.length()-1] == '\r') ) ) {
+		s = s.substr(0, s.length()-1);
+	}
+	if ( (s.length() > 1) && (s[0] == '"') ) {
+		s = s.substr(1, string::npos);
+	}
+	if ( (s.length() > 1) && (s[s.length()-1] == '"') ) {
+		s = s.substr(0, s.length()-1);
+	}
+}
+
+void Config::symbolExpand(string& s) {
+	symbolExpand(symbols, s);
+}
+
+void Config::envSymbolExpand(string& s) {
+	symbolExpand(envSymbols, s);
+}
+
+void Config::symbolExpand(map<string, string>& symbols, string& s) {
+	bool expanded;
+	do {
+		expanded = false;
+		for (map<string, string>::iterator i = symbols.begin(); i != symbols.end(); ++i) {
+			string search = "%" + i->first + "%";
+			string replace = i->second;
+			size_t pos = s.find(search);
+			if (pos != string::npos) {
+				expanded = true;
+				s.replace(pos, search.length(), replace);
+			}
+		}
+	} while (expanded);
+}
+
+string Config::pString(string name) {
+	map<string, string>::iterator i = symbols.find(name);
+	if (i == symbols.end()) {
+		logError(cout << "access of missing property '" << name << "' (" << debugInfo << ")" << endl);
+		exit(4);
+	}
+	return i->second;
+}
+
+bool Config::pBool(string name) {
+	string val = pString(name);
+
+	if ( (val == "yes") ||
+	     (val == "Yes") ||
+	     (val == "YES") ||
+		 (val == "true") ||
+	     (val == "True") ||
+	     (val == "TRUE"))
+	{
+		return true;
+	}
+
+	return false;
+}
+
+double Config::pDouble(string name) {
+	string val = pString(name);
+
+	return atof(val.c_str());
+}
+
+int Config::pInt(string name) {
+	string val = pString(name);
+
+	return atoi(val.c_str());
+}
Index: Config.h
===================================================================
--- Config.h	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
+++ Config.h	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -0,0 +1,101 @@
+#ifndef IncConfig
+#define IncConfig
+
+#include <string>
+#include <map>
+#include <list>
+using namespace std;
+
+/*
+   Config
+
+   Parse structured config files
+
+   Config files contains lines with name-value assignements in the form "<name> = <value>".
+   Trailing and leading whitespace is stripped. Parsed config entries are stored in
+   a symbol map.
+   
+   Lines beginning with '#' are a comment and ignored.
+
+   Config files may be structured (to arbitrary depth). To start a new config sub group
+   (or sub section) use a line in the form of "<name> = (".
+   Subsequent entries are stured in the sub group, until a line containing ")" is found.
+
+   Values may reuse already defined names as a variable which gets expanded during
+   the parsing process. Names for expansion are searched from the current sub group
+   upwards. Finally the process environment is searched, so also environment
+   variables may be used as expansion symbols in the config file.
+
+   Errors and warnings are handled by emitting logging messages (see log.h/log.cpp)
+   or by calling exit() for severe errors. Depending on project needs this may be replaced
+   by exeptions, error return codes, ...
+ */
+
+class Config {
+	public:
+		/* Parse config file 'configFile'. If the process environment
+		 * is provided, environment variables can be used as expansion symbols.
+		 */
+		Config(string configFile, char** envp = 0);
+
+		~Config();
+		
+		// get string config entry
+		string pString(string name);
+
+		/* get boolean config entry
+		 * A value of Yes/yes/YES/true/True/TRUE leads to true,
+		 * all other values leads to false.
+		 */
+		bool pBool(string name);
+
+		// get double config entry; value is parsed using atof()
+		double pDouble(string name);
+
+		// get int config entry; value is parsed using atoi()
+		int pInt(string name);
+
+		// get the symbol map (e.g. for iterating over all symbols)
+		inline map<string, string>& getSymbols() {
+			return symbols;
+		}
+
+		// get config sub group
+		inline Config* group(string name) {
+			return groups[name];
+		}
+
+		// get config sub group map (e.g. for iterating over all groups)
+		inline map<string, Config*>& getGroups() {
+			return groups;
+		}
+
+	private:
+		// private constructor for sub groups
+		Config(string name, string parentDebugInfo);
+
+		// helper functions for parsing
+		void add(string name, string value);
+		void split(string in, string& left, string& right, char c);
+		void trim(string& s);
+		void symbolExpand(string& s);
+		void symbolExpand(map<string, string>& symbols, string& s);
+		void envSymbolExpand(string& s);
+		
+		// config group symbol map
+		map<string, string> symbols;
+
+		// environment symbol map
+		map<string, string> envSymbols;
+
+		// config sub group map
+		map<string, Config*> groups;
+
+		// stack of config groups for parsing (only used in top config element)
+		list<Config*> groupStack;
+
+		// debug info used for logging messages
+		string debugInfo;
+};
+
+#endif
Index: Log.cpp
===================================================================
--- Log.cpp	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
+++ Log.cpp	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -0,0 +1,3 @@
+#include "Log.h"
+
+LogLevel logLevel = LOG_INFO;
Index: Log.h
===================================================================
--- Log.h	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
+++ Log.h	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -0,0 +1,14 @@
+#ifndef IncLog
+#define IncLog
+
+enum LogLevel { LOG_QUIET, LOG_ERROR, LOG_INFO, LOG_DEBUG };
+
+extern LogLevel logLevel;
+
+#define logError(A) ((logLevel >= LOG_ERROR)?((A),0):(0))
+#define logInfo(A) ((logLevel >= LOG_INFO)?((A),0):(0))
+#define logDebug(A) ((logLevel >= LOG_DEBUG)?((A),0):(0))
+
+void debugBreak();
+
+#endif
Index: Makefile
===================================================================
--- Makefile	(revision a316c652c21d53db0f7c1b136950d3ccf7f99975)
+++ Makefile	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -6,6 +6,6 @@
 all: netslow
 
-netslow: netslow.o NetFlow9Processor.o NetFlowDataTemplateCache.o NetFlowDataTemplate.o FlowSet.o FlowSetRecord.o
-	$(CC) -o netslow netslow.o NetFlow9Processor.o NetFlowDataTemplateCache.o NetFlowDataTemplate.o FlowSet.o FlowSetRecord.o  $(LIBS)
+netslow: netslow.o NetFlow9Processor.o NetFlowDataTemplateCache.o NetFlowDataTemplate.o FlowSet.o FlowSetRecord.o Config.o
+	$(CC) -o netslow netslow.o NetFlow9Processor.o NetFlowDataTemplateCache.o NetFlowDataTemplate.o FlowSet.o FlowSetRecord.o Config.o $(LIBS)
 
 
@@ -16,4 +16,5 @@
 FlowSet.o: FlowSet.cpp
 FlowSetRecord.o: FlowSetRecord.cpp
+Config.o: Config.cpp
 
 clean:
Index: NetFlow9Processor.cpp
===================================================================
--- NetFlow9Processor.cpp	(revision a316c652c21d53db0f7c1b136950d3ccf7f99975)
+++ NetFlow9Processor.cpp	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -122,4 +122,7 @@
 			field = *it;
 			printf("    -> Code: %d, length: %d\n", field.code, field.length);
+
+			//TODO: Form a standard record and return it
+
 			nextIndex += field.length;
 		}
Index: NetFlow9Processor.h
===================================================================
--- NetFlow9Processor.h	(revision a316c652c21d53db0f7c1b136950d3ccf7f99975)
+++ NetFlow9Processor.h	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -19,5 +19,5 @@
 
 
-	private:
+	protected:
 
 		NetFlowDataTemplateCache *dataTemplateCache;
Index: NetSlow.vcxproj
===================================================================
--- NetSlow.vcxproj	(revision a316c652c21d53db0f7c1b136950d3ccf7f99975)
+++ NetSlow.vcxproj	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -63,8 +63,12 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <IncludePath>..\include\x86_64-linux-gnu;..\include;$(IncludePath)</IncludePath>
+    <SourcesToCopyRemotelyOverride>@(SourcesToCopyRemotely)</SourcesToCopyRemotelyOverride>
+    <AdditionalSourcesToCopyMapping>netslow.conf</AdditionalSourcesToCopyMapping>
   </PropertyGroup>
   <ItemGroup>
+    <ClCompile Include="Config.cpp" />
     <ClCompile Include="FlowSet.cpp" />
     <ClCompile Include="FlowSetRecord.cpp" />
+    <ClCompile Include="Log.cpp" />
     <ClCompile Include="NetFlow9Processor.cpp" />
     <ClCompile Include="NetFlowDataTemplate.cpp" />
@@ -73,6 +77,8 @@
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="Config.h" />
     <ClInclude Include="FlowSet.h" />
     <ClInclude Include="FlowSetRecord.h" />
+    <ClInclude Include="Log.h" />
     <ClInclude Include="NetFlow9Processor.h" />
     <ClInclude Include="NetFlowDataTemplate.h" />
@@ -84,4 +90,9 @@
     <None Include="Makefile" />
   </ItemGroup>
+  <ItemGroup>
+    <Text Include="netslow.conf">
+      <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent>
+    </Text>
+  </ItemGroup>
   <ItemDefinitionGroup />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
Index: netslow.conf
===================================================================
--- netslow.conf	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
+++ netslow.conf	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -0,0 +1,44 @@
+# Example netslow config file
+
+# Define the IP address to bind to
+# (use 0.0.0.0 for all IPs)
+bind_ip = 0.0.0.0
+
+# Define the port to bind to
+bind_port = 9997
+
+# The most efficient way to run the program is with one router
+# reporting to one IP/port combination so that very few checks
+# are required on each packet.  In this mode each router will
+# require it's own instance of netslow.
+# 
+# Define whether to use single router mode
+single_router = yes
+
+# The program can autodetect which IP(s) to listen for,
+# assuming they all run the same reporting protocol. Please
+# ensure upstream filtering is applied to prevent DoS by
+# mallicious actors (eg good firewall rules)
+auto_learn = no
+auto_protocol = NF9
+
+# Define manually configured routers.  In single router mode
+# only router1 is used
+router1 = (
+
+# Define a name (optional)
+	name = BNG2
+
+# Define the IP address of the router to listen for
+	ip = 8.8.8.8
+
+# Define the protocol used by the router
+	protocol = NF9
+)
+
+# Define multiple routers if required
+router2 = (
+	name = BNG1
+	ip = 8.8.4.4
+	protocol = NF9
+)
Index: netslow.cpp
===================================================================
--- netslow.cpp	(revision a316c652c21d53db0f7c1b136950d3ccf7f99975)
+++ netslow.cpp	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -8,12 +8,14 @@
 #include "NetFlow9Processor.h"
 #include "FlowSet.h"
+#include "Config.h"
 
 //TODO: Base this on the MTU of the interface
 #define BUFSIZE 2048	/* Assuming we're using a 1500MTU */
 
-int main(int argc, char **argv) {
+int main(int argc, char **argv, char* envp[]) {
 
 	struct sockaddr_in localAddress;				/* Address to bind */
-	struct sockaddr_in remoteAddress;				/* Remote address */
+	struct sockaddr_in remoteAddress;				/* Remote address to listen for */
+	struct sockaddr_in remotePacketAddress;			/* Remote packet address */
 	socklen_t addressLength = sizeof(remoteAddress);/* Length of addresses */
 	int bytesReceived;								/* Number of bytes received */
@@ -23,5 +25,7 @@
 	int retval;										/* Return value from processor */
 	FlowSet *processedFlowSet;						/* Returned flowset after processing */
+	string filename = "/home/ian/projects/NetSlow/netslow.conf";	/* Configuration file - TODO: Add to CLI Arguments */
 
+	Config *config = new Config(filename, envp);
 
 	//Create a UDP socket - currently IPv4 only
@@ -33,11 +37,10 @@
 	}
 
-	//Bind the socket to any valid IP address and the port defined in the header
-	//TODO: Move to CLI args for interfaces/IPs/ports
-
+	//Bind the socket to IP address and port defined in the config file
+	//TODO: Also support IPv6
 	memset((char *)&localAddress, 0, sizeof(localAddress));
 	localAddress.sin_family = AF_INET;
-	localAddress.sin_addr.s_addr = htonl(INADDR_ANY);
-	localAddress.sin_port = htons(SERVICE_PORT);
+	inet_pton(AF_INET, config->pString("bind_ip").c_str(), &(localAddress.sin_addr));
+	localAddress.sin_port = htons(config->pInt("bind_port"));
 
 	if (bind(fd, (struct sockaddr *)&localAddress, sizeof(localAddress)) < 0) {
@@ -46,31 +49,32 @@
 	}
 
-	remoteAddress.sin_family = AF_INET;
-	//TODO: Massive fudge for testing - get real IP and make array of Processors
-	//TODO: One per IP.  Also support IPv6
-	inet_pton(AF_INET, "8.8.8.8", &(remoteAddress.sin_addr));
-	NetFlow9 = new NetFlow9Processor((struct sockaddr *)&remoteAddress, &addressLength);
 
-	//NetFlow is very simple, so we can simply loop over all packets
-	//received.
-	for (;;) {
-//		printf("waiting on port %d\n", SERVICE_PORT);
-		bytesReceived = recvfrom(fd, buffer, BUFSIZE, 0, (struct sockaddr *)&remoteAddress, &addressLength);
+	if (config->pBool("single_router")) {
+		remoteAddress.sin_family = AF_INET;
+		//TODO: Support IPv6
+		inet_pton(AF_INET, config->group("router1")->pString("ip").c_str(), &(remoteAddress.sin_addr));
+		NetFlow9 = new NetFlow9Processor((struct sockaddr *)&remoteAddress, &addressLength);
 
-		if (remoteAddress.sin_family != AF_INET) {
-			continue; //Ooops something wrong with this packet
-		}
+		//NetFlow is very simple, so we can simply loop over all packets
+		//received.
+		for (;;) {
+			//		printf("waiting on port %d\n", SERVICE_PORT);
+			bytesReceived = recvfrom(fd, buffer, BUFSIZE, 0, (struct sockaddr *)&remotePacketAddress, &addressLength);
 
-//		printf("received %d bytes\n", bytesReceived);
-		if (bytesReceived > 2) {
-			int version = (buffer[0] << 8 ) + buffer [1];
+			if (remotePacketAddress.sin_family != AF_INET || remotePacketAddress.sin_addr.s_addr != remoteAddress.sin_addr.s_addr) {
+				continue; //Ooops something wrong with this packet
+			}
+
+			//		printf("received %d bytes\n", bytesReceived);
+			if (bytesReceived > 2) {
+				int version = (buffer[0] << 8) + buffer[1];
 
 
-			switch (version) {
+				switch (version) {
 				case 9:
 					//NFv9
 //					printf("Len: %d...  Running NetFlow9Packet Processor\n", bytesReceived);
-					retval = NetFlow9->ProcessPacket(buffer,bytesReceived,processedFlowSet);
-					if ( processedFlowSet == 0 ) {
+					retval = NetFlow9->ProcessPacket(buffer, bytesReceived, processedFlowSet);
+					if (processedFlowSet == 0) {
 						printf("Error in NF9Processor - invalid pointer\n");
 					}
@@ -78,7 +82,8 @@
 				default:
 					return 254;
+				}
+
+				printf("PacketProcessor returned %d\n", processedFlowSet);
 			}
-
-			printf("PacketProcessor returned %d\n",processedFlowSet);
 		}
 	}
Index: port.h
===================================================================
--- port.h	(revision a316c652c21d53db0f7c1b136950d3ccf7f99975)
+++ port.h	(revision 5f0e41ecee466658d1e94cb2c711f9305b52a975)
@@ -6,4 +6,4 @@
  */
 
-#define SERVICE_PORT	9995	/* hard-coded port number */
+#define SERVICE_PORT	9997	/* hard-coded port number */
 
