From cc535c95ca97a44aaf74f7ff58cd2b4152f2f29b Mon Sep 17 00:00:00 2001 From: Will Andrews Date: Wed, 4 Jul 2018 03:36:46 +0000 Subject: [PATCH] Revert r335833. Several third-parties use at least some of these ioctls. While it would be better for regression testing if they were used in base (or at least in the test suite), it's currently not worth the trouble to push through removal. Submitted by: antoine, markj --- share/man/man4/pf.4 | 36 +++- sys/net/pfvar.h | 9 +- sys/netpfil/pf/pf_ioctl.c | 408 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 449 insertions(+), 4 deletions(-) diff --git a/share/man/man4/pf.4 b/share/man/man4/pf.4 index b9d3bfc7ca78..c009ba2bbfa3 100644 --- a/share/man/man4/pf.4 +++ b/share/man/man4/pf.4 @@ -139,8 +139,9 @@ Clear the buffer address pool and get a .Va ticket for subsequent .Dv DIOCADDADDR , +.Dv DIOCADDRULE , and -.Dv DIOCADDRULE +.Dv DIOCCHANGERULE calls. .It Dv DIOCADDADDR Fa "struct pfioc_pooladdr *pp" .Pp @@ -148,6 +149,8 @@ Add the pool address .Va addr to the buffer address pool to be used in the following .Dv DIOCADDRULE +or +.Dv DIOCCHANGERULE call. All other members of the structure are ignored. .It Dv DIOCADDRULE Fa "struct pfioc_rule *pr" @@ -316,6 +319,21 @@ This ioctl returns if the given anchor does not exist or .Er EBUSY if another process is concurrently updating a ruleset. +.It Dv DIOCADDSTATE Fa "struct pfioc_state *ps" +Add a state entry. +.Bd -literal +struct pfioc_state { + struct pfsync_state state; +}; +.Ed +.It Dv DIOCGETSTATE Fa "struct pfioc_state *ps" +Extract the entry identified by the +.Va id +and +.Va creatorid +fields of the +.Va state +structure from the state table. .It Dv DIOCKILLSTATES Fa "struct pfioc_state_kill *psk" Remove matching entries from the state table. This ioctl returns the number of killed states in @@ -421,6 +439,11 @@ On exit, is always set to the total size required to hold all state table entries (i.e., it is set to .Li sizeof(struct pf_state) * nr ) . +.It Dv DIOCCHANGERULE Fa "struct pfioc_rule *pcr" +Add or remove the +.Va rule +in the ruleset specified by +.Va rule.action . .Pp The type of operation to be performed is indicated by .Va action , @@ -452,6 +475,15 @@ indicates the rule number against which or .Dv PF_CHANGE_REMOVE actions are applied. +.\" It Dv DIOCCHANGEALTQ Fa "struct pfioc_altq *pcr" +.It Dv DIOCCHANGEADDR Fa "struct pfioc_pooladdr *pca" +Add or remove the pool address +.Va addr +from the rule specified by +.Va r_action , +.Va r_num , +and +.Va anchor . .It Dv DIOCSETTIMEOUT Fa "struct pfioc_tm *pt" .Bd -literal struct pfioc_tm { @@ -478,6 +510,8 @@ Get the state timeout of The value will be placed into the .Va seconds field. +.It Dv DIOCCLRRULECTRS +Clear per-rule statistics. .It Dv DIOCSETLIMIT Fa "struct pfioc_limit *pl" Set the hard limits on the memory pools used by the packet filter. .Bd -literal diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index d8c253ab7681..824b8ec3f488 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1385,17 +1385,19 @@ struct pfioc_iface { #define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) /* XXX cut 8 - 17 */ #define DIOCCLRSTATES _IOWR('D', 18, struct pfioc_state_kill) -/* XXX cut 19 */ +#define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) #define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if) #define DIOCGETSTATUS _IOWR('D', 21, struct pf_status) #define DIOCCLRSTATUS _IO ('D', 22) #define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) #define DIOCSETDEBUG _IOWR('D', 24, u_int32_t) #define DIOCGETSTATES _IOWR('D', 25, struct pfioc_states) +#define DIOCCHANGERULE _IOWR('D', 26, struct pfioc_rule) /* XXX cut 26 - 28 */ #define DIOCSETTIMEOUT _IOWR('D', 29, struct pfioc_tm) #define DIOCGETTIMEOUT _IOWR('D', 30, struct pfioc_tm) -/* XXX cut 31-38 */ +#define DIOCADDSTATE _IOWR('D', 37, struct pfioc_state) +#define DIOCCLRRULECTRS _IO ('D', 38) #define DIOCGETLIMIT _IOWR('D', 39, struct pfioc_limit) #define DIOCSETLIMIT _IOWR('D', 40, struct pfioc_limit) #define DIOCKILLSTATES _IOWR('D', 41, struct pfioc_state_kill) @@ -1404,12 +1406,13 @@ struct pfioc_iface { #define DIOCADDALTQ _IOWR('D', 45, struct pfioc_altq) #define DIOCGETALTQS _IOWR('D', 47, struct pfioc_altq) #define DIOCGETALTQ _IOWR('D', 48, struct pfioc_altq) -/* XXX cut 49 */ +#define DIOCCHANGEALTQ _IOWR('D', 49, struct pfioc_altq) #define DIOCGETQSTATS _IOWR('D', 50, struct pfioc_qstats) #define DIOCBEGINADDRS _IOWR('D', 51, struct pfioc_pooladdr) #define DIOCADDADDR _IOWR('D', 52, struct pfioc_pooladdr) #define DIOCGETADDRS _IOWR('D', 53, struct pfioc_pooladdr) #define DIOCGETADDR _IOWR('D', 54, struct pfioc_pooladdr) +#define DIOCCHANGEADDR _IOWR('D', 55, struct pfioc_pooladdr) /* XXX cut 55 - 57 */ #define DIOCGETRULESETS _IOWR('D', 58, struct pfioc_ruleset) #define DIOCGETRULESET _IOWR('D', 59, struct pfioc_ruleset) diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 577ba3616fbc..e0383ce31473 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -1003,6 +1003,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td case DIOCGETRULE: case DIOCGETADDRS: case DIOCGETADDR: + case DIOCGETSTATE: case DIOCSETSTATUSIF: case DIOCGETSTATUS: case DIOCCLRSTATUS: @@ -1010,6 +1011,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td case DIOCSETDEBUG: case DIOCGETSTATES: case DIOCGETTIMEOUT: + case DIOCCLRRULECTRS: case DIOCGETLIMIT: case DIOCGETALTQS: case DIOCGETALTQ: @@ -1052,6 +1054,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td case DIOCGETRULES: case DIOCGETADDRS: case DIOCGETADDR: + case DIOCGETSTATE: case DIOCGETSTATUS: case DIOCGETSTATES: case DIOCGETTIMEOUT: @@ -1393,6 +1396,227 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td break; } + case DIOCCHANGERULE: { + struct pfioc_rule *pcr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *oldrule = NULL, *newrule = NULL; + struct pfi_kif *kif = NULL; + struct pf_pooladdr *pa; + u_int32_t nr = 0; + int rs_num; + + if (pcr->action < PF_CHANGE_ADD_HEAD || + pcr->action > PF_CHANGE_GET_TICKET) { + error = EINVAL; + break; + } + if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { + error = EINVAL; + break; + } + + if (pcr->action != PF_CHANGE_REMOVE) { +#ifndef INET + if (pcr->rule.af == AF_INET) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (pcr->rule.af == AF_INET6) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + newrule = malloc(sizeof(*newrule), M_PFRULE, M_WAITOK); + bcopy(&pcr->rule, newrule, sizeof(struct pf_rule)); + if (newrule->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + newrule->states_cur = counter_u64_alloc(M_WAITOK); + newrule->states_tot = counter_u64_alloc(M_WAITOK); + newrule->src_nodes = counter_u64_alloc(M_WAITOK); + newrule->cuid = td->td_ucred->cr_ruid; + newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0; + TAILQ_INIT(&newrule->rpool.list); + } + +#define ERROUT(x) { error = (x); goto DIOCCHANGERULE_error; } + + PF_RULES_WLOCK(); + if (!(pcr->action == PF_CHANGE_REMOVE || + pcr->action == PF_CHANGE_GET_TICKET) && + pcr->pool_ticket != V_ticket_pabuf) + ERROUT(EBUSY); + + ruleset = pf_find_ruleset(pcr->anchor); + if (ruleset == NULL) + ERROUT(EINVAL); + + rs_num = pf_get_ruleset_number(pcr->rule.action); + if (rs_num >= PF_RULESET_MAX) + ERROUT(EINVAL); + + if (pcr->action == PF_CHANGE_GET_TICKET) { + pcr->ticket = ++ruleset->rules[rs_num].active.ticket; + ERROUT(0); + } else if (pcr->ticket != + ruleset->rules[rs_num].active.ticket) + ERROUT(EINVAL); + + if (pcr->action != PF_CHANGE_REMOVE) { + if (newrule->ifname[0]) { + newrule->kif = pfi_kif_attach(kif, + newrule->ifname); + pfi_kif_ref(newrule->kif); + } else + newrule->kif = NULL; + + if (newrule->rtableid > 0 && + newrule->rtableid >= rt_numfibs) + error = EBUSY; + +#ifdef ALTQ + /* set queue IDs */ + if (newrule->qname[0] != 0) { + if ((newrule->qid = + pf_qname2qid(newrule->qname)) == 0) + error = EBUSY; + else if (newrule->pqname[0] != 0) { + if ((newrule->pqid = + pf_qname2qid(newrule->pqname)) == 0) + error = EBUSY; + } else + newrule->pqid = newrule->qid; + } +#endif /* ALTQ */ + if (newrule->tagname[0]) + if ((newrule->tag = + pf_tagname2tag(newrule->tagname)) == 0) + error = EBUSY; + if (newrule->match_tagname[0]) + if ((newrule->match_tag = pf_tagname2tag( + newrule->match_tagname)) == 0) + error = EBUSY; + if (newrule->rt && !newrule->direction) + error = EINVAL; + if (!newrule->log) + newrule->logif = 0; + if (newrule->logif >= PFLOGIFS_MAX) + error = EINVAL; + if (pf_addr_setup(ruleset, &newrule->src.addr, newrule->af)) + error = ENOMEM; + if (pf_addr_setup(ruleset, &newrule->dst.addr, newrule->af)) + error = ENOMEM; + if (pf_anchor_setup(newrule, ruleset, pcr->anchor_call)) + error = EINVAL; + TAILQ_FOREACH(pa, &V_pf_pabuf, entries) + if (pa->addr.type == PF_ADDR_TABLE) { + pa->addr.p.tbl = + pfr_attach_table(ruleset, + pa->addr.v.tblname); + if (pa->addr.p.tbl == NULL) + error = ENOMEM; + } + + newrule->overload_tbl = NULL; + if (newrule->overload_tblname[0]) { + if ((newrule->overload_tbl = pfr_attach_table( + ruleset, newrule->overload_tblname)) == + NULL) + error = EINVAL; + else + newrule->overload_tbl->pfrkt_flags |= + PFR_TFLAG_ACTIVE; + } + + pf_mv_pool(&V_pf_pabuf, &newrule->rpool.list); + if (((((newrule->action == PF_NAT) || + (newrule->action == PF_RDR) || + (newrule->action == PF_BINAT) || + (newrule->rt > PF_NOPFROUTE)) && + !newrule->anchor)) && + (TAILQ_FIRST(&newrule->rpool.list) == NULL)) + error = EINVAL; + + if (error) { + pf_free_rule(newrule); + PF_RULES_WUNLOCK(); + break; + } + + newrule->rpool.cur = TAILQ_FIRST(&newrule->rpool.list); + newrule->evaluations = 0; + newrule->packets[0] = newrule->packets[1] = 0; + newrule->bytes[0] = newrule->bytes[1] = 0; + } + pf_empty_pool(&V_pf_pabuf); + + if (pcr->action == PF_CHANGE_ADD_HEAD) + oldrule = TAILQ_FIRST( + ruleset->rules[rs_num].active.ptr); + else if (pcr->action == PF_CHANGE_ADD_TAIL) + oldrule = TAILQ_LAST( + ruleset->rules[rs_num].active.ptr, pf_rulequeue); + else { + oldrule = TAILQ_FIRST( + ruleset->rules[rs_num].active.ptr); + while ((oldrule != NULL) && (oldrule->nr != pcr->nr)) + oldrule = TAILQ_NEXT(oldrule, entries); + if (oldrule == NULL) { + if (newrule != NULL) + pf_free_rule(newrule); + PF_RULES_WUNLOCK(); + error = EINVAL; + break; + } + } + + if (pcr->action == PF_CHANGE_REMOVE) { + pf_unlink_rule(ruleset->rules[rs_num].active.ptr, + oldrule); + ruleset->rules[rs_num].active.rcount--; + } else { + if (oldrule == NULL) + TAILQ_INSERT_TAIL( + ruleset->rules[rs_num].active.ptr, + 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( + ruleset->rules[rs_num].active.ptr, + oldrule, newrule, entries); + ruleset->rules[rs_num].active.rcount++; + } + + nr = 0; + TAILQ_FOREACH(oldrule, + ruleset->rules[rs_num].active.ptr, entries) + oldrule->nr = nr++; + + ruleset->rules[rs_num].active.ticket++; + + pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); + pf_remove_if_empty_ruleset(ruleset); + + PF_RULES_WUNLOCK(); + break; + +#undef ERROUT +DIOCCHANGERULE_error: + PF_RULES_WUNLOCK(); + if (newrule != NULL) { + counter_u64_free(newrule->states_cur); + counter_u64_free(newrule->states_tot); + counter_u64_free(newrule->src_nodes); + free(newrule, M_PFRULE); + } + if (kif != NULL) + free(kif, PFI_MTYPE); + break; + } + case DIOCCLRSTATES: { struct pf_state *s; struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; @@ -1499,6 +1723,38 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td break; } + case DIOCADDSTATE: { + struct pfioc_state *ps = (struct pfioc_state *)addr; + struct pfsync_state *sp = &ps->state; + + if (sp->timeout >= PFTM_MAX) { + error = EINVAL; + break; + } + if (pfsync_state_import_ptr != NULL) { + PF_RULES_RLOCK(); + error = pfsync_state_import_ptr(sp, PFSYNC_SI_IOCTL); + PF_RULES_RUNLOCK(); + } else + error = EOPNOTSUPP; + break; + } + + case DIOCGETSTATE: { + struct pfioc_state *ps = (struct pfioc_state *)addr; + struct pf_state *s; + + s = pf_find_state_byid(ps->state.id, ps->state.creatorid); + if (s == NULL) { + error = ENOENT; + break; + } + + pfsync_state_export(&ps->state, s); + PF_STATE_UNLOCK(s); + break; + } + case DIOCGETSTATES: { struct pfioc_states *ps = (struct pfioc_states *)addr; struct pf_state *s; @@ -1729,6 +1985,22 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td break; } + case DIOCCLRRULECTRS: { + /* obsoleted by DIOCGETRULE with action=PF_GET_CLR_CNTR */ + struct pf_ruleset *ruleset = &pf_main_ruleset; + struct pf_rule *rule; + + PF_RULES_WLOCK(); + TAILQ_FOREACH(rule, + ruleset->rules[PF_RULESET_FILTER].active.ptr, entries) { + rule->evaluations = 0; + rule->packets[0] = rule->packets[1] = 0; + rule->bytes[0] = rule->bytes[1] = 0; + } + PF_RULES_WUNLOCK(); + break; + } + case DIOCGIFSPEED: { struct pf_ifspeed *psp = (struct pf_ifspeed *)addr; struct pf_ifspeed ps; @@ -1883,6 +2155,11 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td break; } + case DIOCCHANGEALTQ: + /* CHANGEALTQ not supported yet! */ + error = ENODEV; + break; + case DIOCGETQSTATS: { struct pfioc_qstats *pq = (struct pfioc_qstats *)addr; struct pf_altq *altq; @@ -2037,6 +2314,137 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td break; } + case DIOCCHANGEADDR: { + struct pfioc_pooladdr *pca = (struct pfioc_pooladdr *)addr; + struct pf_pool *pool; + struct pf_pooladdr *oldpa = NULL, *newpa = NULL; + struct pf_ruleset *ruleset; + struct pfi_kif *kif = NULL; + + if (pca->action < PF_CHANGE_ADD_HEAD || + pca->action > PF_CHANGE_REMOVE) { + error = EINVAL; + break; + } + if (pca->addr.addr.type != PF_ADDR_ADDRMASK && + pca->addr.addr.type != PF_ADDR_DYNIFTL && + pca->addr.addr.type != PF_ADDR_TABLE) { + error = EINVAL; + break; + } + + if (pca->action != PF_CHANGE_REMOVE) { +#ifndef INET + if (pca->af == AF_INET) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (pca->af == AF_INET6) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + newpa = malloc(sizeof(*newpa), M_PFRULE, M_WAITOK); + bcopy(&pca->addr, newpa, sizeof(struct pf_pooladdr)); + if (newpa->ifname[0]) + kif = malloc(sizeof(*kif), PFI_MTYPE, M_WAITOK); + newpa->kif = NULL; + } + +#define ERROUT(x) { error = (x); goto DIOCCHANGEADDR_error; } + PF_RULES_WLOCK(); + ruleset = pf_find_ruleset(pca->anchor); + if (ruleset == NULL) + ERROUT(EBUSY); + + pool = pf_get_pool(pca->anchor, pca->ticket, pca->r_action, + pca->r_num, pca->r_last, 1, 1); + if (pool == NULL) + ERROUT(EBUSY); + + if (pca->action != PF_CHANGE_REMOVE) { + if (newpa->ifname[0]) { + newpa->kif = pfi_kif_attach(kif, newpa->ifname); + pfi_kif_ref(newpa->kif); + kif = NULL; + } + + switch (newpa->addr.type) { + case PF_ADDR_DYNIFTL: + error = pfi_dynaddr_setup(&newpa->addr, + pca->af); + break; + case PF_ADDR_TABLE: + newpa->addr.p.tbl = pfr_attach_table(ruleset, + newpa->addr.v.tblname); + if (newpa->addr.p.tbl == NULL) + error = ENOMEM; + break; + } + if (error) + goto DIOCCHANGEADDR_error; + } + + switch (pca->action) { + case PF_CHANGE_ADD_HEAD: + oldpa = TAILQ_FIRST(&pool->list); + break; + case PF_CHANGE_ADD_TAIL: + oldpa = TAILQ_LAST(&pool->list, pf_palist); + break; + default: + oldpa = TAILQ_FIRST(&pool->list); + for (int i = 0; oldpa && i < pca->nr; i++) + oldpa = TAILQ_NEXT(oldpa, entries); + + if (oldpa == NULL) + ERROUT(EINVAL); + } + + if (pca->action == PF_CHANGE_REMOVE) { + TAILQ_REMOVE(&pool->list, oldpa, entries); + switch (oldpa->addr.type) { + case PF_ADDR_DYNIFTL: + pfi_dynaddr_remove(oldpa->addr.p.dyn); + break; + case PF_ADDR_TABLE: + pfr_detach_table(oldpa->addr.p.tbl); + break; + } + if (oldpa->kif) + pfi_kif_unref(oldpa->kif); + free(oldpa, M_PFRULE); + } else { + if (oldpa == NULL) + TAILQ_INSERT_TAIL(&pool->list, newpa, entries); + else if (pca->action == PF_CHANGE_ADD_HEAD || + pca->action == PF_CHANGE_ADD_BEFORE) + TAILQ_INSERT_BEFORE(oldpa, newpa, entries); + else + TAILQ_INSERT_AFTER(&pool->list, oldpa, + newpa, entries); + } + + pool->cur = TAILQ_FIRST(&pool->list); + PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr, pca->af); + PF_RULES_WUNLOCK(); + break; + +#undef ERROUT +DIOCCHANGEADDR_error: + if (newpa != NULL) { + if (newpa->kif) + pfi_kif_unref(newpa->kif); + free(newpa, M_PFRULE); + } + PF_RULES_WUNLOCK(); + if (kif != NULL) + free(kif, PFI_MTYPE); + break; + } + case DIOCGETRULESETS: { struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; struct pf_ruleset *ruleset;