Index: lib/libpfctl/libpfctl.c =================================================================== --- lib/libpfctl/libpfctl.c +++ lib/libpfctl/libpfctl.c @@ -743,8 +743,10 @@ { struct pfioc_nv nv; struct pfctl_states_stats stats; - nvlist_t *nvl = NULL; + nvlist_t *nvl = NULL, *nvl_req = NULL; + void *nvlpacked; const nvlist_t * const *slist; + uint64_t token = 0; size_t found_count; size_t size_estimate; int error = 0; @@ -757,10 +759,26 @@ TAILQ_INIT(&states->states); size_estimate = 64 + (stats.states * stats.state_size_hint); + /* Never more than 100MB at a time. */ + size_estimate = MIN(size_estimate, 100 * 1024 * 1024); + + nv.len = 0; nv.data = malloc(size_estimate); - nv.len = nv.size = size_estimate; + nv.size = size_estimate; for (;;) { + if (token != 0) { + /* Tell the kernel where to resume from. */ + nvl_req = nvlist_create(0); + nvlist_add_number(nvl_req, "token", token); + + nvlpacked = nvlist_pack(nvl_req, &nv.len); + assert(nv.len <= nv.size); + memcpy(nv.data, nvlpacked, nv.len); + nvlist_destroy(nvl_req); + free(nvlpacked); + } + if (ioctl(dev, DIOCGETSTATESNV, &nv)) { error = errno; goto out; @@ -773,33 +791,22 @@ goto out; } - states->count = nvlist_get_number(nvl, "count"); - - /* Are there any states? */ - if (states->count == 0) - break; - if (nvlist_exists_nvlist_array(nvl, "states")) slist = nvlist_get_nvlist_array(nvl, "states", &found_count); - else + else { found_count = 0; - if (found_count < states->count) { - size_t new_size = nv.size + - (nv.size * states->count / (found_count + 1) * 2); - - /* Our buffer is too small. Estimate what we need based - * on how many states fit in the previous allocation - * and how many states there are. Doubled for margin. - * */ - nv.data = realloc(nv.data, new_size); - nv.size = new_size; - - if (nv.data == NULL) { - error = ENOMEM; - goto out; + /* If we've made no progress and have no states our + * allocation is just too small. */ + if (token == nvlist_get_number(nvl, "token")) { + nv.size *= 2; + nv.data = realloc(nv.data, nv.size); + if (nv.data == NULL) { + error = ENOMEM; + goto out; + } + continue; } - continue; } for (size_t i = 0; i < found_count; i++) { @@ -812,8 +819,13 @@ pf_nvstate_to_state(slist[i], s); TAILQ_INSERT_TAIL(&states->states, s, entry); + states->count++; } - break; + + if (nvlist_get_bool(nvl, "done")) + break; + + token = nvlist_get_number(nvl, "token"); } out: