diff options
author | slioch <slioch@eng-140.vyatta.com> | 2009-07-17 15:06:04 -0700 |
---|---|---|
committer | slioch <slioch@eng-140.vyatta.com> | 2009-07-17 15:06:04 -0700 |
commit | 25d9851b65fed9297caaa01b47ec6bae76d8c954 (patch) | |
tree | f408dedd6fddd8cc156002f56f0618b5b0efa261 | |
parent | c1e5c748aac314a2f0652393fbb9f8c079055106 (diff) | |
download | vyatta-wanloadbalance-25d9851b65fed9297caaa01b47ec6bae76d8c954.tar.gz vyatta-wanloadbalance-25d9851b65fed9297caaa01b47ec6bae76d8c954.zip |
reworked target code to support multiple targets and different target types.
user can now specify a sequence of targets to test (ordered by rule number). The first success satifies the success
criteria for the test and no further tests will be performed on the interface for this period.
Additional test types can be coded and added to the target framework.
configuration has changed as a result of the rework.
-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] |