Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -4525,6 +4525,7 @@ netpfil/pf/pf_ioctl.c optional pf inet netpfil/pf/pf_lb.c optional pf inet netpfil/pf/pf_norm.c optional pf inet +netpfil/pf/pf_nv.c optional pf inet netpfil/pf/pf_osfp.c optional pf inet netpfil/pf/pf_ruleset.c optional pf inet netpfil/pf/pf_table.c optional pf inet Index: sys/modules/pf/Makefile =================================================================== --- sys/modules/pf/Makefile +++ sys/modules/pf/Makefile @@ -4,7 +4,7 @@ KMOD= pf SRCS= pf.c pf_if.c pf_lb.c pf_osfp.c pf_ioctl.c pf_norm.c pf_table.c \ - pf_ruleset.c in4_cksum.c \ + pf_ruleset.c pf_nv.c in4_cksum.c \ bus_if.h device_if.h \ opt_pf.h opt_inet.h opt_inet6.h opt_bpf.h opt_sctp.h opt_global.h Index: sys/net/pfvar.h =================================================================== --- sys/net/pfvar.h +++ sys/net/pfvar.h @@ -1231,6 +1231,7 @@ #define DIOCSTART _IO ('D', 1) #define DIOCSTOP _IO ('D', 2) #define DIOCADDRULE _IOWR('D', 4, struct pfioc_rule) +#define DIOCADDRULENV _IOWR('D', 4, struct pfioc_nv) #define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) #define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) /* XXX cut 8 - 17 */ Index: sys/netpfil/pf/pf.h =================================================================== --- sys/netpfil/pf/pf.h +++ sys/netpfil/pf/pf.h @@ -187,6 +187,12 @@ #define PF_TABLE_NAME_SIZE 32 #define PF_QNAME_SIZE 64 +struct pfioc_nv { + void *data; + size_t len; /* The length of the nvlist data. */ + size_t size; /* The total size of the data buffer. */ +}; + struct pf_rule; /* keep synced with pfi_kif, used in RB_FIND */ Index: sys/netpfil/pf/pf_ioctl.c =================================================================== --- sys/netpfil/pf/pf_ioctl.c +++ sys/netpfil/pf/pf_ioctl.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,8 @@ #include #endif +#include "pf_nv.h" + static struct pf_kpool *pf_get_kpool(char *, u_int32_t, u_int8_t, u_int32_t, u_int8_t, u_int8_t, u_int8_t); @@ -1623,6 +1626,293 @@ return (0); } +static int +pf_nvaddr_to_addr(const nvlist_t *nvl, struct pf_addr *paddr) +{ + return (pf_nvbinary(nvl, "addr", paddr, sizeof(struct in6_addr))); +} + +static int +pf_nvpool_to_pool(const nvlist_t *nvl, struct pf_kpool *kpool) +{ + int error = 0; + + bzero(kpool, sizeof(*kpool)); + + PFNV_CHK(pf_nvbinary(nvl, "key", &kpool->key, sizeof(kpool->key))); + + if (nvlist_exists_nvlist(nvl, "counter")) { + PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "counter"), + &kpool->counter)); + } + + PFNV_CHK(pf_nvint(nvl, "tblidx", &kpool->tblidx)); + PFNV_CHK(pf_nvuint_16_array(nvl, "proxy_port", kpool->proxy_port, 2, + NULL)); + PFNV_CHK(pf_nvuint_8(nvl, "opts", &kpool->opts)); + +errout: + return (error); +} + +static int +pf_nvaddr_wrap_to_addr_wrap(const nvlist_t *nvl, struct pf_addr_wrap *addr) +{ + int error = 0; + + bzero(addr, sizeof(*addr)); + + PFNV_CHK(pf_nvuint_8(nvl, "type", &addr->type)); + PFNV_CHK(pf_nvuint_8(nvl, "iflags", &addr->iflags)); + PFNV_CHK(pf_nvstring(nvl, "ifname", addr->v.ifname, + sizeof(addr->v.ifname))); + PFNV_CHK(pf_nvstring(nvl, "tblname", addr->v.tblname, + sizeof(addr->v.tblname))); + + if (! nvlist_exists_nvlist(nvl, "addr")) + return (EINVAL); + PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), + &addr->v.a.addr)); + + if (! nvlist_exists_nvlist(nvl, "mask")) + return (EINVAL); + PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "mask"), + &addr->v.a.mask)); + + switch (addr->type) { + case PF_ADDR_DYNIFTL: + case PF_ADDR_TABLE: + case PF_ADDR_RANGE: + case PF_ADDR_ADDRMASK: + case PF_ADDR_NOROUTE: + case PF_ADDR_URPFFAILED: + break; + default: + return (EINVAL); + } + +errout: + return (error); +} + +static int +pf_validate_op(u_int8_t op) +{ + switch (op) { + case PF_OP_NONE: + case PF_OP_IRG: + case PF_OP_EQ: + case PF_OP_NE: + case PF_OP_LT: + case PF_OP_LE: + case PF_OP_GT: + case PF_OP_GE: + case PF_OP_XRG: + case PF_OP_RRG: + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr) +{ + int error = 0; + + if (! nvlist_exists_nvlist(nvl, "addr")) + return (EINVAL); + + PFNV_CHK(pf_nvaddr_wrap_to_addr_wrap(nvlist_get_nvlist(nvl, "addr"), + &addr->addr)); + PFNV_CHK(pf_nvuint_16_array(nvl, "port", addr->port, 2, NULL)); + PFNV_CHK(pf_nvuint_8(nvl, "neg", &addr->neg)); + PFNV_CHK(pf_nvuint_8(nvl, "port_op", &addr->port_op)); + + PFNV_CHK(pf_validate_op(addr->port_op)); + +errout: + return (error); +} + +static int +pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid) +{ + int error = 0; + + bzero(uid, sizeof(*uid)); + + PFNV_CHK(pf_nvuint_32_array(nvl, "uid", uid->uid, 2, NULL)); + PFNV_CHK(pf_nvuint_8(nvl, "op", &uid->op)); + + PFNV_CHK(pf_validate_op(uid->op)); + +errout: + return (error); +} + +static int +pf_nvrule_gid_to_rule_gid(const nvlist_t *nvl, struct pf_rule_gid *gid) +{ + /* Cheat a little. These stucts are the same, other than the name of + * the first field. */ + return (pf_nvrule_uid_to_rule_uid(nvl, (struct pf_rule_uid *)gid)); +} + +static int +pf_nvrule_to_krule(const nvlist_t *nvl, struct pf_krule **prule) +{ + struct pf_krule *rule; + int error = 0; + + rule = malloc(sizeof(*rule), M_PFRULE, M_WAITOK | M_ZERO); + + PFNV_CHK(pf_nvuint_32(nvl, "nr", &rule->nr)); + + if (! nvlist_exists_nvlist(nvl, "src")) { + error = EINVAL; + goto errout; + } + error = pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"), + &rule->src); + if (error != 0) + goto errout; + + if (! nvlist_exists_nvlist(nvl, "dst")) { + error = EINVAL; + goto errout; + } + PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"), + &rule->dst)); + + PFNV_CHK(pf_nvstring(nvl, "label", rule->label, sizeof(rule->label))); + PFNV_CHK(pf_nvstring(nvl, "ifname", rule->ifname, + sizeof(rule->ifname))); + PFNV_CHK(pf_nvstring(nvl, "qname", rule->qname, sizeof(rule->qname))); + PFNV_CHK(pf_nvstring(nvl, "pqname", rule->pqname, + sizeof(rule->pqname))); + PFNV_CHK(pf_nvstring(nvl, "tagname", rule->tagname, + sizeof(rule->tagname))); + PFNV_CHK(pf_nvstring(nvl, "match_tagname", rule->match_tagname, + sizeof(rule->match_tagname))); + PFNV_CHK(pf_nvstring(nvl, "overload_tblname", rule->overload_tblname, + sizeof(rule->overload_tblname))); + + if (! nvlist_exists_nvlist(nvl, "rpool")) + return (EINVAL); + PFNV_CHK(pf_nvpool_to_pool(nvlist_get_nvlist(nvl, "rpool"), + &rule->rpool)); + + PFNV_CHK(pf_nvuint_32(nvl, "os_fingerprint", &rule->os_fingerprint)); + + PFNV_CHK(pf_nvint(nvl, "rtableid", &rule->rtableid)); + PFNV_CHK(pf_nvuint_32_array(nvl, "timeout", rule->timeout, PFTM_MAX, NULL)); + PFNV_CHK(pf_nvuint_32(nvl, "max_states", &rule->max_states)); + PFNV_CHK(pf_nvuint_32(nvl, "max_src_nodes", &rule->max_src_nodes)); + PFNV_CHK(pf_nvuint_32(nvl, "max_src_states", &rule->max_src_states)); + PFNV_CHK(pf_nvuint_32(nvl, "max_src_conn", &rule->max_src_conn)); + PFNV_CHK(pf_nvuint_32(nvl, "max_src_conn_rate.limit", + &rule->max_src_conn_rate.limit)); + PFNV_CHK(pf_nvuint_32(nvl, "max_src_conn_rate.seconds", + &rule->max_src_conn_rate.seconds)); + PFNV_CHK(pf_nvuint_32(nvl, "rt_listid", &rule->rt_listid)); + PFNV_CHK(pf_nvuint_32(nvl, "prob", &rule->prob)); + PFNV_CHK(pf_nvuint_32(nvl, "cuid", &rule->cuid)); + PFNV_CHK(pf_nvuint_32(nvl, "cpid", &rule->cpid)); + + PFNV_CHK(pf_nvuint_16(nvl, "return_icmp", &rule->return_icmp)); + PFNV_CHK(pf_nvuint_16(nvl, "return_icmp6", &rule->return_icmp6)); + + PFNV_CHK(pf_nvuint_16(nvl, "max_mss", &rule->max_mss)); + PFNV_CHK(pf_nvuint_16(nvl, "scrub_flags", &rule->scrub_flags)); + + if (! nvlist_exists_nvlist(nvl, "uid")) { + error = EINVAL; + goto errout; + } + PFNV_CHK(pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "uid"), + &rule->uid)); + + if (! nvlist_exists_nvlist(nvl, "gid")) { + error = EINVAL; + goto errout; + } + PFNV_CHK(pf_nvrule_gid_to_rule_gid(nvlist_get_nvlist(nvl, "gid"), + &rule->gid)); + + PFNV_CHK(pf_nvuint_32(nvl, "rule_flag", &rule->rule_flag)); + PFNV_CHK(pf_nvuint_8(nvl, "action", &rule->action)); + PFNV_CHK(pf_nvuint_8(nvl, "direction", &rule->direction)); + PFNV_CHK(pf_nvuint_8(nvl, "log", &rule->log)); + PFNV_CHK(pf_nvuint_8(nvl, "logif", &rule->logif)); + PFNV_CHK(pf_nvuint_8(nvl, "quick", &rule->quick)); + PFNV_CHK(pf_nvuint_8(nvl, "ifnot", &rule->ifnot)); + PFNV_CHK(pf_nvuint_8(nvl, "match_tag_not", &rule->match_tag_not)); + PFNV_CHK(pf_nvuint_8(nvl, "natpass", &rule->natpass)); + + PFNV_CHK(pf_nvuint_8(nvl, "keep_state", &rule->keep_state)); + PFNV_CHK(pf_nvuint_8(nvl, "af", &rule->af)); + PFNV_CHK(pf_nvuint_8(nvl, "proto", &rule->proto)); + PFNV_CHK(pf_nvuint_8(nvl, "type", &rule->type)); + PFNV_CHK(pf_nvuint_8(nvl, "code", &rule->code)); + PFNV_CHK(pf_nvuint_8(nvl, "flags", &rule->flags)); + PFNV_CHK(pf_nvuint_8(nvl, "flagset", &rule->flagset)); + PFNV_CHK(pf_nvuint_8(nvl, "min_ttl", &rule->min_ttl)); + PFNV_CHK(pf_nvuint_8(nvl, "allow_opts", &rule->allow_opts)); + PFNV_CHK(pf_nvuint_8(nvl, "rt", &rule->rt)); + PFNV_CHK(pf_nvuint_8(nvl, "return_ttl", &rule->return_ttl)); + PFNV_CHK(pf_nvuint_8(nvl, "tos", &rule->tos)); + PFNV_CHK(pf_nvuint_8(nvl, "set_tos", &rule->set_tos)); + PFNV_CHK(pf_nvuint_8(nvl, "anchor_relative", &rule->anchor_relative)); + PFNV_CHK(pf_nvuint_8(nvl, "anchor_wildcard", &rule->anchor_wildcard)); + + PFNV_CHK(pf_nvuint_8(nvl, "flush", &rule->flush)); + PFNV_CHK(pf_nvuint_8(nvl, "prio", &rule->prio)); + + PFNV_CHK(pf_nvuint_8_array(nvl, "set_prio", &rule->prio, 2, NULL)); + + if (nvlist_exists_nvlist(nvl, "divert")) { + const nvlist_t *nvldivert = nvlist_get_nvlist(nvl, "divert"); + + if (! nvlist_exists_nvlist(nvldivert, "addr")) { + error = EINVAL; + goto errout; + } + PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvldivert, "addr"), + &rule->divert.addr)); + PFNV_CHK(pf_nvuint_16(nvldivert, "port", &rule->divert.port)); + } + + /* Validation */ +#ifndef INET + if (rule->af == AF_INET) { + error = EAFNOSUPPORT; + goto errout; + } +#endif /* INET */ +#ifndef INET6 + if (rule->af == AF_INET6) { + error = EAFNOSUPPORT; + goto errout; + } +#endif /* INET6 */ + + PFNV_CHK(pf_check_rule_addr(&rule->src)); + PFNV_CHK(pf_check_rule_addr(&rule->dst)); + + *prule = rule; + + return (0); + +errout: + pf_krule_free(rule); + *prule = NULL; + + return (error); +} + static int pf_rule_to_krule(const struct pf_rule *rule, struct pf_krule *krule) { @@ -1732,6 +2022,163 @@ return (0); } +static int +pf_ioctl_addrule(struct pf_krule *rule, u_int32_t ticket, + u_int32_t pool_ticket, const char *anchor, const char *anchor_call, + struct thread *td) +{ + struct pf_kruleset *ruleset; + struct pf_krule *tail; + struct pf_kpooladdr *pa; + struct pfi_kkif *kif = NULL; + int rs_num; + int error = 0; + + if ((rule->return_icmp >> 8) > ICMP_MAXTYPE) { + error = EINVAL; + goto errout_unlocked; + } + +#define ERROUT(x) { error = (x); goto errout; } + + if (rule->ifname[0]) + kif = pf_kkif_create(M_WAITOK); + rule->evaluations = counter_u64_alloc(M_WAITOK); + for (int i = 0; i < 2; i++) { + rule->packets[i] = counter_u64_alloc(M_WAITOK); + rule->bytes[i] = counter_u64_alloc(M_WAITOK); + } + rule->states_cur = counter_u64_alloc(M_WAITOK); + rule->states_tot = counter_u64_alloc(M_WAITOK); + rule->src_nodes = counter_u64_alloc(M_WAITOK); + rule->cuid = td->td_ucred->cr_ruid; + rule->cpid = td->td_proc ? td->td_proc->p_pid : 0; + TAILQ_INIT(&rule->rpool.list); + + PF_RULES_WLOCK(); + ruleset = pf_find_kruleset(anchor); + if (ruleset == NULL) + ERROUT(EINVAL); + rs_num = pf_get_ruleset_number(rule->action); + if (rs_num >= PF_RULESET_MAX) + ERROUT(EINVAL); + if (ticket != ruleset->rules[rs_num].inactive.ticket) { + DPFPRINTF(PF_DEBUG_MISC, + ("ticket: %d != [%d]%d\n", ticket, rs_num, + ruleset->rules[rs_num].inactive.ticket)); + ERROUT(EBUSY); + } + if (pool_ticket != V_ticket_pabuf) { + DPFPRINTF(PF_DEBUG_MISC, + ("pool_ticket: %d != %d\n", pool_ticket, + V_ticket_pabuf)); + ERROUT(EBUSY); + } + + tail = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, + pf_krulequeue); + if (tail) + rule->nr = tail->nr + 1; + else + rule->nr = 0; + if (rule->ifname[0]) { + rule->kif = pfi_kkif_attach(kif, rule->ifname); + pfi_kkif_ref(rule->kif); + } else + rule->kif = NULL; + + if (rule->rtableid > 0 && rule->rtableid >= rt_numfibs) + error = EBUSY; + +#ifdef ALTQ + /* set queue IDs */ + if (rule->qname[0] != 0) { + if ((rule->qid = pf_qname2qid(rule->qname)) == 0) + error = EBUSY; + else if (rule->pqname[0] != 0) { + if ((rule->pqid = + pf_qname2qid(rule->pqname)) == 0) + error = EBUSY; + } else + rule->pqid = rule->qid; + } +#endif + if (rule->tagname[0]) + if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0) + error = EBUSY; + if (rule->match_tagname[0]) + if ((rule->match_tag = + pf_tagname2tag(rule->match_tagname)) == 0) + error = EBUSY; + if (rule->rt && !rule->direction) + error = EINVAL; + if (!rule->log) + rule->logif = 0; + if (rule->logif >= PFLOGIFS_MAX) + error = EINVAL; + if (pf_addr_setup(ruleset, &rule->src.addr, rule->af)) + error = ENOMEM; + if (pf_addr_setup(ruleset, &rule->dst.addr, rule->af)) + error = ENOMEM; + if (pf_kanchor_setup(rule, ruleset, anchor_call)) + error = EINVAL; + if (rule->scrub_flags & PFSTATE_SETPRIO && + (rule->set_prio[0] > PF_PRIO_MAX || + rule->set_prio[1] > PF_PRIO_MAX)) + 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; + } + + rule->overload_tbl = NULL; + if (rule->overload_tblname[0]) { + if ((rule->overload_tbl = pfr_attach_table(ruleset, + rule->overload_tblname)) == NULL) + error = EINVAL; + else + rule->overload_tbl->pfrkt_flags |= + PFR_TFLAG_ACTIVE; + } + + pf_mv_kpool(&V_pf_pabuf, &rule->rpool.list); + if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) || + (rule->action == PF_BINAT)) && rule->anchor == NULL) || + (rule->rt > PF_NOPFROUTE)) && + (TAILQ_FIRST(&rule->rpool.list) == NULL)) + error = EINVAL; + + if (error) { + pf_free_rule(rule); + rule = NULL; + ERROUT(error); + } + + rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list); + counter_u64_zero(rule->evaluations); + for (int i = 0; i < 2; i++) { + counter_u64_zero(rule->packets[i]); + counter_u64_zero(rule->bytes[i]); + } + TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr, + rule, entries); + ruleset->rules[rs_num].inactive.rcount++; + PF_RULES_WUNLOCK(); + + return (0); + +#undef ERROUT +errout: + PF_RULES_WUNLOCK(); +errout_unlocked: + pf_kkif_free(kif); + pf_krule_free(rule); + return (error); +} + static int pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { @@ -1882,161 +2329,78 @@ } break; - case DIOCADDRULE: { - struct pfioc_rule *pr = (struct pfioc_rule *)addr; - struct pf_kruleset *ruleset; - struct pf_krule *rule, *tail; - struct pf_kpooladdr *pa; - struct pfi_kkif *kif = NULL; - int rs_num; + case DIOCADDRULENV: { + struct pfioc_nv *nv = (struct pfioc_nv *)addr; + nvlist_t *nvl = NULL; + void *nvlpacked = NULL; + struct pf_krule *rule = NULL; + const char *anchor = "", *anchor_call = ""; + u_int32_t ticket = 0, pool_ticket = 0; - if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { - error = EINVAL; - break; - } +#define ERROUT(x) do { error = (x); goto DIOCADDRULENV_error; } while (0) - rule = malloc(sizeof(*rule), M_PFRULE, M_WAITOK); - error = pf_rule_to_krule(&pr->rule, rule); - if (error != 0) { - free(rule, M_PFRULE); - break; - } + if (nv->len > pf_ioctl_maxcount) + ERROUT(ENOMEM); - if (rule->ifname[0]) - kif = pf_kkif_create(M_WAITOK); - rule->evaluations = counter_u64_alloc(M_WAITOK); - for (int i = 0; i < 2; i++) { - rule->packets[i] = counter_u64_alloc(M_WAITOK); - rule->bytes[i] = counter_u64_alloc(M_WAITOK); - } - rule->states_cur = counter_u64_alloc(M_WAITOK); - rule->states_tot = counter_u64_alloc(M_WAITOK); - rule->src_nodes = counter_u64_alloc(M_WAITOK); - rule->cuid = td->td_ucred->cr_ruid; - rule->cpid = td->td_proc ? td->td_proc->p_pid : 0; - TAILQ_INIT(&rule->rpool.list); -#define ERROUT(x) { error = (x); goto DIOCADDRULE_error; } + nvlpacked = malloc(nv->len, M_TEMP, M_WAITOK); + if (nvlpacked == NULL) + ERROUT(ENOMEM); - PF_RULES_WLOCK(); - pr->anchor[sizeof(pr->anchor) - 1] = 0; - ruleset = pf_find_kruleset(pr->anchor); - if (ruleset == NULL) + error = copyin(nv->data, nvlpacked, nv->len); + if (error) + ERROUT(error); + + nvl = nvlist_unpack(nvlpacked, nv->len, 0); + if (nvl == NULL) + ERROUT(EBADMSG); + + if (! nvlist_exists_number(nvl, "ticket")) ERROUT(EINVAL); - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) + ticket = nvlist_get_number(nvl, "ticket"); + + if (! nvlist_exists_number(nvl, "pool_ticket")) ERROUT(EINVAL); - if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { - DPFPRINTF(PF_DEBUG_MISC, - ("ticket: %d != [%d]%d\n", pr->ticket, rs_num, - ruleset->rules[rs_num].inactive.ticket)); - ERROUT(EBUSY); - } - if (pr->pool_ticket != V_ticket_pabuf) { - DPFPRINTF(PF_DEBUG_MISC, - ("pool_ticket: %d != %d\n", pr->pool_ticket, - V_ticket_pabuf)); - ERROUT(EBUSY); - } + pool_ticket = nvlist_get_number(nvl, "pool_ticket"); - tail = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, - pf_krulequeue); - if (tail) - rule->nr = tail->nr + 1; - else - rule->nr = 0; - if (rule->ifname[0]) { - rule->kif = pfi_kkif_attach(kif, rule->ifname); - pfi_kkif_ref(rule->kif); - } else - rule->kif = NULL; + if (! nvlist_exists_nvlist(nvl, "rule")) + ERROUT(EINVAL); - if (rule->rtableid > 0 && rule->rtableid >= rt_numfibs) - error = EBUSY; + error = pf_nvrule_to_krule(nvlist_get_nvlist(nvl, "rule"), + &rule); + if (error) + ERROUT(error); -#ifdef ALTQ - /* set queue IDs */ - if (rule->qname[0] != 0) { - if ((rule->qid = pf_qname2qid(rule->qname)) == 0) - error = EBUSY; - else if (rule->pqname[0] != 0) { - if ((rule->pqid = - pf_qname2qid(rule->pqname)) == 0) - error = EBUSY; - } else - rule->pqid = rule->qid; - } -#endif - if (rule->tagname[0]) - if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0) - error = EBUSY; - if (rule->match_tagname[0]) - if ((rule->match_tag = - pf_tagname2tag(rule->match_tagname)) == 0) - error = EBUSY; - if (rule->rt && !rule->direction) - error = EINVAL; - if (!rule->log) - rule->logif = 0; - if (rule->logif >= PFLOGIFS_MAX) - error = EINVAL; - if (pf_addr_setup(ruleset, &rule->src.addr, rule->af)) - error = ENOMEM; - if (pf_addr_setup(ruleset, &rule->dst.addr, rule->af)) - error = ENOMEM; - if (pf_kanchor_setup(rule, ruleset, pr->anchor_call)) - error = EINVAL; - if (rule->scrub_flags & PFSTATE_SETPRIO && - (rule->set_prio[0] > PF_PRIO_MAX || - rule->set_prio[1] > PF_PRIO_MAX)) - 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; - } + if (nvlist_exists_string(nvl, "anchor")) + anchor = nvlist_get_string(nvl, "anchor"); + if (nvlist_exists_string(nvl, "anchor_call")) + anchor_call = nvlist_get_string(nvl, "anchor_call"); - rule->overload_tbl = NULL; - if (rule->overload_tblname[0]) { - if ((rule->overload_tbl = pfr_attach_table(ruleset, - rule->overload_tblname)) == NULL) - error = EINVAL; - else - rule->overload_tbl->pfrkt_flags |= - PFR_TFLAG_ACTIVE; - } + /* Frees rule on error */ + error = pf_ioctl_addrule(rule, ticket, pool_ticket, anchor, + anchor_call, td); + break; +#undef ERROUT +DIOCADDRULENV_error: + pf_krule_free(rule); - pf_mv_kpool(&V_pf_pabuf, &rule->rpool.list); - if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) || - (rule->action == PF_BINAT)) && rule->anchor == NULL) || - (rule->rt > PF_NOPFROUTE)) && - (TAILQ_FIRST(&rule->rpool.list) == NULL)) - error = EINVAL; + break; + } + case DIOCADDRULE: { + struct pfioc_rule *pr = (struct pfioc_rule *)addr; + struct pf_krule *rule; - if (error) { - pf_free_rule(rule); - PF_RULES_WUNLOCK(); + rule = malloc(sizeof(*rule), M_PFRULE, M_WAITOK); + error = pf_rule_to_krule(&pr->rule, rule); + if (error != 0) { + free(rule, M_PFRULE); break; } - rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list); - counter_u64_zero(rule->evaluations); - for (int i = 0; i < 2; i++) { - counter_u64_zero(rule->packets[i]); - counter_u64_zero(rule->bytes[i]); - } - TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr, - rule, entries); - ruleset->rules[rs_num].inactive.rcount++; - PF_RULES_WUNLOCK(); - break; + pr->anchor[sizeof(pr->anchor) - 1] = 0; -#undef ERROUT -DIOCADDRULE_error: - PF_RULES_WUNLOCK(); - pf_krule_free(rule); - pf_kkif_free(kif); + /* Frees rule on error */ + error = pf_ioctl_addrule(rule, pr->ticket, pr->pool_ticket, + pr->anchor, pr->anchor_call, td); break; } Index: sys/netpfil/pf/pf_nv.h =================================================================== --- /dev/null +++ sys/netpfil/pf/pf_nv.h @@ -0,0 +1,53 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Rubicon Communications, LLC (Netgate) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#ifndef _PF_NV_H_ +#define _PF_NV_H_ + +#include +#include + +int pf_nvbinary(const nvlist_t *, const char *, void *, size_t); +int pf_nvint(const nvlist_t *, const char *, int *); +int pf_nvuint_8(const nvlist_t *, const char *, u_int8_t *); +int pf_nvuint_8_array(const nvlist_t *, const char *, u_int8_t *, + size_t, size_t *); +int pf_nvuint_16(const nvlist_t *, const char *, u_int16_t *); +int pf_nvuint_16_array(const nvlist_t *, const char *, u_int16_t *, + size_t, size_t *); +int pf_nvuint_32(const nvlist_t *, const char *, u_int32_t *); +int pf_nvuint_32_array(const nvlist_t *, const char *, u_int32_t *, + size_t, size_t *); +int pf_nvstring(const nvlist_t *, const char *, char *, size_t); + +#define PFNV_CHK(x) do { \ + error = (x); \ + if (error != 0) \ + goto errout; \ + } while (0) + +#endif Index: sys/netpfil/pf/pf_nv.c =================================================================== --- /dev/null +++ sys/netpfil/pf/pf_nv.c @@ -0,0 +1,127 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Rubicon Communications, LLC (Netgate) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include "pf_nv.h" + +#define PV_NV_IMPL_UINT(fnname, type, max) \ + int \ + fnname(const nvlist_t *nvl, const char *name, type *val) \ + { \ + uint64_t raw; \ + if (! nvlist_exists_number(nvl, name)) \ + return (EINVAL); \ + raw = nvlist_get_number(nvl, name); \ + if (raw > max) \ + return (ERANGE); \ + *val = (type)raw; \ + return (0); \ + } \ + int \ + fnname ## _array(const nvlist_t *nvl, const char *name, type *array, \ + size_t maxelems, size_t *nelems) \ + { \ + const u_int64_t *n; \ + size_t nitems; \ + bzero(array, sizeof(type) * maxelems); \ + if (! nvlist_exists_number_array(nvl, name)) \ + return (EINVAL); \ + n = nvlist_get_number_array(nvl, name, &nitems); \ + if (nitems > maxelems) \ + return (E2BIG); \ + if (nelems != NULL) \ + *nelems = nitems; \ + for (size_t i = 0; i < nitems; i++) { \ + if (n[i] > max) \ + return (ERANGE); \ + array[i] = (type)n[i]; \ + } \ + return (0); \ + } +int +pf_nvbinary(const nvlist_t *nvl, const char *name, void *data, + size_t expected_size) +{ + const u_int8_t *nvdata; + size_t len; + + bzero(data, expected_size); + + if (! nvlist_exists_binary(nvl, name)) + return (EINVAL); + + nvdata = (const u_int8_t *)nvlist_get_binary(nvl, name, &len); + if (len > expected_size) + return (EINVAL); + + memcpy(data, nvdata, len); + + return (0); +} + +PV_NV_IMPL_UINT(pf_nvuint_8, u_int8_t, UINT8_MAX) +PV_NV_IMPL_UINT(pf_nvuint_16, u_int16_t, UINT16_MAX); +PV_NV_IMPL_UINT(pf_nvuint_32, u_int32_t, UINT32_MAX) + +int +pf_nvint(const nvlist_t *nvl, const char *name, int *val) +{ + int64_t raw; + + if (! nvlist_exists_number(nvl, name)) + return (EINVAL); + + raw = nvlist_get_number(nvl, name); + if (raw > INT_MAX || raw < INT_MIN) + return (ERANGE); + + *val = (int)raw; + + return (0); +} + +int +pf_nvstring(const nvlist_t *nvl, const char *name, char *str, size_t maxlen) +{ + int ret; + + if (! nvlist_exists_string(nvl, name)) + return (EINVAL); + + ret = strlcpy(str, nvlist_get_string(nvl, name), maxlen); + if (ret >= maxlen) + return (EINVAL); + + return (0); +}