pf_status.since = since; pf_status.debug = debug;
pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p){	int error = 0;	int s;	if (!(flags & FWRITE))		return (EACCES);	/* XXX keep in sync with switch() below */	if (securelevel > 1)		switch (cmd) {		case DIOCSTART:		case DIOCSTOP:		case DIOCBEGINRULES:		case DIOCADDRULE:		case DIOCCOMMITRULES:		case DIOCBEGINNATS:		case DIOCADDNAT:		case DIOCCOMMITNATS:		case DIOCBEGINBINATS:		case DIOCADDBINAT:		case DIOCCOMMITBINATS:		case DIOCBEGINRDRS:		case DIOCADDRDR:		case DIOCCOMMITRDRS:		case DIOCCLRSTATES:		case DIOCCHANGERULE:		case DIOCCHANGENAT:		case DIOCCHANGEBINAT:		case DIOCCHANGERDR:		case DIOCSETTIMEOUT:			return EPERM;		}	switch (cmd) {	case DIOCSTART:		if (pf_status.running)			error = EEXIST;		else {			u_int32_t states = pf_status.states;			bzero(&pf_status, sizeof(struct pf_status));			pf_status.running = 1;			pf_status.states = states;			microtime(&pftv);			pf_status.since = pftv.tv_sec;			DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n"));		}		break;	case DIOCSTOP:		if (!pf_status.running)			error = ENOENT;		else {			pf_status.running = 0;			DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n"));		}		break;	case DIOCBEGINRULES: {		u_int32_t *ticket = (u_int32_t *)addr;		struct pf_rule *rule;		while ((rule = TAILQ_FIRST(pf_rules_inactive)) != NULL) {			TAILQ_REMOVE(pf_rules_inactive, rule, entries);			pool_put(&pf_rule_pl, rule);		}		*ticket = ++ticket_rules_inactive;		break;	}	case DIOCADDRULE: {		struct pfioc_rule *pr = (struct pfioc_rule *)addr;		struct pf_rule *rule, *tail;		if (pr->ticket != ticket_rules_inactive) {			error = EBUSY;			break;		}		rule = pool_get(&pf_rule_pl, PR_NOWAIT);		if (rule == NULL) {			error = ENOMEM;			break;		}		bcopy(&pr->rule, rule, sizeof(struct pf_rule));#ifndef INET		if (rule->af == AF_INET) {			pool_put(&pf_rule_pl, rule);			error = EAFNOSUPPORT;			break;		}#endif /* INET */#ifndef INET6		if (rule->af == AF_INET6) {			pool_put(&pf_rule_pl, rule);			error = EAFNOSUPPORT;			break;		}#endif /* INET6 */		tail = TAILQ_LAST(pf_rules_inactive, pf_rulequeue);		if (tail)			rule->nr = tail->nr + 1;		else			rule->nr = 0;		rule->ifp = NULL;		if (rule->ifname[0]) {			rule->ifp = ifunit(rule->ifname);			if (rule->ifp == NULL) {				pool_put(&pf_rule_pl, rule);				error = EINVAL;				break;			}		} else			rule->ifp = NULL;		if (rule->rt_ifname[0]) {			rule->rt_ifp = ifunit(rule->rt_ifname);			if (rule->rt_ifname == NULL) {				pool_put(&pf_rule_pl, rule);				error = EINVAL;				break;			}		} else			rule->rt_ifp = NULL;		rule->evaluations = rule->packets = rule->bytes = 0;		TAILQ_INSERT_TAIL(pf_rules_inactive, rule, entries);		break;	}	case DIOCCOMMITRULES: {		u_int32_t *ticket = (u_int32_t *)addr;		struct pf_rulequeue *old_rules;		struct pf_rule *rule;		struct pf_tree_node *n;		if (*ticket != ticket_rules_inactive) {			error = EBUSY;			break;		}		/* Swap rules, keep the old. */		s = splsoftnet();		/*		 * Rules are about to get freed, clear rule pointers in states		 */		for (n = pf_tree_first(tree_ext_gwy); n != NULL;		    n = pf_tree_next(n))			n->state->rule = NULL;		old_rules = pf_rules_active;		pf_rules_active = pf_rules_inactive;		pf_rules_inactive = old_rules;		ticket_rules_active = ticket_rules_inactive;		pf_calc_skip_steps(pf_rules_active);		splx(s);		/* Purge the old rule list. */		while ((rule = TAILQ_FIRST(old_rules)) != NULL) {			TAILQ_REMOVE(old_rules, rule, entries);			pool_put(&pf_rule_pl, rule);		}		break;	}	case DIOCGETRULES: {		struct pfioc_rule *pr = (struct pfioc_rule *)addr;		struct pf_rule *tail;		s = splsoftnet();		tail = TAILQ_LAST(pf_rules_active, pf_rulequeue);		if (tail)			pr->nr = tail->nr + 1;		else			pr->nr = 0;		pr->ticket = ticket_rules_active;		splx(s);		break;	}	case DIOCGETRULE: {		struct pfioc_rule *pr = (struct pfioc_rule *)addr;		struct pf_rule *rule;		if (pr->ticket != ticket_rules_active) {			error = EBUSY;			break;		}		s = splsoftnet();		rule = TAILQ_FIRST(pf_rules_active);		while ((rule != NULL) && (rule->nr != pr->nr))			rule = TAILQ_NEXT(rule, entries);		if (rule == NULL) {			error = EBUSY;			splx(s);			break;		}		bcopy(rule, &pr->rule, sizeof(struct pf_rule));		splx(s);		break;	}	case DIOCCHANGERULE: {		struct pfioc_changerule *pcr = (struct pfioc_changerule *)addr;		struct pf_rule *oldrule = NULL, *newrule = NULL;		u_int32_t nr = 0;		if (pcr->action < PF_CHANGE_ADD_HEAD ||		    pcr->action > PF_CHANGE_REMOVE) {			error = EINVAL;			break;		}		if (pcr->action != PF_CHANGE_REMOVE) {			newrule = pool_get(&pf_rule_pl, PR_NOWAIT);			if (newrule == NULL) {				error = ENOMEM;				break;			}			bcopy(&pcr->newrule, newrule, sizeof(struct pf_rule));#ifndef INET			if (newrule->af == AF_INET) {				pool_put(&pf_rule_pl, newrule);				error = EAFNOSUPPORT;				break;			}#endif /* INET */#ifndef INET6			if (newrule->af == AF_INET6) {				pool_put(&pf_rule_pl, newrule);				error = EAFNOSUPPORT;				break;			}#endif /* INET6 */			newrule->ifp = NULL;			if (newrule->ifname[0]) {				newrule->ifp = ifunit(newrule->ifname);				if (newrule->ifp == NULL) {					pool_put(&pf_rule_pl, newrule);					error = EINVAL;					break;				}			}			newrule->evaluations = newrule->packets = 0;			newrule->bytes = 0;		}		s = splsoftnet();		if (pcr->action == PF_CHANGE_ADD_HEAD)			oldrule = TAILQ_FIRST(pf_rules_active);		else if (pcr->action == PF_CHANGE_ADD_TAIL)			oldrule = TAILQ_LAST(pf_rules_active, pf_rulequeue);		else {			oldrule = TAILQ_FIRST(pf_rules_active);			while ((oldrule != NULL) && pf_compare_rules(oldrule,			    &pcr->oldrule))				oldrule = TAILQ_NEXT(oldrule, entries);			if (oldrule == NULL) {				error = EINVAL;				splx(s);				break;			}		}		if (pcr->action == PF_CHANGE_REMOVE) {			struct pf_tree_node *n;			for (n = pf_tree_first(tree_ext_gwy); n != NULL;			    n = pf_tree_next(n))				if (n->state->rule == oldrule)					n->state->rule = NULL;			TAILQ_REMOVE(pf_rules_active, oldrule, entries);			pool_put(&pf_rule_pl, oldrule);		} else {			if (oldrule == NULL)				TAILQ_INSERT_TAIL(pf_rules_active, newrule,				    entries);			else if (pcr->action == PF_CHANGE_ADD_HEAD ||			    pcr->action == PF_CHANGE_ADD_BEFORE)				TAILQ_INSERT_BEFORE(oldrule, newrule, entries);			else				TAILQ_INSERT_AFTER(pf_rules_active, oldrule,				    newrule, entries);		}		TAILQ_FOREACH(oldrule, pf_rules_active, entries)			oldrule->nr = nr++;		pf_calc_skip_steps(pf_rules_active);		ticket_rules_active++;		splx(s);		break;	}	case DIOCBEGINNATS: {		u_int32_t *ticket = (u_int32_t *)addr;		struct pf_nat *nat;		while ((nat = TAILQ_FIRST(pf_nats_inactive)) != NULL) {			TAILQ_REMOVE(pf_nats_inactive, nat, entries);			pool_put(&pf_nat_pl, nat);		}		*ticket = ++ticket_nats_inactive;		break;	}	case DIOCADDNAT: {		struct pfioc_nat *pn = (struct pfioc_nat *)addr;		struct pf_nat *nat;		if (pn->ticket != ticket_nats_inactive) {			error = EBUSY;			break;		}		nat = pool_get(&pf_nat_pl, PR_NOWAIT);		if (nat == NULL) {			error = ENOMEM;			break;		}		bcopy(&pn->nat, nat, sizeof(struct pf_nat));#ifndef INET		if (nat->af == AF_INET) {			pool_put(&pf_nat_pl, nat);			error = EAFNOSUPPORT;			break;		}#endif /* INET */#ifndef INET6		if (nat->af == AF_INET6) {			pool_put(&pf_nat_pl, nat);			error = EAFNOSUPPORT;			break;		}#endif /* INET6 */		if (nat->ifname[0]) {			nat->ifp = ifunit(nat->ifname);			if (nat->ifp == NULL) {				pool_put(&pf_nat_pl, nat);				error = EINVAL;				break;			}		} else			nat->ifp = NULL;		TAILQ_INSERT_TAIL(pf_nats_inactive, nat, entries);		break;	}	case DIOCCOMMITNATS: {		u_int32_t *ticket = (u_int32_t *)addr;		struct pf_natqueue *old_nats;		struct pf_nat *nat;		if (*ticket != ticket_nats_inactive) {			error = EBUSY;			break;		}		/* Swap nats, keep the old. */		s = splsoftnet();		old_nats = pf_nats_active;		pf_nats_active = pf_nats_inactive;		pf_nats_inactive = old_nats;		ticket_nats_active = ticket_nats_inactive;		splx(s);		/* Purge the old nat list */		while ((nat = TAILQ_FIRST(old_nats)) != NULL) {			TAILQ_REMOVE(old_nats, nat, entries);			pool_put(&pf_nat_pl, nat);		}		break;	}	case DIOCGETNATS: {		struct pfioc_nat *pn = (struct pfioc_nat *)addr;		struct pf_nat *nat;		pn->nr = 0;		s = splsoftnet();		TAILQ_FOREACH(nat, pf_nats_active, entries)			pn->nr++;		pn->ticket = ticket_nats_active;		splx(s);		break;	}	case DIOCGETNAT: {		struct pfioc_nat *pn = (struct pfioc_nat *)addr;		struct pf_nat *nat;		u_int32_t nr;		if (pn->ticket != ticket_nats_active) {			error = EBUSY;			break;		}		nr = 0;		s = splsoftnet();		nat = TAILQ_FIRST(pf_nats_active);		while ((nat != NULL) && (nr < pn->nr)) {			nat = TAILQ_NEXT(nat, entries);			nr++;		}		if (nat == NULL) {			error = EBUSY;			splx(s);			break;		}		bcopy(nat, &pn->nat, sizeof(struct pf_nat));		splx(s);		break;	}	case DIOCCHANGENAT: {		struct pfioc_changenat *pcn = (struct pfioc_changenat *)addr;		struct pf_nat *oldnat = NULL, *newnat = NULL;		if (pcn->action < PF_CHANGE_ADD_HEAD ||		    pcn->action > PF_CHANGE_REMOVE) {			error = EINVAL;			break;		}		if (pcn->action != PF_CHANGE_REMOVE) {			newnat = pool_get(&pf_nat_pl, PR_NOWAIT);			if (newnat == NULL) {				error = ENOMEM;				break;			}			bcopy(&pcn->newnat, newnat, sizeof(struct pf_nat));#ifndef INET			if (newnat->af == AF_INET) {				pool_put(&pf_nat_pl, newnat);				error = EAFNOSUPPORT;				break;			}#endif /* INET */#ifndef INET6			if (newnat->af == AF_INET6) {				pool_put(&pf_nat_pl, newnat);				error = EAFNOSUPPORT;				break;			}#endif /* INET6 */			newnat->ifp = NULL;			if (newnat->ifname[0]) {				newnat->ifp = ifunit(newnat->ifname);				if (newnat->ifp == NULL) {					pool_put(&pf_nat_pl, newnat);					error = EINVAL;					break;				}			}		}		s = splsoftnet();		if (pcn->action == PF_CHANGE_ADD_HEAD)			oldnat = TAILQ_FIRST(pf_nats_active);		else if (pcn->action == PF_CHANGE_ADD_TAIL)			oldnat = TAILQ_LAST(pf_nats_active, pf_natqueue);		else {			oldnat = TAILQ_FIRST(pf_nats_active);			while ((oldnat != NULL) && pf_compare_nats(oldnat,			    &pcn->oldnat))				oldnat = TAILQ_NEXT(oldnat, entries);			if (oldnat == NULL) {				error = EINVAL;				splx(s);				break;			}		}		if (pcn->action == PF_CHANGE_REMOVE) {			TAILQ_REMOVE(pf_nats_active, oldnat, entries);			pool_put(&pf_nat_pl, oldnat);		} else {			if (oldnat == NULL)				TAILQ_INSERT_TAIL(pf_nats_active, newnat,				    entries);			else if (pcn->action == PF_CHANGE_ADD_HEAD ||			    pcn->action == PF_CHANGE_ADD_BEFORE)				TAILQ_INSERT_BEFORE(oldnat, newnat, entries);			else				TAILQ_INSERT_AFTER(pf_nats_active, oldnat,				    newnat, entries);		}		ticket_nats_active++;		splx(s);		break;	}	case DIOCBEGINBINATS: {		u_int32_t *ticket = (u_int32_t *)addr;		struct pf_binat *binat;		while ((binat = TAILQ_FIRST(pf_binats_inactive)) != NULL) {			TAILQ_REMOVE(pf_binats_inactive, binat, entries);			pool_put(&pf_binat_pl, binat);		}		*ticket = ++ticket_binats_inactive;		break;	}	case DIOCADDBINAT: {		struct pfioc_binat *pb = (struct pfioc_binat *)addr;		struct pf_binat *binat;		if (pb->ticket != ticket_binats_inactive) {			error = EBUSY;			break;		}		binat = pool_get(&pf_binat_pl, PR_NOWAIT);		if (binat == NULL) {			error = ENOMEM;			break;		}		bcopy(&pb->binat, binat, sizeof(struct pf_binat));#ifndef INET		if (binat->af == AF_INET) {			pool_put(&pf_binat_pl, binat);			error = EAFNOSUPPORT;			break;		}#endif /* INET */#ifndef INET6		if (binat->af == AF_INET6) {			pool_put(&pf_binat_pl, binat);			error = EAFNOSUPPORT;			break;		}#endif /* INET6 */		if (binat->ifname[0]) {			binat->ifp = ifunit(binat->ifname);			if (binat->ifp == NULL) {				pool_put(&pf_binat_pl, binat);				error = EINVAL;				break;			}		} else			binat->ifp = NULL;		TAILQ_INSERT_TAIL(pf_binats_inactive, binat, entries);		break;	}	case DIOCCOMMITBINATS: {		u_int32_t *ticket = (u_int32_t *)addr;		struct pf_binatqueue *old_binats;		struct pf_binat *binat;		if (*ticket != ticket_binats_inactive) {			error = EBUSY;			break;		}		/* Swap binats, keep the old. */		s = splsoftnet();		old_binats = pf_binats_active;		pf_binats_active = pf_binats_inactive;		pf_binats_inactive = old_binats;		ticket_binats_active = ticket_binats_inactive;		splx(s);		/* Purge the old binat list */		while ((binat = TAILQ_FIRST(old_binats)) != NULL) {			TAILQ_REMOVE(old_binats, binat, entries);			pool_put(&pf_binat_pl, binat);		}		break;	}	case DIOCGETBINATS: {		struct pfioc_binat *pb = (struct pfioc_binat *)addr;		struct pf_binat *binat;		pb->nr = 0;		s = splsoftnet();		TAILQ_FOREACH(binat, pf_binats_active, entries)			pb->nr++;		pb->ticket = ticket_binats_active;		splx(s);		break;	}	case DIOCGETBINAT: {		struct pfioc_binat *pb = (struct pfioc_binat *)addr;		struct pf_binat *binat;		u_int32_t nr;		if (pb->ticket != ticket_binats_active) {			error = EBUSY;			break;		}		nr = 0;		s = splsoftnet();		binat = TAILQ_FIRST(pf_binats_active);		while ((binat != NULL) && (nr < pb->nr)) {			binat = TAILQ_NEXT(binat, entries);			nr++;		}		if (binat == NULL) {			error = EBUSY;			splx(s);			break;		}		bcopy(binat, &pb->binat, sizeof(struct pf_binat));		splx(s);		break;	}	case DIOCCHANGEBINAT: {		struct pfioc_changebinat *pcn = (struct pfioc_changebinat *)addr;		struct pf_binat *oldbinat = NULL, *newbinat = NULL;		if (pcn->action < PF_CHANGE_ADD_HEAD ||		    pcn->action > PF_CHANGE_REMOVE) {			error = EINVAL;			break;		}		if (pcn->action != PF_CHANGE_REMOVE) {			newbinat = pool_get(&pf_binat_pl, PR_NOWAIT);			if (newbinat == NULL) {				error = ENOMEM;				break;			}			bcopy(&pcn->newbinat, newbinat,				sizeof(struct pf_binat));#ifndef INET			if (newbinat->af == AF_INET) {				pool_put(&pf_binat_pl, newbinat);				error = EAFNOSUPPORT;				break;			}#endif /* INET */#ifndef INET6			if (newbinat->af == AF_INET6) {				pool_put(&pf_binat_pl, newbinat);				error = EAFNOSUPPORT;				break;			}#endif /* INET6 */			newbinat->ifp = NULL;			if (newbinat->ifname[0]) {				newbinat->ifp = ifunit(newbinat->ifname);				if (newbinat->ifp == NULL) {					pool_put(&pf_binat_pl, newbinat);					error = EINVAL;					break;				}			}		}		s = splsoftnet();		if (pcn->action == PF_CHANGE_ADD_HEAD)			oldbinat = TAILQ_FIRST(pf_binats_active);		else if (pcn->action == PF_CHANGE_ADD_TAIL)			oldbinat = TAILQ_LAST(pf_binats_active, pf_binatqueue);		else {			oldbinat = TAILQ_FIRST(pf_binats_active);			while ((oldbinat != NULL) && pf_compare_binats(oldbinat,			    &pcn->oldbinat))				oldbinat = TAILQ_NEXT(oldbinat, entries);			if (oldbinat == NULL) {				error = EINVAL;				splx(s);				break;			}		}		if (pcn->action == PF_CHANGE_REMOVE) {			TAILQ_REMOVE(pf_binats_active, oldbinat, entries);			pool_put(&pf_binat_pl, oldbinat);		} else {			if (oldbinat == NULL)				TAILQ_INSERT_TAIL(pf_binats_active, newbinat,				    entries);			else if (pcn->action == PF_CHANGE_ADD_HEAD ||			    pcn->action == PF_CHANGE_ADD_BEFORE)				TAILQ_INSERT_BEFORE(oldbinat, newbinat,				    entries);			else				TAILQ_INSERT_AFTER(pf_binats_active, oldbinat,				    newbinat, entries);		}		ticket_binats_active++;		splx(s);		break;	}	case DIOCBEGINRDRS: {		u_int32_t *ticket = (u_int32_t *)addr;		struct pf_rdr *rdr;		while ((rdr = TAILQ_FIRST(pf_rdrs_inactive)) != NULL) {			TAILQ_REMOVE(pf_rdrs_inactive, rdr, entries);			pool_put(&pf_rdr_pl, rdr);		}		*ticket = ++ticket_rdrs_inactive;		break;	}	case DIOCADDRDR: {		struct pfioc_rdr *pr = (struct pfioc_rdr *)addr;		struct pf_rdr *rdr;		if (pr->ticket != ticket_rdrs_inactive) {			error = EBUSY;			break;		}		rdr = pool_get(&pf_rdr_pl, PR_NOWAIT);		if (rdr == NULL) {			error = ENOMEM;			break;		}		bcopy(&pr->rdr, rdr, sizeof(struct pf_rdr));#ifndef INET		if (rdr->af == AF_INET) {			pool_put(&pf_rdr_pl, rdr);			error = EAFNOSUPPORT;			break;		}#endif /* INET */#ifndef INET6		if (rdr->af == AF_INET6) {			pool_put(&pf_rdr_pl, rdr);			error = EAFNOSUPPORT;			break;		}#endif /* INET6 */		if (rdr->ifname[0]) {			rdr->ifp = ifunit(rdr->ifname);			if (rdr->ifp == NULL) {				pool_put(&pf_rdr_pl, rdr);				error = EINVAL;				break;			}		} else			rdr->ifp = NULL;		TAILQ_INSERT_TAIL(pf_rdrs_inactive, rdr, entries);		break;	}	case DIOCCOMMITRDRS: {		u_int32_t *ticket = (u_int32_t *)addr;		struct pf_rdrqueue *old_rdrs;		struct pf_rdr *rdr;		if (*ticket != ticket_rdrs_inactive) {			error = EBUSY;			break;		}		/* Swap rdrs, keep the old. */		s = splsoftnet();		old_rdrs = pf_rdrs_active;		pf_rdrs_active = pf_rdrs_inactive;		pf_rdrs_inactive = old_rdrs;		ticket_rdrs_active = ticket_rdrs_inactive;		splx(s);		/* Purge the old rdr list */		while ((rdr = TAILQ_FIRST(old_rdrs)) != NULL) {			TAILQ_REMOVE(old_rdrs, rdr, entries);			pool_put(&pf_rdr_pl, rdr);		}		break;	}	case DIOCGETRDRS: {		struct pfioc_rdr *pr = (struct pfioc_rdr *)addr;		struct pf_rdr *rdr;		pr->nr = 0;		s = splsoftnet();		TAILQ_FOREACH(rdr, pf_rdrs_active, entries)			pr->nr++;		pr->ticket = ticket_rdrs_active;		splx(s);		break;	}	case DIOCGETRDR: {		struct pfioc_rdr *pr = (struct pfioc_rdr *)addr;		struct pf_rdr *rdr;		u_int32_t nr;		if (pr->ticket != ticket_rdrs_active) {			error = EBUSY;			break;		}		nr = 0;		s = splsoftnet();		rdr = TAILQ_FIRST(pf_rdrs_active);		while ((rdr != NULL) && (nr < pr->nr)) {			rdr = TAILQ_NEXT(rdr, entries);			nr++;		}		if (rdr == NULL) {			error = EBUSY;			splx(s);			break;		}		bcopy(rdr, &pr->rdr, sizeof(struct pf_rdr));		splx(s);		break;	}	case DIOCCHANGERDR: {		struct pfioc_changerdr *pcn = (struct pfioc_changerdr *)addr;		struct pf_rdr *oldrdr = NULL, *newrdr = NULL;		if (pcn->action < PF_CHANGE_ADD_HEAD ||		    pcn->action > PF_CHANGE_REMOVE) {			error = EINVAL;			break;		}		if (pcn->action != PF_CHANGE_REMOVE) {			newrdr = pool_get(&pf_rdr_pl, PR_NOWAIT);			if (newrdr == NULL) {				error = ENOMEM;				break;			}			bcopy(&pcn->newrdr, newrdr, sizeof(struct pf_rdr));#ifndef INET			if (newrdr->af == AF_INET) {				pool_put(&pf_rdr_pl, newrdr);				error = EAFNOSUPPORT;				break;			}#endif /* INET */#ifndef INET6			if (newrdr->af == AF_INET6) {				pool_put(&pf_rdr_pl, newrdr);				error = EAFNOSUPPORT;				break;			}#endif /* INET6 */			newrdr->ifp = NULL;			if (newrdr->ifname[0]) {				newrdr->ifp = ifunit(newrdr->ifname);				if (newrdr->ifp == NULL) {					pool_put(&pf_rdr_pl, newrdr);					error = EINVAL;					break;				}			}		}		s = splsoftnet();		if (pcn->action == PF_CHANGE_ADD_HEAD)			oldrdr = TAILQ_FIRST(pf_rdrs_active);		else if (pcn->action == PF_CHANGE_ADD_TAIL)			oldrdr = TAILQ_LAST(pf_rdrs_active, pf_rdrqueue);		else {			oldrdr = TAILQ_FIRST(pf_rdrs_active);			while ((oldrdr != NULL) && pf_compare_rdrs(oldrdr,			    &pcn->oldrdr))				oldrdr = TAILQ_NEXT(oldrdr, entries);			if (oldrdr == NULL) {				error = EINVAL;				splx(s);				break;			}		}		if (pcn->action == PF_CHANGE_REMOVE) {			TAILQ_REMOVE(pf_rdrs_active, oldrdr, entries);			pool_put(&pf_rdr_pl, oldrdr);		} else {			if (oldrdr == NULL)				TAILQ_INSERT_TAIL(pf_rdrs_active, newrdr,				    entries);			else if (pcn->action == PF_CHANGE_ADD_HEAD ||			    pcn->action == PF_CHANGE_ADD_BEFORE)				TAILQ_INSERT_BEFORE(oldrdr, newrdr, entries);			else				TAILQ_INSERT_AFTER(pf_rdrs_active, oldrdr,				    newrdr, entries);		}		ticket_rdrs_active++;		splx(s);		break;	}	case DIOCCLRSTATES: {		struct pf_tree_node *n;		s = splsoftnet();		for (n = pf_tree_first(tree_ext_gwy); n != NULL;		    n = pf_tree_next(n))			n->state->expire = 0;		pf_purge_expired_states();		pf_status.states = 0;		splx(s);		break;	}	case DIOCGETSTATE: {		struct pfioc_state *ps = (struct pfioc_state *)addr;		struct pf_tree_node *n;		u_int32_t nr;		nr = 0;		s = splsoftnet();		n = pf_tree_first(tree_ext_gwy);		while ((n != NULL) && (nr < ps->nr)) {			n = pf_tree_next(n);			nr++;		}		if (n == NULL) {			error = EBUSY;			splx(s);			break;		}		bcopy(n->state, &ps->state, sizeof(struct pf_state));		splx(s);		microtime(&pftv);		ps->state.creation = pftv.tv_sec - ps->state.creation;		if (ps->state.expire <= pftv.tv_sec)			ps->state.expire = 0;		else			ps->state.expire -= pftv.tv_sec;		break;	}	case DIOCGETSTATES: {		struct pfioc_states *ps = (struct pfioc_states *)addr;		struct pf_tree_node *n;		struct pf_state *p, pstore;		u_int32_t nr = 0;		int space = ps->ps_len;		if (space == 0) {			s = splsoftnet();			n = pf_tree_first(tree_ext_gwy);			while (n != NULL) {				n = pf_tree_next(n);				nr++;			}			splx(s);			ps->ps_len = sizeof(struct pf_state) * nr;			return (0);		}		microtime(&pftv);		s = splsoftnet();		p = ps->ps_states;		n = pf_tree_first(tree_ext_gwy);		while (n && (nr + 1) * sizeof(*p) <= ps->ps_len) {			bcopy(n->state, &pstore, sizeof(pstore));			pstore.creation = pftv.tv_sec - pstore.creation;			if (pstore.expire <= pftv.tv_sec)				pstore.expire = 0;			else				pstore.expire -= pftv.tv_sec;			error = copyout(&pstore, p, sizeof(*p));			if (error) {				splx(s);				goto fail;			}			p++;			nr++;			n = pf_tree_next(n);		}		ps->ps_len = sizeof(struct pf_state) * nr;		splx(s);		break;	}	case DIOCSETSTATUSIF: {		struct pfioc_if *pi = (struct pfioc_if *)addr;		struct ifnet *ifp;		if ((ifp = ifunit(pi->ifname)) == NULL)			error = EINVAL;		else			status_ifp = ifp;		break;	}	case DIOCGETSTATUS: {		struct pf_status *s = (struct pf_status *)addr;		bcopy(&pf_status, s, sizeof(struct pf_status));		break;	}	case DIOCCLRSTATUS: {		u_int8_t running = pf_status.running;		u_int32_t states = pf_status.states;		bzero(&pf_status, sizeof(struct pf_status));		pf_status.running = running;		pf_status.states = states;		break;	}	case DIOCNATLOOK: {		struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr;		struct pf_state *st;		struct pf_tree_key key;		int direction = pnl->direction;		key.af = pnl->af;		key.proto = pnl->proto;		/*		 * userland gives us source and dest of connetion, reverse		 * the lookup so we ask for what happens with the return		 * traffic, enabling us to find it in the state tree.		 */		PF_ACPY(&key.addr[1], &pnl->saddr, pnl->af);		key.port[1] = pnl->sport;		PF_ACPY(&key.addr[0], &pnl->daddr, pnl->af);		key.port[0] = pnl->dport;		if (!pnl->proto ||		    PF_AZERO(&pnl->saddr, pnl->af) ||		    PF_AZERO(&pnl->daddr, pnl->af) ||		    !pnl->dport || !pnl->sport)			error = EINVAL;		else {			s = splsoftnet();			if (direction == PF_IN)				st = pf_find_state(tree_ext_gwy, &key);			else				st = pf_find_state(tree_lan_ext, &key);			if (st != NULL) {				if (direction  == PF_IN) {					PF_ACPY(&pnl->rsaddr, &st->lan.addr,					    st->af);					pnl->rsport = st->lan.port;					PF_ACPY(&pnl->rdaddr, &pnl->daddr,					    pnl->af);					pnl->rdport = pnl->dport;				} else {					PF_ACPY(&pnl->rdaddr, &st->gwy.addr,					    st->af);					pnl->rdport = st->gwy.port;					PF_ACPY(&pnl->rsaddr, &pnl->saddr,					    pnl->af);					pnl->rsport = pnl->sport;				}			} else				error = ENOENT;			splx(s);		}		break;	}	case DIOCSETTIMEOUT: {		struct pfioc_tm *pt = (struct pfioc_tm *)addr;		int old;		if (pt->timeout < 0 || pt->timeout >= PFTM_MAX ||		    pt->seconds < 0) {			error = EINVAL;			goto fail;		}		old = *pftm_timeouts[pt->timeout];		*pftm_timeouts[pt->timeout] = pt->seconds;		pt->seconds = old;		break;	}	case DIOCGETTIMEOUT: {		struct pfioc_tm *pt = (struct pfioc_tm *)addr;		if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) {			error = EINVAL;			goto fail;		}		pt->seconds = *pftm_timeouts[pt->timeout];		break;	}	case DIOCSETDEBUG: {		u_int32_t *level = (u_int32_t *)addr;		pf_status.debug = *level;		break;	}	default:		error = ENODEV;		break;	}fail:	return (error);}