diff options
author | Michael Larson <slioch@eng-140.vyatta.com> | 2007-12-14 13:40:31 -0800 |
---|---|---|
committer | Michael Larson <slioch@eng-140.vyatta.com> | 2007-12-14 13:40:31 -0800 |
commit | 59b21be36b2f4cd74aa0cea53492e3023b59770b (patch) | |
tree | c1f1893bcba148eff202148f548328155bf729d4 /src | |
download | vyatta-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.cpp | 1 | ||||
-rw-r--r-- | src/.gitignore | 9 | ||||
-rw-r--r-- | src/hosttool.cpp | 395 | ||||
-rw-r--r-- | src/hosttool.h | 86 | ||||
-rw-r--r-- | src/lbdata.cc | 212 | ||||
-rw-r--r-- | src/lbdata.hh | 146 | ||||
-rw-r--r-- | src/lbdatafactory.cc | 334 | ||||
-rw-r--r-- | src/lbdatafactory.hh | 94 | ||||
-rw-r--r-- | src/lbdecision.cc | 318 | ||||
-rw-r--r-- | src/lbdecision.hh | 70 | ||||
-rw-r--r-- | src/lboutput.cc | 58 | ||||
-rw-r--r-- | src/lboutput.hh | 45 | ||||
-rw-r--r-- | src/lbpathtest.cc | 336 | ||||
-rw-r--r-- | src/lbpathtest.hh | 73 | ||||
-rw-r--r-- | src/lbrule.hh | 5 | ||||
-rw-r--r-- | src/loadbalance.cc | 110 | ||||
-rw-r--r-- | src/loadbalance.conf | 50 | ||||
-rw-r--r-- | src/loadbalance.hh | 75 | ||||
-rw-r--r-- | src/main.cc | 138 | ||||
-rw-r--r-- | src/rl_str_proc.cc | 82 | ||||
-rw-r--r-- | src/rl_str_proc.hh | 24 |
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__ |