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;