/*********************************************************************** * Copyright (c) 2005 Henning Peters * * * * 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. * * * * ------------------------------------------------------------------- * * project : NAT/FW NSLP * * author : Henning Peters * * Niklas Steinleitner * ***********************************************************************/ #include #include "NatFwServer.h" #include "NsisConfiguration.h" #include "DebugLogger.h" #include "GistException.h" NatFwServer::NatFwServer() { DebugLogger::print(DEBUG_INFO, "NSIS NAT/Firewall NSLP daemon starting...\n"); if (NsisConfiguration::natfw_externalAddress == -1) { throw new GistException("Please specify one address as the external address in your configuration file"); } this->ip1 = NsisConfiguration::general_localIPv4s[NsisConfiguration::natfw_externalAddress]; this->isEdgeRouter = NsisConfiguration::isEdgeRouter(4); this->isRouter = NsisConfiguration::isRouter(4); this->isNAT = NsisConfiguration::natfw_isNat; this->isFW = NsisConfiguration::natfw_isFW; this->cMode = true; // for simulation nodes to know their neighbors next = NULL; previous = NULL; if (NsisConfiguration::natfw_reservableIPv4count != 0) { list rlist; for (int j=NsisConfiguration::natfw_reservablePortsStart; j<=NsisConfiguration::natfw_reservablePortsEnd; j++) { for (int i = 0; i < NsisConfiguration::natfw_reservableIPv4count; i++) { NatFwResource r; r.addr = NsisConfiguration::natfw_reservableIPv4s[i]; r.port = j; rlist.push_back(r); } } this->reservableAddr = rlist; } } NatFwResource NatFwServer::makeReservation() { if (reservableAddr.empty()) { throw new GistException("no reservable addresses"); } NatFwResource r = reservableAddr.front(); NatFwDebugLogger::print(DEBUG_INFO, "reserved ip: %s, port: %d\n", IPaddr::printIP(r.addr), r.port); reservableAddr.pop_front(); reservedAddr.push_back(r); return r; } void NatFwServer::cancelReservation(NatFwResource r) { NatFwDebugLogger::print(DEBUG_INFO, "released ip: %s, port: %d\n", IPaddr::printIP(r.addr), r.port); reservedAddr.remove(r); reservableAddr.push_back(r); } void NatFwServer::run() { NatFwDebugLogger::print(DEBUG_INFO, "Router: %s\n", isRouter ? "yes" : "no"); NatFwDebugLogger::print(DEBUG_INFO, "Edge-Router: %s\n", isEdgeRouter ? "yes" : "no"); NatFwDebugLogger::print(DEBUG_INFO, "NAT: %s\n", isNAT ? "yes" : "no"); NatFwDebugLogger::print(DEBUG_INFO, "Firewall: %s\n", isFW ? "yes" : "no"); if (this->isNAT && reservableAddr.size() == 0) { NatFwDebugLogger::print(DEBUG_CRIT, "router is NAT without reservable resources!\n"); exit(1); } if (!this->isNAT && reservableAddr.size() > 0) { NatFwDebugLogger::print(DEBUG_WARN, "router is no NAT, but you specified reservable resources!\n"); } if (this->isEdgeRouter) { int i=1; for (list::iterator it = reservableAddr.begin(); it != reservableAddr.end(); it++) { NatFwDebugLogger::print(DEBUG_ALL, "#%05d reservable ip: %s, port: %d\n", i, IPaddr::printIP(it->addr), it->port); i++; } } NatFwDebugLogger::print(DEBUG_INFO, "external IP addr: %s\n", IPaddr::printIP(this->ip1)); if (isRouter) { NatFwDebugLogger::print(DEBUG_INFO, "router is a Firewall!\n"); nat = new NatApi(); fw = new FwApi(); fw->initNatFwChains(); } else { NatFwDebugLogger::print(DEBUG_INFO, "machine is no Firewall!\n"); } api = new NslpApi(); hashtable = new HashTable(); cb_status_timeout = new NslpApiTimerCallback(); cb_status_timeout->SetCallback(this, &NatFwServer::status_timeout); status_timer = new Timer(NULL, 5, true, cb_status_timeout); srand(time(NULL)); if (!NsisConfiguration::natfw_simulation) { nslpSock = createServerSocket(); clientSockCount = 0; clientCallback = new NslpApiSocketCallback(); clientCallback->SetCallback(this, &NatFwServer::handleClientSocket); serverCallback = new NslpApiSocketCallback(); serverCallback->SetCallback(this, &NatFwServer::acceptClient); nslpApiCallback = new NslpApiCallback(); nslpApiCallback->SetCallback(this, &NatFwServer::handleRecvMessage, &NatFwServer::handleMessageStatus, &NatFwServer::handleNetworkNotification); api->register_nslpid(NATFW_NSLP_ID, true); api->connectToGist(nslpApiCallback); NatFwDebugLogger::print(DEBUG_INFO, "connected to GIST server\n"); api->addSocket(nslpSock, NULL, serverCallback); while(true) { api->waitForSignal(); } } } NatFwServer::~NatFwServer() { NatFwDebugLogger::print(3, "NatFwServer::~NatFwServer\n"); fw->deleteNatFwChains(); /* delete api; TODO: segfault delete fw; delete nslpApiCallback; delete clientCallback; delete serverCallback;*/ unlink(NATFW_NSLP_PATH_TO_UNIX_SOCKET); } void NatFwServer::status_timeout(void * args) { NatFwDebugLogger::print(2, "GenericObject::count: %d\n", GenericObject::count); NatFwDebugLogger::print(2, "NatBinding::count: %d\n", NatBinding::count); NatFwDebugLogger::print(2, "FwPinhole::count: %d\n",FwPinhole::count); } bool NatFwServer::isPrivateAddress(IPaddr addr) { return NsisConfiguration::isNetworkPrivate(addr.version, addr); } bool NatFwServer::fromPrivate(msg_routing_info_base_t * mri) { bool isSrcAddrPrivate = NatFwServer::isPrivateAddress(mri->srcAddr.v4.addr); bool isDestAddrPrivate = NatFwServer::isPrivateAddress(mri->destAddr.v4.addr); int direction; if (mri->msgRoutingMethod == GIST_DEFAULT_MRM) { direction = ((msg_routing_info_path_coupled_t *)mri)->d; NatFwDebugLogger::print(DEBUG_ALL, "path-coupled MRM, direction: %d\n", ((msg_routing_info_path_coupled_t *)mri)->d); } else if (mri->msgRoutingMethod == GIST_LOOSEEND_MRM) { direction = ((msg_routing_info_loose_end_t *)mri)->d; NatFwDebugLogger::print(DEBUG_ALL, "loose-end MRM, direction: %d\n", ((msg_routing_info_loose_end_t *)mri)->d); } else { throw new GistException("Unable to handle unknown MRM"); } NatFwDebugLogger::print(DEBUG_ALL, "srcAddr: %s\n", IPaddr::printIP(IPaddr(mri->srcAddr.v4.addr))); NatFwDebugLogger::print(DEBUG_ALL, "destAddr: %s\n", IPaddr::printIP(IPaddr(mri->destAddr.v4.addr))); NatFwDebugLogger::print(DEBUG_ALL, "isSrcAddrPrivate: %d\n", isSrcAddrPrivate); NatFwDebugLogger::print(DEBUG_ALL, "isDestAddrPrivate: %d\n", isDestAddrPrivate); NatFwDebugLogger::print(DEBUG_ALL, "natural direction: %d\n", direction); if (direction == DOWNSTREAM && !isDestAddrPrivate && isSrcAddrPrivate) { // from private side NatFwDebugLogger::print(DEBUG_INFO, "fromPrivate\n"); return true; } else if (direction == DOWNSTREAM && !isSrcAddrPrivate) { // from public side NatFwDebugLogger::print(DEBUG_INFO, "fromPublic\n"); return false; } else if (direction == UPSTREAM && !isDestAddrPrivate) { // from public side NatFwDebugLogger::print(DEBUG_INFO, "fromPublic\n"); return false; } else if (direction == UPSTREAM && !isSrcAddrPrivate && isDestAddrPrivate) { // from private side NatFwDebugLogger::print(DEBUG_INFO, "fromPrivate\n"); return true; } NatFwDebugLogger::print(DEBUG_INFO, "unknown direction\n"); throw new std::runtime_error("unknown direction"); } int NatFwServer::SendMessage(NatFwFsmData * data) { NatFwDebugLogger::print(3, "SendMessage(srcIP: %s, ",IPaddr::printIP(data->recv_msg->mri->srcAddr.v4.addr)); NatFwDebugLogger::print(3, "destIP: %s)\n", IPaddr::printIP(data->recv_msg->mri->destAddr.v4.addr)); return data->server->SendMessage(data->recv_msg->nslp_data, data->recv_msg->nd_size, NULL, NATFW_NSLP_ID, (unsigned char *)data->recv_msg->sid, data->recv_msg->mri, 0, cMode, false, false, true, 2000, 10); } int NatFwServer::SendMessage(char *nslp_data, unsigned int nd_size, char *nslp_message_handle, unsigned short nslp_id, unsigned char sid[SID_SIZE], msg_routing_info_base_t *mri, IPaddr sii_handle, bool reliability, bool security, bool local_processing, bool installRoutingState, unsigned int timeout, unsigned char ip_ttl) { if (!NsisConfiguration::natfw_simulation) { return api->SendMessage(nslp_data, nd_size, (unsigned char*)nslp_message_handle, nslp_id, sid, mri, sii_handle, reliability, security, local_processing, installRoutingState, timeout, ip_ttl); } /* simulation only */ api_recv_msg msg; msg.nslp_data = nslp_data; msg.nd_size = nd_size; memcpy(msg.sid, sid, SID_SIZE); msg.reliability = reliability; msg.security = security; msg.mri = mri; msg.ip_ttl = ip_ttl; msg.sii_handle = sii_handle; IPaddr addr; addr.version = 0; IPaddr destIP; if (mri->msgRoutingMethod == GIST_DEFAULT_MRM) { destIP = ((msg_routing_info_path_coupled_t *)mri)->d == DOWNSTREAM ? IPaddr(mri->destAddr.v4.addr) : IPaddr(mri->srcAddr.v4.addr); } else if (mri->msgRoutingMethod == GIST_LOOSEEND_MRM) { destIP = ((msg_routing_info_loose_end_t *)mri)->d == UPSTREAM ? IPaddr(mri->srcAddr.v4.addr) : IPaddr(mri->destAddr.v4.addr); } else { throw new GistException("Unable to handle unknown MRM"); } // context switch NsisConfiguration::readConfigFromFile(simconf, true); addr = NsisConfiguration::findOutgoingLocalAddress(4, destIP); NatFwDebugLogger::print(DEBUG_INFO, "destination IP: %s\n", IPaddr::printIP(destIP)); NatFwDebugLogger::print(DEBUG_INFO, "selected IP: %s\n", IPaddr::printIP(addr)); for (int i = 0; i < NsisConfiguration::general_localIPv4count; i++) { if (i > 1) { NatFwDebugLogger::print(DEBUG_ERR, "simulation only supports two IP addresses\n"); } if (addr == NsisConfiguration::general_localIPv4s[i]) { if (i == 0 && next) { // context switch DebugLogger::identity = next->simhost; NsisConfiguration::readConfigFromFile(next->simconf, true); next->handleRecvMessage(&msg); } else if (i == 1 && previous || i == 0 && previous && !next) { // context switch DebugLogger::identity = previous->simhost; NsisConfiguration::readConfigFromFile(previous->simconf, true); previous->handleRecvMessage(&msg); } else { NatFwDebugLogger::print(DEBUG_ERR, "dead code end reached\n"); } } } return 0; } bool NatFwServer::isPrivateAddress(unsigned int addr) { IPaddr priv; priv.v4 = addr; priv.version = 4; return isPrivateAddress(priv); } bool NatFwServer::isLocalAddress(IPaddr addr) { // TODO: Handle the prefix correctly (by christian.dickmann) if (NsisConfiguration::isLocalIPaddr(addr, addr.version == 4 ? 32 : 128)) { return true; } // TODO (christian.dickmann) fix this mess if (ip1 == addr) return true; /* for (list::iterator it = reservedAddr.begin(); it != reservedAddr.end(); it++) { if (*it == addr) return true; } */ return false; } bool NatFwServer::isLocalAddress(unsigned int addr) { IPaddr priv; priv.v4 = addr; priv.version = 4; return isLocalAddress(priv); } void NatFwServer::newSession(unsigned char sid[16]) { for(unsigned int i=0; i<16; i++) { sid[i] = (unsigned char)rand(); } NatFwDebugLogger::print(3, "new sid: %8x%8x%8x%8x\n", htonl(((unsigned int*)sid)[0]), htonl(((unsigned int*)sid)[1]), htonl(((unsigned int*)sid)[2]), htonl(((unsigned int*)sid)[3])); } void NatFwServer::printSession(unsigned char sid[16]) { NatFwDebugLogger::print(3, "sid: %8x%8x%8x%8x\n", htonl(((unsigned int*)sid)[0]), htonl(((unsigned int*)sid)[1]), htonl(((unsigned int*)sid)[2]), htonl(((unsigned int*)sid)[3])); } void NatFwServer::handleClientSocket(int sockfd, void * sockData) { char buffer[sizeof(NatFwClientRequest)]; memset(buffer, 0, sizeof(buffer)); int res = recv(sockfd, buffer, sizeof(NatFwClientRequest), 0); if (res <= 0) { closeSocket(sockfd); return; } clientTrigger((NatFwClientRequest *) buffer, sockfd); } void NatFwServer::clientTrigger(NatFwClientRequest * clientRequest, int sockfd) { NatFwFsmData * data = NULL; switch(clientRequest->type) { case NATFW_TYPE_REA: data = new NatFwFsmData; data->clientRequest = clientRequest; data->clientResponse = new NatFwClientResponse; NatFwClient::resetResponseMsg(data->clientResponse); data->recv_msg = NULL; data->fsm = new NatFwFsm(NatFwFsm::st_nr_idle); data->server = this; data->sock = sockfd; newSession(data->clientResponse->sid); api->setNslpState(data->clientResponse->sid, data); if (clientRequest->proxy) { data->fsm->triggerEvent(NatFwFsm::ev_tg_reaproxy, data); } else { data->fsm->triggerEvent(NatFwFsm::ev_tg_rea, data); } break; case NATFW_TYPE_CREATE: if (clientRequest->lifetime > 0) { data = new NatFwFsmData; data->clientRequest = clientRequest; data->clientResponse = new NatFwClientResponse; NatFwClient::resetResponseMsg(data->clientResponse); data->recv_msg = NULL; data->fsm = new NatFwFsm(NatFwFsm::st_ni_idle); data->server = this; data->sock = sockfd; newSession(data->clientResponse->sid); api->setNslpState(data->clientResponse->sid, data); data->fsm->triggerEvent(NatFwFsm::ev_tg_create, data); } else { data = (NatFwFsmData *)api->getNslpState(clientRequest->sid); if (data != NULL) { NatFwDebugLogger::print(DEBUG_WARN, "session not found\n"); break; } data->fsm->triggerEvent(NatFwFsm::ev_tg_teardown, data); } break; default: NatFwDebugLogger::print(DEBUG_ERR, "unknown type\n"); break; } } void NatFwServer::serverTrigger(NatFwClientResponse * msg) { if (NsisConfiguration::natfw_simulation) { NatFwClient::lastResponse = msg; return; } NatFwClient::printResponseMsg(msg); NatFwFsmData * data = (NatFwFsmData *)api->getNslpState(msg->sid); if (data != NULL) { send(data->sock, msg, sizeof(NatFwClientResponse), 0); } else { NatFwDebugLogger::print(DEBUG_CRIT, "session not found"); } } void NatFwServer::handleRecvMessage(api_recv_msg * s) { NatFwFsmData * data = NULL; NatFwFsm * fsm = NULL; int dflag; IPaddr srcAddr; IPaddr destAddr; data = (NatFwFsmData *)api->getNslpState(s->sid); if (data == NULL) { NatFwDebugLogger::print(DEBUG_INFO, "unknown session\n"); if (s->mri->msgRoutingMethod == GIST_DEFAULT_MRM) { dflag = ((msg_routing_info_path_coupled_t*) s->mri)->d; srcAddr.v4 = ((msg_routing_info_path_coupled_t*) s->mri)->srcAddr.v4.addr; srcAddr.version = 4; destAddr.v4 = ((msg_routing_info_path_coupled_t*) s->mri)->destAddr.v4.addr; destAddr.version = 4; } else if (s->mri->msgRoutingMethod == GIST_LOOSEEND_MRM) { dflag = ((msg_routing_info_loose_end_t*) s->mri)->d; srcAddr.v4 = ((msg_routing_info_loose_end_t*) s->mri)->srcAddr.v4.addr; srcAddr.version = 4; destAddr.v4 = ((msg_routing_info_loose_end_t*) s->mri)->destAddr.v4.addr; destAddr.version = 4; } else { NatFwDebugLogger::print(DEBUG_ERR, "unknown MRM\n"); } } unsigned char nslpType; memcpy(&nslpType, s->nslp_data, 1); NatFwDebugLogger::print(DEBUG_INFO, "message type: %x\n", nslpType); /* This is fast-path forwarding without FSM interaction. Intermediate * nodes relay all messages, except REA and CREATE. REA messages are intercepted * at edge-NAT nodes only. */ if (data == NULL) { /* REA messages are supposed to never arrive at NR, but only NF. * But the edge-router might also be the NI. TODO: Is this handling correct then? */ if (nslpType == NATFW_MESSAGETYPE_REA && this->isRouter) { fsm = new NatFwFsm(NatFwFsm::st_nf_idle); /* When a CREATE hits an edge router there might be a reservation * from an internal host, but the MRI is signaled towards the edge router. * Edge-NATs might be also NR. It depends whether there is a reservation * to act like a NF or as an NR (no reservation). */ } else if (nslpType == NATFW_MESSAGETYPE_CREATE && this->isRouter) { // TODO: check reservation fsm = new NatFwFsm(NatFwFsm::st_nf_idle); /* } else if (!isLocalAddress(destAddr) && dflag == DOWNSTREAM || !isLocalAddress(srcAddr) && dflag == UPSTREAM) { * Enable fast path only for non-session flows * NatFwDebugLogger::print(3, "FAST PATH\n"); NatFwDebugLogger::print(3, "fsmForwarder, srcIP: %s\n", IPaddr::printIP(srcAddr.v4)); NatFwDebugLogger::print(3, "fsmForwarder, destIP: %s\n", IPaddr::printIP(destAddr.v4)); api->SendMessage(s->nslp_data, s->nd_size, NULL, NATFW_NSLP_ID, (unsigned char *)s->sid, s->mri, NULL, NULL, cMode, false, false, 2000, 10); return;*/ } printSession(s->sid); if (fsm == NULL) { fsm = new NatFwFsm(NatFwFsm::st_nr_idle); } data = new NatFwFsmData; data->fsm = fsm; data->server = this; api->setNslpState(s->sid, data); } printSession(s->sid); data->recv_msg = s; switch(nslpType) { case NATFW_MESSAGETYPE_CREATE: data->fsm->triggerEvent(NatFwFsm::ev_rx_create, data); break; case NATFW_MESSAGETYPE_REA: data->fsm->triggerEvent(NatFwFsm::ev_rx_rea, data); break; case NATFW_MESSAGETYPE_TRACE: break; case NATFW_MESSAGETYPE_RESPONSE: data->fsm->triggerEvent(NatFwFsm::ev_rx_response, data); break; case NATFW_MESSAGETYPE_NOTIFY: break; default: NatFwDebugLogger::print(DEBUG_ERR, "unknown message type: %x\n", nslpType); } } void NatFwServer::handleMessageStatus(api_msg_status *s, char * status_string) { NatFwDebugLogger::print(DEBUG_INFO, "handleMessageStatus (%s)\n", status_string); } void NatFwServer::handleNetworkNotification(api_nw_notification *s) { NatFwDebugLogger::print(DEBUG_INFO, "handleNetworkNotification\n"); } int NatFwServer::createServerSocket() { NatFwDebugLogger::print(DEBUG_INFO, "createServerSocket\n"); int sock; struct sockaddr_un serv_addr; int len; // in case the app didn't exit cleanly unlink(NATFW_NSLP_PATH_TO_UNIX_SOCKET); if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { NatFwDebugLogger::print(DEBUG_CRIT, "socket creation failed"); exit(-1); } memset((char *)&serv_addr, 0, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; strcpy(serv_addr.sun_path, NATFW_NSLP_PATH_TO_UNIX_SOCKET); len = sizeof(serv_addr); if (bind(sock, (struct sockaddr *)&serv_addr,len) < 0) { NatFwDebugLogger::print(DEBUG_CRIT, "unable to bind unix socket"); exit(-1); } // set bits to rwxrwxrwx if (chmod(NATFW_NSLP_PATH_TO_UNIX_SOCKET, S_IRWXU|S_IRWXG|S_IRWXO) < 0) { NatFwDebugLogger::print(DEBUG_CRIT, "chmod on socket failed"); exit(-1); } listen(sock, 5); return sock; } void NatFwServer::closeSocket(int sock) { api->removeSocket(sock); clientSockCount--; } void NatFwServer::acceptClient(int sockfd, void * sockData) { int l = 0; int sock = 0; struct sockaddr_un client_addr; memset(&client_addr, 0, l = sizeof(client_addr)); if ((sock = accept(nslpSock, (struct sockaddr *) &client_addr, (socklen_t *) &l)) < 0) { NatFwDebugLogger::print(DEBUG_CRIT, "accept() failed"); exit(-1); } DebugLogger::print(DEBUG_INFO, "client connected\n"); addSocket(sock); } void NatFwServer::addSocket(int sock) { clientSockCount++; api->addSocket(sock, NULL, clientCallback); }