/* RevArpDaemon.cpp Reverse ARP Daemon for Alasin Copyright (C) 2003 Mike Saywell Southampton Open Wireless Network, http://www.sown.org.uk ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ This class does the grunt work. It is responsible for discovering the neighbour and assigning an IP address on a single interface. It's called RevArpDaemon as traditionally it used ReverseARP to do this. This is no longer the case and class should really be renamed, maybe next release eh? See Packet.h for details on the protocol. ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "RevArpDaemon.h" RevArpDaemon::RevArpDaemon(AddressManager* am, int interface) { this->man = am; this->sock = socket(PF_PACKET, SOCK_DGRAM, 0); this->run = false; if(this->sock == -1) { cerr << "Failed to create socket " << strerror(errno) << endl; exit(1); } this->if_id = interface; // Make sure the interface is up this->ifup(); // Remove any existing IPs this->deAssign(); } void RevArpDaemon::assign(struct in_addr* network, struct in_addr* netmask, bool master) { int ip = 1; // IP is from peers address range so we take 2nd address if(!master) ip = 2; struct ifreq* ifr = new struct ifreq; ifr->ifr_ifindex = this->if_id; if(ioctl(this->sock, SIOCGIFNAME, ifr, sizeof(struct ifreq))==-1) { perror("RevArpDaemon::assign(1): "); } struct sockaddr_in* sin = (struct sockaddr_in*) &ifr->ifr_addr; sin->sin_family = AF_INET; // IP sin->sin_addr.s_addr = network->s_addr+htonl(ip); if(ioctl(this->sock, SIOCSIFADDR, ifr, sizeof(struct ifreq))==-1) { perror("Setting IP: "); } // Netmask sin->sin_addr.s_addr = netmask->s_addr; if(ioctl(this->sock, SIOCSIFNETMASK, ifr, sizeof(struct ifreq))==-1) { perror("Setting Netmask: "); } // Broadcast sin->sin_addr.s_addr = network->s_addr+htonl(3); if(ioctl(this->sock, SIOCSIFBRDADDR, ifr, sizeof(struct ifreq))==-1) { perror("Setting Broadcast: "); } } void RevArpDaemon::deAssign() { struct ifreq* ifr = new struct ifreq; ifr->ifr_ifindex = this->if_id; if(ioctl(this->sock, SIOCGIFNAME, ifr, sizeof(struct ifreq))==-1) { perror("RevArpDaemon::assign(1): "); } struct sockaddr_in* sin = (struct sockaddr_in*) &ifr->ifr_addr; sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(INADDR_ANY); if(ioctl(this->sock, SIOCSIFADDR, ifr, sizeof(struct ifreq))==-1) { perror("*Removing IP"); } delete ifr; } void RevArpDaemon::ifup() { struct ifreq* ifr = new struct ifreq; ifr->ifr_ifindex = this->if_id; if(ioctl(this->sock, SIOCGIFNAME, ifr, sizeof(struct ifreq))==-1) { perror("RevArpDaemon (1): "); } ifr->ifr_flags=IFF_UP; if(ioctl(this->sock, SIOCSIFFLAGS, ifr, sizeof(struct ifreq))==-1) { perror("RevArpDaemon (2): "); } } void RevArpDaemon::start() { if(this->run) return; // don't spawn multiple threads! this->run = true; pthread_create(&thread, NULL, &radLauncher, this); } void* RevArpDaemon::doMain() { Packet* p = new Packet(ALADIN_HELLO); char device[IF_NAMESIZE]; if_indextoname(this->if_id, device); while (this->run) { p->reset(); p->setType(ALADIN_HELLO); if (Packet::send(p, this->if_id) < 0) { this->run = false; continue; } if (DEBUG) cerr << "Sent HELLO on " << device << endl; // Listen for replies over next 10 seconds if (Packet::receive(p, this->if_id, 10) < 0) { continue; } // The other party has responded to our HELLO if(p->getType() == ALADIN_ACK) { if (DEBUG) cerr << "Rcvd ACK on " << device << endl; // Wait a second sleep(1); // Send a REQUEST p->reset(); p->setType(ALADIN_REQUEST); if (Packet::send(p, this->if_id) < 0) { this->run = false; continue; } if (DEBUG) cerr << "Sent REQUEST on " << device << endl; // Listen for replies over next 10 seconds if (Packet::receive(p, this->if_id, 10) < 0) { continue; } // Check for ALADIN_REPLY if(p->getType() == ALADIN_REPLY) { struct in_addr* network = new struct in_addr; struct in_addr* netmask = new struct in_addr; network->s_addr = p->getNetwork(); netmask->s_addr = p->getNetmask(); if (DEBUG) { cerr << "Rcvd REPLY on " << device << endl; cout << "Network: " << inet_ntoa(*network) << endl; cout << "Netmask: " << inet_ntoa(*netmask) << endl; } this->assign(network, netmask, false); delete network; delete netmask; // Wait a second sleep(1); // Send an ACK p->reset(); p->setType(ALADIN_ACK); if(Packet::send(p, this->if_id) < 0) { this->run = false; continue; } if (DEBUG) cerr << "Sent ACK on " << device << endl; int loop = 0; // Enter ACK loop while (loop < 3) { if (Packet::receive(p, this->if_id, 10) < 0) { loop++; if (DEBUG) cerr << "No reply to HELLO, if " << device << ", try " << loop << endl; } else { // Check for ALADIN_ACK if(p->getType() == ALADIN_ACK) { loop = 0; if (DEBUG) cerr << "Rcvd ACK on " << device << endl << "(sleeping)" << endl; // Sleeeeep sleep(5); } else { if (DEBUG) cerr << "Rcvd non-ACK on " << device << ", restarting." << endl; loop = 3; continue; } } // Send an ACK p->reset(); p->setType(ALADIN_ACK); if(Packet::send(p, this->if_id) < 0) { this->run = false; loop = 3; continue; } if (DEBUG) cerr << "Sent ACK on " << device << endl; } if (DEBUG) cerr << "Peer has vanished, removing IP from " << device << endl; this->deAssign(); } else { char tmp[64]; p->prnType(tmp); if (DEBUG) cerr << "Expected REPLY but got " << tmp << "!" << endl; } } // The other party has missed our HELLO, so we'll ACK else if(p->getType() == ALADIN_HELLO) { if (DEBUG) cerr << "Rcvd HELLO on " << device << endl; // Allocate the address now, if this fails // we can ignore the HELLO and request an // IP from the peers pool struct AddressAllocation* address = this->man->allocate(); this->man->deallocate(address); address = this->man->allocate(); if(!address) { cerr << "Failed to get an IP allocation (configure a bigger address range!)" << endl; continue; } if (DEBUG) { cerr << "Got network allocation of " << inet_ntoa(address->network); cerr << "/" << inet_ntoa(address->netmask) << endl; cerr << "For interface " << device << endl; } // Wait a second sleep(1); // Send an ACK p->reset(); p->setType(ALADIN_ACK); if(Packet::send(p, this->if_id) < 0) { this->run = false; continue; } if (DEBUG) cerr << "Sent ACK on " << device << endl; // Listen for replies over next 10 seconds if (Packet::receive(p, this->if_id, 10) < 0) { continue; } // Check for ALADIN_REQUEST if(p->getType() == ALADIN_REQUEST) { if (DEBUG) cerr << "Rcvd REQUEST on " << device << endl; // Wait a second sleep(1); // Send a REPLY p->reset(); p->setType(ALADIN_REPLY); p->setNetwork(&address->network); p->setNetmask(&address->netmask); if(Packet::send(p, this->if_id) < 0) { this->run = false; continue; } if (DEBUG) cerr << "Sent REPLY on " << device << endl; // Listen for replies over next 10 seconds if (Packet::receive(p, this->if_id, 10) < 0) { continue; } // Check for ALADIN_ACK if(p->getType() == ALADIN_ACK) { // Peer has accepted the address this->assign(&address->network, &address->netmask, true); sleep(1); // Send an ACK p->reset(); p->setType(ALADIN_ACK); if(Packet::send(p, this->if_id) < 0) { this->run = false; continue; } if (DEBUG) cerr << "Sent ACK on " << device << endl; int loop = 0; // Enter ACK loop while (loop < 3) { if (Packet::receive(p, this->if_id, 10) < 0) { loop++; if (DEBUG) cerr << "No reply to HELLO, if " << device << ", try " << loop << endl; } else { // Check for ALADIN_ACK if(p->getType() == ALADIN_ACK) { loop = 0; if (DEBUG) cerr << "Rcvd ACK on " << device << endl << "(sleeping)" << endl; // Sleeeeep sleep(5); } else { if (DEBUG) cerr << "Rcvd non-ACK on " << device << endl << "(sleeping)" << endl; loop++; } } // Send an ACK p->reset(); p->setType(ALADIN_ACK); if(Packet::send(p, this->if_id) < 0) { this->run = false; loop = 3; continue; } if (DEBUG) cerr << "Sent ACK on " << device << endl; } if (DEBUG) cerr << "Peer has vanished, removing IP from " << device << endl; this->deAssign(); } } // De-allocate the address this->man->deallocate(address); } sleep(2); } delete p; this->run = false; cerr << "RevArpDaemon is terminating on interface " << if_id << endl; } void RevArpDaemon::stop() { this->run = false; } bool RevArpDaemon::isRunning() { return this->run; } void* radLauncher(void *obj) { return static_cast(obj)->doMain(); }