INET Framework for OMNeT++/OMNEST
AclContainer.cc
Go to the documentation of this file.
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