Logo Search packages:      
Sourcecode: netams version File versions  Download package

s_processor.c

/*************************************************************************
***   Authentication, authorization, accounting + firewalling package
***   Copyright 1998-2002 Anton Vinokurov <anton@netams.com>
***   Copyright 2002-2008 NeTAMS Development Team
***   This code is GPL v3
***   For latest version and more info, visit this project web page
***   located at http://www.netams.com
***
*************************************************************************/
/* $Id: s_processor.c,v 1.219 2009-08-01 09:23:55 anton Exp $ */

#include "netams.h"

void sPConstructStoreMessages(oid netunit, policy_data *pd);

int cAutoAssignIP(struct cli_def *cli, char **argv, int argc, u_char no_flag);
int cAutoUnits(struct cli_def *cli, char **argv, int argc, u_char no_flag);
int cDefault(struct cli_def *cli, char **argv, int argc, u_char no_flag);
int cRestrict(struct cli_def *cli, char **argv, int argc, u_char no_flag);
void FillStatData(void *res, void *row, char* (*getRowData)(void*, void* , u_char));

FIFO *Mux_in;
Service_Processor *Processor;
//////////////////////////////////////////////////////////////////////////
Service_Processor::Service_Processor():Service(SERVICE_PROCESSOR){
      def=NULL;
      delay=PROCESSOR_DELAY; // in seconds
      lifetime=PROCESSOR_LIFETIME;
      restrict_all=DROP;
      restrict_local=PASS;
      access_script=NULL;
      auto_assign=NULL;
      auto_units=NULL;

      Mux_in = new FIFO(MAX_UNITS*5);
      fifo = NULL;

      mac_control_units_checked=0;
      mac_control_units_violated=0;
      //this pointers might be defined from others places, remember it
//    >fifo=NULL;
//    st_root=NULL;

      Processor=this;
}

Service_Processor::~Service_Processor() {
      //destroy Multiplexer fifo
      delete Mux_in;

      //clar AutoAssign config
      AutoAssignEntry *ase;
      while(auto_assign) {
            ase=auto_assign;
            auto_assign=auto_assign->next;
            aFree(ase);
      }

      //clear AutoUnits config
      AutoUnitsEntry *aue;
      while(auto_units) {
            aue=auto_units;
            auto_units=auto_units->next;
            aFree(aue->put_to_group);
            aFree(aue->prefix);
            aFree(aue);
      }

      if(access_script) aFree(access_script);
      if(def) delete def;
      Processor=NULL;
}
//////////////////////////////////////////////////////////////////////////
int Service_Processor::ProcessCfg(struct cli_def *cli, char **param, int argc, u_char no_flag){
      if (STRARG(param[0], "unit"))
            cUnit(cli, param, argc, no_flag);
      else if (STRARG(param[0], "policy"))
            cPolicy(cli, param, argc, no_flag);
      else if (STRARG(param[0], "default"))
            cDefault(cli, param, argc, no_flag);
      else if (STRARG(param[0], "restrict"))
            cRestrict(cli, param, argc, no_flag);
      else if (STRARG(param[0], "lookup-delay")) {
            unsigned t_delay=strtol(param[1], NULL, 10);
            if (t_delay>=1 && t_delay<24*60*60) {
                  cli_error(cli, "lookup delay is set to %u seconds", t_delay);
                  delay=t_delay;
            }
            else
                  cli_error(cli, "lookup delay value invalid");
      }
      else if (STRARG(param[0], "flow-lifetime")) {
            unsigned time=strtol(param[1], NULL, 10);
            if (lifetime>=1 && lifetime<24*60*60) {
                  cli_error(cli, "flow lifetime is set to %u seconds", time);
                  lifetime=time;
            }
            else
                  cli_error(cli, "flow lifetime value invalid");
      }
      else if (STRARG(param[0], "access-script")) {
            if(access_script) aFree(access_script);
            access_script=set_string(param[1]);
            cli_error(cli, "access control script name is set to '%s'", param[1]);
      }
      else if (STRARG(param[0], "auto-assign"))
            cAutoAssignIP(cli, param, argc, no_flag);
      else if (STRARG(param[0], "auto-units"))
            cAutoUnits(cli, param, argc, no_flag);
      return CLI_OK;
}
//////////////////////////////////////////////////////////////////////////
void Service_Processor::Worker(){
      unsigned long long loop_iteration=0;
      Service *dependent=NULL;

#ifdef HAVE_BILLING
      //Billing wakes up us when billing data read and restoring will be completed
      if(Billing) Sleep();
#endif
      Service* st = aStorageGetAccepted(ST_CONN_SUMMARY);
      if (!st)
            aLog(D_WARN, "no storages with SUMMARY data are available\n");
      else {
            aLog(D_INFO, "will use storage %d for SUMMARY data\n", st->instance);
            while(((Service_Storage_Interface*)st)->stLoad(ST_CONN_SUMMARY, &FillStatData ) < 0) {
                  aLog(D_WARN, "Service processor can't obtain data from storage:%u\n", st->instance);
                  Sleep(10);
            }
      }

      Processor->Sleep(1+delay/10);
      // loop for incoming data forever
      while(1) {
            //generate message for units
            MessagesGenerator();

            //deliver generated messages to storages
            MessagesMultiplexer();

            // detect data-dependent services and wake up them
            dependent=Services->getServiceNextByType(SERVICE_LOGIN, NULL); if (dependent) dependent->Wakeup();
            dependent=Services->getServiceNextByType(SERVICE_QUOTA, NULL); if (dependent) dependent->Wakeup();
            dependent=Services->getServiceNextByType(SERVICE_BILLING, NULL); if (dependent) dependent->Wakeup();

            Processor->Sleep(delay);
            loop_iteration++;
      } // infinite while
      // we will never reach this point
}
//////////////////////////////////////////////////////////////////////////
void Service_Processor::Cancel(){
      unsigned tmp=lifetime=0; // this will flush all data when we will generate messages
      MessagesGenerator();
      lifetime=tmp;

      //deliver generated messages to storages
      MessagesMultiplexer();
}
//////////////////////////////////////////////////////////////////////////
void Service_Processor::ShowCfg(struct cli_def *cli, u_char flags){

      if(delay != PROCESSOR_DELAY) cli_print(cli, "lookup-delay %d", delay);
      if(lifetime != PROCESSOR_LIFETIME) cli_print(cli, "flow-lifetime %d", lifetime);

      // first, policy rules
      PolicyL->ShowConfig(cli, flags);

      // default parameters
      if (def && def->ap) {
            cli_bufprint(cli, "default acct-policy");
            def->ap->ListForCfg(cli, flags);
            cli_bufprint(cli, "\n");

      }
      if (def && def->fp) {
            cli_bufprint(cli, "default fw-policy");
            def->fp->ListForCfg(cli, flags);
            cli_bufprint(cli, "\n");
      }
      // restrict parameters
      cli_print(cli, "restrict all %s local %s", restrict_all?"drop":"pass", restrict_local?"drop":"pass");

      // auto-assign ip addresses
      for(AutoAssignEntry *e=auto_assign; e!=NULL; e=e->next ) {
            char tmp[32], buf[32];
            cli_print(cli, "auto-assign %s %s",
                        inet_ntop(AF_INET, &(e->start), buf, 32),
                        inet_ntop(AF_INET, &(e->stop), tmp, 32));
      }

      // auto-units
      for(AutoUnitsEntry *e=auto_units; e!=NULL; e=e->next ) {
            cli_bufprint(cli, "auto-units %u", e->id);
            if (e->naming==AU_NAMING_BY_DNS)
                  cli_bufprint(cli, " type %s naming by-dns",
                        e->type==AU_TYPE_HOST?"host":"user");
            else
                  cli_bufprint(cli, " type %s naming prefix%u %s",
                        e->type==AU_TYPE_HOST?"host":"user", e->naming, e->prefix);
            if (e->put_to_group && Units->getUnit(e->put_to_group))
                  cli_bufprint(cli, " group %s", e->put_to_group);
            cli_bufprint(cli, "\n");
      }

      if(!(flags&CFG_SHOW_BRIEF))
            Units->ShowConfig(cli,flags);

      if (access_script) cli_print(cli, "access-script \"%s\"", access_script);
}
//////////////////////////////////////////////////////////////////////////
int cShowProcessor(struct cli_def *cli, const char *cmd, char **argv, int argc){
      Service_Processor *cfg=Processor;
      u_short hld_size=sizeof(MsgHolder);

      MsgMgr->Usage(cli);

      cli_print(cli, "INPUT  Multiplexer");
      cli_print(cli, "\t current: %u\tmax: %u\ttotal: %lu\t(%ub)",
            Mux_in->num_items, Mux_in->max_items, Mux_in->total_items,Mux_in->num_holders*hld_size);
      cli_print(cli, "OUTPUT Multiplexers");

      Service *s=NULL;
      while((s = Services->getServiceNextByType(SERVICE_STORAGE, s))) {
            s->ShowInfo(cli);
      }
#ifdef HAVE_BILLING
      if(cfg->fifo) {
            cli_print(cli, "   Billing");
            cli_print(cli, "\t current: %u\tmax: %u\ttotal: %lu\t(%ub)",
                  cfg->fifo->num_items, cfg->fifo->max_items,
                  cfg->fifo->total_items, cfg->fifo->num_holders*hld_size);
      }
#endif
      if(cfg->def) {
            if(cfg->def->ap) {
                  cli_bufprint(cli, "Default acct policy:");
                  cfg->def->ap->List(cli);
                  cli_bufprint(cli, "\n");
            }
            if(cfg->def->fp) {
                  cli_bufprint(cli, "Default   fw policy:");
                  cfg->def->fp->List(cli);
                  cli_bufprint(cli, "\n");
            }
      }
      if (cfg->mac_control_units_checked) {
            cli_print(cli, "MAC address control checked %llu, violated %llu",
                  cfg->mac_control_units_checked, cfg->mac_control_units_violated);
      }
      return CLI_OK;
}
//////////////////////////////////////////////////////////////////////////
void sPConstructStoreOidMessage(NetUnit *u, Policy *p) {
      Message_Store *msgs;
      msgs = (Message_Store*)MsgMgr->New(MSG_STORE);
      msgs->ap=p?p->id:0;
      msgs->netunit=u?u->id:0;
      msgs->prefix='O';
      Mux_in->Push((Message*)msgs);
      aDebug(DEBUG_PROC_MUX, "P<-P O unit:%06X store oid\n", msgs->netunit);

      return;
}
//////////////////////////////////////////////////////////////////////////
void sPConstructStoreMessages(oid netunit, policy_data *pd){
      Message_Store *msg;

      for(u_char i=0;i<5;i++) {
            msg=(Message_Store*)MsgMgr->New(MSG_STORE);
            msg->ts=pd->to;
            msg->ap=pd->policy->id;
            msg->netunit=netunit;
            msg->prefix=pstat_prefix[i];
            switch(pstat_prefix[i]){
                  case 'F':
                        memcpy(msg->data, (struct pstat *)&pd->flow, sizeof (struct pstat));
                        break;
                  case 'M':
                        memcpy(msg->data, (struct pstat *)&pd->m, sizeof (struct pstat));
                        break;
                  case 'W':
                        memcpy(msg->data, (struct pstat *)&pd->w, sizeof (struct pstat));
                        break;
                  case 'D':
                        memcpy(msg->data, (struct pstat *)&pd->d, sizeof (struct pstat));
                        break;
                  case 'H':
                        memcpy(msg->data, (struct pstat *)&pd->h, sizeof (struct pstat));
                        break;
            }
            aDebug(DEBUG_PROC_MUX, "DS->P %c in unit:%06X acct:%06X from:%lu to:%lu in:%llu out:%llu\n",msg->prefix, msg->netunit, msg->ap, msg->data->from, msg->ts, msg->data->in, msg->data->out);
            Mux_in->Push((Message*)msg);
      }
}
//////////////////////////////////////////////////////////////////////////
int cAutoAssignIP(struct cli_def *cli, char **param, int argc, u_char no_flag) {
      in_addr start;
      in_addr stop;
      AutoAssignEntry *e,*p=NULL;

      if (param[1]) inet_aton(param[1], &start);
      if (param[2]) inet_aton(param[2], &stop);

      for(e=Processor->auto_assign; e!=NULL; e=e->next) {
            if(e->start.s_addr==start.s_addr && e->stop.s_addr==stop.s_addr) break;
            p=e;
      }

      if(no_flag) {
            if(!e) return CLI_OK; // nothing to remove
            if(Processor->auto_assign==e) Processor->auto_assign=e->next;
            else p->next=e->next;
            aFree(e);
            cli_error(cli, "auto-assign %s - %s removed", param[1], param[2]);
      } else {
            if(e) return CLI_OK; // already exist
            e=(AutoAssignEntry*)aMalloc(sizeof(AutoAssignEntry));
            e->start.s_addr=start.s_addr;
            e->stop.s_addr=stop.s_addr;
            if(Processor->auto_assign==NULL) Processor->auto_assign=e;
            else p->next=e;
            e->next=NULL;

            char buf1[32], buf2[32];
            cli_error(cli, "auto-assign from %s to %s",
                  inet_ntop(AF_INET, &start, buf1, 32), inet_ntop(AF_INET, &stop, buf2, 32));
        }

      return CLI_OK;
}
//////////////////////////////////////////////////////////////////////////
int cAutoUnits(struct cli_def *cli, char **param, int argc, u_char no_flag) {
      AutoUnitsEntry *e,*p=NULL;

      u_char id=strtol(param[1], NULL, 10);
      if (!id) return CLI_OK;

      for(e=Processor->auto_units; e!=NULL; e=e->next) {
            if(e->id==id) break;
            p=e;
      }

      if (no_flag) {
            if(!e) return CLI_OK; // nothing to remove
            if(Processor->auto_units==e) Processor->auto_units=e->next;
            else p->next=e->next;
            aFree(e->put_to_group);
            aFree(e->prefix);
            aFree(e);
            cli_error(cli, "auto-units %d removed", id);
      } else {
            if(!e)
                  e = (AutoUnitsEntry*)aMalloc(sizeof(AutoUnitsEntry));

            if (STRARG(param[2], "type")) {
                  if (STREQ("host", param[3]))
                        e->type=AU_TYPE_HOST;
                  else if (STREQ("user", param[3]))
                        e->type=AU_TYPE_USER;
            }

            int k=6;
            if (STREQ(param[4], "naming")) {
                  if (STREQ(param[5], "by-dns"))
                        e->naming=AU_NAMING_BY_DNS;
                  else {
                        if (STREQ(param[5], "prefix1"))
                              e->naming=AU_NAMING_PREFIX1;
                        else if (STREQ(param[5], "prefix2"))
                              e->naming=AU_NAMING_PREFIX2;
                        if (argc>6) {
                              e->prefix=set_string(param[6]);
                              k++;
                        }
                  }
            }

            // we cannot deal with group ID or check group name since units are not defined yet
            if (STRARG(param[k], "group"))
                  e->put_to_group=set_string(param[k+1]);

            if(e->id)
                  cli_error(cli, "auto-units %u modified, type %u, naming %u, gr=%s",
                        id, e->type, e->naming, e->put_to_group?e->put_to_group:"(no)");
            else {
                  e->id=id;
                  if(Processor->auto_units==NULL)
                        Processor->auto_units=e;
                  else p->next=e;
                  e->next=NULL;
                  cli_error(cli, "auto-units %u added, type %u, naming %u, gr=%s",
                        id, e->type, e->naming, e->put_to_group?e->put_to_group:"(no)");
            }
      }
      return CLI_OK;
}

void CreateAutoUnit(oid parent_id, in_addr addr) {
      AutoUnitsEntry *e;
      NetUnit *net=(NetUnit*)Units->getById(parent_id);

      if(!net) return;

      //we need this to protect parent being removed in operation
      netams_rwlock_rdlock(&Units->rwlock);

      u_char id=((NetUnit_net*)net)->auto_units_id;

        for(e=Processor->auto_units; e!=NULL; e=e->next) {
                if(e->id==id) break;
        }

      if(!e) {
            netams_rwlock_unlock(&Units->rwlock);
            return;
      }

      NetUnit *u=NULL;
      au_type_enum type = e->type;
    au_naming_enum naming = e->naming;
    char *prefix = e->prefix;

      if (type == AU_TYPE_HOST) {
            u=new NetUnit_host();
            ((NetUnit_host*)u)->ip.s_addr=addr.s_addr;

      }
      else if (type == AU_TYPE_USER) {
            u=new NetUnit_user();
            ((NetUnit_user*)u)->ip.s_addr=addr.s_addr;

      }

      //set acct and fw policy acording of parent net
      if(net->ap) net->ap->SetForUnit(POLICY_ACCT, u);
      if(net->fp) net->fp->SetForUnit(POLICY_FW, u);

      netams_rwlock_unlock(&Units->rwlock);

      u->id=newOid(0);
      char a[32], *b;
      char buf[32];
      bzero(a, 31); bzero(buf, 31);

      inet_ntop(AF_INET, &addr, a, 32);

      aLog(D_WARN, "auto-creating unit %06X, ip=\"%s\"\n", u->id, a);

      if (naming == AU_NAMING_BY_DNS) {
            struct hostent *hp;
            hp=gethostbyaddr((const char *)&addr, sizeof addr, AF_INET);
            if (hp) {
                  u->name=set_string(hp->h_name);
            }
            else { // DNS failed!
                  print_to_string( &u->name, "%s", a);
            }
      }
      else if (naming == AU_NAMING_PREFIX1) {
            strncpy(buf, strrchr(a, '.')+1, 3);
            print_to_string( &u->name, "%s%s", prefix, buf);
      }
      else if (naming == AU_NAMING_PREFIX2) {
            b=strchr(a, '.');
            strncpy(buf, strchr(b+1, '.')+1, 7);
            print_to_string( &u->name, "%s%s", prefix, buf);
      }

      if (e->put_to_group) {
            NetUnit *parent = Units->getUnit(e->put_to_group);
            if (parent && parent->type==NETUNIT_GROUP)
                  Units->Unit2Group(u, (NetUnit_group*)parent, ADD);
            aLog(D_WARN, "\tthis unit %s (%06X) was put to group %s (%06X)\n", u->name, u->id, parent->name?parent->name:"<\?\?>", parent->id);

      }

      //obviously the config was changed
      if (is_running) gettimeofday(&when_config_changed, NULL);

      sPConstructStoreOidMessage(u);

      //copy ds list
      u_char *dsl;
      ELIST_FOR_EACH(net->dslist, dsl)
            u->setDSList(*dsl);

      aLog(D_WARN, "auto-creating unit %s (%06X) for net %s (%06X)\n", u->name, u->id, net->name, net->id);

      u->unit2trees(ADD);
      Units->Insert(u);
}
//////////////////////////////////////////////////////////////////////////
int cDefault(struct cli_def *cli, char **param, int argc, u_char no_flag){
      u_char i=1;

      if (STRARG(param[1], "acct-policy")) {
            if(!Processor->def)
                  Processor->def = new NetUnit(NETUNIT_UNKNOWN);
            PolicyAdd(Processor->def, &i, POLICY_ACCT, cli, param, no_flag);
      }
      else if (STRARG(param[1], "fw-policy")) {
            if(!Processor->def)
                  Processor->def = new NetUnit(NETUNIT_UNKNOWN);
            PolicyAdd(Processor->def, &i, POLICY_FW, cli, param, no_flag);
      }
      if(is_running)
            cli_error(cli, "WARNING: New default policies begin to work AFTER reload!!!");

      return CLI_OK;
}
//////////////////////////////////////////////////////////////////////////
int cRestrict(struct cli_def *cli, char **param, int argc, u_char no_flag){
      Service_Processor *cfg=(Service_Processor*)cli->service;

      for(u_char i=1; i<argc; i+=2) {
            if (STREQ(param[i], "all")) {
                  if (STREQ(param[i+1], "drop")){
                        cfg->restrict_all=DROP;
                        cli_error(cli, "restricting ALL traffic to DROP");
                  }
                  else if (STREQ(param[i+1], "pass")){
                        cfg->restrict_all=PASS;
                        cli_error(cli, "restricting ALL traffic to PASS");
                  }
            }
            else if (STREQ(param[i], "local")) {
                  if (STREQ(param[i+1], "drop")){
                        cfg->restrict_local=DROP;
                        cli_error(cli, "restricting LOCAL traffic to DROP");
                  }
                  else if (STREQ(param[i+1], "pass")){
                        cfg->restrict_local=PASS;
                        cli_error(cli, "restricting LOCAL traffic to PASS");
                  }
            }
      }
      FW_CHECK_CHANGED(time(NULL))
      return CLI_OK;
}
//////////////////////////////////////////////////////////////////////////////////////////
void Service_Processor::MessagesGenerator() {
      u_char fill=0;
      struct time_counters tc;
      time_t now;
      struct timeval start, stop;
      unsigned long len;

      now=time(NULL);
      gettimeofday(&start, NULL);

      if (unsigned(now - aGetActual('H',now)) <= 2*delay) {
            fill=1;
            PrepareTimeCounters(&tc);
      }

      netams_rwlock_rdlock(&Units->rwlock);

      for (NetUnit *u=(NetUnit*)Units->root; u!=NULL; u=(NetUnit*)u->next) {
            if(!u->ap) continue; // no need to check unit without account policy
            netams_rwlock_wrlock(&u->ap->rwlock);
            for (policy_data *pd=u->ap->root; pd!=NULL; pd=pd->next) {
                  if ( fill || (unsigned(now-pd->flow.from) >= lifetime) ) {
                        if( pd->flow.in!=0 || pd->flow.out!=0 ) {
                              //update counters for current flow
                              len=pd->flow.in;
                              pd->h.in+=len;
                              pd->d.in+=len;
                              pd->w.in+=len;
                              pd->m.in+=len;

                              len=pd->flow.out;
                              pd->h.out+=len;
                              pd->d.out+=len;
                              pd->w.out+=len;
                              pd->m.out+=len;

                              pd->to=now;
                              //construct messages for policy_data pd of unit u
                              sPConstructStoreMessages(u->id,pd);

                              pd->flow.in=pd->flow.out=0;
                        }
                        pd->flow.from=now;

                        if (fill) FillTimeCounters(pd,&tc);
                  }
            }
            netams_rwlock_unlock(&u->ap->rwlock);
      }
      netams_rwlock_unlock(&Units->rwlock);

      gettimeofday(&stop, NULL);

      aDebug(DEBUG_PROC_MUX, "lookup takes %.4f  seconds\n", ((double)(stop.tv_usec-start.tv_usec))/1000000);
}
//////////////////////////////////////////////////////////////////////////
void Service_Processor::MessagesMultiplexer() {
      Message *msg;
      Message_Store *smsg;

      Service *st;
      Service **slist, **rlist;

      slist = aStorageGetAcceptedAll(ST_CONN_SUMMARY);
      rlist = aStorageGetAcceptedAll(ST_CONN_RAW);

      while((msg=Mux_in->TryPop())) {
            //we have only store messages here
            smsg=(Message_Store*)msg;

            switch (smsg->prefix){
                  case 'M':
                  case 'W':
                  case 'D':
                  case 'H':
                  case 'O':
                        //here we depends that item list ordered and SUMMARY goes first
                        ELIST_FOR_EACH(slist, st) {
                              st->ProcessMessage(msg);
                              aDebug(DEBUG_PROC_MUX, "P->ST %c st:%u unit:%06X acct:%06X from:%lu to:%lu\n",
                                     smsg->prefix, st->instance, smsg->netunit, smsg->ap, smsg->data->from, smsg->ts);
                        }
                        break;
                  case 'F':
                        ELIST_FOR_EACH(rlist, st) {
                              st->ProcessMessage(msg);
                              aDebug(DEBUG_PROC_MUX, "P->ST %c st:%u unit:%06X acct:%06X from:%lu to:%lu\n",
                                    smsg->prefix, st->instance, smsg->netunit, smsg->ap, smsg->data->from, smsg->ts);
                        }
                        if (fifo)
                              fifo->Push(msg); //pass F message to billing to be counted
                        break;
            } //switch

            //pop message from Mux
            //we need to use TryPop() before cause possible race
            //when pushing to storages
            Mux_in->Pop();

      } // all messages processed

      //wakeup all storages
      ELIST_FOR_EACH(slist, st) {
            st->Wakeup();
      }
      ELIST_FREE(slist);

      ELIST_FOR_EACH(rlist, st) {
            //do not wakeup already waked
            if(!((Service_Storage_Interface*)st)->isAccepted(ST_CONN_SUMMARY))
                  st->Wakeup();
      }
      ELIST_FREE(rlist);
}
//////////////////////////////////////////////////////////////////////////
int cMac(struct cli_def *cli, const char *cmd, char **argv, int argc) {
      u_char mac_control_block=0, mac_control_alert=0, mac_control_fixate=0, mac_control_control=0;

      if (STREQ(argv[1], "control")){
            for (u_char i=1; i<argc; i++) {
                  if (STREQ(argv[i], "alert"))
                        mac_control_alert=1;
                  else if (STREQ(argv[i], "block"))
                        mac_control_block=1;
            }
            mac_control_control=1;
      } else if (STREQ(argv[1], "fixate"))
            mac_control_fixate=1;

      unsigned num_units;
      // make a snapshot of units [id:ip:mac] table
      netams_rwlock_rdlock(&Units->rwlock);
      num_units=Units->getNum();

      MacControlEntry *table = (MacControlEntry*)aMalloc(num_units * sizeof (struct MacControlEntry));
      NetUnit *d;
      int i;
      MacControlEntry *t;
      for(d=(NetUnit*)Units->root, i=0; d!=NULL; d=(NetUnit*)d->next, i++)    {
            t=&table[i]; //*sizeof(struct MacControlEntry);
            t->id = d->id;
            t->state = MAC_CTL_SKIP;
            t->sp = d->sys_policy;
            if (!t->sp || t->sp&SP_DENY_MAC) { // do check only if no syspolicy or syspolicy==SP_MAC
                  if (d->type==NETUNIT_HOST) {
                        NetUnit_host *h = (NetUnit_host *)d;
                        memcpy(&t->ip, &h->ip, sizeof (struct in_addr));
                        if (h->mac) {
                              memcpy(&t->mac, h->mac, sizeof (struct ether_addr));
                              t->state = MAC_CTL_CHECK;
                        }
                  }
                  else if (d->type==NETUNIT_USER) {
                        NetUnit_user *h = (NetUnit_user *)d;
                        memcpy(&t->ip, &h->ip, sizeof (struct in_addr));
                        if (h->mac) {
                              memcpy(&t->mac, h->mac, sizeof (struct ether_addr));
                              t->state = MAC_CTL_CHECK;
                        }
                  } // do check only if no syspolicy or syspolicy==SP_MAC
                  if (mac_control_fixate) t->state = MAC_CTL_FIXATE;
            }
      }
      netams_rwlock_unlock(&Units->rwlock);

      // cycle through system ARP cache and do check
      // pizjeno from our s_login.c
      #if defined(FREEBSD) || defined(OPENBSD)
            char *buffer;
            struct ether_addr *e;
            int mib[6];
            size_t needed;
            char *lim, *next;
            struct rt_msghdr *rtm;
            struct sockaddr_inarp *sin;
            struct sockaddr_dl *sdl;

            mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_FLAGS; mib[5] = RTF_LLINFO;
            if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0
            || (buffer = (char*)aMalloc(needed)) == NULL
            || (sysctl(mib, 6, buffer, &needed, NULL, 0) < 0)) {
                  cli_error(cli, "failed to check arp table\n");
                  return CLI_OK;
            }

            lim = buffer + needed;

            for (next = buffer; next < lim; next += rtm->rtm_msglen) {
                  rtm = (struct rt_msghdr *)next;
                  sin = (struct sockaddr_inarp *)(rtm + 1);
                  sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin_len));
                  e=(struct ether_addr*)LLADDR(sdl);
                  aMacControl_check(table, num_units, &sin->sin_addr, e);
            }
            aFree(buffer);
      #endif

      #if defined(LINUX)
            char buffer[256];
            char b_ip[32], b_hw[32], b_trash[32];
            struct in_addr in;
            struct ether_addr *e;
            FILE *arp;

            arp=fopen("/proc/net/arp", "rt");
            if (arp==NULL) {
                  cli_error(cli, "failed to open /proc/net/arp\n");
                  return CLI_OK;
            }

            if (NULL==fgets(buffer, 255, arp)) goto FINISH_LINUX_CHECK_P;

            while (!feof(arp)){
                  if (NULL==fgets(buffer, 255, arp)) break;
                  bzero(b_ip, 31); bzero(b_hw, 8);
                  sscanf(buffer, "%s%s%s%s", b_ip, b_trash, b_trash, b_hw);
                  if (inet_aton(b_ip, &in) && (e=ether_aton(b_hw)))
                        aMacControl_check(table, num_units, &in, e);
            }

      FINISH_LINUX_CHECK_P:
            fclose(arp);
      #endif

      // check the table again for matched violators & enforce
      NetUnit *u;
      struct ether_addr **mac;
      unsigned mac_fixate_chk=0, mac_fixate_upd=0;
      char buf[32];

      //if (mac_control_fixate) netams_rwlock_wrlock(&Units->rwlock);
      for (unsigned i=0; i<num_units; i++){
            t=&table[i];//*sizeof(struct MacControlEntry);
            if (t->state!=MAC_CTL_SKIP) Processor->mac_control_units_checked++;
            if (t->state==MAC_CTL_VIOL) {
                  aLog(D_WARN, "MAC violated OID %06X IP %s ARP_MAC %s\n", t->id, inet_ntop(AF_INET, &t->ip, buf, 32), ether_ntoa(&t->mac));
                  LogEvent(SYSTEM, t->id, 0, 0, "MAC FAIL OID %06X IP %s ARP_MAC %s\n", t->id, buf, ether_ntoa(&t->mac));
                  Processor->mac_control_units_violated++;
                  u=(NetUnit*)Units->getById(t->id);
                  if (mac_control_block) u->SetSysPolicy(SP_DENY_MAC, ADD);
                  if (mac_control_alert) aMacControl_alert(t, u, 1);
            }
            else if (t->state==MAC_CTL_BACK) {
                  aLog(D_WARN, "MAC restored OID %06X IP %s ARP_MAC %s\n", t->id, inet_ntop(AF_INET, &t->ip, buf, 32), ether_ntoa(&t->mac));
                  LogEvent(SYSTEM, t->id, 0, 0, "MAC RESTORED OID %06X IP %s ARP_MAC %s\n", t->id, buf, ether_ntoa(&t->mac));
                  u=(NetUnit*)Units->getById(t->id);
                  if (mac_control_block) u->SetSysPolicy(SP_DENY_MAC, REMOVE);
                  if (mac_control_alert) aMacControl_alert(t, u, 0);
            }
            else if (t->state==MAC_CTL_UPDATE) {
                  mac_fixate_chk++;
                  u=(NetUnit*)Units->getById(t->id);
                  if (u->type==NETUNIT_HOST) mac = &((NetUnit_host *)u)->mac;
                  else if (u->type==NETUNIT_USER) mac = &((NetUnit_user *)u)->mac;
                  else continue;
                  if (*mac==NULL) {
                        *mac=(struct ether_addr *)aMalloc(sizeof (struct ether_addr));
                        mac_fixate_upd++;
                  }
                  memcpy(*mac, &t->mac, sizeof (struct ether_addr));
            }
      }
      if (mac_control_fixate) {
            //netams_rwlock_unlock(&Units->rwlock);
            cli_error(cli, "MAC fixate: checked %u(%u) units, updated %u", mac_fixate_chk, num_units, mac_fixate_upd);
      }
      aFree(table);
      return CLI_OK;
}
//////////////////////////////////////////////////////////////////////////
void aMacControl_check(MacControlEntry* table, unsigned num_units, in_addr *ip, struct ether_addr* mac){
      MacControlEntry *t;
      int check_flag;
      for (unsigned i=0; i<num_units; i++){
            t=&table[i]; //*sizeof(struct MacControlEntry);
            if (t->state==MAC_CTL_CHECK) { // this unit has some MAC to do checking
                  if (!memcmp(&t->ip, ip, sizeof (struct in_addr))) { // IP matched
                        check_flag=memcmp(&t->mac, mac, sizeof (struct ether_addr));
                        //printf("ip=%s mac=%s state=%u sp=%d check=%d\n", inet_ntoa(*ip), ether_ntoa(mac), t->state, t->sp, check_flag);
                        if (check_flag && !(t->sp&SP_DENY_MAC)) { // violated
                              t->state=MAC_CTL_VIOL;
                              //printf("\tviolated! %d\n", t->state);
                              memcpy(&t->mac, mac, sizeof (struct ether_addr)); // put actual MAC (from ARP) onto the table
                        } else if (!check_flag && t->sp&SP_DENY_MAC) { // restored back
                              t->state=MAC_CTL_BACK;
                        }
                  }
            } else if (t->state==MAC_CTL_FIXATE) { // just copy MAC onto table
                  if (!memcmp(&t->ip, ip, sizeof (struct in_addr))) { // IP matched
                        memcpy(&t->mac, mac, sizeof (struct ether_addr));
                        t->state=MAC_CTL_UPDATE;
                  }
            }
      }
}
//////////////////////////////////////////////////////////////////////////
void aMacControl_alert(MacControlEntry* t, NetUnit *u, u_char dir){
      // pizjeno from our s_quota.c
      Service* alerter=Services->getServiceNextByType(SERVICE_ALERTER,NULL);
      if (!alerter) return;

      Message *msg;
      alert *al;

      msg = MsgMgr->New(MSG_ALERT);

      al=((Message_Alert*)msg)->al;
      al->sent=time(NULL);
      al->expired=al->sent+60*60; // one hour expire
      al->report_id=0x06101;
      al->tries=0;

      // recipients are all users with UPERM_ALL
      User *d;
      u_char i=0;
      netams_rwlock_rdlock(&Users->rwlock);
      for(d=(User*)Users->root; d!=NULL && i<MAX_ID_PER_ALERT; d=(User*)d->next)    {
            if (d->permissions==UPERM_ALL && !d->hidden) {
                  al->user_id[i]=d->id;
                  aDebug(DEBUG_ALERT, "USER (mac control) RCPT %u added: %06X\n", i+1, d->id);
                  i++;
            }
      }
      netams_rwlock_unlock(&Users->rwlock);

      if (i==0) {
            aDebug(DEBUG_ALERT, "alert (mac control) abandoned because of no recipients\n");
            return; // nowhere to send
      }

      char *subject, *message, buffer[255];
      subject=message=NULL;

      timeU2T(time(NULL), buffer);

      print_to_string(&message, "This is automatically generated report by %s\nTime: %s\n\n",
            SHOW_VERSION, buffer);

      switch (dir) {
            case 0:
                  print_to_string(&subject, "NeTAMS MAC Control: unit %s MAC returned", u->name?u->name:"<>");
                  print_to_string(&message, "NeTAMS MAC Control service have detected hardware (MAC) address returned:\n");
                  break;
            case 1:
                  print_to_string(&subject, "NeTAMS MAC Control: unit %s MAC violation", u->name?u->name:"<>");
                  print_to_string(&message, "NeTAMS MAC Control service have detected hardware (MAC) address violation:\n");
                  break;
      }

      print_to_string(&message, "\nOID:\t\t\t%06X\nName:\t\t\t%s\nIP:\t\t\t%s\nARP MAC:\t\t%s\n", u->id, u->name?u->name:"<\?\?>", inet_ntop(AF_INET, &t->ip, buffer, 32), ether_ntoa(&t->mac));
      //kill(getpid(), 17);
      if (u->type==NETUNIT_HOST) {
            NetUnit_host* h = (NetUnit_host*)u;
            if (h->mac) print_to_string(&message, "Allowed MAC:\t%s\n", ether_ntoa(h->mac));
      }
      else if (u->type==NETUNIT_USER) {
            NetUnit_user* h = (NetUnit_user*)u;
            if (h->mac) print_to_string(&message, "Allowed MAC:\t%s\n", ether_ntoa(h->mac));
      }

      al->data=NULL;
      print_to_string(&al->data, "%s\n%s\n", subject, message);
      aFree(subject); aFree(message);

      aDebug(DEBUG_ALERT, "alert (mac control) %u complete, data is %u bytes\n", al->alert_id, strlen(al->data));

      alerter->ProcessMessage(msg);
}
//////////////////////////////////////////////////////////////////////////
void FillStatData(void *res, void *row, char* (*getRowData)(void*, void* , u_char)) {
        oid id;
        char prefix;
        time_t t_from;
        NetUnit *u;
        pstat *ps=NULL;

        sscanf(getRowData(res, row, 0), "%u", &id);     //netunit_oid
        if(!(u=(NetUnit*)Units->getById(id))) return;
        if(u->ap == NULL) return;

        sscanf(getRowData(res, row, 1), "%u", &id);     //policy_oid
        sscanf(getRowData(res, row, 2), "%lu", (u_long*)&t_from);
        sscanf(getRowData(res, row, 3), "%c", &prefix);

        netams_rwlock_wrlock(&u->ap->rwlock);
        for(policy_data *pd=u->ap->root; pd!=NULL; pd=pd->next) {
                if(pd->policy->id != id) continue;

                switch(prefix) {
                        case 'M':
                                ps = &pd->m;
                                break;
                        case 'W':
                                ps = &pd->w;
                                break;
                        case 'D':
                                ps = &pd->d;
                                break;
                        case 'H':
                                ps = &pd->h;
                                break;
                }

                if(ps->from == t_from) {
                        unsigned long long bytes;
                        sscanf(getRowData(res, row, 4), "%llu", &bytes); ps->in += bytes;
                        sscanf(getRowData(res, row, 5), "%llu", &bytes); ps->out+= bytes;
                }
                break;
        }
        netams_rwlock_unlock(&u->ap->rwlock);
        if(ps)
                        aDebug(DEBUG_PROC_MUX, "PROC<-ST/%c bytes from=%lu in=%llu out=%llu\n",
                        prefix, (unsigned long)ps->from,ps->in, ps->out);
}
//////////////////////////////////////////////////////////////////////////

Generated by  Doxygen 1.6.0   Back to index