Browse Source

Filter work, add name and desc to netconf response, small compiler warning fix.

Adam Ierymenko 12 years ago
parent
commit
3745377872
5 changed files with 192 additions and 186 deletions
  1. 26 29
      netconf-service/netconf.cpp
  2. 90 54
      node/Filter.cpp
  3. 35 102
      node/Filter.hpp
  4. 1 1
      node/RuntimeEnvironment.hpp
  5. 40 0
      node/Utils.hpp

+ 26 - 29
netconf-service/netconf.cpp

@@ -81,29 +81,6 @@ static Mutex stdoutWriteLock;
 static Connection *dbCon = (Connection *)0;
 static char mysqlHost[64],mysqlPort[64],mysqlDatabase[64],mysqlUser[64],mysqlPassword[64];
 
-static void connectOrReconnect()
-{
-	for(;;) {
-		delete dbCon;
-		try {
-			dbCon = new Connection(mysqlDatabase,mysqlHost,mysqlUser,mysqlPassword,(unsigned int)strtol(mysqlPort,(char **)0,10));
-			if (dbCon->connected()) {
-				fprintf(stderr,"(re?)-connected to mysql server successfully\n");
-				break;
-			} else {
-				fprintf(stderr,"unable to connect to database server (connection closed), trying again in 1s...\n");
-				usleep(1000000);
-			}
-		} catch (std::exception &exc) {
-			fprintf(stderr,"unable to connect to database server (%s), trying again in 1s...\n",exc.what());
-			usleep(1000000);
-		} catch ( ... ) {
-			fprintf(stderr,"unable to connect to database server (unknown exception), trying again in 1s...\n");
-			usleep(1000000);
-		}
-	}
-}
-
 int main(int argc,char **argv)
 {
 	{
@@ -140,7 +117,20 @@ int main(int argc,char **argv)
 	char buf[131072];
 	std::string dictBuf;
 
-	connectOrReconnect();
+	try {
+		dbCon = new Connection(mysqlDatabase,mysqlHost,mysqlUser,mysqlPassword,(unsigned int)strtol(mysqlPort,(char **)0,10));
+		if (dbCon->connected()) {
+			fprintf(stderr,"connected to mysql server successfully\n");
+			break;
+		} else {
+			fprintf(stderr,"unable to connect to database server\n");
+			return -1;
+		}
+	} catch (std::exception &exc) {
+		fprintf(stderr,"unable to connect to database server: %s\n",exc.what());
+		return -1;
+	}
+
 	for(;;) {
 		for(int l=0;l<4;) {
 			int n = (int)read(STDIN_FILENO,buf + l,4 - l);
@@ -164,8 +154,10 @@ int main(int argc,char **argv)
 		Dictionary request(dictBuf);
 		dictBuf = "";
 
-		if (!dbCon->connected())
-			connectOrReconnect();
+		if (!dbCon->connected()) {
+			fprintf(stderr,"connection to database server lost\n");
+			return -1;
+		}
 
 		try {
 			const std::string &reqType = request.get("type");
@@ -213,13 +205,16 @@ int main(int argc,char **argv)
 				}
 
 				bool isOpen = false;
+				std::string name,desc;
 				{
 					Query q = dbCon->query();
-					q << "SELECT isOpen FROM Network WHERE id = " << nwid;
+					q << "SELECT name,desc,isOpen FROM Network WHERE id = " << nwid;
 					StoreQueryResult rs = q.store();
-					if (rs.num_rows() > 0)
+					if (rs.num_rows() > 0) {
+						name = rs[0]["name"].c_str();
+						desc = rs[0]["desc"].c_str();
 						isOpen = ((int)rs[0]["isOpen"] > 0);
-					else {
+					} else {
 						Dictionary response;
 						response["peer"] = peerIdentity.address().toString();
 						response["nwid"] = request.get("nwid");
@@ -243,6 +238,8 @@ int main(int argc,char **argv)
 				sprintf(buf,"%.16llx",(unsigned long long)nwid);
 				netconf["nwid"] = buf;
 				netconf["isOpen"] = (isOpen ? "1" : "0");
+				netconf["name"] = name;
+				netconf["desc"] = desc;
 				sprintf(buf,"%llx",(unsigned long long)Utils::now());
 				netconf["ts"] = buf;
 

+ 90 - 54
node/Filter.cpp

@@ -30,6 +30,8 @@
 #include <string.h>
 #include <stdint.h>
 
+#include <algorithm>
+
 #include "RuntimeEnvironment.hpp"
 #include "Logger.hpp"
 #include "Filter.hpp"
@@ -40,6 +42,61 @@ namespace ZeroTier {
 const char *const Filter::UNKNOWN_NAME = "(unknown)";
 const Range<unsigned int> Filter::ANY;
 
+static inline Range<unsigned int> __parseRange(char *r)
+	throw(std::invalid_argument)
+{
+	char *saveptr = (char *)0;
+	unsigned int a = 0;
+	unsigned int b = 0;
+	unsigned int fn = 0;
+	for(char *f=Utils::stok(r,"-",&saveptr);(f);f=Utils::stok((char *)0,"-",&saveptr)) {
+		if (*f) {
+			switch(fn++) {
+				case 0:
+					if (*f != '*')
+						a = b = (unsigned int)strtoul(f,(char **)0,10);
+					break;
+				case 1:
+					if (*f != '*')
+						b = (unsigned int)strtoul(f,(char **)0,10);
+					break;
+				default:
+					throw std::invalid_argument("rule range must be <int>, <int>-<int>, or *");
+			}
+		}
+	}
+	return Range<unsigned int>(a,b);
+}
+
+Filter::Rule::Rule(const char *s)
+	throw(std::invalid_argument)
+{
+	char *saveptr = (char *)0;
+	char tmp[256];
+	if (!Utils::scopy(tmp,sizeof(tmp),s))
+		throw std::invalid_argument("rule string too long");
+	unsigned int fn = 0;
+	for(char *f=Utils::stok(tmp,";",&saveptr);(f);f=Utils::stok((char *)0,";",&saveptr)) {
+		if (*f) {
+			switch(fn++) {
+				case 0:
+					_etherType = __parseRange(f);
+					break;
+				case 1:
+					_protocol = __parseRange(f);
+					break;
+				case 2:
+					_port = __parseRange(f);
+					break;
+				default:
+					throw std::invalid_argument("rule string has unknown extra fields");
+			}
+		}
+	}
+	if (fn != 3)
+		throw std::invalid_argument("rule string must contain 3 fields");
+}
+
 bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int len) const
 	throw(std::invalid_argument)
 {
@@ -166,7 +223,7 @@ std::string Filter::Rule::toString() const
 			s.append(buf);
 			break;
 	}
-	s.push_back('/');
+	s.push_back(';');
 	switch(_protocol.magnitude()) {
 		case 0:
 			s.push_back('*');
@@ -180,7 +237,7 @@ std::string Filter::Rule::toString() const
 			s.append(buf);
 			break;
 	}
-	s.push_back('/');
+	s.push_back(';');
 	switch(_port.magnitude()) {
 		case 0:
 			s.push_back('*');
@@ -198,37 +255,50 @@ std::string Filter::Rule::toString() const
 	return s;
 }
 
-void Filter::add(const Rule &r,const Action &a)
+Filter::Filter(const char *s)
+	throw(std::invalid_argument)
 {
-	Mutex::Lock _l(_chain_m);
-	for(std::vector<Entry>::iterator i(_chain.begin());i!=_chain.end();++i) {
-		if (i->rule == r) {
-			_chain.erase(i);
-			break;
+	char tmp[16384];
+	if (!Utils::scopy(tmp,sizeof(tmp),s))
+		throw std::invalid_argument("filter string too long");
+	char *saveptr = (char *)0;
+	unsigned int fn = 0;
+	for(char *f=Utils::stok(tmp,"-",&saveptr);(f);f=Utils::stok((char *)0,"-",&saveptr)) {
+		try {
+			_rules.push_back(Rule(f));
+			++fn;
+		} catch (std::invalid_argument &exc) {
+			char tmp[256];
+			sprintf(tmp,"invalid rule at index %u: %s",fn,exc.what());
+			throw std::invalid_argument(tmp);
 		}
 	}
-	_chain.push_back(Entry(r,a));
+	std::sort(_rules.begin(),_rules.end());
 }
 
-std::string Filter::toString(const char *sep) const
+std::string Filter::toString() const
 {
-	if (!sep)
-		sep = ",";
-
 	std::string s;
 
-	bool first = true;
-	Mutex::Lock _l(_chain_m);
-	for(std::vector<Entry>::const_iterator i(_chain.begin());i!=_chain.end();++i) {
-		s.append(i->rule.toString());
-		if (first)
-			first = false;
-		else s.append(sep);
+	for(std::vector<Rule>::const_iterator r(_rules.begin());r!=_rules.end();++r) {
+		if (s.length() > 0)
+			s.push_back(',');
+		s.append(r->toString());
 	}
 
 	return s;
 }
 
+void Filter::add(const Rule &r)
+{
+	for(std::vector<Rule>::iterator rr(_rules.begin());rr!=_rules.end();++rr) {
+		if (r == *rr)
+			return;
+	}
+	_rules.push_back(r);
+	std::sort(_rules.begin(),_rules.end());
+}
+
 const char *Filter::etherTypeName(const unsigned int etherType)
 	throw()
 {
@@ -335,38 +405,4 @@ const char *Filter::icmp6TypeName(const unsigned int icmp6Type)
 	return UNKNOWN_NAME;
 }
 
-Filter::Action Filter::operator()(const RuntimeEnvironment *_r,unsigned int etherType,const void *frame,unsigned int len) const
-{
-	Mutex::Lock _l(_chain_m);
-
-	TRACE("starting match against %d rules",(int)_chain.size());
-
-	int ruleNo = 0;
-	for(std::vector<Entry>::const_iterator r(_chain.begin());r!=_chain.end();++r,++ruleNo) {
-		try {
-			if (r->rule(etherType,frame,len)) {
-				TRACE("match: %s",r->rule.toString().c_str());
-
-				switch(r->action) {
-					case ACTION_ALLOW:
-					case ACTION_DENY:
-						return r->action;
-					default:
-						break;
-				}
-			} else {
-				TRACE("no match: %s",r->rule.toString().c_str());
-			}
-		} catch (std::invalid_argument &exc) {
-			LOG("filter: unable to parse packet on rule %s (%d): %s",r->rule.toString().c_str(),ruleNo,exc.what());
-			return ACTION_UNPARSEABLE;
-		} catch ( ... ) {
-			LOG("filter: unable to parse packet on rule %s (%d): unknown exception",r->rule.toString().c_str(),ruleNo);
-			return ACTION_UNPARSEABLE;
-		}
-	}
-
-	return ACTION_ALLOW;
-}
-
 } // namespace ZeroTier

+ 35 - 102
node/Filter.hpp

@@ -29,13 +29,14 @@
 #define _ZT_FILTER_HPP
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+
 #include <string>
 #include <vector>
 #include <utility>
 #include <stdexcept>
 
-#include "Mutex.hpp"
 #include "Range.hpp"
 
 /* Ethernet frame types that might be relevant to us */
@@ -125,7 +126,11 @@ namespace ZeroTier {
 class RuntimeEnvironment;
 
 /**
- * A simple Ethernet frame level filter supporting basic IP port DENY
+ * A simple Ethernet frame level filter
+ *
+ * This doesn't specify actions, since it's used as a deny filter. The rule
+ * in ZT1 is "that which is not explicitly prohibited is allowed." (Except for
+ * ethertypes, which are handled by a whitelist.)
  */
 class Filter
 {
@@ -145,8 +150,6 @@ public:
 
 	/**
 	 * A filter rule
-	 *
-	 * This behaves as an immutable value object.
 	 */
 	class Rule
 	{
@@ -159,6 +162,15 @@ public:
 		{
 		}
 
+		/**
+		 * Construct a rule from a string-serialized value
+		 *
+		 * @param s String formatted rule, such as returned by toString()
+		 * @throws std::invalid_argument String formatted rule is not valid
+		 */
+		Rule(const char *s)
+			throw(std::invalid_argument);
+
 		/**
 		 * Construct a new rule
 		 *
@@ -191,6 +203,8 @@ public:
 			throw(std::invalid_argument);
 
 		/**
+		 * Serialize rule as string
+		 *
 		 * @return Human readable representation of rule
 		 */
 		std::string toString() const;
@@ -222,105 +236,36 @@ public:
 		Range<unsigned int> _port;
 	};
 
-	/**
-	 * Action if a rule matches
-	 */
-	enum Action
-	{
-		ACTION_DENY = 0,
-		ACTION_ALLOW = 1,
-		ACTION_UNPARSEABLE = 2
-	};
+	Filter() {}
 
 	/**
-	 * Entry in filter chain
+	 * @param s String-serialized filter representation
 	 */
-	struct Entry
-	{
-		Entry() {}
-		Entry(const Rule &r,const Action &a) :
-			rule(r),
-			action(a)
-		{
-		}
-
-		Rule rule;
-		Action action;
-	};
-
-	Filter() :
-		_chain(),
-		_chain_m()
-	{
-	}
-
-	Filter(const Filter &f) :
-		_chain(),
-		_chain_m()
-	{
-		Mutex::Lock _l(f._chain_m);
-		_chain = f._chain;
-	}
-
-	inline Filter &operator=(const Filter &f)
-	{
-		Mutex::Lock _l1(_chain_m);
-		Mutex::Lock _l2(f._chain_m);
-		_chain = f._chain;
-		return *this;
-	}
+	Filter(const char *s)
+		throw(std::invalid_argument);
 
 	/**
-	 * Remove all filter entries
+	 * @return Comma-delimited list of string-format rules
 	 */
-	inline void clear()
-	{
-		Mutex::Lock _l(_chain_m);
-		_chain.clear();
-	}
+	std::string toString() const;
 
 	/**
-	 * Append a rule/action pair to this chain
-	 *
-	 * If an identical rule already exists it is removed and a new entry is
-	 * added to the end with the new action. (Two identical rules with the
-	 * same action wouldn't make sense.)
+	 * Add a rule to this filter
 	 *
-	 * @param r Rule to add
-	 * @param a Action if rule matches
+	 * @param r Rule to add to filter
 	 */
-	void add(const Rule &r,const Action &a);
+	void add(const Rule &r);
 
-	/**
-	 * @return Number of rules in filter chain
-	 */
-	inline unsigned int length() const
-		throw()
+	inline bool operator()(unsigned int etype,const void *data,unsigned int len) const
+		throw(std::invalid_argument)
 	{
-		Mutex::Lock _l(_chain_m);
-		return (unsigned int)_chain.size();
-	}
-
-	/**
-	 * @return Entry in filter chain or null entry if out of bounds
-	 */
-	inline Entry operator[](const unsigned int i) const
-		throw()
-	{
-		Mutex::Lock _l(_chain_m);
-		if (i < _chain.size())
-			return _chain[i];
-		return Entry();
+		for(std::vector<Rule>::const_iterator r(_rules.begin());r!=_rules.end();++r) {
+			if ((*r)(etype,data,len))
+				return true;
+		}
+		return false;
 	}
 
-	/**
-	 * Get a string representation of this filter
-	 *
-	 * @param sep Separator between filter rules, or NULL for comma (default)
-	 * @return Human-readable string
-	 */
-	std::string toString(const char *sep = (const char *)0) const;
-
 	static const char *etherTypeName(const unsigned int etherType)
 		throw();
 	static const char *ipProtocolName(const unsigned int ipp)
@@ -330,20 +275,8 @@ public:
 	static const char *icmp6TypeName(const unsigned int icmp6Type)
 		throw();
 
-	/**
-	 * Match against an Ethernet frame
-	 *
-	 * @param _r Runtime environment
-	 * @param etherType Ethernet frame type
-	 * @param frame Ethernet frame data
-	 * @param len Length of frame in bytes
-	 * @return Action if matched or ACTION_ALLOW if not matched
-	 */
-	Action operator()(const RuntimeEnvironment *_r,unsigned int etherType,const void *frame,unsigned int len) const;
-
 private:
-	std::vector<Entry> _chain;
-	Mutex _chain_m;
+	std::vector<Rule> _rules;
 };
 
 } // namespace ZeroTier

+ 1 - 1
node/RuntimeEnvironment.hpp

@@ -64,9 +64,9 @@ public:
 		shutdownInProgress(false),
 		log((Logger *)0),
 		prng((CMWC4096 *)0),
-		demarc((Demarc *)0),
 		multicaster((Multicaster *)0),
 		sw((Switch *)0),
+		demarc((Demarc *)0),
 		topology((Topology *)0),
 		sysEnv((SysEnv *)0),
 		nc((NodeConfig *)0)

+ 40 - 0
node/Utils.hpp

@@ -443,6 +443,46 @@ public:
 	 */
 	static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
 
+	/**
+	 * Tokenize a string
+	 *
+	 * @param str String to split
+	 * @param delim Delimiters
+	 * @param saveptr Pointer to a char * for temporary reentrant storage
+	 */
+	static inline char *stok(char *str,const char *delim,char **saveptr)
+		throw()
+	{
+#ifdef __WINDOWS__
+		return strtok_s(str,delim,saveptr);
+#else
+		return strtok_r(str,delim,saveptr);
+#endif
+	}
+
+	/**
+	 * Perform a safe C string copy
+	 *
+	 * @param dest Destination buffer
+	 * @param len Length of buffer
+	 * @param src Source string
+	 * @return True on success, false on overflow (buffer will still be 0-terminated)
+	 */
+	static inline bool scopy(char *dest,unsigned int len,const char *src)
+		throw()
+	{
+		if (!len)
+			return false; // sanity check
+		char *end = dest + len;
+		while ((*dest++ = *src++)) {
+			if (dest == end) {
+				dest[len - 1] = (char)0;
+				return false;
+			}
+		}
+		return true;
+	}
+
 	/**
 	 * Trim whitespace from the start and end of a string
 	 *