summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--scripts/vyatta-wanloadbalance.pl52
-rw-r--r--src/lbdata.cc71
-rw-r--r--src/lbdata.hh85
-rw-r--r--src/lbdatafactory.cc111
-rw-r--r--src/lbdatafactory.hh19
-rw-r--r--src/lbdecision.cc22
-rw-r--r--src/lboutput.cc23
-rw-r--r--src/lbpathtest.cc322
-rw-r--r--src/lbpathtest.hh21
-rw-r--r--src/lbtest_icmp.cc356
-rw-r--r--src/lbtest_icmp.hh101
-rw-r--r--templates/load-balancing/wan/interface-health/node.tag/rule/node.def5
-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.def2
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]