#include #include #include #include #include "netsimul.h" #include "netsimul.m" static CONFIG_FILE f_chains ("/proc/net/ip_fwchains",help_nil,CONFIGF_OPTIONAL); static const char K_ACCEPT[]="ACCEPT"; static const char K_REJECT[]="REJECT"; static const char K_DENY[]="DENY"; static const char K_MASQ[]="MASQ"; static const char K_INPUT[]="input"; static const char K_FORWARD[]="forward"; static const char K_OUTPUT[]="output"; PUBLIC FIRE_PACKET::FIRE_PACKET( unsigned long _from, unsigned _from_port, unsigned long _to, unsigned _to_port, unsigned _proto, const char *_inter) { strcpy_cut (inter,_inter,sizeof(inter)); proto = _proto; from.ip = _from; from.mask = 0xffffffff; to.ip = _to; to.mask = 0xffffffff; from_port = _from_port; to_port = _to_port; } PUBLIC FIRE_RULE::FIRE_RULE ( FIRE_NETWORK &_from, FIRE_RANGE &_rfrom, FIRE_NETWORK &_to, FIRE_RANGE &_rto, const char *_inter, unsigned _proto, FIRE_CHAIN *_member, // This rule is a target of this chain FIRE_CHAIN *_target) // This rule jumps to this chain { from = _from; to = _to; rfrom = _rfrom; rto = _rto; strcpy (inter,_inter); proto = _proto; member = _member; target = _target; } PUBLIC bool FIRE_RULE::match (FIRE_PACKET &pk) { bool ret = false; #if 0 if (strcmp(member->name,"F-00184")==0){ fprintf (stderr,":%s: :%s: %d %d %08lx %08lx %08lx %08lx %08lx %08lx\n",inter,pk.inter,proto,pk.proto ,from.ip,from.mask,pk.from.ip ,to.ip,to.mask,pk.to.ip); } #endif if ((inter[0] == '\0' || pk.inter[0] == '\0' || strcmp(inter,pk.inter)==0) && (proto == pk.proto || proto == 0) && from.ip == (from.mask & pk.from.ip) && to.ip == (to.mask & pk.to.ip)){ if (proto == IPPROTO_TCP || proto == IPPROTO_UDP){ if (pk.from_port >= rfrom.start && pk.from_port <= rfrom.stop && pk.to_port >= rto.start && pk.to_port <= rto.stop){ ret = true; } }else{ // No port cheching needed ret = true; } } return ret; } /* Return true is this rule is not a dispatch rule (does not jump to another chain). */ PUBLIC bool FIRE_RULE::is_terminal() const { return strcmp(target->getname(),K_ACCEPT)==0 || strcmp(target->getname(),K_DENY)==0 || strcmp(target->getname(),K_REJECT)==0 || strcmp(target->getname(),K_MASQ)==0; } /* Return true if this rule is an ACCEPT rule. */ PUBLIC bool FIRE_RULE::is_accept() const { return strcmp(target->getname(),K_ACCEPT)==0; } /* Return true if this rule is an MASQ rule. */ PUBLIC bool FIRE_RULE::is_masq() const { return strcmp(target->getname(),K_MASQ)==0; } PUBLIC void FIRE_RULE::dump (SSTRING &buf) { char protostr[10]; if (proto == 0){ strcpy (protostr,"all"); }else{ struct protoent *pro = getprotobynumber (proto); if (pro != NULL){ strcpy (protostr,pro->p_name); }else{ sprintf (protostr,"%d",proto); } } char ip1[20],mask1[20],ip2[20],mask2[20]; ipnum_ip2a (from.ip,ip1); ipnum_ip2a (from.mask,mask1); ipnum_ip2a (to.ip,ip2); ipnum_ip2a (to.mask,mask2); char fromrange[20],torange[20]; if (proto != IPPROTO_TCP && proto != IPPROTO_UDP){ fromrange[0] = torange[0] = '\0'; }else{ if (rfrom.start == rfrom.stop){ sprintf (fromrange,"%u",rfrom.start); }else{ sprintf (fromrange,"%u:%u",rfrom.start,rfrom.stop); } if (rto.start == rto.stop){ sprintf (torange,"%u",rto.start); }else{ sprintf (torange,"%u:%u",rto.start,rto.stop); } } char interstr[20]; if (inter[0] == '\0'){ strcpy (interstr,"Any"); }else{ strcpy (interstr,inter); } buf.setfromf ("%s\t%s\t%s\t%s\t%s/%s\t%s\t%s/%s\t%s" ,member->name,target->name,protostr ,interstr ,ip1,mask1,fromrange ,ip2,mask2,torange); } /* Return the name of the target chain used if this rule match This can be the pseudo chain ACCEPT, DENY or REJECT */ PUBLIC const char *FIRE_RULE::gettarget() const { return target->getname(); } PUBLIC FIRE_RULE *FIRE_RULES::getitem(int no) const { return (FIRE_RULE*)ARRAY::getitem(no); } PUBLIC FIRE_CHAIN::FIRE_CHAIN(const char *_name) { strcpy (name,_name); } PUBLIC void FIRE_CHAIN::add (FIRE_RULE *rule) { rules.add (rule); } PUBLIC FIRE_CHAINS::FIRE_CHAINS() { add (new FIRE_CHAIN(K_ACCEPT)); add (new FIRE_CHAIN(K_DENY)); add (new FIRE_CHAIN(K_REJECT)); add (new FIRE_CHAIN(K_INPUT)); add (new FIRE_CHAIN(K_FORWARD)); add (new FIRE_CHAIN(K_OUTPUT)); } PUBLIC FIRE_CHAIN *FIRE_CHAINS::getitem (int no) const { return (FIRE_CHAIN*)ARRAY::getitem(no); } PUBLIC FIRE_CHAIN *FIRE_CHAINS::locate (const char *chain) { FIRE_CHAIN *ret = NULL; int n = getnb(); for (int i=0; iname,chain)==0){ ret = c; break; } } return ret; } PUBLIC int FIRE_CHAINS::read () { int ret = -1; FILE_CFG *fin = f_chains.fopen ("r"); if (fin != NULL){ char chain[9],inter[20],target[9]; FIRE_NETWORK from,to; FIRE_RANGE rfrom,rto; unsigned proto; unsigned pk_h,pk_l; // Packets counter, 64 bits unsigned bytes_h,bytes_l; // Bytes counter, 64 bits unsigned fwmark; unsigned flg,invflg; unsigned tos,xormask; unsigned outputsize; unsigned redirport; FIRE_CHAIN *last = NULL; char buf[1000]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ int n = sscanf(buf ,"%s " /* Chain name */ "%08lx/%08lx->%08lx/%08lx " /* Source & Destination IPs */ "%s " /* Interface */ "%x %x " /* fw_flg and fw_invflg fields */ "%u " /* Protocol */ "%u %u %u %u " /* Packet & byte counters */ "%u-%u %u-%u " /* Source & Dest port ranges */ "A%x X%x " /* TOS and and xor masks */ "%x " /* Redirection port */ "%u " /* fw_mark field */ "%u " /* output size */ "%s\n" /* Target */ ,chain ,&from.ip,&from.mask,&to.ip,&to.mask ,inter ,&flg,&invflg ,&proto ,&pk_h,&pk_l,&bytes_h,&bytes_l ,&rfrom.start,&rfrom.stop,&rto.start,&rto.stop ,&tos,&xormask ,&redirport ,&fwmark ,&outputsize ,target); if (n==23){ if (last == NULL || strcmp(last->name,chain)!=0){ last = locate (chain); if (last == NULL){ last = new FIRE_CHAIN (chain); add (last); } } FIRE_CHAIN *ctarget = locate (target); if (ctarget == NULL){ ctarget = new FIRE_CHAIN(target); add (ctarget); } if (inter[0] == '-') inter[0] = '\0'; last->add (new FIRE_RULE(from,rfrom,to,rto,inter,proto,last,ctarget)); } } fclose (fin); ret = 0; } return ret; } /* Walk one firewall chain and collect the matching path. Return -1 if some errors, 1 if the packet would normally goes through, 0 if it would be blocked (deny,reject) */ PUBLIC int FIRE_CHAIN::trace (FIRE_RULES &res, FIRE_PACKET &pk) { int ret = 0; int n = rules.getnb(); for (int i=0; imatch(pk)){ res.add (r); if (r->is_terminal()){ if (r->is_accept()){ ret = 1; }else if (r->is_masq()){ ret = 2; }else{ ret = 0; } }else{ FIRE_CHAIN *target = r->target; ret = target->trace(res,pk); } break; } } return ret; } PUBLIC FIRE_RULE *FIRE_CHAIN::getitem (int no) const { return rules.getitem(no); } PUBLIC int FIRE_CHAIN::getnb () const { return rules.getnb(); } PUBLIC const char *FIRE_CHAIN::getname () const { return name; } PUBLIC int FIRE_CHAINS::trace( FIRE_RULES &rules, FIRE_PACKET &pk, const char *chain_name, const char *interface) { int ret = -1; rules.neverdelete(); FIRE_CHAIN *chain = locate (chain_name); if (chain != NULL){ strcpy (pk.inter,interface); ret = chain->trace(rules,pk); } return ret; } /* Walk the firewall rules and collect the matching path. Return -1 if some errors, 1 if the packet would normally goes through, 0 if it would be blocked (deny,reject) */ PUBLIC int FIRE_CHAINS::trace ( FIRE_RULES &rules, FIRE_PACKET &pk, const char *interface_in, // Interface the packet came in const char *interface_out, // Interface used to go out bool test_input, // Test the packet against input rules bool test_forward, // Test the packet against forward rules bool test_output) // Test the packet against output rules { int ret = 0; if (test_input){ ret = trace (rules,pk,K_INPUT,interface_in); } if (test_forward){ ret = trace (rules,pk,K_FORWARD,interface_out); } if (test_output){ ret = trace (rules,pk,K_OUTPUT,interface_out); } return ret; }