diff options
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | scripts/vyatta-wanloadbalance.pl | 52 | ||||
-rw-r--r-- | src/lbdata.cc | 71 | ||||
-rw-r--r-- | src/lbdata.hh | 85 | ||||
-rw-r--r-- | src/lbdatafactory.cc | 111 | ||||
-rw-r--r-- | src/lbdatafactory.hh | 19 | ||||
-rw-r--r-- | src/lbdecision.cc | 22 | ||||
-rw-r--r-- | src/lboutput.cc | 23 | ||||
-rw-r--r-- | src/lbpathtest.cc | 322 | ||||
-rw-r--r-- | src/lbpathtest.hh | 21 | ||||
-rw-r--r-- | src/lbtest_icmp.cc | 356 | ||||
-rw-r--r-- | src/lbtest_icmp.hh | 101 | ||||
-rw-r--r-- | templates/load-balancing/wan/interface-health/node.tag/rule/node.def | 5 | ||||
-rw-r--r-- | templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/icmp/node.def (renamed from templates/load-balancing/wan/interface-health/node.tag/ping/node.def) | 0 | ||||
-rw-r--r-- | templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/resp-time/node.def (renamed from templates/load-balancing/wan/interface-health/node.tag/resp-time/node.def) | 0 | ||||
-rw-r--r-- | templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/target/node.def | 2 |
16 files changed, 806 insertions, 387 deletions
diff --git a/Makefile.am b/Makefile.am index 19b7e48..a5487a7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,7 +8,7 @@ etcdir = /etc AM_CPPFLAGS = -I src -Wall -DDEBUG -CLEANFILES = src/main.o src/lbdata.o src/lbdatafactory.o src/loadbalance.o src/rl_str_proc.o src/lbpathtest.o src/lboutput.o +CLEANFILES = src/main.o src/lbdata.o src/lbdatafactory.o src/loadbalance.o src/rl_str_proc.o src/lbpathtest.o src/lbtest_icmp.o src/lboutput.o sbin_SCRIPTS = scripts/vyatta-wanloadbalance.pl sbin_SCRIPTS += scripts/vyatta-wanloadbalance.init @@ -26,6 +26,7 @@ src_wan_lb_SOURCES += src/lbdatafactory.cc src_wan_lb_SOURCES += src/loadbalance.cc src_wan_lb_SOURCES += src/rl_str_proc.cc src_wan_lb_SOURCES += src/lbpathtest.cc +src_wan_lb_SOURCES += src/lbtest_icmp.cc src_wan_lb_SOURCES += src/lbdecision.cc src_wan_lb_SOURCES += src/lboutput.cc diff --git a/scripts/vyatta-wanloadbalance.pl b/scripts/vyatta-wanloadbalance.pl index 397591b..cfc8755 100644 --- a/scripts/vyatta-wanloadbalance.pl +++ b/scripts/vyatta-wanloadbalance.pl @@ -25,7 +25,6 @@ sub write_health { my $valid = "false"; - if ($config->exists("load-balancing wan disable-source-nat")) { print FILE_LCK "disable-source-nat\n"; } @@ -44,6 +43,7 @@ sub write_health { $config->setLevel("load-balancing wan interface-health"); my @eths = $config->listNodes(); foreach my $ethNode (@eths) { + $config->setLevel("load-balancing wan interface-health"); print FILE_LCK "\tinterface " . $ethNode . " {\n"; @@ -51,17 +51,7 @@ sub write_health { if (defined $option) { print FILE_LCK "\t\tfailure-ct " . $option . "\n"; } - - $option = $config->returnValue("$ethNode ping"); - if (defined $option) { - print FILE_LCK "\t\ttarget " . $option . "\n"; - } - - $option = $config->returnValue("$ethNode resp-time"); - if (defined $option) { - print FILE_LCK "\t\tping-resp " . $option*1000 . "\n"; - } - + $option = $config->returnValue("$ethNode success-count"); if (defined $option) { print FILE_LCK "\t\tsuccess-ct " . $option . "\n"; @@ -69,13 +59,49 @@ sub write_health { $option = $config->returnValue("$ethNode nexthop"); if (defined $option) { - print FILE_LCK "\t\tnexthop " . $option . "\n"; + print FILE_LCK "\t\tnexthop " . $option . "\n"; $valid = "true"; } else { print "nexthop must be specified\n"; exit 1; } + + $config->setLevel("load-balancing wan interface-health $ethNode rule"); + my @rules = $config->listNodes(); + foreach my $rule (@rules) { + print FILE_LCK "\t\trule " . $rule . " {\n"; + +# my $icmp = $config->returnValue("$rule icmp"); +# if (defined $icmp) { + print FILE_LCK "\t\t\ttype icmp {\n"; +# } + +# my $ttl = $config->returnValue("$rule ttl"); +# if (defined $ttl) { +# print FILE_LCK "\t\t\ttype udp {\n"; +# print FILE_LCK "\t\t\t\tttl " . $ttl . "\n"; +# } + +# if (defined $icmp && defined $ttl) { +# print "Only a single test type can be defined (ttl or icmp)\n"; +# exit 1; +# } + + $option = $config->returnValue("$rule target"); + if (defined $option) { + print FILE_LCK "\t\t\t\ttarget " . $option . "\n"; + } + + $option = $config->returnValue("$rule resp-time"); + if (defined $option) { + print FILE_LCK "\t\t\t\tresp-time " . $option*1000 . "\n"; + } + print FILE_LCK "\t\t\t}\n"; + + print FILE_LCK "\t\t}\n"; + } + print FILE_LCK "\t}\n"; } print FILE_LCK "}\n\n"; diff --git a/src/lbdata.cc b/src/lbdata.cc index da92d3c..0d06918 100644 --- a/src/lbdata.cc +++ b/src/lbdata.cc @@ -42,6 +42,57 @@ LBHealth::put(int rtt) } } + +/** + * + * + **/ +void +LBHealth::start_new_test_cycle() +{ + _test_iter = _test_coll.begin(); + if (_test_iter != _test_coll.end()) { + _test_iter->second->init(); + } + _test_success = false; +} + +/** + * + * + **/ +void +LBHealth::send_test() +{ + if (_test_success == true || _test_iter == _test_coll.end()) { + return; //means we are done + } + _test_iter->second->send(*this); +} + +/** + * + * + **/ +bool +LBHealth::recv_test() +{ + if (_test_success == true || _test_iter == _test_coll.end()) { + return false; + } + int rtt = _test_iter->second->recv(*this); + if (rtt != -1) { + put(rtt); //push test result + _test_success = true; + return true; //means we have successfully completed the test + } + if (++_test_iter == _test_coll.end()) { + put(-1); + return true; //end of tests + } + return false; +} + /** * * @@ -129,18 +180,24 @@ void LBData::dump() { - cout << "health" << endl; + cout << "health:" << endl; LBData::InterfaceHealthIter h_iter = _iface_health_coll.begin(); while (h_iter != _iface_health_coll.end()) { - cout << " " << h_iter->first << endl; - cout << " " << h_iter->second._success_ct << endl; - cout << " " << h_iter->second._failure_ct << endl; - cout << " " << h_iter->second._ping_target << endl; - cout << " " << h_iter->second._ping_resp_time << endl; + cout << " interface: " << h_iter->first << endl; + cout << " nexthop: " << h_iter->second._nexthop << endl; + cout << " success ct: " << h_iter->second._success_ct << endl; + cout << " failure ct: " << h_iter->second._failure_ct << endl; + LBHealth::TestIter t_iter = h_iter->second._test_coll.begin(); + while (t_iter != h_iter->second._test_coll.end()) { + cout << " test: " << t_iter->first << endl; + cout << " target: " << t_iter->second->_target << endl; + cout << " resp time:" << t_iter->second->_resp_time << endl; + ++t_iter; + } ++h_iter; } - cout << endl << "wan" << endl; + cout << endl << "wan:" << endl; LBData::LBRuleIter r_iter = _lb_rule_coll.begin(); while (r_iter != _lb_rule_coll.end()) { cout << " rule: " << r_iter->first << endl; diff --git a/src/lbdata.hh b/src/lbdata.hh index e85adbf..53f6cde 100644 --- a/src/lbdata.hh +++ b/src/lbdata.hh @@ -8,6 +8,7 @@ #ifndef __LBDATA_HH__ #define __LBDATA_HH__ +#include <assert.h> #include <map> #include <set> #include <vector> @@ -15,6 +16,12 @@ using namespace std; +class LBHealth; + +/** + * + * + **/ class LBRule { public: typedef map<string, int> InterfaceDistColl; @@ -48,6 +55,10 @@ class LBRule { }; +/** + * + * + **/ class LBHealthHistory { public: LBHealthHistory(int buffer_size); @@ -55,7 +66,6 @@ public: //push in the ping response for this... int push(int rtt); - public: //results of health testing unsigned long _last_success; @@ -68,16 +78,59 @@ public: int _index; }; +/** + * + * + **/ +class LBTest { +public: + LBTest(bool debug) : _debug(debug) {} + virtual ~LBTest() {} + + virtual void + init() = 0; + + virtual void + send(LBHealth &health) = 0; + + virtual int + recv(LBHealth &health) = 0; + +public: + bool _debug; + string _target; + int _resp_time; +}; + +/** + * + * + **/ class LBHealth { - public: - LBHealth(int interface_index) : +public: + typedef map<int,LBTest*> TestColl; + typedef map<int,LBTest*>::iterator TestIter; + typedef map<int,LBTest*>::const_iterator TestConstIter; + +public: + LBHealth() : + _success_ct(0), + _failure_ct(0), + _hresults(10), + _is_active(true), + _state_changed(true), + _last_time_state_changed(0), + _interface_index(0) + {} + + LBHealth(int interface_index, string &interface) : _success_ct(0), _failure_ct(0), - _ping_resp_time(0), _hresults(10), _is_active(true), _state_changed(true), _last_time_state_changed(0), + _interface(interface), _interface_index(interface_index) {} @@ -95,21 +148,39 @@ class LBHealth { unsigned long failure_count() const {return _failure_ct;} + //test interfaces + void + start_new_test_cycle(); + + void + send_test(); + + bool + recv_test(); + +public: //variables int _success_ct; int _failure_ct; - string _ping_target; - int _ping_resp_time; string _nexthop; string _dhcp_nexthop; LBHealthHistory _hresults; bool _is_active; bool _state_changed; unsigned long _last_time_state_changed; + string _interface; int _interface_index; string _address; -}; + TestColl _test_coll; +private: //variables + TestIter _test_iter; + bool _test_success; +}; +/** + * + * + **/ class LBData { public: typedef map<int,LBRule> LBRuleColl; diff --git a/src/lbdatafactory.cc b/src/lbdatafactory.cc index c1b77ed..a00887b 100644 --- a/src/lbdatafactory.cc +++ b/src/lbdatafactory.cc @@ -16,6 +16,7 @@ #include <iostream> #include "rl_str_proc.hh" #include "lbdata.hh" +#include "lbtest_icmp.hh" #include "lbdatafactory.hh" using namespace std; @@ -23,8 +24,9 @@ using namespace std; LBDataFactory::LBDataFactory(bool debug) : _debug(debug), - _lb_health(0), - _interface_index(0) + _lb_health(), + _interface_index(0), + _current_test_rule_number(0) { } @@ -120,22 +122,38 @@ LBDataFactory::process(const vector<string> &path, int depth, const string &key, std::transform(value.begin(), value.end(), std::back_inserter(l_value), static_cast < int(*)(int) > (std::tolower)); + if (_debug) { + cout << "LBDataFactory::process(" << depth << "): " << key << ":" << value << endl; + } + if (path[0] == "disable-source-nat") { process_disablesourcenat(l_key,l_value); } else if (path[0] == "flush-conntrack") { process_flushconntrack(l_key,l_value); } + else if (path[0] == "hook") { + process_hook(l_key,l_value); + } else if (path[0] == "health") { - if (l_key == "interface") { + if (depth == 2 && key == "interface") { process_health(l_key,l_value); } - else if (l_key == "hook") { - process_health_hook(l_key,l_value); - } - else { + else if (depth == 2) { process_health_interface(l_key,l_value); } + else if (depth == 3) { + process_health_interface_rule(l_key,l_value); + } + else if (depth == 4 && key == "type") { + process_health_interface_rule_type(l_key,l_value); + } + else if (depth == 4 && key == "target") { + process_health_interface_rule_type_target(l_key,l_value); + } + else if (depth == 4 && key == "resp-time") { + process_health_interface_rule_type_resptime(l_key,l_value); + } } else if (path[0] == "rule") { if (depth > 0 && path[1] == "source") { @@ -180,35 +198,30 @@ LBDataFactory::process_flushconntrack(const string &key, const string &value) _lb_data._flush_conntrack = true; } - void -LBDataFactory::process_health(const string &key, const string &value) +LBDataFactory::process_hook(const string &key, const string &value) { if (value.empty() == false) { - LBData::InterfaceHealthIter iter = _lb_data._iface_health_coll.find(key); - if (iter == _lb_data._iface_health_coll.end()) { - _lb_data._iface_health_coll.insert(pair<string,LBHealth>(value,LBHealth(++_interface_index))); - } - _health_iter = _lb_data._iface_health_coll.find(value); + _lb_data._hook = value; } } void -LBDataFactory::process_health_hook(const string &key, const string &value) +LBDataFactory::process_health(const string &key, const string &value) { if (value.empty() == false) { - _lb_data._hook = value; + LBData::InterfaceHealthIter iter = _lb_data._iface_health_coll.find(key); + if (iter == _lb_data._iface_health_coll.end()) { + _lb_data._iface_health_coll.insert(pair<string,LBHealth>(value,LBHealth(++_interface_index,const_cast<string&>(value)))); + } + _health_iter = _lb_data._iface_health_coll.find(value); } } - void LBDataFactory::process_health_interface(const string &key, const string &value) { - if (key == "target") { - _health_iter->second._ping_target = value; - } - else if (key == "success-ct") { + if (key == "success-ct") { int num = strtoul(value.c_str(), NULL, 10); if (num > 0) { _health_iter->second._success_ct = num; @@ -232,18 +245,6 @@ LBDataFactory::process_health_interface(const string &key, const string &value) syslog(LOG_ERR, "wan_lb: illegal failure-ct specified in configuration file: %s", value.c_str()); } } - else if (key == "ping-resp") { - int num = strtoul(value.c_str(), NULL, 10); - if (num > 0) { - _health_iter->second._ping_resp_time = num; - } - else { - if (_debug) { - cerr << "illegal ping-resp specified: " << value << endl; - } - syslog(LOG_ERR, "wan_lb: illegal ping-resp specified in configuration file: %s", value.c_str()); - } - } else if (key == "nexthop") { _health_iter->second._nexthop = value; } @@ -260,6 +261,50 @@ LBDataFactory::process_health_interface(const string &key, const string &value) } void +LBDataFactory::process_health_interface_rule_type_target(const string &key, const string &value) +{ + _test_iter->second->_target = value; +} + +void +LBDataFactory::process_health_interface_rule_type_resptime(const string &key, const string &value) +{ + _test_iter->second->_resp_time = strtoul(value.c_str(), NULL, 10); +} + +void +LBDataFactory::process_health_interface_rule_type_ttl(const string &key, const string &value) +{ + //nothing yet +} + +void +LBDataFactory::process_health_interface_rule_type(const string &key, const string &value) +{ + if (value == "icmp") { + if (_debug) { + cout << "LBDataFactory::process_health_interface_rule_type(): setting up icmp test" << endl; + } + LBTestICMP *test = new LBTestICMP(_debug); + _health_iter->second._test_coll.insert(pair<int,LBTest*>(_current_test_rule_number,test)); + } + else if (value == "udp") { + /* + LBTestUDP test = new LBTestUDP(); + _health_iter->second._test_coll.insert(pair<int,LBTest>(_current_test_rule_number,test)); + */ + } + _test_iter = _health_iter->second._test_coll.find(_current_test_rule_number); +} + + +void +LBDataFactory::process_health_interface_rule(const string &key, const string &value) +{ + _current_test_rule_number = strtoul(value.c_str(), NULL, 10); +} + +void LBDataFactory::process_rule(const string &key, const string &value) { if (key.empty()) { diff --git a/src/lbdatafactory.hh b/src/lbdatafactory.hh index 12648e8..74b2df6 100644 --- a/src/lbdatafactory.hh +++ b/src/lbdatafactory.hh @@ -50,12 +50,27 @@ private: process_health(const string &key, const string &value); void - process_health_hook(const string &key, const string &value); + process_hook(const string &key, const string &value); void process_health_interface(const string &key, const string &value); void + process_health_interface_rule(const string &key, const string &value); + + void + process_health_interface_rule_type(const string &key, const string &value); + + void + process_health_interface_rule_type_target(const string &key, const string &value); + + void + process_health_interface_rule_type_resptime(const string &key, const string &value); + + void + process_health_interface_rule_type_ttl(const string &key, const string &value); + + void process_rule(const string &key, const string &value); void @@ -88,10 +103,12 @@ private: LBRule _lb_rule; LBData _lb_data; int _interface_index; + int _current_test_rule_number; LBData::LBRuleIter _rule_iter; LBData::InterfaceHealthIter _health_iter; LBRule::InterfaceDistIter _rule_iface_iter; + LBHealth::TestIter _test_iter; }; #endif //__LBDATALOADER_HH__ diff --git a/src/lbdecision.cc b/src/lbdecision.cc index e902c82..261aadd 100644 --- a/src/lbdecision.cc +++ b/src/lbdecision.cc @@ -260,16 +260,18 @@ LBDecision::run(LBData &lb_data) while (iter != state_changed_coll.end()) { //set state //set interface - setenv("WLB_INTERFACE_NAME",iter->first.c_str(),1); - setenv("WLB_INTERFACE_STATE",iter->second.c_str(),1); - - syslog(LOG_WARNING, "executing script: %s",lb_data._hook.c_str()); - - execute(lb_data._hook, stdout); - //unset state - //unset interface - unsetenv("WLB_INTERFACE_NAME"); - unsetenv("WLB_INTERFACE_STATE"); + if (lb_data._hook.empty() == false) { + setenv("WLB_INTERFACE_NAME",iter->first.c_str(),1); + setenv("WLB_INTERFACE_STATE",iter->second.c_str(),1); + + syslog(LOG_WARNING, "executing script: %s",lb_data._hook.c_str()); + + execute(lb_data._hook, stdout); + //unset state + //unset interface + unsetenv("WLB_INTERFACE_NAME"); + unsetenv("WLB_INTERFACE_STATE"); + } ++iter; } } diff --git a/src/lboutput.cc b/src/lboutput.cc index 35118d3..3ad37e1 100644 --- a/src/lboutput.cc +++ b/src/lboutput.cc @@ -67,17 +67,22 @@ LBOutput::write(const LBData &lbdata) line += space + string("Last Status Change: ") + string(tbuf); - string target = iter->second._ping_target; - if (target.empty()) { - if (iter->second._nexthop == "dhcp") { - target = iter->second._dhcp_nexthop; + LBHealth::TestConstIter titer = iter->second._test_coll.begin(); + while (titer != iter->second._test_coll.end()) { + string target = titer->second->_target; + if (target.empty()) { + if (iter->second._nexthop == "dhcp") { + target = iter->second._dhcp_nexthop; + } + else { + target = iter->second._nexthop; + } } - else { - target = iter->second._nexthop; - } - } + + line += space + string("Target: Ping ") + target + "\n"; - line += space + string("Target: Ping ") + target + "\n"; + ++titer; + } char btmp[256]; string time_buf; diff --git a/src/lbpathtest.cc b/src/lbpathtest.cc index d7ed6cd..7fd45a5 100644 --- a/src/lbpathtest.cc +++ b/src/lbpathtest.cc @@ -24,6 +24,7 @@ #include <stdlib.h> #include <iostream> #include <string> +#include <vector> #include <algorithm> #include "lbdata.hh" @@ -31,318 +32,69 @@ using namespace std; +/** + * + * + **/ LBPathTest::LBPathTest(bool debug) : - _debug(debug), - _send_sock(0), - _recv_sock(0), - _packet_id(0) + _debug(debug) { - struct protoent *ppe = getprotobyname("icmp"); - _send_sock = socket(PF_INET, SOCK_RAW, ppe->p_proto); - if (_send_sock < 0){ - if (_debug) { - cerr << "LBPathTest::LBPathTest(): no send sock: " << _send_sock << endl; - } - syslog(LOG_ERR, "wan_lb: failed to acquired socket"); - _send_sock = 0; - return; - } - - //set options for broadcasting. - int val = 1; - setsockopt(_send_sock, SOL_SOCKET, SO_BROADCAST, &val, 4); - setsockopt(_send_sock, SOL_SOCKET, SO_REUSEADDR, &val, 4); - - struct sockaddr_in addr; - memset( &addr, 0, sizeof( struct sockaddr_in )); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = 0; - - _recv_sock = socket(PF_INET, SOCK_RAW, ppe->p_proto); - if (_recv_sock < 0) { - if (_debug) { - cerr << "LBPathTest::LBPathTest(): no recv sock: " << _recv_sock << endl; - } - syslog(LOG_ERR, "wan_lb: failed to acquired socket"); - _recv_sock = 0; - return; - } - if (bind(_recv_sock, (struct sockaddr*)&addr, sizeof(addr))==-1) { - if (_debug) { - cerr << "failed on bind" << endl; - } - syslog(LOG_ERR, "wan_lb: failed to bind recv sock"); - } } +/** + * + * + **/ LBPathTest::~LBPathTest() { - if (_recv_sock) - close(_recv_sock); - - if (_send_sock) - close(_send_sock); } +/** + * + * + **/ void LBPathTest::start(LBData &lb_data) { + //iterate through the tests until success per interface or complete if (_debug) { - cout << "LBPathTest::start(): starting health test. client ct: " << lb_data._iface_health_coll.size() << endl; + cout << "LBPathTest::start(): init" << endl; } - map<int,PktData> results; - - struct timeval send_time; - gettimeofday(&send_time,NULL); + set<LBHealth*> coll; - int ct = 0; - //iterate over packets and send LBData::InterfaceHealthIter iter = lb_data._iface_health_coll.begin(); while (iter != lb_data._iface_health_coll.end()) { - string target = iter->second._ping_target; - if (target.empty()) { - if (iter->second._nexthop == "dhcp") { - target = iter->second._dhcp_nexthop; - } - else { - target = iter->second._nexthop; - } - } - - //don't have target yet... - if (target.empty()) { - return; - } - if (_debug) { - cout << "LBPathTest::start(): sending ping test for: " << iter->first << " for " << target << endl; - } - _packet_id = ++_packet_id % 32767; - send(iter->first, target, _packet_id); - results.insert(pair<int,PktData>(_packet_id,PktData(iter->first,-1))); + iter->second.start_new_test_cycle(); + coll.insert(&(iter->second)); - ++ct; ++iter; } - - //use gettimeofday to calculate time to millisecond - - //use sysinfo to make sure we don't get stuck in a loop with timechange - struct sysinfo si; - sysinfo(&si); - //for now hardcode to 5 second overall timeout - int timeout = si.uptime + 5; //seconds - int cur_time = si.uptime; - - //then iterate over recv socket and receive and record - while (ct > 0 && cur_time < timeout) { - int id = receive(); - if (_debug) { - cout << "LBPathTest::start(): " << id << endl; - } - //update current time for comparison - sysinfo(&si); - timeval recv_time; - gettimeofday(&recv_time,NULL); - cur_time = si.uptime; - map<int,PktData>::iterator r_iter = results.find(id); - if (r_iter != results.end()) { - - //calculate time in milliseconds - int secs = 0; - int msecs = recv_time.tv_usec - send_time.tv_usec; - if (msecs < 0) { - secs = recv_time.tv_sec - send_time.tv_sec - 1; - } - else { - secs = recv_time.tv_sec - send_time.tv_sec; - } - //time in milliseconds below - int rtt = abs(msecs) / 1000 + 1000 * secs; - - LBData::InterfaceHealthIter iter = lb_data._iface_health_coll.find(r_iter->second._iface); - if (iter != lb_data._iface_health_coll.end()) { - //check to see if this returned in the configured time, otherwise apply timeout - if (_debug) { - cout << "LBPathTest::start(): received pkt: " << iter->first << ", rtt: " << rtt << endl; - } - if (rtt < iter->second._ping_resp_time) { - iter->second.put(rtt); - } - else { - iter->second.put(-1); - } - } - results.erase(r_iter); - --ct; - } - } - //we're done waiting, mark the rest as non-responsive - map<int,PktData>::iterator r_iter = results.begin(); - while (r_iter != results.end()) { - LBData::InterfaceHealthIter iter = lb_data._iface_health_coll.find(r_iter->second._iface); - if (iter != lb_data._iface_health_coll.end()) { - iter->second.put(-1); - } - ++r_iter; - } - - if (_debug) { - cout << "LBPathTest::start(): finished heath test" << endl; - } -} - -void -LBPathTest::send(const string &iface, const string &target_addr, int packet_id) -{ - int err; - sockaddr_in taddr; - timeval send_time; - icmphdr *icmp_hdr; - int icmp_pktsize = 40; - char buffer[icmp_pktsize]; - - if (iface.empty() || target_addr.empty()) { - return; - } - - // bind a socket to a device name (might not work on all systems): - if (setsockopt(_send_sock, SOL_SOCKET, SO_BINDTODEVICE, iface.c_str(), iface.size()) != 0) { - syslog(LOG_ERR, "wan_lb: failure to bind to interface: %s", iface.c_str()); - return; //will allow the test to time out then - } - - //convert target_addr to ip addr - struct hostent *h = gethostbyname(target_addr.c_str()); - if (h == NULL) { + while (!coll.empty()) { if (_debug) { - cerr << "LBPathTest::send() Error in resolving hostname" << endl; + cout << "LBPathTest::start(): sending " << coll.size() << " tests" << endl; } - syslog(LOG_ERR, "wan_lb: error in resolving configured hostname: %s", target_addr.c_str()); - return; - } - - icmp_hdr = (struct icmphdr *)buffer; - icmp_hdr->type = ICMP_ECHO; - icmp_hdr->code = 0; - icmp_hdr->checksum = 0; - icmp_hdr->un.echo.id = htons(getpid()); - icmp_hdr->un.echo.sequence = 0; - int length = sizeof(buffer); - - //we'll put in time of packet sent for the heck of it, may - //want to use this in future tests, feel free to remove. - gettimeofday(&send_time, (struct timezone*)NULL); - char* datap = &buffer[8]; - memcpy(datap, (char*)&send_time.tv_sec, sizeof(send_time.tv_sec)); - datap = &buffer[12]; - memcpy(datap, (char*)&send_time.tv_usec, sizeof(send_time.tv_usec)); - datap = &buffer[16]; - memcpy(datap, (char*)&packet_id, sizeof(packet_id)); //packet id - datap = &buffer[18]; - int val(icmp_pktsize); - memcpy(datap, (char*)&val, 2); //packet id - - icmp_hdr->un.echo.sequence = 1; - icmp_hdr->checksum = 0; - icmp_hdr->checksum = in_checksum((unsigned short *)icmp_hdr,length>>1); - - struct in_addr ia; - memcpy(&ia, h->h_addr_list[0], sizeof(ia)); - unsigned long addr = ia.s_addr; - - taddr.sin_addr.s_addr = addr; - taddr.sin_family = AF_INET; - bzero(&(taddr.sin_zero), 8); - - //need to direct this packet out a specific interface!!!!!!!!!!!!! - err = sendto(_send_sock, buffer, icmp_pktsize, 0, (struct sockaddr*)&taddr, sizeof(taddr)); - if (_debug) { - cout << "lbpathtest: sendto: " << err << ", packet id: " << packet_id << endl; - } - if(err < 0) - { + //send all interface tests together + set<LBHealth*>::iterator i = coll.begin(); + while (i != coll.end()) { + (*i)->send_test(); + ++i; + } + if (_debug) { - if (errno == EBADF) - cout << "EBADF" << endl; - else if (errno == ENOTSOCK) - cout << "ENOTSOCK" << endl; - else if (errno == EFAULT) - cout << "EFAULT" << endl; - else if (errno == EMSGSIZE) - cout << "EMSGSIZE" << endl; - else if (errno == EWOULDBLOCK) - cout << "EWOULDBLOCK" << endl; - else if (errno == EAGAIN) - cout << "EAGAIN" << endl; - else if (errno == ENOBUFS) - cout << "ENOBUFS" << endl; - else if (errno == EINTR) - cout << "EINTR" << endl; - else if (errno == ENOMEM) - cout << "ENOMEM" << endl; - else if (errno == EACCES) - cout << "EACCES" << endl; - else if (errno == EINVAL) - cout << "EINVAL" << endl; - else if (errno == EPIPE) - cout << "EPIPE" << endl; - else - cout << "unknown error: " << errno << endl; + cout << "LBPathTest::start(): waiting on recv" << endl; } - syslog(LOG_ERR, "wan_lb: error on sending icmp packet: %d", errno); - } -} - -int -LBPathTest::receive() -{ - int icmp_pktsize = 40; - char resp_buf[icmp_pktsize]; - icmphdr *icmp_hdr; - timeval wait_time; - fd_set readfs; - int ret; - - FD_ZERO(&readfs); - FD_SET(_recv_sock, &readfs); - - wait_time.tv_usec = 0; - wait_time.tv_sec = 3; //3 second timeout - - while (select(_recv_sock+1, &readfs, NULL, NULL, &wait_time) != 0) - { - ret = recv(_recv_sock, &resp_buf, icmp_pktsize, 0); - if (ret != -1) - { - icmp_hdr = (struct icmphdr *)(resp_buf + sizeof(iphdr)); - if (icmp_hdr->type == ICMP_ECHOREPLY) - { - if (_debug) { - cout << "LBPathTest::receive(): " << endl; - } - //process packet data - char* data; - int id = 0; - data = (char*)(&resp_buf) + 36; - memcpy(&id, data, sizeof(unsigned short)); - return id; + //wait on responses + i = coll.begin(); + while (i != coll.end()) { + if ((*i)->recv_test()) { + coll.erase(i++); + } + else { + ++i; } } } - return -1; -} - -unsigned short -LBPathTest::in_checksum(const unsigned short *buffer, int length) const -{ - unsigned long sum; - for (sum=0; length>0; length--) - sum += *buffer++; - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); - return ~sum; } diff --git a/src/lbpathtest.hh b/src/lbpathtest.hh index 903f58d..366a42a 100644 --- a/src/lbpathtest.hh +++ b/src/lbpathtest.hh @@ -16,14 +16,6 @@ using namespace std; -class PktData -{ -public: - PktData(string iface, int rtt) : _iface(iface),_rtt(rtt) {} - string _iface; - int _rtt; -}; - class LBPathTest { public: @@ -34,20 +26,7 @@ public: start(LBData &lb_data); private: - void - send(const string &iface, const string &target_addr, int packet_id); - - int - receive(); - - unsigned short - in_checksum(const unsigned short *buf, int lenght) const; - -private: bool _debug; - int _send_sock; - int _recv_sock; - int _packet_id; }; diff --git a/src/lbtest_icmp.cc b/src/lbtest_icmp.cc new file mode 100644 index 0000000..c032b42 --- /dev/null +++ b/src/lbtest_icmp.cc @@ -0,0 +1,356 @@ +/* + * Module: lbpathtest.cc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ +#include <syslog.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/sysinfo.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/udp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <errno.h> +#include <string.h> +#include <memory> +#include <time.h> +#include <sys/timeb.h> +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <string> +#include <algorithm> + +#include "lbdata.hh" +#include "lbtest_icmp.hh" + +ICMPEngine LBTestICMP::_engine; + +using namespace std; + +/** + * + * + **/ +void +ICMPEngine::init() +{ + _results.erase(_results.begin(),_results.end()); + if (_initialized == true) { + return; + } + _initialized = true; + if (_debug) { + cout << "LBTestICMP::init(): initializing test system" << endl; + } + + struct protoent *ppe = getprotobyname("icmp"); + _send_sock = socket(PF_INET, SOCK_RAW, ppe->p_proto); + if (_send_sock < 0){ + if (_debug) { + cerr << "LBTestICMP::LBTestICMP(): no send sock: " << _send_sock << endl; + } + syslog(LOG_ERR, "wan_lb: failed to acquired socket"); + _send_sock = 0; + return; + } + + //set options for broadcasting. + int val = 1; + setsockopt(_send_sock, SOL_SOCKET, SO_BROADCAST, &val, 4); + setsockopt(_send_sock, SOL_SOCKET, SO_REUSEADDR, &val, 4); + + struct sockaddr_in addr; + memset( &addr, 0, sizeof( struct sockaddr_in )); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = 0; + + _recv_sock = socket(PF_INET, SOCK_RAW, ppe->p_proto); + if (_recv_sock < 0) { + if (_debug) { + cerr << "LBTestICMP::LBTestICMP(): no recv sock: " << _recv_sock << endl; + } + syslog(LOG_ERR, "wan_lb: failed to acquired socket"); + _recv_sock = 0; + return; + } + if (bind(_recv_sock, (struct sockaddr*)&addr, sizeof(addr))==-1) { + if (_debug) { + cerr << "failed on bind" << endl; + } + syslog(LOG_ERR, "wan_lb: failed to bind recv sock"); + } +} + +/** + * + * + **/ +int +ICMPEngine::process(LBHealth &health,LBTestICMP *data) +{ + //iterate over packets and send + string target = data->_target; + if (target.empty()) { + if (health._nexthop == "dhcp") { + target = health._dhcp_nexthop; + } + else { + target = health._nexthop; + } + } + + //don't have target yet... + if (target.empty()) { + return -1; + } + if (_debug) { + cout << "LBTestICMP::start(): sending ping test for: " << health._interface << " for " << target << endl; + } + _packet_id = ++_packet_id % 32767; + send(health._interface, target, _packet_id); + _results.insert(pair<int,PktData>(_packet_id,PktData(health._interface,-1))); +} + +/** + * + * + **/ +int +ICMPEngine::recv(LBHealth &health,LBTestICMP *data) +{ + struct timeval send_time; + gettimeofday(&send_time,NULL); + + //use gettimeofday to calculate time to millisecond + //then iterate over recv socket and receive and record + //use sysinfo to make sure we don't get stuck in a loop with timechange + struct sysinfo si; + sysinfo(&si); + //for now hardcode to 5 second overall timeout + unsigned long timeout = si.uptime + 5; //seconds + unsigned long cur_time = si.uptime; + while (cur_time < timeout) { + int id = receive(); + if (_debug) { + cout << "LBTestICMP::start(): " << id << endl; + } + //update current time for comparison + struct sysinfo si; + sysinfo(&si); + timeval recv_time; + gettimeofday(&recv_time,NULL); + cur_time = si.uptime; + map<int,PktData>::iterator r_iter = _results.find(id); + if (r_iter != _results.end()) { + + //calculate time in milliseconds + int secs = 0; + int msecs = recv_time.tv_usec - send_time.tv_usec; + if (msecs < 0) { + secs = recv_time.tv_sec - send_time.tv_sec - 1; + } + else { + secs = recv_time.tv_sec - send_time.tv_sec; + } + //time in milliseconds below + int rtt = abs(msecs) / 1000 + 1000 * secs; + if (rtt < data->_resp_time) { + return rtt; + } + else { + return -1; + } + _results.erase(r_iter); + } + } + + if (_debug) { + cout << "LBTestICMP::start(): finished heath test" << endl; + } + return -1; +} + + + + + + +/** + * + * + **/ +void +ICMPEngine::send(const string &iface, const string &target_addr, int packet_id) +{ + int err; + sockaddr_in taddr; + timeval send_time; + icmphdr *icmp_hdr; + int icmp_pktsize = 40; + char buffer[icmp_pktsize]; + + if (iface.empty() || target_addr.empty()) { + return; + } + + // bind a socket to a device name (might not work on all systems): + if (setsockopt(_send_sock, SOL_SOCKET, SO_BINDTODEVICE, iface.c_str(), iface.size()) != 0) { + syslog(LOG_ERR, "wan_lb: failure to bind to interface: %s", iface.c_str()); + return; //will allow the test to time out then + } + + //convert target_addr to ip addr + struct hostent *h = gethostbyname(target_addr.c_str()); + if (h == NULL) { + if (_debug) { + cerr << "LBTestICMP::send() Error in resolving hostname" << endl; + } + syslog(LOG_ERR, "wan_lb: error in resolving configured hostname: %s", target_addr.c_str()); + return; + } + + icmp_hdr = (struct icmphdr *)buffer; + icmp_hdr->type = ICMP_ECHO; + icmp_hdr->code = 0; + icmp_hdr->checksum = 0; + icmp_hdr->un.echo.id = htons(getpid()); + icmp_hdr->un.echo.sequence = 0; + int length = sizeof(buffer); + + //we'll put in time of packet sent for the heck of it, may + //want to use this in future tests, feel free to remove. + gettimeofday(&send_time, (struct timezone*)NULL); + char* datap = &buffer[8]; + memcpy(datap, (char*)&send_time.tv_sec, sizeof(send_time.tv_sec)); + datap = &buffer[12]; + memcpy(datap, (char*)&send_time.tv_usec, sizeof(send_time.tv_usec)); + datap = &buffer[16]; + memcpy(datap, (char*)&packet_id, sizeof(packet_id)); //packet id + datap = &buffer[18]; + int val(icmp_pktsize); + memcpy(datap, (char*)&val, 2); //packet id + + icmp_hdr->un.echo.sequence = 1; + icmp_hdr->checksum = 0; + icmp_hdr->checksum = in_checksum((unsigned short *)icmp_hdr,length>>1); + + struct in_addr ia; + memcpy(&ia, h->h_addr_list[0], sizeof(ia)); + unsigned long addr = ia.s_addr; + + taddr.sin_addr.s_addr = addr; + taddr.sin_family = AF_INET; + bzero(&(taddr.sin_zero), 8); + + //need to direct this packet out a specific interface!!!!!!!!!!!!! + err = sendto(_send_sock, buffer, icmp_pktsize, 0, (struct sockaddr*)&taddr, sizeof(taddr)); + if (_debug) { + cout << "LBTestICMP::send(): sendto: " << err << ", packet id: " << packet_id << endl; + } + if(err < 0) + { + if (_debug) { + if (errno == EBADF) + cout << "EBADF" << endl; + else if (errno == ENOTSOCK) + cout << "ENOTSOCK" << endl; + else if (errno == EFAULT) + cout << "EFAULT" << endl; + else if (errno == EMSGSIZE) + cout << "EMSGSIZE" << endl; + else if (errno == EWOULDBLOCK) + cout << "EWOULDBLOCK" << endl; + else if (errno == EAGAIN) + cout << "EAGAIN" << endl; + else if (errno == ENOBUFS) + cout << "ENOBUFS" << endl; + else if (errno == EINTR) + cout << "EINTR" << endl; + else if (errno == ENOMEM) + cout << "ENOMEM" << endl; + else if (errno == EACCES) + cout << "EACCES" << endl; + else if (errno == EINVAL) + cout << "EINVAL" << endl; + else if (errno == EPIPE) + cout << "EPIPE" << endl; + else + cout << "unknown error: " << errno << endl; + } + syslog(LOG_ERR, "wan_lb: error on sending icmp packet: %d", errno); + } +} + +/** + * + * + **/ +int +ICMPEngine::receive() +{ + int icmp_pktsize = 40; + char resp_buf[icmp_pktsize]; + icmphdr *icmp_hdr; + timeval wait_time; + fd_set readfs; + int ret; + + FD_ZERO(&readfs); + FD_SET(_recv_sock, &readfs); + + wait_time.tv_usec = 0; + wait_time.tv_sec = 3; //3 second timeout + + if (_debug) { + cout << "LBTestICMP::receive(): start" << endl; + } + + while (select(_recv_sock+1, &readfs, NULL, NULL, &wait_time) != 0) + { + ret = ::recv(_recv_sock, &resp_buf, icmp_pktsize, 0); + if (ret != -1) + { + if (_debug) { + cout << "LBTestICMP::receive(): recv: " << ret << endl; + } + + icmp_hdr = (struct icmphdr *)(resp_buf + sizeof(iphdr)); + if (icmp_hdr->type == ICMP_ECHOREPLY) + { + if (_debug) { + cout << "LBTestICMP::receive(): " << endl; + } + //process packet data + char* data; + int id = 0; + data = (char*)(&resp_buf) + 36; + memcpy(&id, data, sizeof(unsigned short)); + return id; + } + } + } + return -1; +} + +/** + * + * + **/ +unsigned short +ICMPEngine::in_checksum(const unsigned short *buffer, int length) const +{ + unsigned long sum; + for (sum=0; length>0; length--) + sum += *buffer++; + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return ~sum; +} + diff --git a/src/lbtest_icmp.hh b/src/lbtest_icmp.hh new file mode 100644 index 0000000..4eb94e8 --- /dev/null +++ b/src/lbtest_icmp.hh @@ -0,0 +1,101 @@ +/* + * Module: lbtest_icmp.hh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ +#ifndef __LBTEST_ICMP_HH__ +#define __LBTEST_ICMP_HH__ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <iostream> +#include "lbdata.hh" + +using namespace std; + +class LBTestICMP; + +/** + * + * + **/ +class PktData +{ +public: + PktData(string iface, int rtt) : _iface(iface),_rtt(rtt) {} + string _iface; + int _rtt; +}; + + +/** + * + * + **/ +class ICMPEngine +{ +public: + ICMPEngine() : + _debug(true), + _initialized(false), + _send_sock(0), + _recv_sock(0), + _packet_id(0) + {} + + void + init(); + + int + process(LBHealth &health,LBTestICMP *data); + + int + recv(LBHealth &health,LBTestICMP *data); + +private: + void + send(const string &iface, const string &target_addr, int packet_id); + + int + receive(); + + unsigned short + in_checksum(const unsigned short *buf, int lenght) const; + +private: + bool _debug; + bool _initialized; + int _send_sock; + int _recv_sock; + int _packet_id; + map<int,PktData> _results; +}; + +/** + * + * + **/ +class LBTestICMP : public LBTest +{ +public: + LBTestICMP(bool debug) : LBTest(debug) {} + ~LBTestICMP() {} + + void + init() {_engine.init();} + + void + send(LBHealth &health) {_engine.process(health,this);} + + int + recv(LBHealth &health) {return _engine.recv(health,this);} + +private: + static ICMPEngine _engine; //singleton + bool _debug; +}; + +#endif //__LBTEST_ICMP_HH__ diff --git a/templates/load-balancing/wan/interface-health/node.tag/rule/node.def b/templates/load-balancing/wan/interface-health/node.tag/rule/node.def new file mode 100644 index 0000000..169f3b9 --- /dev/null +++ b/templates/load-balancing/wan/interface-health/node.tag/rule/node.def @@ -0,0 +1,5 @@ +tag: +type: u32 +help: Set rule number + + diff --git a/templates/load-balancing/wan/interface-health/node.tag/ping/node.def b/templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/icmp/node.def index e44ae5c..e44ae5c 100644 --- a/templates/load-balancing/wan/interface-health/node.tag/ping/node.def +++ b/templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/icmp/node.def diff --git a/templates/load-balancing/wan/interface-health/node.tag/resp-time/node.def b/templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/resp-time/node.def index 1636278..1636278 100644 --- a/templates/load-balancing/wan/interface-health/node.tag/resp-time/node.def +++ b/templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/resp-time/node.def diff --git a/templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/target/node.def b/templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/target/node.def new file mode 100644 index 0000000..bc4c9b7 --- /dev/null +++ b/templates/load-balancing/wan/interface-health/node.tag/rule/node.tag/target/node.def @@ -0,0 +1,2 @@ +type: txt +help: Set health target [ip|uri] |