|
INET Framework for OMNeT++/OMNEST
|
00001 // 00002 // Copyright (C) 2000 Institut fuer Telematik, Universitaet Karlsruhe 00003 // Copyright (C) 2004 Andras Varga 00004 // 00005 // This program is free software; you can redistribute it and/or 00006 // modify it under the terms of the GNU Lesser General Public License 00007 // as published by the Free Software Foundation; either version 2 00008 // of the License, or (at your option) any later version. 00009 // 00010 // This program is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU Lesser General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU Lesser General Public License 00016 // along with this program; if not, see <http://www.gnu.org/licenses/>. 00017 // 00018 00019 // Cleanup and rewrite: Andras Varga, 2004 00020 00021 #include <omnetpp.h> 00022 #include <string.h> 00023 00024 #include "IPDatagram.h" 00025 #include "IPControlInfo.h" 00026 #include "ICMP.h" 00027 00028 Define_Module(ICMP); 00029 00030 00031 void ICMP::handleMessage(cMessage *msg) 00032 { 00033 cGate *arrivalGate = msg->getArrivalGate(); 00034 00035 // process arriving ICMP message 00036 if (!strcmp(arrivalGate->getName(), "localIn")) 00037 { 00038 processICMPMessage(check_and_cast<ICMPMessage *>(msg)); 00039 return; 00040 } 00041 00042 // request from application 00043 if (!strcmp(arrivalGate->getName(), "pingIn")) 00044 { 00045 sendEchoRequest(PK(msg)); 00046 return; 00047 } 00048 } 00049 00050 00051 void ICMP::sendErrorMessage(IPDatagram *origDatagram, ICMPType type, ICMPCode code) 00052 { 00053 Enter_Method("sendErrorMessage(datagram, type=%d, code=%d)", type, code); 00054 00055 // get ownership 00056 take(origDatagram); 00057 00058 // don't send ICMP error messages for multicast messages 00059 if (origDatagram->getDestAddress().isMulticast()) 00060 { 00061 EV << "won't send ICMP error messages for multicast message " << origDatagram << endl; 00062 delete origDatagram; 00063 return; 00064 } 00065 00066 // do not reply with error message to error message 00067 if (origDatagram->getTransportProtocol() == IP_PROT_ICMP) 00068 { 00069 ICMPMessage *recICMPMsg = check_and_cast<ICMPMessage *>(origDatagram->getEncapsulatedPacket()); 00070 if (recICMPMsg->getType()<128) 00071 { 00072 EV << "ICMP error received -- do not reply to it" << endl; 00073 delete origDatagram; 00074 return; 00075 } 00076 } 00077 00078 // assemble a message name 00079 char msgname[32]; 00080 static long ctr; 00081 sprintf(msgname, "ICMP-error-#%ld-type%d-code%d", ++ctr, type, code); 00082 00083 // debugging information 00084 EV << "sending ICMP error " << msgname << endl; 00085 00086 // create and send ICMP packet 00087 ICMPMessage *errorMessage = new ICMPMessage(msgname); 00088 errorMessage->setType(type); 00089 errorMessage->setCode(code); 00090 errorMessage->encapsulate(origDatagram); 00091 00092 // ICMP message length: the internet header plus the first 8 bytes of 00093 // the original datagram's data is returned to the sender. 00094 // 00095 // NOTE: since we just overwrite the errorMessage length without actually 00096 // truncating origDatagram, one can get "packet length became negative" 00097 // error when decapsulating the origDatagram on the receiver side. 00098 // A workaround is to avoid decapsulation, or to manually set the 00099 // errorMessage length to be larger than the encapsulated message. 00100 int dataLength = origDatagram->getByteLength() - origDatagram->getHeaderLength(); 00101 int truncatedDataLength = dataLength <= 8 ? dataLength : 8; 00102 errorMessage->setByteLength(8 + origDatagram->getHeaderLength() + truncatedDataLength); 00103 00104 // if srcAddr is not filled in, we're still in the src node, so we just 00105 // process the ICMP message locally, right away 00106 if (origDatagram->getSrcAddress().isUnspecified()) 00107 { 00108 // pretend it came from the IP layer 00109 IPControlInfo *controlInfo = new IPControlInfo(); 00110 controlInfo->setSrcAddr(IPAddress::LOOPBACK_ADDRESS); // FIXME maybe use configured loopback address 00111 controlInfo->setProtocol(IP_PROT_ICMP); 00112 errorMessage->setControlInfo(controlInfo); 00113 00114 // then process it locally 00115 processICMPMessage(errorMessage); 00116 } 00117 else 00118 { 00119 sendToIP(errorMessage, origDatagram->getSrcAddress()); 00120 } 00121 } 00122 00123 void ICMP::sendErrorMessage(cPacket *transportPacket, IPControlInfo *ctrl, ICMPType type, ICMPCode code) 00124 { 00125 Enter_Method("sendErrorMessage(transportPacket, ctrl, type=%d, code=%d)", type, code); 00126 00127 IPDatagram *datagram = ctrl->removeOrigDatagram(); 00128 take(transportPacket); 00129 take(datagram); 00130 datagram->encapsulate(transportPacket); 00131 sendErrorMessage(datagram, type, code); 00132 } 00133 00134 void ICMP::processICMPMessage(ICMPMessage *icmpmsg) 00135 { 00136 switch (icmpmsg->getType()) 00137 { 00138 case ICMP_DESTINATION_UNREACHABLE: 00139 errorOut(icmpmsg); 00140 break; 00141 case ICMP_REDIRECT: 00142 errorOut(icmpmsg); 00143 break; 00144 case ICMP_TIME_EXCEEDED: 00145 errorOut(icmpmsg); 00146 break; 00147 case ICMP_PARAMETER_PROBLEM: 00148 errorOut(icmpmsg); 00149 break; 00150 case ICMP_ECHO_REQUEST: 00151 processEchoRequest(icmpmsg); 00152 break; 00153 case ICMP_ECHO_REPLY: 00154 processEchoReply(icmpmsg); 00155 break; 00156 case ICMP_TIMESTAMP_REQUEST: 00157 processEchoRequest(icmpmsg); 00158 break; 00159 case ICMP_TIMESTAMP_REPLY: 00160 processEchoReply(icmpmsg); 00161 break; 00162 default: 00163 opp_error("Unknown ICMP type %d", icmpmsg->getType()); 00164 } 00165 } 00166 00167 void ICMP::errorOut(ICMPMessage *icmpmsg) 00168 { 00169 send(icmpmsg, "errorOut"); 00170 } 00171 00172 void ICMP::processEchoRequest(ICMPMessage *request) 00173 { 00174 // turn request into a reply 00175 ICMPMessage *reply = request; 00176 reply->setName((std::string(request->getName())+"-reply").c_str()); 00177 reply->setType(ICMP_ECHO_REPLY); 00178 00179 // swap src and dest 00180 // TBD check what to do if dest was multicast etc? 00181 IPControlInfo *ctrl = check_and_cast<IPControlInfo *>(reply->getControlInfo()); 00182 IPAddress src = ctrl->getSrcAddr(); 00183 IPAddress dest = ctrl->getDestAddr(); 00184 ctrl->setSrcAddr(dest); 00185 ctrl->setDestAddr(src); 00186 00187 sendToIP(reply); 00188 } 00189 00190 void ICMP::processEchoReply(ICMPMessage *reply) 00191 { 00192 IPControlInfo *ctrl = check_and_cast<IPControlInfo*>(reply->removeControlInfo()); 00193 cPacket *payload = reply->decapsulate(); 00194 payload->setControlInfo(ctrl); 00195 delete reply; 00196 send(payload, "pingOut"); 00197 } 00198 00199 void ICMP::sendEchoRequest(cPacket *msg) 00200 { 00201 IPControlInfo *ctrl = check_and_cast<IPControlInfo*>(msg->removeControlInfo()); 00202 ctrl->setProtocol(IP_PROT_ICMP); 00203 ICMPMessage *request = new ICMPMessage(msg->getName()); 00204 request->setType(ICMP_ECHO_REQUEST); 00205 request->encapsulate(msg); 00206 request->setControlInfo(ctrl); 00207 sendToIP(request); 00208 } 00209 00210 void ICMP::sendToIP(ICMPMessage *msg, const IPAddress& dest) 00211 { 00212 IPControlInfo *controlInfo = new IPControlInfo(); 00213 controlInfo->setDestAddr(dest); 00214 controlInfo->setProtocol(IP_PROT_ICMP); 00215 msg->setControlInfo(controlInfo); 00216 00217 send(msg, "sendOut"); 00218 } 00219 00220 void ICMP::sendToIP(ICMPMessage *msg) 00221 { 00222 // assumes IPControlInfo is already attached 00223 send(msg, "sendOut"); 00224 } 00225