diff options
| author | slioch <slioch@eng-140.vyatta.com> | 2009-07-21 11:52:01 -0700 |
|---|---|---|
| committer | slioch <slioch@eng-140.vyatta.com> | 2009-07-21 11:52:01 -0700 |
| commit | c8175dbdd23b4aeaca6df22dd35fc699e35134e1 (patch) | |
| tree | ab2d92a0526d8cc82f929de3e8dd28b3b8fe7986 /src | |
| parent | f947ef5883cd890a2cdc3e250f6790f3a630492f (diff) | |
| download | vyatta-wanloadbalance-c8175dbdd23b4aeaca6df22dd35fc699e35134e1.tar.gz vyatta-wanloadbalance-c8175dbdd23b4aeaca6df22dd35fc699e35134e1.zip | |
additional files needed
Diffstat (limited to 'src')
| -rw-r--r-- | src/lbtest_ttl.cc | 431 | ||||
| -rw-r--r-- | src/lbtest_ttl.hh | 129 |
2 files changed, 560 insertions, 0 deletions
diff --git a/src/lbtest_ttl.cc b/src/lbtest_ttl.cc new file mode 100644 index 0000000..843bc57 --- /dev/null +++ b/src/lbtest_ttl.cc @@ -0,0 +1,431 @@ +/* + * Module: lbpathtest.cc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ +#include <syslog.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/sysinfo.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/udp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <errno.h> +#include <string.h> +#include <memory> +#include <time.h> +#include <sys/timeb.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <string> +#include <algorithm> + +#include "lbdata.hh" +#include "lbtest_ttl.hh" + +TTLEngine LBTestTTL::_engine; + +using namespace std; + +/** + * + * + **/ +void +TTLEngine::init() +{ + if (_debug) { + cout << "LBTestICMP::init(): initializing test system" << endl; + } + _results.erase(_results.begin(),_results.end()); +} + +/** + * + * + **/ +int +TTLEngine::process(LBHealth &health,LBTestTTL *data) +{ + if (_debug) { + cout << "TTLEngine::process()" << endl; + } + //iterate over packets and send + string target = data->_target; + if (target.empty()) { + if (health._nexthop == "dhcp") { + target = health._dhcp_nexthop; + } + else { + target = health._nexthop; + } + } + + //don't have target yet... + if (target.empty()) { + return -1; + } + if (_debug) { + cout << "TTLEngine::process(): sending ttl test for: " << health._interface << " for " << target << endl; + } + _packet_id = get_new_packet_id(); + + send(data->_send_raw_sock, health._interface,target,_packet_id,health._address,data->get_ttl(),data->get_port()); + _results.insert(pair<int,PktData>(_packet_id,PktData(health._interface,-1))); +} + +/** + * + * + **/ +int +TTLEngine::recv(LBHealth &health,LBTestTTL *data) +{ + if (_results.empty() == true) { + return -1; + } + + if (_debug) { + cout << "TTLEngine::recv()" << endl; + } + struct timeval send_time; + gettimeofday(&send_time,NULL); + + //use gettimeofday to calculate time to millisecond + //then iterate over recv socket and receive and record + //use sysinfo to make sure we don't get stuck in a loop with timechange + struct sysinfo si; + sysinfo(&si); + //for now hardcode to 5 second overall timeout + unsigned long timeout = si.uptime + 5; //seconds + unsigned long cur_time = si.uptime; + while (cur_time < timeout) { + int id = receive(data->_recv_icmp_sock); + if (_debug) { + cout << "TTLEngine::recv(): " << id << endl; + } + //update current time for comparison + struct sysinfo si; + sysinfo(&si); + timeval recv_time; + gettimeofday(&recv_time,NULL); + cur_time = si.uptime; + map<int,PktData>::iterator r_iter = _results.find(id); + if (r_iter != _results.end()) { + + //calculate time in milliseconds + int secs = 0; + int msecs = recv_time.tv_usec - send_time.tv_usec; + if (msecs < 0) { + secs = recv_time.tv_sec - send_time.tv_sec - 1; + } + else { + secs = recv_time.tv_sec - send_time.tv_sec; + } + //time in milliseconds below + int rtt = abs(msecs) / 1000 + 1000 * secs; + if (rtt < data->_resp_time) { + data->_state = LBTest::K_SUCCESS; + return rtt; + } + else { + data->_state = LBTest::K_FAILURE; + return -1; + } + _results.erase(r_iter); + } + } + + if (_debug) { + cout << "TTLEngine::recv(): finished heath test" << endl; + } + data->_state = LBTest::K_FAILURE; + return -1; +} + + + + + + +/** + * + * + **/ +void +TTLEngine::send(int send_sock, const string &iface, const string &target_addr, unsigned short packet_id, string saddress, int ttl, unsigned short port) +{ + if (_debug) { + cout << "TTLEngine::send(): packet_id: " << packet_id << ", ttl: " << ttl << ", port: " << port << endl; + } + + if (saddress.empty()) { + if (_debug) { + cout << "TTLEngine::send() source address is empty" << endl; + } + return; + } + + if (_debug) { + cout << "TTLEngine::send() source address is: " << saddress << endl; + } + + int err; + sockaddr_in taddr; + timeval send_time; + char buffer[42]; + struct iphdr *ip; + struct udphdr *udp; + + if (iface.empty() || target_addr.empty()) { + return; + } + + //convert target_addr to ip addr + struct hostent *h = gethostbyname(target_addr.c_str()); + if (h == NULL) { + if (_debug) { + cerr << "TTLEngine::send() Error in resolving hostname" << endl; + } + syslog(LOG_ERR, "wan_lb: error in resolving configured hostname: %s", target_addr.c_str()); + return; + } + + // bind a socket to a device name (might not work on all systems): + if (setsockopt(send_sock, SOL_SOCKET, SO_BINDTODEVICE, iface.c_str(), iface.size()) != 0) { + if (_debug) { + cerr << "TTLEngine::send() failure to bind to interface: " << iface << endl; + } + syslog(LOG_ERR, "wan_lb: failure to bind to interface: %s", iface.c_str()); + return; //will allow the test to time out then + } + + ip = (iphdr*)buffer; + + ip->ihl = 5; + ip->version = 4; + ip->tos = 0; + ip->tot_len = sizeof(iphdr) + sizeof(udphdr);// + 8; + ip->id = htons(getpid()); + ip->frag_off = 0; + ip->ttl = ttl; + ip->protocol = IPPROTO_UDP; + ip->check = 0; + in_addr_t addr = inet_addr(saddress.c_str()); //m_ulSourceAddr; + ip->saddr = addr; + + struct in_addr ia; + memcpy(&ia, h->h_addr_list[0], sizeof(ia)); + ip->daddr = ia.s_addr; + ip->check = in_checksum((unsigned short*)ip, sizeof(iphdr)); + + udp = (udphdr*)(buffer + sizeof(iphdr)); + //now udp stuff + udp->source = htons(packet_id);//was usid--not sure about this... + udp->dest = htons(packet_id); + udp->len = sizeof(udphdr); + udp->check = 0; + + unsigned long data_length = 8; //bytes + + //copy in headers + udp->check = udp_checksum(IPPROTO_UDP,(char*)udp, sizeof(struct udphdr)+data_length, ip->saddr, ip->daddr); + + //set destination port and address + bzero(&(taddr), sizeof(taddr)); + taddr.sin_addr.s_addr = ia.s_addr; + taddr.sin_family = AF_INET; + taddr.sin_port = packet_id; + err = sendto(send_sock, buffer, sizeof(iphdr)+sizeof(udphdr)+data_length, 0, (struct sockaddr*)&taddr, sizeof(taddr)); + if (_debug) { + cout << "TTLEngine::send(): send " << err << " bytes" << endl; + } + if (_debug) { + if(err < 0) { + if (errno == EBADF) + cout << "EBADF" << endl; + else 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 == ENOBUFS) + cout << "ENOBUFS" << endl; + else if (errno == EINTR) + cout << "EINTR" << endl; + else if (errno == ENOMEM) + cout << "ENOMEM" << endl; + else if (errno == EINVAL) + cout << "EINVAL" << endl; + else if (errno == EPIPE) + cout << "EPIPE" << endl; + else + cout << "unknown error: " << errno << endl; + } + } +} + +/** + * + * + **/ +int +TTLEngine::receive(int recv_sock) +{ + timeval wait_time; + int ret; + int icmp_pktsize = 40; + char resp_buf[icmp_pktsize]; + struct sockaddr_in dest_addr; + unsigned int addr_len; + fd_set readfs; + unsigned short packet_id = 0; + + FD_ZERO(&readfs); + FD_SET(recv_sock, &readfs); + + wait_time.tv_usec = 0; + wait_time.tv_sec = 3; //3 second timeout + + if (_debug) { + cout << "TTLEngine::receive(): start" << endl; + } + + //NEW-OLD STUFF HERE + + while (select(recv_sock+1, &readfs, NULL, NULL, &wait_time) != 0) { + int bytes_recv = recvfrom(recv_sock, &resp_buf, 56, 0, (struct sockaddr*)&dest_addr, &addr_len); + if (_debug) { + cout << "TTLEngine::receive() received: " << bytes_recv << endl; + } + if (bytes_recv != -1) { + //process packet data + char* datap; + datap = (char*)(&resp_buf) + 49; + memcpy(&packet_id, datap, sizeof(unsigned short)); + return packet_id; + } + else { + cerr << "TTLEngine::receive(): error from recvfrom" << endl; + } + } + return -1; +} + + + + +/** + * LatencyTool::inChecksum() + * Performs ip checksum computation + * + **/ +unsigned short +TTLEngine::in_checksum(unsigned short *pAddr, int iLen) +{ + int iSum = 0; + unsigned short usAnswer = 0; + unsigned short *pW = pAddr; + int iRemain = iLen; + + while(iRemain > 1) + { + iSum += *pW++; + iRemain -= sizeof(unsigned short); + } + if(iRemain==1) + { + *(u_char *)(&usAnswer)=*(u_char*)pW; + iSum += usAnswer; + } + iSum = (iSum>>16) + (iSum&0xffff); + iSum += (iSum>>16); + usAnswer = ~iSum; + return(usAnswer); +} + + +/** + * LatencyTool::UDPChecksum() + * UDP check computation + * + **/ +unsigned short +TTLEngine::udp_checksum(unsigned char ucProto, char *pPacket, int iLength, unsigned long ulSourceAddress, unsigned long ulDestAddress) +{ + struct PsuedoHdr + { + struct in_addr sourceAddr; + struct in_addr destAddr; + unsigned char ucPlaceHolder; + unsigned char ucProtocol; + unsigned short usLength; + } PsuedoHdr; + + struct PsuedoHdr psuedoHdr; + char *pTempPacket; + unsigned short usAnswer; + psuedoHdr.ucProtocol = ucProto; + psuedoHdr.usLength = htons(iLength); + psuedoHdr.ucPlaceHolder = 0; + psuedoHdr.sourceAddr.s_addr = ulSourceAddress; + psuedoHdr.destAddr.s_addr = ulDestAddress; + + if((pTempPacket = (char*)malloc(sizeof(PsuedoHdr) + iLength)) == NULL) + { + cerr << "ActionDropConn::UDPChecksum(), error in malloc" << endl; + //throw an exception + return 0; + } + + memcpy(pTempPacket, &psuedoHdr, sizeof(PsuedoHdr)); + memcpy((pTempPacket + sizeof(PsuedoHdr)), pPacket, iLength); + + usAnswer = (unsigned short)in_checksum((unsigned short*)pTempPacket, + (iLength + sizeof(PsuedoHdr))); + + free(pTempPacket); + + return usAnswer; +} + +/** + * + * + **/ +unsigned short +TTLEngine::get_new_packet_id() +{ + if (_packet_id >= _max_port_id) + _packet_id = _min_port_id; + return ++_packet_id; +} + +/** + * + * + **/ +string +LBTestTTL::dump() +{ + char buf[20]; + sprintf(buf,"%u",_resp_time); + string foo = string("target: ") + _target + ", resp_time: " + buf; + sprintf(buf,"%u",_ttl); + foo += string(", ttl: ") + buf; + sprintf(buf,"%u",_port); + foo += string(", port: ") + buf; + return foo; +} diff --git a/src/lbtest_ttl.hh b/src/lbtest_ttl.hh new file mode 100644 index 0000000..8f2035f --- /dev/null +++ b/src/lbtest_ttl.hh @@ -0,0 +1,129 @@ +/* + * Module: lbtest_ttl.hh + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ +#ifndef __LBTEST_TTL_HH__ +#define __LBTEST_TTL_HH__ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <iostream> +#include "lbdata.hh" + +using namespace std; + +class LBTestTTL; + +/** + * + * + **/ +class TTLEngine +{ + class PktData + { + public: + PktData(string iface, int rtt) : _iface(iface),_rtt(rtt) {} + string _iface; + int _rtt; + }; + +public: + TTLEngine() : + _debug(true), + _initialized(false), + _packet_id(32767), + _min_port_id(32767), + _max_port_id(55000) + {} + + void + init(); + + int + process(LBHealth &health,LBTestTTL *data); + + int + recv(LBHealth &health,LBTestTTL *data); + +private: + void + send(int sock, const string &iface, const string &target_addr, unsigned short packet_id, string address, int ttl, unsigned short port); + + int + receive(int sock); + + unsigned short + udp_checksum(unsigned char ucProto, char *pPacket, int iLength, unsigned long ulSourceAddress, unsigned long ulDestAddress); + + unsigned short + in_checksum(unsigned short *pAddr, int iLen); + + unsigned short + get_new_packet_id(); + +private: + bool _debug; + bool _initialized; + unsigned short _packet_id; + unsigned short _min_port_id; + unsigned long _max_port_id; + + map<int,PktData> _results; +}; + +/** + * + * + **/ +class LBTestTTL : public LBTest +{ +public: + LBTestTTL(bool debug) : + LBTest(debug), + _ttl(0), + _port(0) + {} + LBTestTTL(bool debug, unsigned short ttl, unsigned short port) : + LBTest(debug), + _ttl(ttl), + _port(port) + {} + ~LBTestTTL() {} + + void + init() {_engine.init();this->LBTest::init();} + + void + send(LBHealth &health) {_engine.process(health,this);} + + int + recv(LBHealth &health) {return _engine.recv(health,this);} + + unsigned short + get_ttl() const {return _ttl;} + + unsigned short + get_port() const {return _port;} + + void + set_ttl(unsigned short ttl) {_ttl = ttl;} + + void + set_port(unsigned short port) {_port = port;} + + string + dump(); + +private: + static TTLEngine _engine; //singleton + bool _debug; + unsigned short _ttl; + unsigned short _port; +}; + +#endif //__LBTEST_TTL_HH__ |
