|
INET Framework for OMNeT++/OMNEST
|
00001 // 00002 // Copyright (C) 2012 Martin Danko 00003 // 00004 // This program is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 // 00009 // This program is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 // GNU Lesser General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this program; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00017 // 00018 00019 00020 #include "AclContainer.h" 00021 00022 Define_Module(AclContainer); 00023 00024 using namespace std; 00025 00026 /* 00027 * ipIsEqual(): IP IS EQUAL 00028 * Method that compares IP address from the packet header 00029 * to IP address presents in current ACL rule. 00030 * @param ip - struct with IP address from ACL rule 00031 * @param packet - struct with IP address from packet 00032 * @see filterPacket() 00033 * @return result of IP address comparison 00034 */ 00035 bool AclContainer::ipIsEqual(TIP* ip, TIP* packet) 00036 { 00037 ev << "ACL rule, ip : " << ip->ipAddr.str() << endl; 00038 ev << "ACL rule, mask: " << ip->netmask.str() << endl; 00039 ev << "packet, ip : " << packet->ipAddr.str() << endl; 00040 ev << "packet_masked : " << packet->ipAddr.doAnd(ip->netmask).str() << endl; 00041 if (ip->ipAddr != (packet->ipAddr.doAnd(ip->netmask))) 00042 { 00043 ev << "IP DOESN'T MATCH" << endl; 00044 return false; 00045 } 00046 ev << "IP MATCH OK" << endl; 00047 return true; 00048 } 00049 00050 /* 00051 * portIsEqual(): PORT IS EQUAL 00052 * Method that compares port number(s) from the packet header 00053 * to port number(s) present in current ACL rule. 00054 * @param ip - struct with ports from ACL rule 00055 * @param packet - struct with ports from packet 00056 * @see filterPacket() 00057 * @return result of port comparison 00058 */ 00059 bool AclContainer::portIsEqual(TIP* ip, TIP* packet) 00060 { 00061 switch (ip->port_op) { 00062 case PORT_EQ: 00063 if (ip->portBeg == packet->portBeg) 00064 return true; 00065 break; 00066 case PORT_NEQ: 00067 if (ip->portBeg != packet->portBeg) 00068 return true; 00069 break; 00070 case PORT_GT: 00071 if (ip->portBeg < packet->portBeg) 00072 return true; 00073 break; 00074 case PORT_LT: 00075 if (ip->portBeg > packet->portBeg) 00076 return true; 00077 break; 00078 case PORT_RNG: 00079 case PORT_NDEF: 00080 if ((ip->portBeg <= packet->portBeg) && (ip->portEnd >= packet->portBeg)) 00081 return true; 00082 break; 00083 default: 00084 cout << "BUG" << endl; 00085 } 00086 return false; 00087 } 00088 00089 /* 00090 * andIpWithMask(): AND IP WITH MASK 00091 * Method that does AND operation between IP Address and network mask in current ACL rule. 00092 * @param rule - struct with ACL rule 00093 * @return Negated wildcard mask [bits(O -> 1, 1 -> 0)]. 00094 * @see loadConfigFromXML() 00095 */ 00096 void AclContainer::andIpWithMask(TRule* rule) 00097 { 00098 rule->source.ipAddr = rule->source.ipAddr.doAnd(rule->source.netmask); 00099 rule->dest.ipAddr = rule->dest.ipAddr.doAnd(rule->dest.netmask); 00100 } 00101 00102 /* 00103 * negateWildcard(): NEGATE WILD CARD MASKs 00104 * Method that negates all the bits of given wildcard mask in ACL rule. 00105 * @param wc - IPAddress structure with wildcard mask 00106 * @see loadConfigFromXML() 00107 */ 00108 IPAddress AclContainer::negateWildcard(IPAddress wc) 00109 { 00110 return (IPAddress(~(wc.getInt()))); 00111 } 00112 00113 /* 00114 * getAction(): GET ACTION 00115 * Method that gets "action" field from XML string and puts it 00116 * to current ACL rule. 00117 * @param action - string with text from XML config file 00118 * @param rule - struct with ACL rule 00119 * @see loadConfigFromXML() 00120 */ 00121 void AclContainer::getAction(std::string action, TRule* rule) 00122 { 00123 if (action == "deny") 00124 rule->action = A_DENY; 00125 else if (action == "permit") 00126 rule->action = A_PERMIT; 00127 } 00128 00129 /* 00130 * getProtocol(): GET PROTOCOL 00131 * Method that gets protocol field from XML string and puts it 00132 * to current ACL rule. It can handle protocol typed as text 00133 * or as a number (according to RFC). 00134 * @param pom - string with text/number from XML config file 00135 * @param rule - struct with ACL rule 00136 * @see loadConfigFromXML() 00137 */ 00138 void AclContainer::getProtocol(std::string pom, TRule* rule) 00139 { 00140 if (pom == "ip" || pom == "4") 00141 rule->protocol = PROT_IP; 00142 else if (pom == "tcp" || pom == "6") 00143 rule->protocol = PROT_TCP; 00144 else if (pom == "udp" || pom == "17") 00145 rule->protocol = PROT_UDP; 00146 else if (pom == "icmp" || pom == "1") 00147 rule->protocol = PROT_ICMP; 00148 else if (pom == "igmp" || pom == "2") 00149 rule->protocol = PROT_IGMP; 00150 else if (pom == "eigrp" || pom == "igrp" || pom == "88") 00151 rule->protocol = PROT_EIGRP; 00152 else if (pom == "ospf" || pom == "89") 00153 rule->protocol = PROT_OSPF; 00154 } 00155 00156 /* 00157 * getPort(): GET PORT 00158 * Method that gets all port information fields from XML string. 00159 * It can handle ports typed as text or as a number (according to RFC). 00160 * @param pom - string contains the port operator from XML file 00161 * @param p_beg - string contains the beginning port no. from XML file 00162 * @param p_end - string contains the ending port no. from XML file 00163 * @param ip - struct with an IP Address in current ACL rule 00164 * @see loadConfigFromXML() 00165 */ 00166 void AclContainer::getPort(std::string pom, std::string p_beg, std::string p_end, TIP *ip) 00167 { 00168 int p1, p2; 00169 stringstream ss (stringstream::in | stringstream::out); 00170 /* if port is given as text string, it is converted to short int number */ 00171 if (p_beg == "ftp-data") p1 = 20; 00172 if (p_beg == "ftp") p1 = 21; 00173 if (p_beg == "telnet") p1 = 23; 00174 if (p_beg == "smtp") p1 = 25; 00175 if (p_beg == "tftp") p1 = 69; 00176 if ((p_beg == "www") || (p_beg == "http") || (p_beg == "www-http")) p1 = 80; 00177 if ((p_beg == "pop3") || (p_beg == "pop")) p1 = 110; 00178 if (p_beg == "snmp") p1 = 161; 00179 if (p_beg == "irc") p1 = 194; 00180 if (p_beg == "ipx") p1 = 213; 00181 if (p_beg == "ldap") p1 = 389; 00182 if (p_beg == "https") p1 = 443; 00183 00184 ss << p_beg; 00185 ss >> p1; 00186 ss << p_end; 00187 ss >> p2; 00188 00189 /* resolution of port operators*/ 00190 if (pom == "eq") // EQ 00191 ip->port_op = PORT_EQ; 00192 else if (pom == "neq") // NEQ 00193 ip->port_op = PORT_NEQ; 00194 else if (pom == "gt") // GT 00195 ip->port_op = PORT_GT; 00196 else if (pom == "lt") // LT 00197 ip->port_op = PORT_LT; 00198 else if (pom == "range") // RANGE 00199 ip->port_op = PORT_RNG; 00200 else // if port operator is "" or not defined: NDEF 00201 { 00202 ip->port_op = PORT_NDEF; 00203 } 00204 switch (ip->port_op) { // based on presence of current port operator 00205 case PORT_EQ: 00206 case PORT_NEQ: 00207 case PORT_GT: 00208 case PORT_LT: // if operator is any of following [eq, neq, gt, lt]: load only one port no. from portBeg 00209 ip->portBeg = p1; 00210 break; 00211 case PORT_RNG: // if operator = RANGE, load two port number in the range of portBeg to portEnd 00212 ip->portBeg = p1; 00213 ip->portEnd = p2; 00214 break; 00215 case PORT_NDEF: // if any port operator is present, port range is full range of ports [0 - 65535] 00216 ip->portBeg = 0; 00217 ip->portEnd = 65535; 00218 break; 00219 } 00220 } 00221 00222 /* 00223 * initialize(): INITIALIZE 00224 * An autonomous method that's been called whenever simulation is started. 00225 * It initializes the ACL filtering module for the simulation needs. 00226 * @param stage - phase of simulation init 00227 */ 00228 void AclContainer::initialize(int stage) 00229 { 00230 if (stage == 4) 00231 { 00232 const char *fileName = par("configFile"); // XML file name read from parameter 00233 if (loadConfigFromXML(fileName)) // success loading configuration data from XML file 00234 { 00235 //cout << "ACL: Configuration successfully loaded." << endl; 00236 } 00237 else // error loading configuration data from XML file 00238 { 00239 //cout << "ACL: Error loading configuration." << endl; 00240 } 00241 00242 /* added class of statistics information onto Watch list */ 00243 WATCH_LIST(stats); 00244 } 00245 } 00246 00247 00248 /* 00249 * loadConfigFromXML(): LOAD CONFIG FROM XML 00250 * Method that loads configuration from XML file to IP ACL structures. 00251 * Also binds list of ACL rules to interface and direction. 00252 * @param fileName - configuration filename (XML) 00253 * @see initialize() 00254 * @return correct/incorrect load of ACL configuration on Router 00255 */ 00256 bool AclContainer::loadConfigFromXML(const char* fileName) 00257 { 00258 /* get access into Routing Table and Interface Table modules */ 00259 IRoutingTable* rt = RoutingTableAccess().get(); 00260 00261 /* resolution of configuration XML file according to specified filename */ 00262 cXMLElement* document = ev.getXMLDocument(fileName); 00263 if (document == NULL) 00264 { /* if no file with such name is provided, error is recognized */ 00265 error("Cannot read AS configuration from file %s", fileName); 00266 return false; 00267 } 00268 00269 /* try to find current router in XML on running instance of this ACL module */ 00270 std::string routerXPath("Router[@id='"); 00271 std::string routerId = rt->getRouterId().str(); 00272 routerXPath += routerId; 00273 routerXPath += "']"; 00274 00275 cXMLElement* router = document->getElementByPath(routerXPath.c_str()); 00276 if (router == NULL) 00277 { /* current router configuration is completely missing in XML configuration - error */ 00278 error("No configuration for Router ID: %s", routerId.c_str()); 00279 return false; 00280 } 00281 cXMLElement* ACLs = router->getFirstChildWithTag("ACLs"); // find ACL configuration part 00282 if (ACLs == NULL) 00283 { /* ACL configuration part is missing in XML configuration */ 00284 //cout << "ACL: ACL is NOT ENABLED on Router id = " << routerId.c_str() << endl; 00285 return true; 00286 } 00287 cXMLElement* ACL = ACLs->getFirstChildWithTag("ACL"); 00288 if (ACL == NULL) 00289 { 00290 //cout << "ACL: ACL is NOT ENABLED on Router id = " << routerId.c_str() << endl; 00291 return true; 00292 } 00293 00294 while (ACL != NULL) 00295 { 00296 cXMLElement *entry = ACL->getFirstChildWithTag("entry"); 00297 TACL _acl; 00298 _acl.aclName = ACL->getAttribute("no"); 00299 00300 while (entry != NULL) 00301 { 00302 cXMLElement *leaf = entry->getFirstChild(); 00303 TRule rule; 00304 Stat stat; 00305 stat.used = 0; 00306 00307 std::string src_port_op = ""; 00308 std::string src_port_beg = ""; 00309 std::string src_port_end = ""; 00310 std::string dst_port_op = ""; 00311 std::string dst_port_beg = ""; 00312 std::string dst_port_end = ""; 00313 00314 /* processing current leaf under XML node <entry> */ 00315 while (leaf != NULL) 00316 { 00317 const char* tagName = leaf->getTagName(); 00318 const char* value = leaf->getNodeValue(); 00319 00320 if (strcmp(tagName, "action") == 0) 00321 getAction(value, &rule); 00322 else if (strcmp(tagName, "IP_src") == 0) 00323 rule.source.ipAddr.set(value); 00324 else if (strcmp(tagName, "WC_src") == 0) 00325 rule.source.netmask = negateWildcard(IPAddress(value)); 00326 else if (strcmp(tagName, "protocol") == 0) 00327 getProtocol(value, &rule); 00328 else if (strcmp(tagName, "IP_dst") == 0) 00329 rule.dest.ipAddr.set(value); 00330 else if (strcmp(tagName, "WC_dst") == 0) 00331 rule.dest.netmask = negateWildcard(IPAddress(value)); 00332 else if (strcmp(tagName, "port_op_src") == 0) 00333 src_port_op = value; 00334 else if (strcmp(tagName, "port_beg_src") == 0) 00335 src_port_beg = value; 00336 else if (strcmp(tagName, "port_end_src") == 0) 00337 src_port_end = value; 00338 else if (strcmp(tagName, "port_op_dst") == 0) 00339 dst_port_op = value; 00340 else if (strcmp(tagName, "port_beg_dst") == 0) 00341 dst_port_beg = value; 00342 else if (strcmp(tagName, "port_end_dst") == 0) 00343 dst_port_end = value; 00344 else if (strcmp(tagName, "orig") == 0) 00345 stat.text = value; 00346 leaf = leaf->getNextSibling(); 00347 } 00348 00349 /* AND (&) IP ADDRESS WITH NETWORK MASK IN CURRENT ACL RULE */ 00350 andIpWithMask(&rule); 00351 /* PROCESS PORT INFORMATION FROM XML CONFIGURATION FILE */ 00352 getPort(src_port_op, src_port_beg, src_port_end, &rule.source); 00353 getPort(dst_port_op, dst_port_beg, dst_port_end, &rule.dest); 00354 00355 /* generate statistics for processed rule */ 00356 stats.push_back(stat); 00357 Stat* pStat = &this->stats.back(); 00358 rule.used = &(pStat->used); 00359 00360 _acl.rules.push_back(rule); // processed rule is added at the end of ACL list 00361 00362 entry = entry->getNextSiblingWithTag("entry"); 00363 } 00364 /* create default rule at the end of the ACL list - deny ip any any */ 00365 TRule rule; 00366 Stat stat; 00367 stat.used = 0; 00368 rule.action = A_DENY; 00369 rule.protocol = PROT_IP; 00370 stat.text = "access-list " + _acl.aclName +" deny ip any any"; 00371 rule.source.ipAddr.set("0.0.0.0"); 00372 rule.dest.ipAddr.set("0.0.0.0"); 00373 rule.source.netmask.set("0.0.0.0"); 00374 rule.dest.netmask.set("0.0.0.0"); 00375 getPort("", "", "", &rule.source); 00376 getPort("", "", "", &rule.dest); 00377 00378 /* generate statistics for the default rule*/ 00379 stats.push_back(stat); 00380 Stat* pStat = &this->stats.back(); 00381 rule.used = &(pStat->used); 00382 00383 _acl.rules.push_back(rule); // implicit rule is finally added at the end of the list 00384 00385 this->acls.push_back(_acl); // current ACL list is added at the end of ACLs (list of lists) 00386 00387 00388 ACL = ACL->getNextSiblingWithTag("ACL"); 00389 } 00390 return true; 00391 } 00392 00393 /* 00394 * getRules(): GET RULES 00395 * Method that gets specific ACL list, which is about to be applied 00396 * on current in/outgoing packet. 00397 * @param gateIndex - index of the arrival gate 00398 * @param dir - direction of data communication 00399 * @see handleMessage() 00400 * @return ACL list corresponding to packet or NULL if it's not 00401 */ 00402 TRULES* AclContainer::getRulesByAclName(std::string name) 00403 { 00404 for (TACL_itc it = this->acls.begin(); it != this->acls.end(); it++) 00405 { 00406 if(it->aclName == name) 00407 return &it->rules; 00408 } 00409 return NULL; 00410 } 00411 00412 /* 00413 * processPacket(): PROCESS PACKET 00414 * Method that is processing current packet, which is casted to IPDatagram. 00415 * It decapsulates packet and save the information such as source address, 00416 * destination address, transport protocol and transfer port numbers. 00417 * It also decides, whether the packet is about to be sent to upper/lower layer, 00418 * or is about to be dropped. 00419 * @param packet - current IP packet casted to IPDatagram 00420 * @param acl - list of ACL rules which fits current packet 00421 * @return Decision of action taken with current packet (permit/deny) 00422 */ 00423 bool AclContainer::processPacket(IPDatagram* packet, TRULES* acl) 00424 { 00425 TIP source, dest; 00426 source.ipAddr = packet->getSrcAddress(); 00427 dest.ipAddr = packet->getDestAddress(); 00428 int protocol = packet->getTransportProtocol(); 00429 00430 ev << "Packet Source IP Address: " << source.ipAddr.str() << endl; 00431 ev << "Packet Dest. IP Address: " << dest.ipAddr.str() << endl; 00432 ev << "Packet Protocol ID : " << protocol << endl; 00433 00434 UDPPacket *udppacket; 00435 TCPSegment *tcppacket; 00436 00437 switch (protocol) 00438 { 00439 case PROT_UDP: // if protocol is UDP, ports need to be checked 00440 udppacket = dynamic_cast<UDPPacket *> (packet->decapsulate()); 00441 source.portBeg = udppacket->getSourcePort(); 00442 ev << "UDP packet, source port: " << source.portBeg << endl; 00443 dest.portBeg = udppacket->getDestinationPort(); 00444 ev << "UDP packet, dest. port: " << dest.portBeg << endl; 00445 delete udppacket; 00446 break; 00447 case PROT_TCP: // if protocol is TCP, ports need to be checked 00448 tcppacket = dynamic_cast<TCPSegment *> (packet->decapsulate()); 00449 source.portBeg = tcppacket->getSrcPort(); 00450 ev << "TCP packet, source port: " << source.portBeg << endl; 00451 dest.portBeg = tcppacket->getDestPort(); 00452 ev << "TCP packet, dest. port: " << dest.portBeg << endl; 00453 delete tcppacket; 00454 break; 00455 default: 00456 ev << "Protocol is other than TCP or UDP, ports don't need to be checked..." << endl; 00457 break; 00458 } 00459 00460 return compareValues(acl, source, dest, protocol); 00461 } 00462 00463 /* 00464 * filterPacket(): FILTER PACKET 00465 * Method that compares information from the packet with whole 00466 * ACL list bound to current packet. Depending on this comparison, 00467 * indication of action is taken. (true = permit, false = deny). 00468 * @param acl - list of ACL rules which fits current packet 00469 * @param source - structure with source data from packet 00470 * @param dest - structure with destination data from packet 00471 * @param protocol - type of transport protocol of the packet 00472 * @return action taken with the current packet (permit/deny) 00473 * @see processPacket() 00474 */ 00475 bool AclContainer::compareValues(TRULES* acl, TIP source, TIP dest, int protocol) 00476 { 00477 for (TRULES_it it = acl->begin(); it != acl->end(); it++) 00478 { 00479 if (!(it->protocol == protocol || it->protocol == PROT_IP)) 00480 continue; 00481 ev << "acl::filterPacket: PROTOCOL MATCH" << endl; 00482 if (!ipIsEqual(&(it->source), &source)) 00483 continue; 00484 ev << "acl::filterPacket: SOURCE IP MATCH" << endl; 00485 if (it->protocol == PROT_UDP || it->protocol == PROT_TCP) 00486 { 00487 if (!portIsEqual(&(it->source), &source)) 00488 continue; 00489 else 00490 ev << "acl::filterPacket: SOURCE PORT MATCH" << endl; 00491 } 00492 if (!ipIsEqual(&(it->dest), &dest)) 00493 continue; 00494 ev << "acl::filterPacket: DESTINATION IP MATCH" << endl; 00495 if (it->protocol == PROT_UDP || it->protocol == PROT_TCP) 00496 { 00497 if (!portIsEqual(&(it->dest), &dest)) 00498 continue; 00499 else 00500 ev << "acl::filterPacket: DESTINATION PORT MATCH" << endl; 00501 } 00502 (*(it->used))++; 00503 return it->action; // if match is found with current record of ACL, action is returned 00504 } 00505 cout << "BUG" << endl; 00506 return false; // if no match in whole ACL is found 00507 } 00508 00509 00510 bool AclContainer::matchPacketToAcl(std::string name, cMessage *msg) 00511 { 00512 TRULES* acl = getRulesByAclName(name); 00513 if (acl == NULL) 00514 return false; 00515 00516 cMessage *copy = msg->dup(); // duplicate the message due to instability during operations with an original message 00517 00518 cPacket *test = (cPacket*)(copy); 00519 string typ = test->getClassName(); 00520 if (typ == "IPDatagram") 00521 { 00522 IPDatagram *packet = dynamic_cast<IPDatagram*> (copy); 00523 bool result = processPacket(packet, acl); 00524 delete copy; 00525 return result; 00526 } 00527 00528 delete copy; 00529 return false; 00530 } 00531 00532 bool AclContainer::existAcl(std::string name) 00533 { 00534 if (getRulesByAclName(name) == NULL) 00535 return false; 00536 return true; 00537 00538 } 00539 00540 00541 /* 00542 * handleMessage(): HANDLE MESSAGE 00543 * Method that is processing current message arrived to gate. 00544 * It checks the index of the arrival gate, its name and index, 00545 * direction of communication according to which gate a packet came. 00546 * Some action is done with the message, it is being forwarded if no 00547 * ACL configuration is found for this router in XML file. 00548 * It also checks the type of packet. If it differs from IPDatagram, 00549 * it just forwards the message. Else it casts it to IPDatagram type. 00550 * @param msg - original message arriving to gate 00551 */ 00552 void AclContainer::handleMessage(cMessage* msg) 00553 { 00554 opp_error("This module doesn't process messages"); 00555 } 00556 00557