#include #include #include #include #include #include #include #include #include #include #include #include "userfirewall.m" static bool debug=false; static bool dotest=false; static bool setup=false; static void n_debug (const char *ctl, ...) { if (debug){ va_list list; va_start (list,ctl); vfprintf (stderr,ctl,list); va_end (list); } } class ARP_ENTRY: public ARRAY_OBJ{ public: unsigned long ip; char dev[20]; bool is_needed; /*~PROTOBEG~ ARP_ENTRY */ public: ARP_ENTRY (const char *_ip, const char *_dev); ARP_ENTRY (unsigned long _ip, const char *_dev); /*~PROTOEND~ ARP_ENTRY */ }; PUBLIC ARP_ENTRY::ARP_ENTRY(const char *_ip, const char *_dev) { is_needed = false; ip = ipnum_aip2l (_ip); strcpy (dev,_dev); } PUBLIC ARP_ENTRY::ARP_ENTRY(unsigned long _ip, const char *_dev) { is_needed = false; ip = _ip; strcpy (dev,_dev); } class ARP_ENTRIES: public ARRAY{ /*~PROTOBEG~ ARP_ENTRIES */ public: int check (unsigned long ip, const char *dev); ARP_ENTRY *getitem (int no)const; ARP_ENTRY *locate (unsigned long ip, const char *dev); int remove_old (void); /*~PROTOEND~ ARP_ENTRIES */ }; PUBLIC ARP_ENTRY *ARP_ENTRIES::getitem (int no) const { return (ARP_ENTRY*)ARRAY::getitem(no); } PUBLIC ARP_ENTRY *ARP_ENTRIES::locate (unsigned long ip, const char *dev) { ARP_ENTRY *ret = NULL; for (int i=0; idev,dev)==0 && a->ip == ip){ ret = a; break; } } return ret; } PUBLIC int ARP_ENTRIES::check(unsigned long ip, const char *dev) { int ret = 0; ARP_ENTRY *a = locate (ip,dev); if (a==NULL){ char ipa[20]; ipnum_ip2a (ip,ipa); char cmd[100]; snprintf (cmd,sizeof(cmd)-1,"/sbin/arp -i %s -Ds %s %s pub" ,dev,ipa,dev); if (dotest){ printf ("%s\n",cmd); }else if (setup){ ret = system (cmd); } // We add it to avoid doing it several time since // several firewall rules may related to both hosts a = new ARP_ENTRY (ip,dev); a->is_needed = true; add (a); }else{ a->is_needed = true; } return ret; } /* Remove old arp entries, not associated with any firewall rules */ PUBLIC int ARP_ENTRIES::remove_old() { int ret = 0; for (int i=0; iis_needed){ char cmd[100],ips[20]; ipnum_ip2a (a->ip,ips); snprintf (cmd,sizeof(cmd)-1,"/sbin/arp -i %s -d %s pub",a->dev,ips); if (dotest){ printf ("%s\n",cmd); }else if (setup){ ret |= system (cmd); } } } return ret; } class IP_RELATION: public ARRAY_OBJ{ public: unsigned long ip1; unsigned long msk1; unsigned long ip2; unsigned long msk2; /*~PROTOBEG~ IP_RELATION */ public: IP_RELATION (unsigned long _ip1, unsigned long _msk1, unsigned long _ip2, unsigned long _msk2); /*~PROTOEND~ IP_RELATION */ }; PUBLIC IP_RELATION::IP_RELATION( unsigned long _ip1, unsigned long _msk1, unsigned long _ip2, unsigned long _msk2) { ip1 = _ip1; msk1 = _msk1; ip2 = _ip2; msk2 = _msk2; } class IP_RELATIONS: public ARRAY{ /*~PROTOBEG~ IP_RELATIONS */ public: IP_RELATION *getitem (int no)const; void remove_dup (void); void sort (void); /*~PROTOEND~ IP_RELATIONS */ }; PUBLIC IP_RELATION *IP_RELATIONS::getitem (int no) const { return (IP_RELATION*)ARRAY::getitem(no); } static int cmp_by_ip (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { IP_RELATION *i1 = (IP_RELATION*)o1; IP_RELATION *i2 = (IP_RELATION*)o2; int ret = i1->ip1 - i2->ip1; if (ret != 0){ ret = i1->ip2 - i2->ip2; } return ret; } PUBLIC void IP_RELATIONS::sort() { ARRAY::sort (cmp_by_ip); } PUBLIC void IP_RELATIONS::remove_dup() { for (int i=0; iip1 == i2->ip1 && i1->ip2 == i2->ip2){ remove_del (i); i--; } } } class INTERFACE: public ARRAY_OBJ{ public: char dev[20]; unsigned long ip; unsigned long mask; unsigned long hostip; // Server attached to this host /*~PROTOBEG~ INTERFACE */ public: INTERFACE (const char *_dev); bool match (unsigned long _ip, unsigned long _msk); /*~PROTOEND~ INTERFACE */ }; static int ifconfig_ioctl( int fd, const char *ifname, int cmd, struct ifreq &ifr) { strcpy(ifr.ifr_name, ifname); return ioctl(fd, cmd,&ifr); } PUBLIC INTERFACE::INTERFACE(const char *_dev) { hostip = 0; ip = 0; mask = 0xffffffff; strcpy (dev,_dev); int skfd = socket(AF_INET, SOCK_DGRAM, 0); if (skfd != -1){ struct ifreq ifr; if (ifconfig_ioctl(skfd, dev, SIOCGIFFLAGS, ifr) >= 0){ if (ifconfig_ioctl(skfd,dev,SIOCGIFADDR, ifr) >= 0){ struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr; ip = htonl(sin->sin_addr.s_addr); if (ifconfig_ioctl(skfd, dev,SIOCGIFNETMASK, ifr) >= 0) { struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_netmask; mask = htonl(sin->sin_addr.s_addr); } } } close (skfd); } } PUBLIC bool INTERFACE::match ( unsigned long _ip, unsigned long _msk) { return _ip == (hostip & _msk); } class INTERFACES: public ARRAY{ /*~PROTOBEG~ INTERFACES */ public: void fillhost (void); INTERFACE *getitem (int no)const; INTERFACE *locate (const char *dev); INTERFACE *locatehost (unsigned long ip); void sort (void); /*~PROTOEND~ INTERFACES */ }; PUBLIC INTERFACE *INTERFACES::getitem (int no) const { return (INTERFACE*)ARRAY::getitem(no); } /* Locate the HOST attached to this vlan interface Return NULL or the INTERFACE object. ip/msk may describe a network instead of a host */ PUBLIC INTERFACE *INTERFACES::locatehost ( unsigned long ip) { INTERFACE *ret = NULL; for (int i=0; ihostip == ip){ ret = inter; break; } } return ret; } PUBLIC INTERFACE *INTERFACES::locate (const char *dev) { INTERFACE *ret = NULL; for (int i=0; idev,dev)==0){ ret = inter; break; } } return ret; } /* Assign a server to each vlan device */ PUBLIC void INTERFACES::fillhost () { INTERFACES *inters; glocal.inters = this; ("/proc/net/route",false); if (noline > 0){ char dev[20],dummy[20]; unsigned long ip,mask; if (sscanf (line,"%s %08lx %s %s %s %s %s %08lx",dev,&ip ,dummy,dummy,dummy,dummy,dummy,&mask)==8 && mask == 0xffffffff){ INTERFACE *inter = glocal.inters->locate (dev); if (inter != NULL){ ip = ntohl (ip); // n_debug ("Assign server %08lx to interface %s\n",ip,dev); inter->hostip = ip; } } } return 0; } static int interfaces_cmp_by_dev (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { INTERFACE *i1 = (INTERFACE*)o1; INTERFACE *i2 = (INTERFACE*)o2; return strcmp(i1->dev,i2->dev); } PUBLIC void INTERFACES::sort() { return ARRAY::sort(interfaces_cmp_by_dev); } static int gen_readarp (ARP_ENTRIES &arps) { ARP_ENTRIES *arps; int ret; glocal.ret = 0; glocal.arps = &arps; ("/proc/net/arp",false); glocal.ret = -1; if (noline > 0){ char ip[20],dummy[20],dev[20]; unsigned flags; if (sscanf (line,"%s %s %x %s %s %s",ip,dummy,&flags,dummy,dummy,dev)==6){ // n_debug ("arpline :%s: %02x :%s:\n",ip,flags,dev); if ((flags & 8)!=0){ // && strncmp(dev,"vlan",4)==0){ glocal.arps->add (new ARP_ENTRY(ip,dev)); } } } return 0; return glocal.ret; } /* Read the firewall rules. This tells us which host is allowed to talk to which other host. */ static int gen_readfw (IP_RELATIONS &rels) { IP_RELATIONS *rels; int ret; glocal.ret = 0; glocal.rels = &rels; ("/proc/net/ip_fwchains",false); glocal.ret = -1; if (noline > 0){ // By checking only ACCEPT rules, we avoid the dispatch // rules to jump to sub-chain. They are generally general // 0.0.0.0 -> something, jump to the chain specific to that thing if (strstr(line,"ACCEPT")!=NULL){ char dummy[20]; unsigned long ip1,ip2,msk1,msk2; if (sscanf (line,"%s %08lx/%08lx->%08lx/%08lx",dummy ,&ip1,&msk1,&ip2,&msk2)==5){ glocal.rels->add (new IP_RELATION(ip1,msk1,ip2,msk2)); } } } return 0; return glocal.ret; } /* Read the list of vlan interfaces */ static int gen_readinter (INTERFACES &inters) { INTERFACES *inters; int ret; glocal.ret = 0; glocal.inters = &inters; ("/proc/net/dev",false); glocal.ret = -1; if (noline > 0){ const char *pt = str_skip(line); const char *end = strchr(pt,':'); if (end != NULL){ int len = (int)(end-pt)+1; char dev[len]; strcpy_cut (dev,pt,len); glocal.inters->add (new INTERFACE(dev)); } } return 0; return glocal.ret; } int main (int argc, char *argv[]) { SSTRINGS clans; // Devices on which we have clients // proxy arps must be done for server int ret = (argc, argv,"userfirewall"); fprintf (stderr,MSG_U(I_USAGEPROXYARP ,"genproxyarp [ --debug ] --test\n" "genproxyarp [ --debug ] --setup\n" "\n" "Installs/Remove proxyarp entries on vlan interfaces\n" "as needed by firewall rules\n" "\n" "--clan : (client lan) A net device has client machines.\n" " Proxy arps must be done for all servers.\n" " Option may be repeated.\n" "--test : Show the command to execute\n" "--setup : Execute the commands\n" )); int ret = -1; if (strcmp(opt,"--debug")==0){ debug = true; ret = 0; }else if (strcmp(opt,"--test")==0){ dotest = true; ret = 0; }else if (strcmp(opt,"--setup")==0){ setup = true; ret = 0; }else if (strcmp(opt,"--clan")==0){ setup = true; glocal.clans.add (new SSTRING(val)); ret = 1; } return ret; int ret = -1; if (!setup && !dotest){ usage(); }else{ ARP_ENTRIES arps; IP_RELATIONS rels; INTERFACES inters; if (gen_readarp(arps)!=-1 && gen_readfw(rels)!=-1 && gen_readinter (inters)!=-1){ rels.sort(); rels.remove_dup(); inters.fillhost(); inters.sort(); if (debug){ for (int i=0; iip,ips1); printf (MSG_U(I_SHOWARP,"Arp entry %s %s\n"),ips1,e->dev); } for (int i=0; iip1,ips1); ipnum_ip2a (e->ip2,ips2); printf (MSG_U(I_SHOWREL,"Relation %s -> %s\n"),ips1,ips2); } for (int i=0; iip,ips1); ipnum_ip2a (e->hostip,ips2); ipnum_ip2a (e->mask,mask); printf (MSG_U(I_SHOWINTER,"Interface %s %s/%s -> %s\n"),e->dev,ips1,mask,ips2); } } ret = 0; // Add the proxy arp on the server vlans for the // client which need to access this server for (int i=0; imsk2 == 0xffffffff){ for (int j=0; jhostip != 0 && inter->match(e->ip1,e->msk1)){ ret |= arps.check (e->ip2,inter->dev); } } } } // Now add the proxy arp for the server if (glocal.clans.getnb() > 0){ for (int i=0; ihostip != 0){ for (int j=0; jhostip ,glocal.clans.getitem(j)->get()); } } } }else{ // We do a proxy arp for all host assigned to an interface // on all interface without a host // We have one host per vlan... for (int i=0; ihostip == 0){ unsigned long mask = inter->mask; unsigned long net = inter->ip & mask; for (int j=0; jhostip != 0 && (inter2->hostip & mask) == net ){ ret |= arps.check (inter2->hostip,inter->dev); } } } } } ret |= arps.remove_old(); } } return ret; return ret; }