The branch stable/13 has been updated by kp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=f9b057eaf625c401e05760ce954830b1f12089ba

commit f9b057eaf625c401e05760ce954830b1f12089ba
Author:     Kristof Provost <[email protected]>
AuthorDate: 2021-03-11 15:21:23 +0000
Commit:     Kristof Provost <[email protected]>
CommitDate: 2021-05-07 08:15:41 +0000

    pf: Introduce nvlist variant of DIOCADDRULE
    
    This will make future extensions of the API much easier.
    The intent is to remove support for DIOCADDRULE in FreeBSD 14.
    
    Reviewed by:    markj (previous version), glebius (previous version)
    MFC after:      4 weeks
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D29557
    
    (cherry picked from commit 5c62eded5a11ebdb1d57134d923596e2b04e9466)
---
 sys/conf/files            |   1 +
 sys/modules/pf/Makefile   |   2 +-
 sys/net/pfvar.h           |   1 +
 sys/netpfil/pf/pf.h       |   6 +
 sys/netpfil/pf/pf_ioctl.c | 647 ++++++++++++++++++++++++++++++++++++----------
 sys/netpfil/pf/pf_nv.c    | 127 +++++++++
 sys/netpfil/pf/pf_nv.h    |  53 ++++
 7 files changed, 697 insertions(+), 140 deletions(-)

diff --git a/sys/conf/files b/sys/conf/files
index 9ec7292a741b..161b70880309 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4523,6 +4523,7 @@ netpfil/pf/pf_if.c                optional pf inet
 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
diff --git a/sys/modules/pf/Makefile b/sys/modules/pf/Makefile
index 148b64c02a9f..7293b30cda9d 100644
--- a/sys/modules/pf/Makefile
+++ b/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
 
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 17f6020513bd..584724b88b13 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1228,6 +1228,7 @@ struct pfioc_iface {
 #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 */
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index 511c60f5abd1..bc6cd92ae7b8 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -187,6 +187,12 @@ enum       { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, 
PF_ADDR_DYNIFTL,
 #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 */
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 8ac534e0d58b..87a52dd41542 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/lock.h>
 #include <sys/mbuf.h>
 #include <sys/module.h>
+#include <sys/nv.h>
 #include <sys/proc.h>
 #include <sys/smp.h>
 #include <sys/socket.h>
@@ -82,6 +83,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/ip_var.h>
 #include <netinet6/ip6_var.h>
 #include <netinet/ip_icmp.h>
+#include <netpfil/pf/pf_nv.h>
 
 #ifdef INET6
 #include <netinet/ip6.h>
@@ -1622,6 +1624,294 @@ pf_check_rule_addr(const struct pf_rule_addr *addr)
        return (0);
 }
 
+static int
+pf_nvaddr_to_addr(const nvlist_t *nvl, struct pf_addr *paddr)
+{
+       return (pf_nvbinary(nvl, "addr", paddr, sizeof(*paddr)));
+}
+
+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_nvuint16_array(nvl, "proxy_port", kpool->proxy_port, 2,
+           NULL));
+       PFNV_CHK(pf_nvuint8(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_nvuint8(nvl, "type", &addr->type));
+       PFNV_CHK(pf_nvuint8(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(uint8_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_nvuint16_array(nvl, "port", addr->port, 2, NULL));
+       PFNV_CHK(pf_nvuint8(nvl, "neg", &addr->neg));
+       PFNV_CHK(pf_nvuint8(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_nvuint32_array(nvl, "uid", uid->uid, 2, NULL));
+       PFNV_CHK(pf_nvuint8(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_nvuint32(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")) {
+               error = EINVAL;
+               goto errout;
+       }
+       PFNV_CHK(pf_nvpool_to_pool(nvlist_get_nvlist(nvl, "rpool"),
+           &rule->rpool));
+
+       PFNV_CHK(pf_nvuint32(nvl, "os_fingerprint", &rule->os_fingerprint));
+
+       PFNV_CHK(pf_nvint(nvl, "rtableid", &rule->rtableid));
+       PFNV_CHK(pf_nvuint32_array(nvl, "timeout", rule->timeout, PFTM_MAX, 
NULL));
+       PFNV_CHK(pf_nvuint32(nvl, "max_states", &rule->max_states));
+       PFNV_CHK(pf_nvuint32(nvl, "max_src_nodes", &rule->max_src_nodes));
+       PFNV_CHK(pf_nvuint32(nvl, "max_src_states", &rule->max_src_states));
+       PFNV_CHK(pf_nvuint32(nvl, "max_src_conn", &rule->max_src_conn));
+       PFNV_CHK(pf_nvuint32(nvl, "max_src_conn_rate.limit",
+           &rule->max_src_conn_rate.limit));
+       PFNV_CHK(pf_nvuint32(nvl, "max_src_conn_rate.seconds",
+           &rule->max_src_conn_rate.seconds));
+       PFNV_CHK(pf_nvuint32(nvl, "prob", &rule->prob));
+       PFNV_CHK(pf_nvuint32(nvl, "cuid", &rule->cuid));
+       PFNV_CHK(pf_nvuint32(nvl, "cpid", &rule->cpid));
+
+       PFNV_CHK(pf_nvuint16(nvl, "return_icmp", &rule->return_icmp));
+       PFNV_CHK(pf_nvuint16(nvl, "return_icmp6", &rule->return_icmp6));
+
+       PFNV_CHK(pf_nvuint16(nvl, "max_mss", &rule->max_mss));
+       PFNV_CHK(pf_nvuint16(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_nvuint32(nvl, "rule_flag", &rule->rule_flag));
+       PFNV_CHK(pf_nvuint8(nvl, "action", &rule->action));
+       PFNV_CHK(pf_nvuint8(nvl, "direction", &rule->direction));
+       PFNV_CHK(pf_nvuint8(nvl, "log", &rule->log));
+       PFNV_CHK(pf_nvuint8(nvl, "logif", &rule->logif));
+       PFNV_CHK(pf_nvuint8(nvl, "quick", &rule->quick));
+       PFNV_CHK(pf_nvuint8(nvl, "ifnot", &rule->ifnot));
+       PFNV_CHK(pf_nvuint8(nvl, "match_tag_not", &rule->match_tag_not));
+       PFNV_CHK(pf_nvuint8(nvl, "natpass", &rule->natpass));
+
+       PFNV_CHK(pf_nvuint8(nvl, "keep_state", &rule->keep_state));
+       PFNV_CHK(pf_nvuint8(nvl, "af", &rule->af));
+       PFNV_CHK(pf_nvuint8(nvl, "proto", &rule->proto));
+       PFNV_CHK(pf_nvuint8(nvl, "type", &rule->type));
+       PFNV_CHK(pf_nvuint8(nvl, "code", &rule->code));
+       PFNV_CHK(pf_nvuint8(nvl, "flags", &rule->flags));
+       PFNV_CHK(pf_nvuint8(nvl, "flagset", &rule->flagset));
+       PFNV_CHK(pf_nvuint8(nvl, "min_ttl", &rule->min_ttl));
+       PFNV_CHK(pf_nvuint8(nvl, "allow_opts", &rule->allow_opts));
+       PFNV_CHK(pf_nvuint8(nvl, "rt", &rule->rt));
+       PFNV_CHK(pf_nvuint8(nvl, "return_ttl", &rule->return_ttl));
+       PFNV_CHK(pf_nvuint8(nvl, "tos", &rule->tos));
+       PFNV_CHK(pf_nvuint8(nvl, "set_tos", &rule->set_tos));
+       PFNV_CHK(pf_nvuint8(nvl, "anchor_relative", &rule->anchor_relative));
+       PFNV_CHK(pf_nvuint8(nvl, "anchor_wildcard", &rule->anchor_wildcard));
+
+       PFNV_CHK(pf_nvuint8(nvl, "flush", &rule->flush));
+       PFNV_CHK(pf_nvuint8(nvl, "prio", &rule->prio));
+
+       PFNV_CHK(pf_nvuint8_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_nvuint16(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)
 {
@@ -1796,6 +2086,163 @@ relock_DIOCKILLSTATES:
        return (killed);
 }
 
+static int
+pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
+    uint32_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)
 {
@@ -1946,161 +2393,83 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, 
int flags, struct thread *td
                }
                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 = "";
+               uint32_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);
+               error = copyin(nv->data, nvlpacked, nv->len);
+               if (error)
+                       ERROUT(error);
 
-               PF_RULES_WLOCK();
-               pr->anchor[sizeof(pr->anchor) - 1] = 0;
-               ruleset = pf_find_kruleset(pr->anchor);
-               if (ruleset == NULL)
+               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;
-               }
+               if ((error = nvlist_error(nvl)))
+                       ERROUT(error);
 
-               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;
+               /* Frees rule on error */
+               error = pf_ioctl_addrule(rule, ticket, pool_ticket, anchor,
+                   anchor_call, td);
 
-               if (error) {
-                       pf_free_rule(rule);
-                       PF_RULES_WUNLOCK();
+               nvlist_destroy(nvl);
+               free(nvlpacked, M_TEMP);
+               break;
+#undef ERROUT
+DIOCADDRULENV_error:
+               pf_krule_free(rule);
+               nvlist_destroy(nvl);
+               free(nvlpacked, M_TEMP);
+
+               break;
+       }
+       case DIOCADDRULE: {
+               struct pfioc_rule       *pr = (struct pfioc_rule *)addr;
+               struct pf_krule         *rule;
+
+               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;
        }
 
diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c
new file mode 100644
index 000000000000..d583844c4086
--- /dev/null
+++ b/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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/limits.h>
+#include <sys/systm.h>
+
+#include <netpfil/pf/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 uint64_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 uint8_t *nvdata;
+       size_t len;
+
+       bzero(data, expected_size);
+
+       if (! nvlist_exists_binary(nvl, name))
+               return (EINVAL);
+
+       nvdata = (const uint8_t *)nvlist_get_binary(nvl, name, &len);
+       if (len > expected_size)
+               return (EINVAL);
+
+       memcpy(data, nvdata, len);
+
+       return (0);
+}
+
+PV_NV_IMPL_UINT(pf_nvuint8, uint8_t, UINT8_MAX)
+PV_NV_IMPL_UINT(pf_nvuint16, uint16_t, UINT16_MAX);
+PV_NV_IMPL_UINT(pf_nvuint32, uint32_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);
+}
diff --git a/sys/netpfil/pf/pf_nv.h b/sys/netpfil/pf/pf_nv.h
new file mode 100644
index 000000000000..f0db1e880e9e
--- /dev/null
+++ b/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 <sys/types.h>
+#include <sys/nv.h>
+
+int    pf_nvbinary(const nvlist_t *, const char *, void *, size_t);
+int    pf_nvint(const nvlist_t *, const char *, int *);
+int    pf_nvuint8(const nvlist_t *, const char *, uint8_t *);
+int    pf_nvuint8_array(const nvlist_t *, const char *, uint8_t *,
+           size_t, size_t *);
+int    pf_nvuint16(const nvlist_t *, const char *, uint16_t *);
+int    pf_nvuint16_array(const nvlist_t *, const char *, uint16_t *,
+           size_t, size_t *);
+int    pf_nvuint32(const nvlist_t *, const char *, uint32_t *);
+int    pf_nvuint32_array(const nvlist_t *, const char *, uint32_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
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to