#include #include "ipset.h" #include "gzip.h" #include "helpers.h" static bool addpool(ipset *ips, char **s, const char *end, int *ct) { char *p, cidr[128]; size_t l; struct cidr4 c4; struct cidr6 c6; for (p=*s; p=sizeof(cidr)) l=sizeof(cidr)-1; memcpy(cidr,*s,l); cidr[l]=0; if (parse_cidr4(cidr,&c4)) { if (!ipset4AddCidr(&ips->ips4, &c4)) { ipsetDestroy(ips); return false; } if (ct) (*ct)++; } else if (parse_cidr6(cidr,&c6)) { if (!ipset6AddCidr(&ips->ips6, &c6)) { ipsetDestroy(ips); return false; } if (ct) (*ct)++; } else DLOG_ERR("bad ip or subnet : %s\n",cidr); } } // skip remaining non-eol chars for (; pfilename) { file_mod_sig fsig; if (!file_mod_signature(hfile->filename, &fsig)) { // stat() error DLOG_PERROR("file_mod_signature"); goto unchanged; } if (FILE_MOD_COMPARE(&hfile->mod_sig,&fsig)) return true; // up to date // check if it's readable. do not destroy in-memory copy if not if (!file_open_test(hfile->filename, O_RDONLY)) { DLOG_PERROR("file_open_test"); goto unchanged; } // don't want to keep backup copy in memory - it will require *2 RAM. Problem on low-ram devices. It's better to fail ipset read than have OOM. // if a file can be opened there're few chances it can't be read. fs corruption, disk error, deleted or made inaccessible between 2 syscals ? // it's all hypotetically possible but very unlikely. but OOM is much more real problem on an embedded device if list is large enough ipsetDestroy(&hfile->ipset); if (!AppendIpset(&hfile->ipset, hfile->filename)) { ipsetDestroy(&hfile->ipset); return false; } hfile->mod_sig=fsig; } return true; unchanged: DLOG_ERR("cannot access ipset file '%s'. in-memory content remains unchanged.\n",hfile->filename); return true; } static bool LoadIpsets(struct ipset_files_head *list) { bool bres=true; struct ipset_file *hfile; LIST_FOREACH(hfile, list, next) { if (!LoadIpset(hfile)) // at least one failed bres=false; } return bres; } bool LoadAllIpsets() { return LoadIpsets(¶ms.ipsets); } static bool SearchIpset(const ipset *ips, const struct in_addr *ipv4, const struct in6_addr *ipv6) { char s_ip[INET6_ADDRSTRLEN]; bool bInSet=false; if (!!ipv4 != !!ipv6) { *s_ip=0; if (ipv4) { if (params.debug) inet_ntop(AF_INET, ipv4, s_ip, sizeof(s_ip)); if (ips->ips4) bInSet = ipset4Check(ips->ips4, ipv4, 32); } if (ipv6) { if (params.debug) inet_ntop(AF_INET6, ipv6, s_ip, sizeof(s_ip)); if (ips->ips6) bInSet = ipset6Check(ips->ips6, ipv6, 128); } DLOG("ipset check for %s : %s\n", s_ip, bInSet ? "positive" : "negative"); } else // ipv4 and ipv6 are both empty or non-empty DLOG("ipset check error !!!!!!!! ipv4=%p ipv6=%p\n",ipv4,ipv6); return bInSet; } static bool IpsetsReloadCheck(const struct ipset_collection_head *ipsets) { struct ipset_item *item; LIST_FOREACH(item, ipsets, next) { if (!LoadIpset(item->hfile)) return false; } return true; } bool IpsetsReloadCheckForProfile(const struct desync_profile *dp) { return IpsetsReloadCheck(&dp->ips_collection) && IpsetsReloadCheck(&dp->ips_collection_exclude); } static bool IpsetCheck_( const struct ipset_collection_head *ips, const struct ipset_collection_head *ips_exclude, const struct in_addr *ipv4, const struct in6_addr *ipv6, const struct in_addr *ipv4r, const struct in6_addr *ipv6r ) { struct ipset_item *item; if (!IpsetsReloadCheck(ips) || !IpsetsReloadCheck(ips_exclude)) return false; LIST_FOREACH(item, ips_exclude, next) { DLOG("[%s] exclude ",item->hfile->filename ? item->hfile->filename : "fixed"); if (SearchIpset(&item->hfile->ipset, ipv4, ipv6)) return false; if (ipv4r || ipv6r) { DLOG("[%s] exclude ",item->hfile->filename ? item->hfile->filename : "fixed"); if (SearchIpset(&item->hfile->ipset, ipv4r, ipv6r)) return false; } } // old behavior compat: all include lists are empty means check passes if (!ipset_collection_is_empty(ips)) { LIST_FOREACH(item, ips, next) { DLOG("[%s] include ",item->hfile->filename ? item->hfile->filename : "fixed"); if (SearchIpset(&item->hfile->ipset, ipv4, ipv6)) return true; if (ipv4r || ipv6r) { DLOG("[%s] include ",item->hfile->filename ? item->hfile->filename : "fixed"); if (SearchIpset(&item->hfile->ipset, ipv4r, ipv6r)) return true; } } return false; } return true; } bool IpsetCheck( const struct desync_profile *dp, const struct in_addr *ipv4, const struct in6_addr *ipv6, const struct in_addr *ipv4r, const struct in6_addr *ipv6r) { if (PROFILE_IPSETS_ABSENT(dp)) return true; DLOG("* ipset check for profile %u (%s)\n",dp->n,PROFILE_NAME(dp)); return IpsetCheck_(&dp->ips_collection,&dp->ips_collection_exclude,ipv4,ipv6,ipv4r,ipv6r); } static struct ipset_file *RegisterIpset_(struct ipset_files_head *ipsets, struct ipset_collection_head *ips_collection, const char *filename) { struct ipset_file *hfile; char pabs[PATH_MAX]; if (filename) { if (!realpath(filename,pabs)) return NULL; if (!(hfile=ipset_files_search(ipsets, pabs))) if (!(hfile=ipset_files_add(ipsets, pabs))) return NULL; if (!ipset_collection_search(ips_collection, pabs)) if (!ipset_collection_add(ips_collection, hfile)) return NULL; } else { if (!(hfile=ipset_files_add(ipsets, NULL))) return NULL; if (!ipset_collection_add(ips_collection, hfile)) return NULL; } return hfile; } struct ipset_file *RegisterIpset(struct desync_profile *dp, bool bExclude, const char *filename) { if (filename && !file_mod_time(filename)) { DLOG_ERR("cannot access ipset file '%s'\n",filename); return NULL; } return RegisterIpset_( ¶ms.ipsets, bExclude ? &dp->ips_collection_exclude : &dp->ips_collection, filename); } static const char *dbg_ipset_fill(const ipset *ips) { if (ips->ips4) if (ips->ips6) return "ipv4+ipv6"; else return "ipv4"; else if (ips->ips6) return "ipv6"; else return "empty"; } void IpsetsDebugProfile(const struct desync_profile *dp, const char *entity) { struct ipset_item *ips_item; LIST_FOREACH(ips_item, &dp->ips_collection, next) { if (ips_item->hfile->filename) DLOG("%s %u (%s) include ipset %s (%s)\n",entity,dp->n,PROFILE_NAME(dp),ips_item->hfile->filename,dbg_ipset_fill(&ips_item->hfile->ipset)); else DLOG("%s %u (%s) include fixed ipset (%s)\n",entity,dp->n,PROFILE_NAME(dp),dbg_ipset_fill(&ips_item->hfile->ipset)); } LIST_FOREACH(ips_item, &dp->ips_collection_exclude, next) { if (ips_item->hfile->filename) DLOG("%s %u (%s) exclude ipset %s (%s)\n",entity,dp->n,PROFILE_NAME(dp),ips_item->hfile->filename,dbg_ipset_fill(&ips_item->hfile->ipset)); else DLOG("%s %u (%s) exclude fixed ipset (%s)\n",entity,dp->n,PROFILE_NAME(dp),dbg_ipset_fill(&ips_item->hfile->ipset)); } } void IpsetsDebug() { if (!params.debug) return; struct ipset_file *hfile; struct desync_profile_list *dpl; LIST_FOREACH(hfile, ¶ms.ipsets, next) { if (hfile->filename) DLOG("ipset file %s (%s)\n",hfile->filename,dbg_ipset_fill(&hfile->ipset)); else DLOG("ipset fixed (%s)\n",dbg_ipset_fill(&hfile->ipset)); } LIST_FOREACH(dpl, ¶ms.desync_profiles, next) { IpsetsDebugProfile(&dpl->dp, "profile"); } LIST_FOREACH(dpl, ¶ms.desync_templates, next) { IpsetsDebugProfile(&dpl->dp, "template"); } }