summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Larson <slioch@eng-140.vyatta.com>2007-12-14 13:40:31 -0800
committerMichael Larson <slioch@eng-140.vyatta.com>2007-12-14 13:40:31 -0800
commit59b21be36b2f4cd74aa0cea53492e3023b59770b (patch)
treec1f1893bcba148eff202148f548328155bf729d4 /src
downloadvyatta-wanloadbalance-59b21be36b2f4cd74aa0cea53492e3023b59770b.tar.gz
vyatta-wanloadbalance-59b21be36b2f4cd74aa0cea53492e3023b59770b.zip
initial check wan lbdebian/0.1
Signed-off-by: Michael Larson <slioch@eng-140.vyatta.com>
Diffstat (limited to 'src')
l---------src/.#hosttool.cpp1
-rw-r--r--src/.gitignore9
-rw-r--r--src/hosttool.cpp395
-rw-r--r--src/hosttool.h86
-rw-r--r--src/lbdata.cc212
-rw-r--r--src/lbdata.hh146
-rw-r--r--src/lbdatafactory.cc334
-rw-r--r--src/lbdatafactory.hh94
-rw-r--r--src/lbdecision.cc318
-rw-r--r--src/lbdecision.hh70
-rw-r--r--src/lboutput.cc58
-rw-r--r--src/lboutput.hh45
-rw-r--r--src/lbpathtest.cc336
-rw-r--r--src/lbpathtest.hh73
-rw-r--r--src/lbrule.hh5
-rw-r--r--src/loadbalance.cc110
-rw-r--r--src/loadbalance.conf50
-rw-r--r--src/loadbalance.hh75
-rw-r--r--src/main.cc138
-rw-r--r--src/rl_str_proc.cc82
-rw-r--r--src/rl_str_proc.hh24
21 files changed, 2661 insertions, 0 deletions
diff --git a/src/.#hosttool.cpp b/src/.#hosttool.cpp
new file mode 120000
index 0000000..ea4c1f7
--- /dev/null
+++ b/src/.#hosttool.cpp
@@ -0,0 +1 @@
+slioch@eng-140.vyatta.com.8654:1196098102 \ No newline at end of file
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..a2b4c94
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,9 @@
+cli_parse.tab.c
+cli_parse.tab.h
+cli_def.lex.c
+cli_def.tab.c
+cli_def.tab.h
+cli_val.lex.c
+delete
+my_*
+show
diff --git a/src/hosttool.cpp b/src/hosttool.cpp
new file mode 100644
index 0000000..4849d03
--- /dev/null
+++ b/src/hosttool.cpp
@@ -0,0 +1,395 @@
+/**
+
+ * Module: Hosts
+
+ * Description: Host IP and last response time for all responding hosts
+
+ * on this LAN segment. This tool uses a broadcast icmp test packet.
+
+ *
+
+ * Note that hosts can also be identified via ARP progressing through
+
+ * all possible addresses on lan segment.
+
+ *
+
+ * Author: Michael Larson
+
+ * email: mike(at)lrlart.com
+
+ * Date: August 2004
+
+ **/
+
+
+
+#include <ostream.h>
+
+#include <sys/time.h>
+
+#include <sys/types.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 <memory>
+
+#include <time.h>
+
+#include <sys/timeb.h>
+
+#include <pthread.h>
+
+#include <stdio.h>
+
+#include <stdlib.h>
+
+#include <iostream.h>
+
+#include <string>
+
+#include <algorithm>
+
+
+
+#include "Hosts.hpp"
+
+#include "HostsResult.hpp"
+
+#include "HostsTool.hpp"
+
+
+
+//constant initialization
+
+const int HostsToolKonstants::packet_data_len_ = 40;
+
+const int HostsToolKonstants::recv_timeout_ = 5;
+
+const int HostsToolKonstants::ip_offset_ = 12;
+
+
+
+/**
+
+ * HostsTool::HostsTool()
+
+ * Constructor. Builds socket for use in tests.
+
+ *
+
+ **/
+
+HostsTool::HostsTool(Task<Test> *complete_task, unsigned long local_ip, unsigned long bc_addr) :
+
+ ToolBase(complete_task),
+
+ local_ip_(local_ip),
+
+ send_sock_(0),
+
+ recv_sock_(0),
+
+ bc_addr_(bc_addr),
+
+ packet_id_(0),
+
+ test_in_progress_(false)
+
+{
+
+ sockaddr_in addr;
+
+
+
+ struct protoent *ppe = getprotobyname("icmp");
+
+ send_sock_ = socket(PF_INET, SOCK_RAW, ppe->p_proto);
+
+ if (send_sock_ < 0)
+
+ {
+
+ cerr << "HostsTool::HostsTool(): no send sock: " << send_sock_ << endl;
+
+ 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);
+
+
+
+ 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)
+
+ {
+
+ cerr << "HostsTool::HostsTool(): no recv sock: " << recv_sock_ << endl;
+
+ recv_sock_ = 0;
+
+ return;
+
+ }
+
+}
+
+
+
+/**
+
+ * HostsTool::~HostsTools()
+
+ * Destructor, cleans up sockets
+
+ *
+
+ **/
+
+HostsTool::~HostsTool()
+
+{
+
+ if (send_sock_ != 0)
+
+ close(send_sock_);
+
+
+
+ if (recv_sock_ != 0)
+
+ close(recv_sock_);
+
+}
+
+
+
+
+
+/**
+
+ * HostsTool::compute()
+
+ * initiates tests received in its message queue.
+
+ *
+
+ **/
+
+void
+
+HostsTool::compute()
+
+{
+
+ while (true)
+
+ {
+
+ auto_ptr<Test> test(get()); //don't bother keeping this Test object..
+
+ cout << "HostsTool::compute(): received test event..." << endl;
+
+ if (test.get() != NULL)
+
+ {
+
+ GUARD(&mutex_); //protect against concurrent access to test_in_progress_ flag
+
+ /*
+
+ Note that on heavily cycled tests some tests may be dropped. This can be fixed
+
+ by monitoring input queue on completion of sending and processing all incoming
+
+ messages into a set. This isn't expected to be a problem with the current impl.
+
+ */
+
+ if (test_in_progress_ == true)
+
+ continue;
+
+ if (test->get_target() == 0)
+
+ {
+
+ send(bc_addr_, packet_id_++);
+
+ }
+
+ else
+
+ {
+
+ send(test->get_target(), packet_id_++);
+
+ }
+
+ }
+
+ }
+
+}
+
+
+
+/**
+
+ * HostsTool::finish()
+
+ * processes completed tests and pushes results to manager
+
+ *
+
+ **/
+
+void
+
+HostsTool::finish()
+
+{
+
+ while (true)
+
+ {
+
+ HostsResult *host_result = receive();
+
+ // cout << "HostsTool::finish(): received result: " << host_result << endl;
+
+ if (host_result != NULL)
+
+ {
+
+ GUARD(&mutex_);
+
+ test_in_progress_ = false;
+
+ if (host_result->empty() == false)
+
+ {
+
+ Test *test = new Test(kHosts);
+
+ test->set_result(host_result);
+
+ // cout << "HostsTool::finish(), dispatching result" << endl;
+
+ results(test);
+
+ }
+
+ }
+
+ }
+
+}
+
+
+
+/**
+
+ * HostsTool::send()
+
+ * pushes the icmp packet out on the wire.
+
+ *
+
+ **/
+
+void
+
+HostsTool::send(unsigned long target_addr, unsigned short packet_id)
+
+{
+
+ int err;
+
+ ->type) << endl;
+
+}
+
+}
+
+ else
+
+ {
+
+ cerr << "HostsTool::receive(): error from recvfrom" << endl;
+
+ }
+
+}
+
+//then push result upwards
+
+return host_result;
+
+}
+
+
+
+/**
+
+ * HostsTool::in_checksum()
+
+ * computes checksum that accompanies sending icmp packet
+
+ *
+
+ **/
+
+unsigned short
+
+HostsTool::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/hosttool.h b/src/hosttool.h
new file mode 100644
index 0000000..06d03a0
--- /dev/null
+++ b/src/hosttool.h
@@ -0,0 +1,86 @@
+/**
+ * Module: HostsTool
+ * Description: Collects lists of hosts on the network responding to broadcast icmp.
+ * This list of hosts are then dispatched for processing of results
+ *
+ * Author: Michael Larson
+ * email: mike(at)lrlart.com
+ * Date: August 2004
+ **/
+#ifndef HOSTSTOOL_HPP_
+#define HOSTSTOOL_HPP_
+
+//forward decls
+class HostsResult;
+
+//header includes
+#include <vector>
+#include "Task.hpp"
+#include "Test.hpp"
+#include "ToolBase.hpp"
+
+/*********************************************
+ ** HostsToolKonstants **
+ *********************************************/
+class HostsToolKonstants
+{
+ public:
+ static const int packet_data_len_;
+ static const int recv_timeout_;
+ static const int ip_offset_;
+};
+
+/*********************************************
+ ** HostsTool **
+ *********************************************/
+class HostsTool : public ToolBase
+{
+ public:
+ /*
+ * Constructor and Destructor
+ */
+ HostsTool(Task<Test> *pCompleteTask, unsigned long local_ip, unsigned long bc_addr_);
+ ~HostsTool();
+
+ /*
+ * compute test result
+ */
+ void
+ compute();
+
+ /*
+ * finished tests
+ */
+ void
+ finish();
+
+ /*
+ * send a new test
+ */
+ void
+ send(unsigned long target_addr, unsigned short packet_id);
+
+ /*
+ * receive results
+ */
+ HostsResult*
+ receive();
+
+ private:
+ /*
+ * checksum for icmp packets
+ */
+ unsigned short
+ in_checksum(const unsigned short *addr, int len) const;
+
+ private:
+ unsigned long local_ip_;
+ int send_sock_;
+ int recv_sock_;
+ unsigned long bc_addr_;
+ int packet_id_;
+ bool test_in_progress_;
+ PERF_MUTEX mutex_;
+};
+
+#endif //HOSTSTOOL_HPP_
diff --git a/src/lbdata.cc b/src/lbdata.cc
new file mode 100644
index 0000000..14a04c0
--- /dev/null
+++ b/src/lbdata.cc
@@ -0,0 +1,212 @@
+/*
+ * Module: lbdata.cc
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#include <sys/time.h>
+#include <time.h>
+#include <iostream>
+
+#include "lbdata.hh"
+
+int LBHealthHistory::_buffer_size = 10;
+
+/**
+ *
+ *
+ **/
+void
+LBHealth::put(int rtt)
+{
+ int activity_ct = _hresults.push(rtt);
+
+ if (rtt == -1) {
+ if (activity_ct >= _failure_ct) {
+ if (_is_active == true) {
+ _state_changed = true;
+ }
+ _is_active = false;
+ }
+ }
+ else {
+ if (activity_ct >= _success_ct) {
+ if (_is_active == false) {
+ _state_changed = true;
+ }
+ _is_active = true;
+ }
+ }
+}
+
+/**
+ *
+ *
+ **/
+LBHealthHistory::LBHealthHistory(int buffer_size) :
+ _last_success(0),
+ _last_failure(0),
+ _index(0)
+{
+ _resp_data.resize(10);
+
+ for (int i = 0; i < _buffer_size; ++i) {
+ _resp_data[i] = 0;
+ }
+}
+
+
+
+/**
+ *
+ *
+ **/
+int
+LBHealthHistory::push(int rtt)
+{
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+
+ if (rtt == -1) {
+ _last_failure = tv.tv_sec;
+ }
+ else {
+ _last_success = tv.tv_sec;
+ }
+
+ _resp_data[_index % _buffer_size] = rtt;
+ ++_index;
+
+ //compute count of sequence of same responses
+ int ct = 0;
+ int start_index = (_index - 1) % _buffer_size;
+ for (int i = 0; i < _buffer_size; ++i) {
+ int index = start_index - i;
+ if (index < 0) {
+ //handle wrap around of index here
+ index = _buffer_size - index;
+ }
+
+ if (_resp_data[index] == -1 && rtt == -1) {
+ ++ct;
+ }
+ else if (_resp_data[index] != -1 && rtt != -1) {
+ ++ct;
+ }
+ else {
+ return ct;
+ }
+ }
+ return ct;
+}
+
+/**
+ *
+ *
+ **/
+bool
+LBData::is_active(const string &iface)
+{
+ InterfaceHealthIter iter = _iface_health_coll.find(iface);
+ if (iter != _iface_health_coll.end()) {
+ return iter->second._is_active;
+ }
+ return false;
+}
+
+/**
+ *
+ *
+ **/
+void
+LBData::dump()
+{
+
+ 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;
+ ++h_iter;
+ }
+
+ 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;
+ cout << " " << r_iter->second._proto << endl;
+ cout << " " << r_iter->second._s_addr << endl;
+ cout << " " << r_iter->second._s_net << endl;
+ cout << " " << r_iter->second._s_port_num << endl;
+ cout << " " << r_iter->second._s_port_name << endl;
+
+ cout << " " << r_iter->second._d_addr << endl;
+ cout << " " << r_iter->second._d_net << endl;
+ cout << " " << r_iter->second._d_port_num << endl;
+ cout << " " << r_iter->second._d_port_name << endl;
+
+ LBRule::InterfaceDistIter ri_iter = r_iter->second._iface_dist_coll.begin();
+ while (ri_iter != r_iter->second._iface_dist_coll.end()) {
+ cout << " interface: " << ri_iter->first << endl;
+ cout << " weight: " << ri_iter->second << endl;
+ ++ri_iter;
+ }
+ ++r_iter;
+ }
+ cout << "end dump" << endl;
+}
+
+/**
+ *
+ *
+ **/
+bool
+LBData::state_changed()
+{
+ LBData::InterfaceHealthIter h_iter = _iface_health_coll.begin();
+ while (h_iter != _iface_health_coll.end()) {
+ if (h_iter->second.state_changed()) {
+ return true;
+ }
+ ++h_iter;
+ }
+ return false;
+}
+
+/**
+ *
+ *
+ **/
+void
+LBData::reset_state_changed()
+{
+ LBData::InterfaceHealthIter h_iter = _iface_health_coll.begin();
+ while (h_iter != _iface_health_coll.end()) {
+ h_iter->second._state_changed = false;
+ ++h_iter;
+ }
+}
diff --git a/src/lbdata.hh b/src/lbdata.hh
new file mode 100644
index 0000000..6d5c5af
--- /dev/null
+++ b/src/lbdata.hh
@@ -0,0 +1,146 @@
+/*
+ * Module: lbdata.hh
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#ifndef __LBDATA_HH__
+#define __LBDATA_HH__
+
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+
+using namespace std;
+
+class LBRule {
+ public:
+ typedef map<string, int> InterfaceDistColl;
+ typedef map<string, int>::iterator InterfaceDistIter;
+
+ typedef enum {ALL,ICMP,UDP,TCP} Protocol;
+
+ LBRule() :
+ _proto("all")
+ {}
+
+ public:
+ string _proto;
+ string _s_addr;
+ string _s_net;
+ string _s_port_num;
+ string _s_port_name;
+
+ string _d_addr;
+ string _d_net;
+ string _d_port_num;
+ string _d_port_name;
+
+ InterfaceDistColl _iface_dist_coll;
+};
+
+
+class LBHealthHistory {
+public:
+ LBHealthHistory(int buffer_size);
+
+ //push in the ping response for this...
+ int push(int rtt);
+
+
+public:
+ //results of health testing
+ unsigned long _last_success;
+ unsigned long _last_failure;
+
+ static int _buffer_size;
+ vector<int> _resp_data;
+ int _index;
+};
+
+class LBHealth {
+ public:
+ LBHealth() :
+ _success_ct(0),
+ _failure_ct(0),
+ _ping_resp_time(0),
+ _hresults(10),
+ _is_active(true),
+ _state_changed(true),
+ _last_success(0),
+ _last_failure(0)
+ {}
+
+ void put(int rtt);
+
+ bool
+ state_changed() {return _state_changed;}
+
+ int _success_ct;
+ int _failure_ct;
+ string _ping_target;
+ int _ping_resp_time;
+ LBHealthHistory _hresults;
+ bool _is_active;
+ bool _state_changed;
+ unsigned long _last_success;
+ unsigned long _last_failure;
+};
+
+
+class LBData {
+ public:
+ typedef map<int,LBRule> LBRuleColl;
+ typedef map<int,LBRule>::iterator LBRuleIter;
+ typedef map<int,LBRule>::const_iterator LBRuleConstIter;
+ typedef map<string,LBHealth> InterfaceHealthColl;
+ typedef map<string,LBHealth>::iterator InterfaceHealthIter;
+ typedef map<string,LBHealth>::const_iterator InterfaceHealthConstIter;
+
+ LBData() {}
+
+ bool
+ error() {return false;}
+
+ bool
+ is_active(const string &iface);
+
+ bool
+ state_changed();
+
+ void
+ reset_state_changed();
+
+ void
+ dump();
+
+ public:
+ string _filename;
+
+ LBRuleColl _lb_rule_coll;
+ InterfaceHealthColl _iface_health_coll;
+};
+
+#endif //__LBDATA_HH__
diff --git a/src/lbdatafactory.cc b/src/lbdatafactory.cc
new file mode 100644
index 0000000..0220416
--- /dev/null
+++ b/src/lbdatafactory.cc
@@ -0,0 +1,334 @@
+/*
+ * Module: lbdatafactory.cc
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#include <syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string>
+#include <algorithm>
+#include <vector>
+#include <iostream>
+#include "rl_str_proc.hh"
+#include "lbdata.hh"
+#include "lbdatafactory.hh"
+
+using namespace std;
+
+
+LBDataFactory::LBDataFactory()
+{
+}
+
+LBDataFactory::~LBDataFactory()
+{
+
+}
+
+bool
+LBDataFactory::load(const string &conf_file)
+{
+ //open file
+ FILE *fp = fopen(conf_file.c_str(), "r");
+ if (fp == NULL) {
+ cerr << "Error opening configuration file: " << conf_file << endl;
+ syslog(LOG_ERR, "wan_lb: configuration file not found: %s", conf_file.c_str());
+ return false;
+ }
+
+ //read line by line and populate vect
+ char str[1025];
+ int depth(0);
+ vector<string> path(10);
+ while (fgets(str, 1024, fp) != 0) {
+ string line(str);
+
+ int pos = line.find("#");
+ line = line.substr(0,pos);
+
+ string key,value;
+
+ StrProc tokens(line, " ");
+ for (int i = 0; i < tokens.size(); ++i) {
+ string symbol = tokens.get(i);
+
+ if (symbol != "{" && symbol != "}") {
+ if (key.empty()) {
+ key = symbol;
+ }
+ else if (value.empty()) {
+ value = symbol;
+ }
+ path[depth] = key;
+ }
+ else if (symbol == "{") {
+ ++depth;
+ }
+ else if (symbol == "}") {
+ --depth;
+ }
+ }
+ if (tokens.size() != 0) {
+ process(path,depth,key,value);
+ }
+ if (depth > 9 || depth < 0) {
+ cerr << "configuration error: malformed configuration file: brackets" << endl;
+ syslog(LOG_ERR, "wan_lb: malformed configuration file: brackets");
+ return false;
+ }
+ }
+
+ fclose(fp);
+ if (depth != 0) {
+ cerr << "configuration error: mismatched brackets in configuration file" << endl;
+ syslog(LOG_ERR, "wan_lb: configuration error due to mismatched brackets");
+ return false;
+ }
+
+#ifdef DEBUG
+ _lb_data.dump();
+#endif
+ return true;
+}
+
+
+void
+LBDataFactory::process(const vector<string> &path, int depth, const string &key, const string &value)
+{
+ string l_key, l_value;
+ std::transform(key.begin(), key.end(), std::back_inserter(l_key),
+ static_cast < int(*)(int) > (std::tolower));
+ std::transform(value.begin(), value.end(), std::back_inserter(l_value),
+ static_cast < int(*)(int) > (std::tolower));
+ if (path[0] == "health") {
+ if (l_key == "interface") {
+ process_health(l_key,l_value);
+ }
+ else {
+ process_health_interface(l_key,l_value);
+ }
+ }
+ else if (path[0] == "rule") {
+ if (depth > 0 && path[1] == "source") {
+ process_rule_source(l_key,l_value);
+ }
+ else if (depth > 0 && path[1] == "destination") {
+ process_rule_destination(l_key,l_value);
+ }
+ else if (depth > 1 && path[1] == "interface") {
+ process_rule_interface(l_key,l_value);
+ }
+ else if (depth > 0 && path[1] == "protocol") {
+ process_rule_protocol(l_key,l_value);
+ }
+ else {
+ process_rule(l_key,l_value);
+ }
+ }
+}
+
+
+void
+LBDataFactory::process_health(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()));
+ }
+ _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") {
+ int num = strtoul(value.c_str(), NULL, 10);
+ if (num > 0) {
+ _health_iter->second._success_ct = num;
+ }
+ else {
+ cerr << "illegal success-ct specified: " << value << endl;
+ syslog(LOG_ERR, "wan_lb: illegal success-ct specified in configuration file: %s", value.c_str());
+ }
+ }
+ else if (key == "failure-ct") {
+ int num = strtoul(value.c_str(), NULL, 10);
+ if (num > 0) {
+ _health_iter->second._failure_ct = num;
+ }
+ else {
+ cerr << "illegal failure-ct specified: " << value << endl;
+ 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 {
+ 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 == "health") {
+ //nothing
+ }
+ else {
+ cout << "LBDataFactory::process_health(): " << "don't understand this symbol: " << key << endl;
+ //nothing
+ }
+
+}
+
+void
+LBDataFactory::process_rule(const string &key, const string &value)
+{
+ if (key.empty()) {
+ return;
+ }
+#ifdef DEBUG
+ cout << "LBDataFactor::process_rule(): " << key << ", " << value << endl;
+#endif
+ int num = strtoul(value.c_str(), NULL, 10);
+ if (num > 0) {
+ _lb_data._lb_rule_coll.insert(pair<int,LBRule>(num,LBRule()));
+ }
+ else {
+ cerr << "Rule number: illegal value" << endl;
+ syslog(LOG_ERR, "wan_lb: illegal rule number: %s", value.c_str());
+ return;
+ }
+ _rule_iter = _lb_data._lb_rule_coll.find(num);
+}
+
+void
+LBDataFactory::process_rule_protocol(const string &key, const string &value)
+{
+ if (key == "protocol") {
+ if (strcasecmp(value.c_str(),"ALL") == 0) {
+ _rule_iter->second._proto = "all";
+ }
+ else if (strcasecmp(value.c_str(),"ICMP") == 0) {
+ _rule_iter->second._proto = "icmp";
+ }
+ else if (strcasecmp(value.c_str(), "UDP") == 0) {
+ _rule_iter->second._proto = "udp";
+ }
+ else if (strcasecmp(value.c_str(),"TCP") == 0) {
+ _rule_iter->second._proto = "tcp";
+ }
+ else {
+ cerr << "protocol not recognized: " << key << ", " << value << endl;
+ syslog(LOG_ERR, "wan_lb: illegal protocol specified: %s", value.c_str());
+ }
+ }
+}
+
+void
+LBDataFactory::process_rule_source(const string &key, const string &value)
+{
+ if (key == "address") {
+ if (inet_addr(value.c_str()) == (unsigned)-1) {
+ cerr << "malformed ip address: " << key << ", " << value << endl;
+ syslog(LOG_ERR, "wan_lb, malformed ip address in configuration: %s,%s", key.c_str(),value.c_str());
+ return;
+ }
+ _rule_iter->second._s_addr = value;
+ }
+ else if (key == "network") {
+ _rule_iter->second._s_net = value;
+ }
+ else if (key == "port-name") {
+ _rule_iter->second._s_port_num = value;
+ }
+ else if (key == "port-number") {
+ _rule_iter->second._s_port_name = value;
+ }
+}
+
+void
+LBDataFactory::process_rule_destination(const string &key, const string &value)
+{
+ if (key == "address") {
+ if (inet_addr(value.c_str()) == (unsigned)-1) {
+ cerr << "malformed ip address: " << key << ", " << value << endl;
+ syslog(LOG_ERR, "wan_lb, malformed ip address in configuration: %s,%s", key.c_str(),value.c_str());
+ return;
+ }
+ _rule_iter->second._d_addr = value;
+ }
+ else if (key == "network") {
+ _rule_iter->second._d_net = value;
+ }
+ else if (key == "port-name") {
+ _rule_iter->second._d_port_num = value;
+ }
+ else if (key == "port-number") {
+ _rule_iter->second._d_port_name = value;
+ }
+}
+
+void
+LBDataFactory::process_rule_interface(const string &key, const string &value)
+{
+#ifdef DEBUG
+ cout << "LBDataFactory::process_rule_interface(): " << key << ", " << value << endl;
+#endif
+ if (key == "interface") {
+ _rule_iter->second._iface_dist_coll.insert(pair<string,int>(value,0));
+ _rule_iface_iter = _rule_iter->second._iface_dist_coll.find(value);
+ }
+ else if (key == "weight") {
+ int num = strtoul(value.c_str(), NULL, 10);
+ if (num > 0) {
+ _rule_iface_iter->second = num;
+ }
+ else {
+ cerr << "illegal interface weight specified: " << value << endl;
+ syslog(LOG_ERR, "wan_lb: illegal interface weight specified in configuration file: %s", value.c_str());
+ }
+ }
+ else {
+ cerr << "LBDataFactory::process_rule(): " << "don't understand this symbol: " << key << endl;
+ }
+}
+
+
+
+LBData
+LBDataFactory::get()
+{
+ return _lb_data;
+}
+
diff --git a/src/lbdatafactory.hh b/src/lbdatafactory.hh
new file mode 100644
index 0000000..29fe609
--- /dev/null
+++ b/src/lbdatafactory.hh
@@ -0,0 +1,94 @@
+/*
+ * Module: lbdatafactory.hh
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#ifndef __LBCONFLOADER_HH__
+#define __LBCONFLOADER_HH__
+
+#include <string>
+#include <vector>
+
+#include "lbdata.hh"
+
+using namespace std;
+
+class LBDataFactory {
+public:
+ typedef vector<string> ConfColl;
+ typedef vector<string>::iterator ConfIter;
+
+public:
+ LBDataFactory();
+ ~LBDataFactory();
+
+ bool
+ load(const string &conf_file);
+
+ LBData
+ get();
+
+private:
+ //parsing goes on here
+ void
+ tokenize(const string& str,
+ vector<string>& tokens,
+ const string& delimiters = " ");
+
+ void
+ process(const vector<string> &path, int depth, const string &key, const string &value);
+
+ void
+ process_health(const string &key, const string &value);
+
+ void
+ process_health_interface(const string &key, const string &value);
+
+ void
+ process_rule(const string &key, const string &value);
+
+ void
+ process_rule_protocol(const string &key, const string &value);
+
+ void
+ process_rule_source(const string &key, const string &value);
+
+ void
+ process_rule_destination(const string &key, const string &value);
+
+ void
+ process_rule_interface(const string &key, const string &value);
+
+private:
+ LBHealth _lb_health;
+ LBRule _lb_rule;
+ LBData _lb_data;
+
+ LBData::LBRuleIter _rule_iter;
+ LBData::InterfaceHealthIter _health_iter;
+ LBRule::InterfaceDistIter _rule_iface_iter;
+};
+
+#endif //__LBCONFLOADER_HH__
diff --git a/src/lbdecision.cc b/src/lbdecision.cc
new file mode 100644
index 0000000..79e86cb
--- /dev/null
+++ b/src/lbdecision.cc
@@ -0,0 +1,318 @@
+/*
+ * Module: lbdecision.cc
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#include <syslog.h>
+#include <iostream>
+#include "lbdata.hh"
+#include "lbdecision.hh"
+
+using namespace std;
+
+/*
+iptables -t mangle -N ISP1
+iptables -t mangle -A ISP1 -j CONNMARK --set-mark 1
+iptables -t mangle -A ISP1 -j MARK --set-mark 1
+iptables -t mangle -A ISP1 -j ACCEPT
+
+iptables -t mangle -N ISP2
+iptables -t mangle -A ISP2 -j CONNMARK --set-mark 2
+iptables -t mangle -A ISP2 -j MARK --set-mark 2
+iptables -t mangle -A ISP2 -j ACCEPT
+
+
+#THIS APPEARS TO ROUGHLY WORK BELOW, AND CAN BE SET UP WITH SPECIFIC FILTERS.
+iptables -t mangle -A PREROUTING -i eth0 -m statistic --mode nth --every 2 --packet 0 -j ISP1
+iptables -t mangle -A PREROUTING -i eth0 -j ISP2
+
+#iptables -t mangle -A PREROUTING -i eth0 -m state --state NEW -m statistic --mode random --probability .01 -j MARK --set-mark 1
+#iptables -t mangle -A PREROUTING -i eth0 -j MARK --set-mark 2
+
+iptables -t raw -N NAT_CONNTRACK
+iptables -t raw -A NAT_CONNTRACK -j ACCEPT
+iptables -t raw -I PREROUTING 1 -j NAT_CONNTRACK
+iptables -t raw -I OUTPUT 1 -j NAT_CONNTRACK
+ip ro add table 10 default via 192.168.1.2 dev eth1
+ip ru add fwmark 1 table 10
+ip ro fl ca
+ip ro add table 20 default via 192.168.2.2 dev eth2
+ip ru add fwmark 2 table 20
+ip ro fl ca
+
+*/
+
+
+/**
+ *
+ *
+ **/
+LBDecision::LBDecision()
+{
+
+}
+
+/**
+ *
+ *
+ **/
+LBDecision::~LBDecision()
+{
+ shutdown();
+}
+
+/**
+ *
+ *
+ **/
+void
+LBDecision::init(LBData &lbdata)
+{
+ //here is where we set up iptables and policy routing for the interfaces
+ /*
+ iptables -t mangle -N ISP1
+ iptables -t mangle -A ISP1 -j CONNMARK --set-mark 1
+ iptables -t mangle -A ISP1 -j MARK --set-mark 1
+ iptables -t mangle -A ISP1 -j ACCEPT
+ */
+
+ char buf[20];
+ int ct = 1;
+
+ /*
+ do we need:
+iptables -t raw -N NAT_CONNTRACK
+iptables -t raw -A NAT_CONNTRACK -j ACCEPT
+iptables -t raw -I PREROUTING 1 -j NAT_CONNTRACK
+iptables -t raw -I OUTPUT 1 -j NAT_CONNTRACK
+
+if so then this stuff goes here!
+ */
+
+
+ //note: doesn't appear to clean up rule table, may need to individually erase each rule
+ // execute(string("ip rule flush"));
+
+ LBData::InterfaceHealthIter iter = lbdata._iface_health_coll.begin();
+ while (iter != lbdata._iface_health_coll.end()) {
+ string iface = iter->first;
+ sprintf(buf,"%d",ct);
+ execute(string("iptables -t mangle -N ISP_") + buf);
+ execute(string("iptables -t mangle -F ISP_") + buf);
+ execute(string("iptables -t mangle -A ISP_") + buf + " -j CONNMARK --set-mark " + buf);
+ execute(string("iptables -t mangle -A ISP_") + buf + " -j MARK --set-mark " + buf);
+
+ //NOTE, WILL NEED A WAY TO CLEAN UP THIS RULE ON RESTART...
+ execute(string("iptables -t mangle -A ISP_") + buf + " -j ACCEPT");
+
+ execute(string("ip route replace table ") + buf + " default dev " + iface);
+ execute(string("ip rule add fwmark ") + buf + " table " + buf);
+
+ _iface_mark_coll.insert(pair<string,int>(iface,ct));
+ ++ct;
+ ++iter;
+ }
+ execute("ip route flush cache");
+}
+
+
+/**
+ * only responsible for
+
+iptables -t mangle -A PREROUTING -i eth0 -m state --state NEW -m statistic --mode random --probability .01 -j MARK --set-mark 1
+iptables -t mangle -A PREROUTING -i eth0 -j MARK --set-mark 2
+
+ *
+ *
+ *
+ **/
+void
+LBDecision::run(LBData &lb_data)
+{
+#ifdef DEBUG
+ cout << "LBDecision::run(), starting decision" << endl;
+#endif
+
+ //first determine if we need to alter the rule set
+ if (!lb_data.state_changed()) {
+ return;
+ }
+
+#ifdef DEBUG
+ cout << "LBDecision::run(), state changed, applying new rule set" << endl;
+#endif
+
+ //then if we do, flush all
+ execute("iptables -t mangle -F PREROUTING");
+
+ //and compute the new set and apply
+ LBData::LBRuleIter iter = lb_data._lb_rule_coll.begin();
+ while (iter != lb_data._lb_rule_coll.end()) {
+ map<int,float> weights = get_new_weights(lb_data,iter->second);
+ map<int,float>::iterator w_iter = weights.begin();
+ map<int,float>::iterator w_end = weights.end();
+ if (w_iter == w_end) {
+ ++iter;
+ continue;
+ }
+ else {
+ --w_end;
+ }
+
+ //NEED TO HANDLE APPLICATION SPECIFIC DETAILS
+ string app_cmd = get_application_cmd(iter->second);
+
+ char fbuf[20],dbuf[20];
+ while (w_iter != w_end) {
+ sprintf(fbuf,"%f",w_iter->second);
+ sprintf(dbuf,"%d",w_iter->first);
+ execute(string("iptables -t mangle -A PREROUTING ") + app_cmd + " -m state --state NEW -m statistic --mode random --probability " + fbuf + " -j ISP_" + dbuf);
+ ++w_iter;
+ }
+ //last one is special case, the catch all rule
+ ++w_iter;
+ sprintf(dbuf,"%d",w_iter->first);
+ execute(string("iptables -t mangle -A PREROUTING ") + app_cmd + " -j ISP_" + dbuf);
+ ++iter;
+ }
+}
+
+/**
+ *
+ *
+ **/
+void
+LBDecision::shutdown()
+{
+ char buf[20];
+
+ //then if we do, flush all
+ execute("iptables -t mangle -F PREROUTING");
+
+ //remove the policy entries
+ InterfaceMarkIter iter = _iface_mark_coll.begin();
+ while (iter != _iface_mark_coll.end()) {
+ sprintf(buf,"%d",iter->second);
+ execute(string("ip rule del fwmark ") + buf);
+ ++iter;
+ }
+}
+
+/**
+ *
+ *
+ **/
+void
+LBDecision::execute(string cmd)
+{
+#ifdef DEBUG
+ cout << "LBDecision::execute(): applying command to system: " << cmd << endl;
+#endif
+
+ FILE *f = popen(cmd.c_str(), "w");
+ if (f) {
+ if (pclose(f) != 0) {
+ cerr << "LBDecision::execute(): error executing command: " << cmd << endl;
+ syslog(LOG_ERR, "Error executing system command: %s", cmd.c_str());
+ }
+ }
+ else {
+ cerr << "LBDecision::execute(): error executing command: " << cmd << endl;
+ syslog(LOG_ERR, "Error executing system command: %s", cmd.c_str());
+ }
+}
+
+map<int,float>
+LBDecision::get_new_weights(LBData &data, LBRule &rule)
+{
+ map<int,float> weights;
+ int group = 0;
+ int ct = 1;
+ LBRule::InterfaceDistIter iter = rule._iface_dist_coll.begin();
+ while (iter != rule._iface_dist_coll.end()) {
+#ifdef DEBUG
+ cout << "LBDecision::get_new_weights(): " << iter->first << " is active: " << (data.is_active(iter->first) ? "true" : "false") << endl;
+#endif
+ if (data.is_active(iter->first)) {
+ weights.insert(pair<int,float>(ct,iter->second));
+ group += iter->second;
+ }
+ ++ct;
+ ++iter;
+ }
+
+ //now weight the overall distribution
+ map<int,float>::iterator w_iter = weights.begin();
+ while (w_iter != weights.end()) {
+ float w = float(w_iter->second) / float(group);
+ group -= w_iter->second; //I THINK THIS NEEDS TO BE ADJUSTED TO THE OVERALL REMAINING VALUES. which is this...
+ w_iter->second = w;
+ ++w_iter;
+ }
+
+ return weights;
+}
+
+/**
+ *
+ *
+ **/
+string
+LBDecision::get_application_cmd(LBRule &rule)
+{
+ string filter;
+
+ if (rule._proto.empty() == false) {
+ filter += "--proto " + rule._proto + " ";
+ }
+
+ if (rule._proto == "icmp") {
+ filter += "--icmp-type any ";
+ }
+ else if (rule._proto == "udp" || rule._proto == "tcp") {
+ if (rule._s_addr.empty() == false) {
+ filter += "--source " + rule._s_addr + " ";
+ }
+ else if (rule._s_net.empty() == false) {
+ filter += "--source " + rule._s_net + " ";
+ }
+
+ if (rule._d_addr.empty() == false) {
+ filter += "--destination " + rule._d_addr + " ";
+ }
+ else if (rule._d_net.empty() == false) {
+ filter += "--destination " + rule._d_net + " ";
+ }
+
+ if (rule._s_port_name.empty() == false) {
+ filter += "--source-port " + rule._s_port_name + " ";
+ }
+ else if (rule._s_port_num.empty() == false) {
+ filter += "--source-port " + rule._s_port_num + " ";
+ }
+ }
+
+ return filter;
+}
diff --git a/src/lbdecision.hh b/src/lbdecision.hh
new file mode 100644
index 0000000..299544a
--- /dev/null
+++ b/src/lbdecision.hh
@@ -0,0 +1,70 @@
+/*
+ * Module: lbdecision.hh
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#ifndef __LBDECISION_HH__
+#define __LBDECISION_HH__
+
+#include <map>
+#include <string>
+#include "lbdata.hh"
+
+using namespace std;
+
+class LBDecision
+{
+public:
+ typedef map<string,int> InterfaceMarkColl;
+ typedef map<string,int>::iterator InterfaceMarkIter;
+
+public:
+ LBDecision();
+ ~LBDecision();
+
+ void
+ init(LBData &lbdata);
+
+ void
+ run(LBData &lbdata);
+
+ void
+ shutdown();
+
+private:
+ void
+ execute(string cmd);
+
+ map<int,float>
+ get_new_weights(LBData &data, LBRule &rule);
+
+ string
+ get_application_cmd(LBRule &rule);
+
+private:
+ InterfaceMarkColl _iface_mark_coll;
+};
+
+#endif //__LBDECISION_HH__
diff --git a/src/lboutput.cc b/src/lboutput.cc
new file mode 100644
index 0000000..e0ed18b
--- /dev/null
+++ b/src/lboutput.cc
@@ -0,0 +1,58 @@
+/*
+ * Module: lboutput.cc
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#include <sys/time.h>
+#include <time.h>
+
+#include <iostream>
+
+#include "lbdata.hh"
+#include "lboutput.hh"
+
+void
+LBOutput::write(const LBData &lbdata)
+{
+ timeval tv;
+ gettimeofday(&tv,NULL);
+
+ //dump out the health data
+ LBData::InterfaceHealthConstIter iter = lbdata._iface_health_coll.begin();
+ while (iter != lbdata._iface_health_coll.end()) {
+ cout << iter->first << " "; //interface
+ cout << string(iter->second._is_active ? "true" : "false") << " "; //status
+ cout << tv.tv_sec - iter->second._last_success << " "; //last success
+ cout << tv.tv_sec - iter->second._last_failure << " "; //last failure
+ ++iter;
+ }
+
+ //dump out the application data
+ LBData::LBRuleConstIter r_iter = lbdata._lb_rule_coll.begin();
+ while (r_iter != lbdata._lb_rule_coll.end()) {
+ cout << "squirt out results here." << endl;
+ ++r_iter;
+ }
+}
diff --git a/src/lboutput.hh b/src/lboutput.hh
new file mode 100644
index 0000000..b68c9f2
--- /dev/null
+++ b/src/lboutput.hh
@@ -0,0 +1,45 @@
+/*
+ * Module: lboutput.hh
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#ifndef __LBOUTPUT_HH__
+#define __LBOUTPUT_HH__
+
+#include "lbdata.hh"
+
+using namespace std;
+
+class LBOutput
+{
+public:
+ LBOutput() {}
+ ~LBOutput() {}
+
+ void
+ write(const LBData &lbdata);
+
+};
+#endif //__LBOUTPUT_HH__
diff --git a/src/lbpathtest.cc b/src/lbpathtest.cc
new file mode 100644
index 0000000..7e052c7
--- /dev/null
+++ b/src/lbpathtest.cc
@@ -0,0 +1,336 @@
+/*
+ * Module: lbpathtest.cc
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#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 <memory>
+#include <time.h>
+#include <sys/timeb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+#include <string>
+#include <algorithm>
+
+#include "lbdata.hh"
+#include "lbpathtest.hh"
+
+using namespace std;
+
+LBPathTest::LBPathTest() :
+ _send_sock(0),
+ _recv_sock(0),
+ _packet_id(0)
+{
+ struct protoent *ppe = getprotobyname("icmp");
+ _send_sock = socket(PF_INET, SOCK_RAW, ppe->p_proto);
+ if (_send_sock < 0){
+ 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) {
+ 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) {
+ 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)
+{
+#ifdef DEBUG
+ cout << "LBPathTest::start(): starting health test. client ct: " << lb_data._iface_health_coll.size() << endl;
+#endif
+
+ map<int,PktData> results;
+
+ struct timeval send_time;
+ gettimeofday(&send_time,NULL);
+
+ 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()) {
+#ifdef DEBUG
+ cout << "LBPathTest::start(): sending ping test for: " << iter->first << " for " << iter->second._ping_target << endl;
+#endif
+ _packet_id = ++_packet_id % 32767;
+ send(iter->first, iter->second._ping_target, _packet_id);
+ results.insert(pair<int,PktData>(_packet_id,PktData(iter->first,-1)));
+
+ ++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();
+#ifdef DEBUG
+ cout << "LBPathTest::start(): " << id << endl;
+#endif
+ //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
+#ifdef DEBUG
+ cout << "LBPathTest::start(): received pkt: " << iter->first << ", rtt: " << rtt << endl;
+#endif
+ 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;
+ }
+
+#ifdef DEBUG
+ cout << "LBPathTest::start(): finished heath test" << endl;
+#endif
+}
+
+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];
+
+ // bind a socket to a device name (might not work on all systems):
+ setsockopt(_send_sock, SOL_SOCKET, SO_BINDTODEVICE, iface.c_str(), iface.size());
+
+ //convert target_addr to ip addr
+ struct hostent *h = gethostbyname(target_addr.c_str());
+ if (h == NULL) {
+ cerr << "LBPathTest::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));
+#ifdef DEBUG
+ cout << "lbpathtest: sendto: " << err << ", packet id: " << packet_id << endl;
+#endif
+ if(err < 0)
+ {
+ 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
+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)
+ {
+#ifdef DEBUG
+ cout << "LBPathTest::receive(): " << endl;
+#endif
+ //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
+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
new file mode 100644
index 0000000..176bf18
--- /dev/null
+++ b/src/lbpathtest.hh
@@ -0,0 +1,73 @@
+/*
+ * Module: lbpathtest.hh
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#ifndef __LBPATHTEST_HH__
+#define __LBPATHTEST_HH__
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <iostream>
+#include "lbdata.hh"
+
+using namespace std;
+
+class PktData
+{
+public:
+ PktData(string iface, int rtt) : _iface(iface),_rtt(rtt) {}
+ string _iface;
+ int _rtt;
+};
+
+class LBPathTest
+{
+public:
+ LBPathTest();
+ ~LBPathTest();
+
+ void
+ 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:
+ int _send_sock;
+ int _recv_sock;
+ int _packet_id;
+};
+
+
+#endif //__LBPATHTEST_HH__
diff --git a/src/lbrule.hh b/src/lbrule.hh
new file mode 100644
index 0000000..b4d2683
--- /dev/null
+++ b/src/lbrule.hh
@@ -0,0 +1,5 @@
+#ifndef __LBRULE_HH__
+#define __LBRULE_HH__
+
+
+#endif //__LBRULE_HH__
diff --git a/src/loadbalance.cc b/src/loadbalance.cc
new file mode 100644
index 0000000..a48ace0
--- /dev/null
+++ b/src/loadbalance.cc
@@ -0,0 +1,110 @@
+/*
+ * Module: loadbalance.cc
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#include <string>
+#include "lbpathtest.hh"
+#include "loadbalance.hh"
+
+using namespace std;
+
+/**
+ *
+ **/
+LoadBalance::LoadBalance() :
+ _cycle_interval(5000)
+{
+}
+
+/**
+ *
+ **/
+LoadBalance::~LoadBalance()
+{
+ _decision.shutdown();
+}
+
+/**
+ *
+ **/
+bool
+LoadBalance::set_conf(const string &conf)
+{
+ _lbdata_factory.load(conf);
+ _lbdata = _lbdata_factory.get();
+ if (_lbdata.error()) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ *
+ **/
+void
+LoadBalance::init()
+{
+ _decision.init(_lbdata);
+}
+
+/**
+ *
+ **/
+bool
+LoadBalance::start_cycle()
+{
+ _lbdata.reset_state_changed();
+ return true;
+}
+
+/**
+ *
+ **/
+void
+LoadBalance::health_test()
+{
+ _ph.start(_lbdata);
+}
+
+/**
+ *
+ **/
+void
+LoadBalance::apply_rules()
+{
+ _decision.run(_lbdata);
+}
+
+/**
+ *
+ **/
+void
+LoadBalance::output()
+{
+ _output.write(_lbdata);
+}
+
diff --git a/src/loadbalance.conf b/src/loadbalance.conf
new file mode 100644
index 0000000..85e6c5d
--- /dev/null
+++ b/src/loadbalance.conf
@@ -0,0 +1,50 @@
+#
+# Sample vyatta load balance configuration file.
+#
+
+health {
+ interface eth1 {
+ target 10.0.0.1
+ success-ct 2
+ failure-ct 1
+ ping-resp 100
+ }
+
+ interface eth2 {
+ target 10.0.0.1
+ success-ct 1
+ failure-ct 1
+ ping-resp 1000
+ }
+}
+
+rule 1 {
+ protocol udp
+ interface eth1 {
+ weight 1
+ }
+ interface eth2 {
+ weight 2
+ }
+}
+
+rule 2 {
+ protocol tcp
+ source {
+ address 1.1.1.1
+ port-number 2222
+ }
+ interface eth1 {
+ weight 1
+ }
+ interface eth2 {
+ weight 3
+ }
+}
+
+#default rule w/o protocol specified
+rule 10 {
+ interface eth2 {
+ weight 1
+ }
+} \ No newline at end of file
diff --git a/src/loadbalance.hh b/src/loadbalance.hh
new file mode 100644
index 0000000..624a3e4
--- /dev/null
+++ b/src/loadbalance.hh
@@ -0,0 +1,75 @@
+/*
+ * Module: loadbalance.hh
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#ifndef __LOADBALANCE_HH__
+#define __LOADBALANCE_HH__
+
+#include <time.h>
+
+#include <string>
+#include <map>
+
+#include "lbdatafactory.hh"
+#include "lbpathtest.hh"
+#include "lbdecision.hh"
+#include "lboutput.hh"
+#include "lbdata.hh"
+
+
+using namespace std;
+
+class LoadBalance
+{
+ public:
+ LoadBalance();
+ ~LoadBalance();
+
+ bool set_conf(const string &filename);
+
+ void init();
+
+ bool start_cycle();
+
+ void health_test();
+
+ void apply_rules();
+
+ void output();
+
+ //temporary stand-in for now...
+ void sleep() {::sleep(5);}
+
+ private:
+ LBDataFactory _lbdata_factory;
+ LBData _lbdata;
+ LBPathTest _ph;
+ LBDecision _decision;
+ LBOutput _output;
+ int _cycle_interval;
+};
+
+#endif //__LOADBALANCE_HH__
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000..e8d93e0
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,138 @@
+/*
+ * Module: main.cc
+ *
+ * **** License ****
+ * Version: VPL 1.0
+ *
+ * The contents of this file are subject to the Vyatta Public License
+ * Version 1.0 ("License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.vyatta.com/vpl
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This code was originally developed by Vyatta, Inc.
+ * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Michael Larson
+ * Date: 2007
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#include <signal.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <iostream>
+#include "loadbalance.hh"
+
+LoadBalance *g_lb = NULL;
+
+
+static void usage()
+{
+ cout << "lb -fh" << endl;
+ cout << "-f [file] configuration file" << endl;
+ cout << "-t load configuration file only and exit" << endl;
+ cout << "-h help" << endl;
+
+}
+
+static void sig_end(int signo)
+{
+ if (g_lb)
+ delete g_lb;
+ cerr << "End signal: " << signo << endl;
+ syslog(LOG_ERR, "wan_lb, exit signal caught, exiting..");
+ exit(0);
+}
+
+static void sig_user(int signo)
+{
+ if (g_lb)
+ delete g_lb;
+ cerr << "User signal: " << signo << endl;
+ syslog(LOG_ERR, "wan_lb, user exit signal caught, exiting..");
+ exit(0);
+}
+
+
+int main(int argc, char* argv[])
+{
+ int ch;
+ bool config_debug_mode = false;
+ string c_file;
+
+ //grab inputs
+ while ((ch = getopt(argc, argv, "f:ht")) != -1) {
+ switch (ch) {
+ case 'f':
+ c_file = optarg;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ case 't':
+ config_debug_mode = true;
+ break;
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ //parse conf file
+ if (c_file.empty()) {
+ cout << "Configuration file is empty" << endl;
+ exit(0);
+ }
+
+ g_lb = new LoadBalance();
+
+ bool success = g_lb->set_conf(c_file);
+ if (success == false) {
+ syslog(LOG_ERR, "wan_lb: error loading configuration file: %s", c_file.c_str());
+ exit(0);
+ }
+
+ if (config_debug_mode) {
+ exit(0);
+ }
+
+#ifdef DEBUG
+ cout << "STARTING CYCLE" << endl;
+#endif
+
+ g_lb->init();
+
+
+ //signal handler here
+ // sighup...
+ signal(SIGINT, sig_end);
+ signal(SIGTERM, sig_end);
+ signal(SIGUSR1, sig_user);
+
+ //drop into event loop
+ do {
+#ifdef DEBUG
+ cout << "main.cc: starting new cycle" << endl;
+#endif
+
+ //health test
+ g_lb->health_test();
+
+ //apply rules
+ g_lb->apply_rules();
+
+ //update show output
+ g_lb->output();
+
+ g_lb->sleep();
+ } while (g_lb->start_cycle());
+ exit(0);
+}
diff --git a/src/rl_str_proc.cc b/src/rl_str_proc.cc
new file mode 100644
index 0000000..3a5d151
--- /dev/null
+++ b/src/rl_str_proc.cc
@@ -0,0 +1,82 @@
+#include "rl_str_proc.hh"
+
+using namespace std;
+
+/**
+ *
+ **/
+StrProc::StrProc(const string &in_str, const string &token)
+{
+ string tmp = in_str;
+
+ //convert tabs to spaces
+ uint32_t pos = 0;
+ string tabtospace = " ";
+ string::iterator iter = tmp.begin();
+ while ((pos = tmp.find("\t", pos)) != string::npos) {
+ tmp.replace(pos, 1, tabtospace);
+ pos += tabtospace.length();
+ }
+
+ //remove the cr
+ pos = tmp.find("\n");
+ if (pos != string::npos) {
+ tmp.replace(pos, 1, "");
+ }
+
+ //now handle the case of the multiple length token
+ //note that we are using the '~' as a token internally
+ uint32_t start = 0, end;
+ while ((start = tmp.find(token, start)) != string::npos) {
+ tmp.replace(start, token.length(), "~");
+ }
+
+
+ while ((start = tmp.find_first_not_of("~")) != string::npos) {
+ tmp = tmp.substr(start, tmp.length() - start);
+ end = tmp.find_first_of("~");
+ _str_coll.push_back(tmp.substr(0, end));
+ tmp = tmp.substr(end+1, tmp.length() - end-1);
+ if (end == string::npos) {
+ break;
+ }
+ }
+}
+
+/**
+ *
+ **/
+string
+StrProc::get(int i)
+{
+ if (uint32_t(i) >= _str_coll.size()) {
+ return string("");
+ }
+ return _str_coll[i];
+}
+
+/**
+ *
+ **/
+string
+StrProc::get(int start, int end)
+{
+ if (uint32_t(start) >= _str_coll.size()) {
+ return string("");
+ }
+
+ string tmp;
+ for (int i = start; (i < end) && (uint32_t(i) < _str_coll.size()); ++i) {
+ tmp += _str_coll[i] + " ";
+ }
+ return tmp.substr(0,tmp.length()-1);
+}
+
+/**
+ *
+ **/
+vector<string>
+StrProc::get()
+{
+ return _str_coll;
+}
diff --git a/src/rl_str_proc.hh b/src/rl_str_proc.hh
new file mode 100644
index 0000000..c59df0a
--- /dev/null
+++ b/src/rl_str_proc.hh
@@ -0,0 +1,24 @@
+#ifndef __RL_STR_PROC_HH__
+#define __RL_STR_PROC_HH__
+
+#include <vector>
+#include <string>
+
+class StrProc
+{
+public:
+ StrProc(const std::string &in, const std::string &token);
+
+ std::string get(int i);
+
+ std::string get(int start, int end);
+
+ std::vector<std::string> get();
+
+ int size() {return _str_coll.size();}
+
+private:
+ std::vector<std::string> _str_coll;
+};
+
+#endif //__RL_STR_PROC_HH__