static void overlord_dns_attack_detect(struct zsession *sess) { if (zcfg()->dns_attack_threshold) { uint64_t pps = spdm_calc(&sess->dns_speed); if (unlikely(pps >= zcfg()->dns_attack_threshold)) { if (!sess->is_dns_attack) { ZERO_LOG(LOG_WARNING, "DNS amplification attack begin detected: session %s, %" PRIu64 " pps", ipv4_to_str(sess->ip), pps); sess->is_dns_attack = true; } } else if (unlikely(sess->is_dns_attack)) { ZERO_LOG(LOG_WARNING, "DNS amplification attack end: session %s", ipv4_to_str(sess->ip)); sess->is_dns_attack = false; } } }
/** * Load uint16 array from config. * @param[in] cfg Configuration option. * @param[in] option Option name. * @param[in,out] array Resulting array. * @return Zero on success. */ static int load_uint16_list(const config_setting_t *cfg, const char *option, UT_array *array) { config_setting_t *cfg_list = config_setting_get_member(cfg, option); if (!cfg_list) { ZERO_LOG(LOG_ERR, "config: missing %s entry", option); return 0; } if (config_setting_type(cfg_list) != CONFIG_TYPE_LIST) { ZERO_LOG(LOG_ERR, "config: invalid %s entry", option); return -1; } int count = config_setting_length(cfg_list); if (0 >= count) { return 0; } utarray_init(array, &ut_uint16_icd); for (int i = 0; i < count; i++) { int entry = config_setting_get_int_elem(cfg_list, i); if (!entry) { ZERO_LOG(LOG_ERR, "config: failed to get next %s record", option); continue; } if (entry < UINT16_MAX) { uint16_t port = entry; utarray_push_back(array, &port); continue; } // if we here, then entry is invalid ZERO_LOG(LOG_ERR, "config: invalid %s item: %d", option, entry); utarray_done(array); return -1; } utarray_sort(array, uint16_cmp); return 0; }
/** * Load configuration file. * @param[in] path Location of configuration file. * @param[in,out] zconf Loaded options will be stored here. * @return Zero on success. */ int zero_config_load(const char *path, struct zero_config *zconf) { int ret = 0; config_t config; config_init(&config); if (NULL == path) { path = ZCFG_DEFAULT_PATH; } if (config_read_file(&config, path)) { const config_setting_t *root = config_root_setting(&config); ret = ret || load_interfaces(root, ZCFG_INTERFACES, &zconf->interfaces) || load_uint_req(root, ZCFG_IFACE_WAIT_TIME, &zconf->iface_wait_time) || load_uint_req(root, ZCFG_OVERLORD_THREADS, &zconf->overlord_threads) || load_kmgt(root, ZCFG_UNAUTH_BW_LIMIT_DOWN, &zconf->unauth_bw_limit[DIR_DOWN], 1024) || load_kmgt(root, ZCFG_UNAUTH_BW_LIMIT_UP, &zconf->unauth_bw_limit[DIR_UP], 1024) || load_string_req(root, ZCFG_RADIUS_CONFIG_FILE, &zconf->radius_config_file) || load_string_req(root, ZCFG_RADIUS_NAS_IDENTIFIER, &zconf->radius_nas_identifier) || load_uint64_req(root, ZCFG_SESSION_TIMEOUT, &zconf->session_timeout) || load_uint64_req(root, ZCFG_SESSION_ACCT_INTERVAL, &zconf->session_acct_interval) || load_uint64_req(root, ZCFG_SESSION_AUTH_INTERVAL, &zconf->session_auth_interval) || load_string_req(root, ZCFG_RC_LISTEN_ADDR, &zconf->rc_listen_addr) || load_kmgt(root, ZCFG_UPSTREAM_P2P_BW_DOWN, &zconf->upstream_p2p_bw[DIR_DOWN], 1024) || load_kmgt(root, ZCFG_UPSTREAM_P2P_BW_UP, &zconf->upstream_p2p_bw[DIR_UP], 1024) || load_ip_mask_list(root, ZCFG_IP_WHITELIST, &zconf->ip_whitelist) || load_uint16_list(root, ZCFG_P2P_PORTS_WHITELIST, &zconf->p2p_ports_whitelist) || load_uint16_list(root, ZCFG_P2P_PORTS_BLACKLIST, &zconf->p2p_ports_blacklist) || load_kmgt(root, ZCFG_NON_CLIENT_BW_DOWN, &zconf->non_client_bw[DIR_DOWN], 1024) || load_kmgt(root, ZCFG_NON_CLIENT_BW_UP, &zconf->non_client_bw[DIR_UP], 1024) || load_kmgt(root, ZCFG_INITIAL_CLIENT_BUCKET_SIZE, &zconf->initial_client_bucket_size, 1024) ; // convert from bits to bytes zconf->unauth_bw_limit[DIR_DOWN] /= 8; zconf->unauth_bw_limit[DIR_UP] /= 8; zconf->upstream_p2p_bw[DIR_DOWN] /= 8; zconf->upstream_p2p_bw[DIR_UP] /= 8; zconf->non_client_bw[DIR_DOWN] /= 8; zconf->non_client_bw[DIR_UP] /= 8; // convert from seconds to microseconds zconf->session_timeout *= 1000000; zconf->session_acct_interval *= 1000000; zconf->session_auth_interval *= 1000000; } else { ZERO_LOG(LOG_ERR, "config: failed to parse %s (error:%s at %d line)", path, config_error_text(&config), config_error_line(&config)); ret = -1; } config_destroy(&config); return ret; }
/** * Load required int value from config. * @param[in] cfg Config section. * @param[in] opt Option name. * @param[out] val Value pointer. * @return Zero on success. */ static int load_int64_req(const config_setting_t *cfg, const char *opt, int64_t *val) { long long int int64_val; if (config_setting_lookup_int64(cfg, opt, &int64_val)) { *val = int64_val; return 0; } else { ZERO_LOG(LOG_ERR, "config: '%s' missing", opt); return -1; } }
/** * Load required string value from config. * @param[in] cfg Config section. * @param[in] opt Option name. * @param[out] val Value pointer. * @return Zero on success. */ static int load_string_req(const config_setting_t *cfg, const char *opt, char **val) { const char *str_val; if (config_setting_lookup_string(cfg, opt, &str_val)) { *val = strdup(str_val); return 0; } else { ZERO_LOG(LOG_ERR, "config: '%s' missing", opt); return -1; } }
/** * Load size with SI prefix. * @param cfg Config section. * @param opt Option name. * @param val Value pointer. * @param base Value base (e.g. 1000, 1024, etc.). * @return Zero on success. */ static int load_kmgt(const config_setting_t *cfg, const char *opt, uint64_t *val, u_int base) { const char *str_val; if (config_setting_lookup_string(cfg, opt, &str_val)) { *val = strtoul(str_val, NULL, 10); char prefix = str_val[strlen(str_val)-1]; if (!isdigit(prefix)) { switch (str_val[strlen(str_val)-1]) { // no prefix case 0: break; // tera case 'T': case 't': *val *= base; // giga case 'G': case 'g': *val *= base; // mega case 'M': case 'm': *val *= base; // kilo case 'K': case 'k': *val *= base; break; default: ZERO_LOG(LOG_ERR, "config: '%s' invalid value", opt); return -1; } } return 0; } else { ZERO_LOG(LOG_ERR, "config: '%s' missing", opt); return -1; } }
/** * Load required unsigned int value from config. * @param[in] cfg Config section. * @param[in] opt Option name. * @param[out] val Value pointer. * @return Zero on success. */ static int load_uint64_req(const config_setting_t *cfg, const char *opt, uint64_t *val) { int64_t int64_val; if (!load_int64_req(cfg, opt, &int64_val)) { if (int64_val < 0) { ZERO_LOG(LOG_ERR, "config: '%s' must be greater than zero", opt); } else { *val = (u_int)int64_val; return 0; } } return -1; }
/** * Set BPF filter on monitor connection. * @param[in] conn Monitor connection. * @param[in] filter Filter string. * @return Zero on success. */ int zmonitor_conn_set_filter(struct zmonitor_conn *conn, const char *filter) { assert(!conn->active); pcap_t *cap = pcap_open_dead(DLT_EN10MB, MONITOR_SNAPLEN); pcap_freecode(&conn->bpf); if (0 != pcap_compile(cap, &conn->bpf, filter, 1, PCAP_NETMASK_UNKNOWN)) { ZERO_LOG(LOG_INFO, "Invalid monitor filter \"%s\": %s", filter, pcap_geterr(cap)); pcap_close(cap); return -1; } pcap_close(cap); return 0; }
/** * Open netmap ring. * @param[in,out] ring * @param[in] ringid Ring ID. * @param[in] cached_mmap_mem Pointer to already mmapped shared netmap memory. */ int znm_open(struct znm_ring *ring, const char *ifname, uint16_t ringid, void *cached_mmap_mem) { struct nmreq req; ring->fd = open(ZNM_DEVICE, O_RDWR); if (ring->fd < 0) { ZERO_ELOG(LOG_ERR, "Unable to open %s", ZNM_DEVICE); return -1; } memset(&req, 0, sizeof(req)); req.nr_version = NETMAP_API; strncpy(req.nr_name, ifname, sizeof(req.nr_name)); req.nr_ringid = ringid; req.nr_flags = NR_REG_ONE_NIC; if (0 == ioctl(ring->fd, NIOCGINFO, &req)) { ring->memsize = req.nr_memsize; if (0 == ioctl(ring->fd, NIOCREGIF, &req)) { if (NULL != cached_mmap_mem) { ring->mem = cached_mmap_mem; } else { ring->mem = mmap(0, ring->memsize, PROT_WRITE | PROT_READ, MAP_SHARED, ring->fd, 0); ring->own_mmap = 1; } if (MAP_FAILED != ring->mem) { ZERO_LOG(LOG_DEBUG, "Attached to %s HW ring %u", ifname, ringid); ring->nifp = NETMAP_IF(ring->mem, req.nr_offset); ring->tx = NETMAP_TXRING(ring->nifp, ringid); ring->rx = NETMAP_RXRING(ring->nifp, ringid); // Success. return 0; } else { ring->mem = NULL; ZERO_ELOG(LOG_ERR, "Unable to mmap netmap shared memory"); } } else { ZERO_ELOG(LOG_ERR, "Unable to register %s with netmap", ifname); } } else { ZERO_ELOG(LOG_ERR, "Unable to query netmap for '%s' info", ifname); } close(ring->fd); return -1; }
/** * Authenticate and set client info. * @param[in] sess Client session. * @return Zero on success (or one of *_RC). */ static int session_authenticate(struct zsession *sess) { int ret = OTHER_RC; VALUE_PAIR *request_attrs = NULL, *response_attrs = NULL, *attrs = NULL; char msg[8192]; // WARNING: libfreeradius-client has unsafe working with this buffer. rc_handle *rh = zinst()->radh; struct in_addr ip_addr; char ip_str[INET_ADDRSTRLEN]; struct zcrules rules; crules_init(&rules); ip_addr.s_addr = htonl(sess->ip); if (unlikely(NULL == inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_USER_NAME, ip_str, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_USER_PASSWORD, "", -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_NAS_IDENTIFIER, zcfg()->radius_nas_identifier, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_CALLING_STATION_ID, ip_str, -1, 0))) { goto end; } ret = rc_auth(rh, 0, request_attrs, &response_attrs, msg); if (OK_RC != ret) { ZERO_LOG(LOG_ERR, "Session authentication failed for %s (code:%d)", ip_str, ret); goto end; } attrs = response_attrs; while (likely(NULL != attrs)) { switch (attrs->attribute) { case PW_FILTER_ID: crules_parse(&rules, attrs->strvalue); break; case PW_SESSION_TIMEOUT: atomic_store_explicit(&sess->max_duration, SEC2USEC(attrs->lvalue), memory_order_release); break; case PW_ACCT_INTERIM_INTERVAL: atomic_store_explicit(&sess->acct_interval, SEC2USEC(attrs->lvalue), memory_order_release); break; } attrs = attrs->next; } if (likely(rules.have.user_id && rules.have.login)) { struct zclient *client = sess->client; client_db_find_or_set_id(zinst()->client_db, rules.user_id, &client); if (client != sess->client) { // found pthread_rwlock_wrlock(&sess->lock_client); atomic_fetch_add_explicit(&client->refcnt, 1, memory_order_relaxed); client_release(sess->client); sess->client = client; client_session_add(sess->client, sess); pthread_rwlock_unlock(&sess->lock_client); } else { client_apply_rules(sess->client, &rules); } atomic_fetch_sub_explicit(&zinst()->unauth_sessions_cnt, 1, memory_order_release); // log successful authentication { UT_string rules_str; utstring_init(&rules_str); utstring_reserve(&rules_str, 1024); attrs = response_attrs; while (likely(NULL != attrs)) { switch (attrs->attribute) { case PW_FILTER_ID: utstring_printf(&rules_str, " %s", attrs->strvalue); break; default: break; } attrs = attrs->next; } zero_syslog(LOG_INFO, "Authenticated session %s (rules:%s)", ip_str, utstring_body(&rules_str)); utstring_done(&rules_str); } } else { ret = OTHER_RC; ZERO_LOG(LOG_ERR, "Session authentication failed for %s (code:%d)", ip_str, ret); } end: crules_free(&rules); if (request_attrs) rc_avpair_free(request_attrs); if (response_attrs) rc_avpair_free(response_attrs); return ret; }
/** * Send radius accounting packet. * @param[in] sess Session * @param[in] status Accounting status (PW_STATUS_START, PW_STATUS_STOP, PW_STATUS_ALIVE) * @param[in] cause Accounting termination cause (used only in case of PW_STATUS_STOP) * @return Zero on success (one of *_RC codes). */ static int session_accounting(struct zsession *sess, uint32_t status, uint32_t term_cause) { int ret = OTHER_RC; VALUE_PAIR *request_attrs = NULL; rc_handle *rh = zinst()->radh; struct in_addr ip_addr; char ip_str[INET_ADDRSTRLEN]; uint64_t traff_down = atomic_load_explicit(&sess->traff_down, memory_order_acquire); uint64_t traff_up = atomic_load_explicit(&sess->traff_up, memory_order_acquire); uint32_t octets_down = (uint32_t) (traff_down % UINT32_MAX); uint32_t octets_up = (uint32_t) (traff_up % UINT32_MAX); uint32_t packets_down = atomic_load_explicit(&sess->packets_down, memory_order_acquire) % UINT32_MAX; uint32_t packets_up = atomic_load_explicit(&sess->packets_up, memory_order_acquire) % UINT32_MAX; uint32_t gigawords_down = 0; uint32_t gigawords_up = 0; char session_id[255]; snprintf(session_id, sizeof(session_id), "%s-%" PRIu32, sess->client->login, sess->ip); ip_addr.s_addr = htonl(sess->ip); if (unlikely(NULL == inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_CALLING_STATION_ID, ip_str, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_FRAMED_IP_ADDRESS, &sess->ip, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_USER_NAME, sess->client->login, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_ACCT_SESSION_ID, session_id, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_NAS_IDENTIFIER, zcfg()->radius_nas_identifier, -1, 0))) { goto end; } if (PW_STATUS_STOP == status) { if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_ACCT_TERMINATE_CAUSE, &term_cause, -1, 0))) { goto end; } } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_ACCT_STATUS_TYPE, &status, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_ACCT_INPUT_OCTETS, &octets_down, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_ACCT_INPUT_PACKETS, &packets_down, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_ACCT_OUTPUT_OCTETS, &octets_up, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_ACCT_OUTPUT_PACKETS, &packets_down, -1, 0))) { goto end; } if (unlikely(UINT32_MAX < traff_down)) { gigawords_down = (uint32_t) (traff_down / UINT32_MAX); if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_ACCT_INPUT_GIGAWORDS, &gigawords_down, -1, 0))) { goto end; } } if (unlikely(UINT32_MAX < traff_up)) { gigawords_up = (uint32_t) (traff_up / UINT32_MAX); if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_ACCT_OUTPUT_GIGAWORDS, &gigawords_up, -1, 0))) { goto end; } } ret = rc_acct(rh, 0, request_attrs); if (unlikely(OK_RC != ret)) { ZERO_LOG(LOG_ERR, "radius accounting failed %s (code:%d)", ip_str, ret); goto end; } atomic_fetch_sub_explicit(&sess->traff_down, traff_down, memory_order_release); atomic_fetch_sub_explicit(&sess->traff_up, traff_up, memory_order_release); atomic_fetch_sub_explicit(&sess->packets_down, packets_down, memory_order_release); atomic_fetch_sub_explicit(&sess->packets_up, packets_up, memory_order_release); end: if (request_attrs) { rc_avpair_free(request_attrs); } return ret; }
/** * Preapre interface for operation in netmap mode. * @param[in] ifname Interface name. * @return Zero on success. */ int znm_prepare_if(const char *ifname) { struct ifreq ifr; struct ethtool_value ethval; int fd, ret = -1; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { ZERO_ELOG(LOG_ERR, "Can not create device control socket"); goto end; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); // check and set interface flags if (0 != ioctl(fd, SIOCGIFFLAGS, &ifr)) { ZERO_ELOG(LOG_ERR, "Failed to get '%s' interface flags", ifname); goto end; } if (0 == (ifr.ifr_flags & IFF_UP)) { ZERO_LOG(LOG_INFO, "'%s' is down, bringing up...", ifname); ifr.ifr_flags |= IFF_UP; } if (0 == (ifr.ifr_flags & IFF_PROMISC)) { ZERO_LOG(LOG_DEBUG, "Set '%s' to promisc mode", ifname); ifr.ifr_flags |= IFF_PROMISC; } if (0 != ioctl(fd, SIOCSIFFLAGS, &ifr)) { ZERO_ELOG(LOG_ERR, "Failed to set '%s' interface flags", ifname); goto end; } ifr.ifr_data = (caddr_t) ðval; // disable generic-segmentation-offload ethval.cmd = ETHTOOL_SGSO; ethval.data = 0; if (0 != ioctl(fd, SIOCETHTOOL, &ifr)) { ZERO_ELOG(LOG_ERR, "Failed to disable GSO feature on '%s' interface", ifname); goto end; } // disable generic-receive-offload ethval.cmd = ETHTOOL_SGRO; ethval.data = 0; if (0 != ioctl(fd, SIOCETHTOOL, &ifr)) { ZERO_ELOG(LOG_ERR, "Failed to disable GRO feature on '%s' interface", ifname); goto end; } // disable tcp-segmentation-offload ethval.cmd = ETHTOOL_STSO; ethval.data = 0; if (0 != ioctl(fd, SIOCETHTOOL, &ifr)) { ZERO_ELOG(LOG_ERR, "Failed to disable TSO feature on '%s' interface", ifname); goto end; } // disable hw rx-checksumming ethval.cmd = ETHTOOL_SRXCSUM; ethval.data = 0; if (0 != ioctl(fd, SIOCETHTOOL, &ifr)) { ZERO_ELOG(LOG_ERR, "Failed to disable rx-checksum feature on '%s' interface", ifname); goto end; } // disable hw tx-checksumming ethval.cmd = ETHTOOL_STXCSUM; ethval.data = 0; if (0 != ioctl(fd, SIOCETHTOOL, &ifr)) { ZERO_ELOG(LOG_ERR, "Failed to disable tx-checksum feature on '%s' interface", ifname); goto end; } // disable ntuple, rx/tx VLAN offload, large-receive-offload ethval.cmd = ETHTOOL_SFLAGS; ethval.data = 0; if (0 != ioctl(fd, SIOCETHTOOL, &ifr)) { ZERO_ELOG(LOG_ERR, "Failed to disable ntuple, VLAN offload, LRO features on '%s' interface", ifname); goto end; } ret = 0; end: if (fd >= 0) close(fd); return ret; }
/** Load interfaces section. * @param[in] cfg Config section. * @param[in] option Option name. * @param[in,out] array Resulting array. * @return Zero on success. */ int load_interfaces(const config_setting_t *cfg, const char *option, UT_array *array) { config_setting_t *cfg_list = config_setting_get_member(cfg, option); if (!cfg_list) { ZERO_LOG(LOG_ERR, "config: missing %s entry", option); return -1; } if (config_setting_type(cfg_list) != CONFIG_TYPE_LIST) { ZERO_LOG(LOG_ERR, "config: invalid %s entry", option); return -1; } int count = config_setting_length(cfg_list); if (0 >= count) { ZERO_LOG(LOG_ERR, "config: empty %s entry", option); return -1; } utarray_init(array, &ut_zif_pair_icd); for (int i = 0; i < count; i++) { struct zif_pair if_pair; const char *str; config_setting_t *entry = config_setting_get_elem(cfg_list, i); if (NULL == entry) { ZERO_LOG(LOG_ERR, "config: failed to read %u-th group of %s entry", i, option); goto fail; } if (!config_setting_lookup_string(entry, ZCFG_LAN, &str)) { ZERO_LOG(LOG_ERR, "config: failed to read '%s' property of %u-th group of %s entry", ZCFG_LAN, i, option); goto fail; } strncpy(if_pair.lan, str, sizeof(if_pair.lan)); if (!config_setting_lookup_string(entry, ZCFG_WAN, &str)) { ZERO_LOG(LOG_ERR, "config: failed to read '%s' property of %u-th group of %s entry", ZCFG_WAN, i, option); goto fail; } strncpy(if_pair.wan, str, sizeof(if_pair.wan)); int affinity = 0; if (!config_setting_lookup_int(entry, ZCFG_AFFINITY, &affinity)) { ZERO_LOG(LOG_ERR, "config: failed to read '%s' property of %u-th group of %s entry", ZCFG_AFFINITY, i, option); goto fail; } if ((affinity < 0) || affinity >= UINT16_MAX) { ZERO_LOG(LOG_ERR, "config: invalid value in '%s' property of %u-th group of %s entry", ZCFG_AFFINITY, i, option); goto fail; } if_pair.affinity = (uint16_t)affinity; utarray_push_back(array, &if_pair); } return 0; fail: utarray_done(array); return -1; }
/** * Load ip-mask array. * @param[in] cfg Config section. * @param[in] option Option name. * @param[in,out] array Resulting array. * @return Zero on success. */ static int load_ip_mask_list(const config_setting_t *cfg, const char *option, UT_array *array) { config_setting_t *cfg_list = config_setting_get_member(cfg, option); if (!cfg_list) { ZERO_LOG(LOG_ERR, "config: missing %s entry", option); return 0; } if (config_setting_type(cfg_list) != CONFIG_TYPE_LIST) { ZERO_LOG(LOG_ERR, "config: invalid %s entry", option); return -1; } int count = config_setting_length(cfg_list); if (0 >= count) { return 0; } utarray_init(array, &ut_ip_range_icd); for (int i = 0; i < count; i++) { struct ip_range range; const char *entry = config_setting_get_string_elem(cfg_list, i); if (!entry) { ZERO_LOG(LOG_ERR, "config: failed to get next %s record", option); continue; } char ip_str[INET_ADDRSTRLEN]; const char *cidr_pos = strchr(entry, '/'); // we search for CIDR, and make sure, that ip part is not bigger than allowed size if (cidr_pos && ((size_t)(cidr_pos - entry) < sizeof(ip_str))) { strncpy(ip_str, entry, cidr_pos - entry); ip_str[cidr_pos - entry] = '\0'; struct in_addr ip_addr; if (0 < inet_pton(AF_INET, ip_str, &ip_addr)) { u_long cidr = strtoul(cidr_pos + 1, NULL, 10); if (cidr != ULONG_MAX && cidr <= 32) { range.ip_start = ntohl(ip_addr.s_addr); range.ip_end = IP_RANGE_END(range.ip_start, cidr); utarray_push_back(array, &range); continue; } } } // if we here, then entry is invalid ZERO_LOG(LOG_ERR, "config: invalid %s item: %s", option, entry); utarray_done(array); return -1; } utarray_sort(array, ip_range_cmp); return 0; }