Changeset View
Standalone View
sys/netpfil/pf/pf_ioctl.c
Show First 20 Lines • Show All 2,102 Lines • ▼ Show 20 Lines | if (securelevel_gt(td->td_ucred, 2)) | ||||
case DIOCGETSTATE: | case DIOCGETSTATE: | ||||
case DIOCGETSTATENV: | case DIOCGETSTATENV: | ||||
case DIOCSETSTATUSIF: | case DIOCSETSTATUSIF: | ||||
case DIOCGETSTATUS: | case DIOCGETSTATUS: | ||||
case DIOCCLRSTATUS: | case DIOCCLRSTATUS: | ||||
case DIOCNATLOOK: | case DIOCNATLOOK: | ||||
case DIOCSETDEBUG: | case DIOCSETDEBUG: | ||||
case DIOCGETSTATES: | case DIOCGETSTATES: | ||||
case DIOCGETSTATESV2: | |||||
case DIOCGETSTATESNV: | case DIOCGETSTATESNV: | ||||
case DIOCGETTIMEOUT: | case DIOCGETTIMEOUT: | ||||
case DIOCCLRRULECTRS: | case DIOCCLRRULECTRS: | ||||
case DIOCGETLIMIT: | case DIOCGETLIMIT: | ||||
case DIOCGETALTQSV0: | case DIOCGETALTQSV0: | ||||
case DIOCGETALTQSV1: | case DIOCGETALTQSV1: | ||||
case DIOCGETALTQV0: | case DIOCGETALTQV0: | ||||
case DIOCGETALTQV1: | case DIOCGETALTQV1: | ||||
Show All 37 Lines | if (!(flags & FWRITE)) | ||||
switch (cmd) { | switch (cmd) { | ||||
case DIOCGETRULES: | case DIOCGETRULES: | ||||
case DIOCGETADDRS: | case DIOCGETADDRS: | ||||
case DIOCGETADDR: | case DIOCGETADDR: | ||||
case DIOCGETSTATE: | case DIOCGETSTATE: | ||||
case DIOCGETSTATENV: | case DIOCGETSTATENV: | ||||
case DIOCGETSTATUS: | case DIOCGETSTATUS: | ||||
case DIOCGETSTATES: | case DIOCGETSTATES: | ||||
case DIOCGETSTATESV2: | |||||
case DIOCGETSTATESNV: | case DIOCGETSTATESNV: | ||||
case DIOCGETTIMEOUT: | case DIOCGETTIMEOUT: | ||||
case DIOCGETLIMIT: | case DIOCGETLIMIT: | ||||
case DIOCGETALTQSV0: | case DIOCGETALTQSV0: | ||||
case DIOCGETALTQSV1: | case DIOCGETALTQSV1: | ||||
case DIOCGETALTQV0: | case DIOCGETALTQV0: | ||||
case DIOCGETALTQV1: | case DIOCGETALTQV1: | ||||
case DIOCGETQSTATSV0: | case DIOCGETQSTATSV0: | ||||
▲ Show 20 Lines • Show All 694 Lines • ▼ Show 20 Lines | if (error) { | ||||
break; | break; | ||||
} | } | ||||
ps->ps_len = sizeof(struct pfsync_state) * nr; | ps->ps_len = sizeof(struct pfsync_state) * nr; | ||||
free(pstore, M_TEMP); | free(pstore, M_TEMP); | ||||
break; | break; | ||||
} | } | ||||
case DIOCGETSTATESV2: { | |||||
struct pfioc_states_v2 *ps = (struct pfioc_states_v2 *)addr; | |||||
struct pf_kstate *s; | |||||
struct pf_state_export *pstore, *p; | |||||
int i, nr; | |||||
if (ps->ps_req_version > PF_STATE_VERSION) { | |||||
error = ENOTSUP; | |||||
break; | |||||
} | |||||
if (ps->ps_len <= 0) { | |||||
nr = uma_zone_get_cur(V_pf_state_z); | |||||
mjg: an upper bound should be checked to make sure this does not request gigabytes of memory
but… | |||||
Not Done Inline ActionsGood point, although now I wonder if we shouldn't do multiple copyout() calls instead. That way we only allocate memory for one state at a time, and can export multiple gigabytes of data without issue. Even then we might still want the support chunked reads, mostly so userspace doesn't have to allocate all of the memory in one go. Even is libpfctl is likely to do that anyway. (But in a future patch). kp: Good point, although now I wonder if we shouldn't do multiple copyout() calls instead. That way… | |||||
Not Done Inline Actionslibpfctl should mmap a big area and let the kernel fault on it as needed multiple calls to copyout is what i'm proposing, but doing it on a per-state would not work.
that said, bare minimum you need to be able to prepare the entire row for export in one go. simple heuristic to do it would to be create a buffer upfront, say for 64 states and roll with it until it is full AND the row is fully covered. If entries from the next row would not fit, you flush it and continue. if 64 is too small, you realloc with a bigger size. mjg: libpfctl should mmap a big area and let the kernel fault on it as needed
multiple calls to… | |||||
Not Done Inline ActionsNote the copyout problems can be worked around, but I don't think it is worth it for this facility. mjg: Note the copyout problems can be worked around, but I don't think it is worth it for this… | |||||
Not Done Inline ActionsWe do already bound the size (up to the number of states * the size of each exported state), so that at least prevents userspace from asking for silly things. I'm inclined to call that good enough for this commit, and to work up a new patch on top of this series to implement your suggestion, for both the old and the new version of DIOCGETSTATES. kp: We do already bound the size (up to the number of states * the size of each exported state), so… | |||||
Not Done Inline ActionsTo my reading the allocation is not bounded right now -- userspace, should they want, can pass an arbitrary size. mjg: To my reading the allocation is not bounded right now -- userspace, should they want, can pass… | |||||
Not Done Inline ActionsYou are correct. I had misread the if (ps->ps_len <= 0) { block. kp: You are correct. I had misread the `if (ps->ps_len <= 0) {` block.
| |||||
ps->ps_len = sizeof(struct pf_state_export) * nr; | |||||
break; | |||||
} | |||||
p = pstore = malloc(ps->ps_len, M_TEMP, M_WAITOK | M_ZERO); | |||||
nr = 0; | |||||
for (i = 0; i <= pf_hashmask; i++) { | |||||
struct pf_idhash *ih = &V_pf_idhash[i]; | |||||
if (LIST_EMPTY(&ih->states)) | |||||
continue; | |||||
PF_HASHROW_LOCK(ih); | |||||
LIST_FOREACH(s, &ih->states, entry) { | |||||
if (s->timeout == PFTM_UNLINKED) | |||||
continue; | |||||
if ((nr+1) * sizeof(*p) > ps->ps_len) { | |||||
PF_HASHROW_UNLOCK(ih); | |||||
goto DIOCGETSTATESV2_full; | |||||
} | |||||
pf_state_export(p, s); | |||||
p++; | |||||
nr++; | |||||
} | |||||
PF_HASHROW_UNLOCK(ih); | |||||
} | |||||
DIOCGETSTATESV2_full: | |||||
error = copyout(pstore, ps->ps_states, | |||||
sizeof(struct pf_state_export) * nr); | |||||
if (error) { | |||||
free(pstore, M_TEMP); | |||||
break; | |||||
} | |||||
ps->ps_len = sizeof(struct pf_state_export) * nr; | |||||
free(pstore, M_TEMP); | |||||
break; | |||||
} | |||||
case DIOCGETSTATESNV: { | case DIOCGETSTATESNV: { | ||||
error = pf_getstates((struct pfioc_nv *)addr); | error = pf_getstates((struct pfioc_nv *)addr); | ||||
break; | break; | ||||
} | } | ||||
case DIOCGETSTATUS: { | case DIOCGETSTATUS: { | ||||
struct pf_status *s = (struct pf_status *)addr; | struct pf_status *s = (struct pf_status *)addr; | ||||
▲ Show 20 Lines • Show All 1,707 Lines • ▼ Show 20 Lines | if (st->nat_rule.ptr == NULL) | ||||
sp->nat_rule = htonl(-1); | sp->nat_rule = htonl(-1); | ||||
else | else | ||||
sp->nat_rule = htonl(st->nat_rule.ptr->nr); | sp->nat_rule = htonl(st->nat_rule.ptr->nr); | ||||
pf_state_counter_hton(st->packets[0], sp->packets[0]); | pf_state_counter_hton(st->packets[0], sp->packets[0]); | ||||
pf_state_counter_hton(st->packets[1], sp->packets[1]); | pf_state_counter_hton(st->packets[1], sp->packets[1]); | ||||
pf_state_counter_hton(st->bytes[0], sp->bytes[0]); | pf_state_counter_hton(st->bytes[0], sp->bytes[0]); | ||||
pf_state_counter_hton(st->bytes[1], sp->bytes[1]); | pf_state_counter_hton(st->bytes[1], sp->bytes[1]); | ||||
} | |||||
void | |||||
pf_state_export(struct pf_state_export *sp, struct pf_kstate *st) | |||||
{ | |||||
bzero(sp, sizeof(*sp)); | |||||
sp->version = PF_STATE_VERSION; | |||||
/* copy from state key */ | |||||
sp->key[PF_SK_WIRE].addr[0] = st->key[PF_SK_WIRE]->addr[0]; | |||||
sp->key[PF_SK_WIRE].addr[1] = st->key[PF_SK_WIRE]->addr[1]; | |||||
sp->key[PF_SK_WIRE].port[0] = st->key[PF_SK_WIRE]->port[0]; | |||||
sp->key[PF_SK_WIRE].port[1] = st->key[PF_SK_WIRE]->port[1]; | |||||
sp->key[PF_SK_STACK].addr[0] = st->key[PF_SK_STACK]->addr[0]; | |||||
sp->key[PF_SK_STACK].addr[1] = st->key[PF_SK_STACK]->addr[1]; | |||||
sp->key[PF_SK_STACK].port[0] = st->key[PF_SK_STACK]->port[0]; | |||||
sp->key[PF_SK_STACK].port[1] = st->key[PF_SK_STACK]->port[1]; | |||||
sp->proto = st->key[PF_SK_WIRE]->proto; | |||||
sp->af = st->key[PF_SK_WIRE]->af; | |||||
/* copy from state */ | |||||
strlcpy(sp->ifname, st->kif->pfik_name, sizeof(sp->ifname)); | |||||
strlcpy(sp->orig_ifname, st->orig_kif->pfik_name, | |||||
sizeof(sp->orig_ifname)); | |||||
bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); | |||||
sp->creation = htonl(time_uptime - st->creation); | |||||
sp->expire = pf_state_expires(st); | |||||
if (sp->expire <= time_uptime) | |||||
sp->expire = htonl(0); | |||||
else | |||||
sp->expire = htonl(sp->expire - time_uptime); | |||||
sp->direction = st->direction; | |||||
sp->log = st->log; | |||||
sp->timeout = st->timeout; | |||||
sp->state_flags = st->state_flags; | |||||
if (st->src_node) | |||||
sp->sync_flags |= PFSYNC_FLAG_SRCNODE; | |||||
if (st->nat_src_node) | |||||
sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE; | |||||
sp->id = st->id; | |||||
sp->creatorid = st->creatorid; | |||||
pf_state_peer_hton(&st->src, &sp->src); | |||||
pf_state_peer_hton(&st->dst, &sp->dst); | |||||
if (st->rule.ptr == NULL) | |||||
sp->rule = htonl(-1); | |||||
else | |||||
sp->rule = htonl(st->rule.ptr->nr); | |||||
if (st->anchor.ptr == NULL) | |||||
sp->anchor = htonl(-1); | |||||
else | |||||
sp->anchor = htonl(st->anchor.ptr->nr); | |||||
if (st->nat_rule.ptr == NULL) | |||||
sp->nat_rule = htonl(-1); | |||||
else | |||||
sp->nat_rule = htonl(st->nat_rule.ptr->nr); | |||||
sp->packets[0] = st->packets[0]; | |||||
sp->packets[1] = st->packets[1]; | |||||
sp->bytes[0] = st->bytes[0]; | |||||
sp->bytes[1] = st->bytes[1]; | |||||
} | } | ||||
static void | static void | ||||
pf_tbladdr_copyout(struct pf_addr_wrap *aw) | pf_tbladdr_copyout(struct pf_addr_wrap *aw) | ||||
{ | { | ||||
struct pfr_ktable *kt; | struct pfr_ktable *kt; | ||||
KASSERT(aw->type == PF_ADDR_TABLE, ("%s: type %u", __func__, aw->type)); | KASSERT(aw->type == PF_ADDR_TABLE, ("%s: type %u", __func__, aw->type)); | ||||
▲ Show 20 Lines • Show All 924 Lines • Show Last 20 Lines |
an upper bound should be checked to make sure this does not request gigabytes of memory
but more importantly this should alloc some amount and export the thing in chunks. can be a separate patch.