summaryrefslogtreecommitdiff
path: root/src/lbpathtest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lbpathtest.cc')
-rw-r--r--src/lbpathtest.cc336
1 files changed, 336 insertions, 0 deletions
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;
+}
+