diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -438,40 +438,6 @@ addr->port_op = nvlist_get_number(nvl, "port_op"); } -static void -pfctl_nv_add_mape(nvlist_t *nvparent, const char *name, - const struct pf_mape_portset *mape) -{ - nvlist_t *nvl = nvlist_create(0); - - nvlist_add_number(nvl, "offset", mape->offset); - nvlist_add_number(nvl, "psidlen", mape->psidlen); - nvlist_add_number(nvl, "psid", mape->psid); - nvlist_add_nvlist(nvparent, name, nvl); - nvlist_destroy(nvl); -} - -static void -pfctl_nv_add_pool(nvlist_t *nvparent, const char *name, - const struct pfctl_pool *pool) -{ - uint64_t ports[2]; - nvlist_t *nvl = nvlist_create(0); - - nvlist_add_binary(nvl, "key", &pool->key, sizeof(pool->key)); - pfctl_nv_add_addr(nvl, "counter", &pool->counter); - nvlist_add_number(nvl, "tblidx", pool->tblidx); - - ports[0] = pool->proxy_port[0]; - ports[1] = pool->proxy_port[1]; - nvlist_add_number_array(nvl, "proxy_port", ports, 2); - nvlist_add_number(nvl, "opts", pool->opts); - pfctl_nv_add_mape(nvl, "mape", &pool->mape); - - nvlist_add_nvlist(nvparent, name, nvl); - nvlist_destroy(nvl); -} - static void pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape) { @@ -500,22 +466,6 @@ pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"), &pool->mape); } -static void -pfctl_nv_add_uid(nvlist_t *nvparent, const char *name, - const struct pf_rule_uid *uid) -{ - uint64_t uids[2]; - nvlist_t *nvl = nvlist_create(0); - - uids[0] = uid->uid[0]; - uids[1] = uid->uid[1]; - nvlist_add_number_array(nvl, "uid", uids, 2); - nvlist_add_number(nvl, "op", uid->op); - - nvlist_add_nvlist(nvparent, name, nvl); - nvlist_destroy(nvl); -} - static void pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid) { @@ -523,19 +473,6 @@ uid->op = nvlist_get_number(nvl, "op"); } -static void -pfctl_nv_add_divert(nvlist_t *nvparent, const char *name, - const struct pfctl_rule *r) -{ - nvlist_t *nvl = nvlist_create(0); - - pfctl_nv_add_addr(nvl, "addr", &r->divert.addr); - nvlist_add_number(nvl, "port", r->divert.port); - - nvlist_add_nvlist(nvparent, name, nvl); - nvlist_destroy(nvl); -} - static void pf_nvdivert_to_divert(const nvlist_t *nvl, struct pfctl_rule *rule) { @@ -926,127 +863,235 @@ return (error); } -int -pfctl_add_rule(int dev, const struct pfctl_rule *r, const char *anchor, - const char *anchor_call, uint32_t ticket, uint32_t pool_ticket) +static void +snl_add_msg_attr_addr_wrap(struct snl_writer *nw, uint32_t type, const struct pf_addr_wrap *addr) { - struct pfioc_nv nv; - uint64_t timeouts[PFTM_MAX]; - uint64_t set_prio[2]; - nvlist_t *nvl, *nvlr; - size_t labelcount; - int ret; + int off; - nvl = nvlist_create(0); - nvlr = nvlist_create(0); + off = snl_add_msg_attr_nested(nw, type); - nvlist_add_number(nvl, "ticket", ticket); - nvlist_add_number(nvl, "pool_ticket", pool_ticket); - nvlist_add_string(nvl, "anchor", anchor); - nvlist_add_string(nvl, "anchor_call", anchor_call); + snl_add_msg_attr_ip6(nw, PF_AT_ADDR, &addr->v.a.addr.v6); + snl_add_msg_attr_ip6(nw, PF_AT_MASK, &addr->v.a.mask.v6); - nvlist_add_number(nvlr, "nr", r->nr); - pfctl_nv_add_rule_addr(nvlr, "src", &r->src); - pfctl_nv_add_rule_addr(nvlr, "dst", &r->dst); + if (addr->type == PF_ADDR_DYNIFTL) + snl_add_msg_attr_string(nw, PF_AT_IFNAME, addr->v.ifname); + if (addr->type == PF_ADDR_TABLE) + snl_add_msg_attr_string(nw, PF_AT_TABLENAME, addr->v.tblname); + snl_add_msg_attr_u8(nw, PF_AT_TYPE, addr->type); + snl_add_msg_attr_u8(nw, PF_AT_IFLAGS, addr->iflags); - labelcount = 0; - while (r->label[labelcount][0] != 0 && - labelcount < PF_RULE_MAX_LABEL_COUNT) { - nvlist_append_string_array(nvlr, "labels", - r->label[labelcount]); - labelcount++; + snl_end_attr_nested(nw, off); +} + +static void +snl_add_msg_attr_rule_addr(struct snl_writer *nw, uint32_t type, const struct pf_rule_addr *addr) +{ + int off; + + off = snl_add_msg_attr_nested(nw, type); + + snl_add_msg_attr_addr_wrap(nw, PF_RAT_ADDR, &addr->addr); + snl_add_msg_attr_u16(nw, PF_RAT_SRC_PORT, addr->port[0]); + snl_add_msg_attr_u16(nw, PF_RAT_DST_PORT, addr->port[1]); + snl_add_msg_attr_u8(nw, PF_RAT_NEG, addr->neg); + snl_add_msg_attr_u8(nw, PF_RAT_OP, addr->port_op); + + snl_end_attr_nested(nw, off); +} + +static void +snl_add_msg_attr_rule_labels(struct snl_writer *nw, uint32_t type, const char labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE]) +{ + int off, i = 0; + + off = snl_add_msg_attr_nested(nw, type); + + while (labels[i][0] != 0 && + i < PF_RULE_MAX_LABEL_COUNT) { + snl_add_msg_attr_string(nw, PF_LT_LABEL, labels[i]); + i++; } - nvlist_add_number(nvlr, "ridentifier", r->ridentifier); - nvlist_add_string(nvlr, "ifname", r->ifname); - nvlist_add_string(nvlr, "qname", r->qname); - nvlist_add_string(nvlr, "pqname", r->pqname); - nvlist_add_string(nvlr, "tagname", r->tagname); - nvlist_add_string(nvlr, "match_tagname", r->match_tagname); - nvlist_add_string(nvlr, "overload_tblname", r->overload_tblname); + snl_end_attr_nested(nw, off); +} + +static void +snl_add_msg_attr_mape(struct snl_writer *nw, uint32_t type, const struct pf_mape_portset *me) +{ + int off; + + off = snl_add_msg_attr_nested(nw, type); + + snl_add_msg_attr_u8(nw, PF_MET_OFFSET, me->offset); + snl_add_msg_attr_u8(nw, PF_MET_PSID_LEN, me->psidlen); + snl_add_msg_attr_u16(nw, PF_MET_PSID, me->psid); + + snl_end_attr_nested(nw, off); +} + +static void +snl_add_msg_attr_rpool(struct snl_writer *nw, uint32_t type, const struct pfctl_pool *pool) +{ + int off; + + off = snl_add_msg_attr_nested(nw, type); - pfctl_nv_add_pool(nvlr, "rpool", &r->rpool); + snl_add_msg_attr(nw, PF_PT_KEY, sizeof(pool->key), &pool->key); + snl_add_msg_attr_ip6(nw, PF_PT_COUNTER, &pool->counter.v6); + snl_add_msg_attr_u32(nw, PF_PT_TBLIDX, pool->tblidx); + snl_add_msg_attr_u16(nw, PF_PT_PROXY_SRC_PORT, pool->proxy_port[0]); + snl_add_msg_attr_u16(nw, PF_PT_PROXY_DST_PORT, pool->proxy_port[1]); + snl_add_msg_attr_u8(nw, PF_PT_OPTS, pool->opts); + snl_add_msg_attr_mape(nw, PF_PT_MAPE, &pool->mape); - nvlist_add_number(nvlr, "os_fingerprint", r->os_fingerprint); + snl_end_attr_nested(nw, off); +} + +static void +snl_add_msg_attr_timeouts(struct snl_writer *nw, uint32_t type, const uint32_t *timeouts) +{ + int off; + + off = snl_add_msg_attr_nested(nw, type); - nvlist_add_number(nvlr, "rtableid", r->rtableid); for (int i = 0; i < PFTM_MAX; i++) - timeouts[i] = r->timeout[i]; - nvlist_add_number_array(nvlr, "timeout", timeouts, PFTM_MAX); - nvlist_add_number(nvlr, "max_states", r->max_states); - nvlist_add_number(nvlr, "max_src_nodes", r->max_src_nodes); - nvlist_add_number(nvlr, "max_src_states", r->max_src_states); - nvlist_add_number(nvlr, "max_src_conn", r->max_src_conn); - nvlist_add_number(nvlr, "max_src_conn_rate.limit", - r->max_src_conn_rate.limit); - nvlist_add_number(nvlr, "max_src_conn_rate.seconds", - r->max_src_conn_rate.seconds); - nvlist_add_number(nvlr, "dnpipe", r->dnpipe); - nvlist_add_number(nvlr, "dnrpipe", r->dnrpipe); - nvlist_add_number(nvlr, "dnflags", r->free_flags); - nvlist_add_number(nvlr, "prob", r->prob); - nvlist_add_number(nvlr, "cuid", r->cuid); - nvlist_add_number(nvlr, "cpid", r->cpid); - - nvlist_add_number(nvlr, "return_icmp", r->return_icmp); - nvlist_add_number(nvlr, "return_icmp6", r->return_icmp6); - - nvlist_add_number(nvlr, "max_mss", r->max_mss); - nvlist_add_number(nvlr, "scrub_flags", r->scrub_flags); - - pfctl_nv_add_uid(nvlr, "uid", &r->uid); - pfctl_nv_add_uid(nvlr, "gid", (const struct pf_rule_uid *)&r->gid); - - nvlist_add_number(nvlr, "rule_flag", r->rule_flag); - nvlist_add_number(nvlr, "action", r->action); - nvlist_add_number(nvlr, "direction", r->direction); - nvlist_add_number(nvlr, "log", r->log); - nvlist_add_number(nvlr, "logif", r->logif); - nvlist_add_number(nvlr, "quick", r->quick); - nvlist_add_number(nvlr, "ifnot", r->ifnot); - nvlist_add_number(nvlr, "match_tag_not", r->match_tag_not); - nvlist_add_number(nvlr, "natpass", r->natpass); - - nvlist_add_number(nvlr, "keep_state", r->keep_state); - nvlist_add_number(nvlr, "af", r->af); - nvlist_add_number(nvlr, "proto", r->proto); - nvlist_add_number(nvlr, "type", r->type); - nvlist_add_number(nvlr, "code", r->code); - nvlist_add_number(nvlr, "flags", r->flags); - nvlist_add_number(nvlr, "flagset", r->flagset); - nvlist_add_number(nvlr, "min_ttl", r->min_ttl); - nvlist_add_number(nvlr, "allow_opts", r->allow_opts); - nvlist_add_number(nvlr, "rt", r->rt); - nvlist_add_number(nvlr, "return_ttl", r->return_ttl); - nvlist_add_number(nvlr, "tos", r->tos); - nvlist_add_number(nvlr, "set_tos", r->set_tos); - nvlist_add_number(nvlr, "anchor_relative", r->anchor_relative); - nvlist_add_number(nvlr, "anchor_wildcard", r->anchor_wildcard); - - nvlist_add_number(nvlr, "flush", r->flush); - - nvlist_add_number(nvlr, "prio", r->prio); - set_prio[0] = r->set_prio[0]; - set_prio[1] = r->set_prio[1]; - nvlist_add_number_array(nvlr, "set_prio", set_prio, 2); - - pfctl_nv_add_divert(nvlr, "divert", r); - - nvlist_add_nvlist(nvl, "rule", nvlr); - nvlist_destroy(nvlr); - - /* Now do the call. */ - nv.data = nvlist_pack(nvl, &nv.len); - nv.size = nv.len; + snl_add_msg_attr_u32(nw, PF_TT_TIMEOUT, timeouts[i]); - ret = ioctl(dev, DIOCADDRULENV, &nv); - if (ret == -1) - ret = errno; + snl_end_attr_nested(nw, off); +} - free(nv.data); - nvlist_destroy(nvl); +static void +snl_add_msg_attr_uid(struct snl_writer *nw, uint32_t type, const struct pf_rule_uid *uid) +{ + int off; - return (ret); + off = snl_add_msg_attr_nested(nw, type); + + snl_add_msg_attr_u32(nw, PF_RUT_UID_LOW, uid->uid[0]); + snl_add_msg_attr_u32(nw, PF_RUT_UID_HIGH, uid->uid[1]); + snl_add_msg_attr_u8(nw, PF_RUT_OP, uid->op); + + snl_end_attr_nested(nw, off); +} + +static void +snl_add_msg_attr_pf_rule(struct snl_writer *nw, uint32_t type, const struct pfctl_rule *r) +{ + int off; + + off = snl_add_msg_attr_nested(nw, type); + + snl_add_msg_attr_rule_addr(nw, PF_RT_SRC, &r->src); + snl_add_msg_attr_rule_addr(nw, PF_RT_DST, &r->dst); + snl_add_msg_attr_rule_labels(nw, PF_RT_LABELS, r->label); + snl_add_msg_attr_u32(nw, PF_RT_RIDENTIFIER, r->ridentifier); + snl_add_msg_attr_string(nw, PF_RT_IFNAME, r->ifname); + snl_add_msg_attr_string(nw, PF_RT_QNAME, r->qname); + snl_add_msg_attr_string(nw, PF_RT_PQNAME, r->pqname); + snl_add_msg_attr_string(nw, PF_RT_TAGNAME, r->tagname); + snl_add_msg_attr_string(nw, PF_RT_MATCH_TAGNAME, r->match_tagname); + snl_add_msg_attr_string(nw, PF_RT_OVERLOAD_TBLNAME, r->overload_tblname); + snl_add_msg_attr_rpool(nw, PF_RT_RPOOL, &r->rpool); + snl_add_msg_attr_u32(nw, PF_RT_OS_FINGERPRINT, r->os_fingerprint); + snl_add_msg_attr_u32(nw, PF_RT_RTABLEID, r->rtableid); + snl_add_msg_attr_timeouts(nw, PF_RT_TIMEOUT, r->timeout); + snl_add_msg_attr_u32(nw, PF_RT_MAX_STATES, r->max_states); + snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_NODES, r->max_src_nodes); + snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_STATES, r->max_src_states); + snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_CONN_RATE_LIMIT, r->max_src_conn_rate.limit); + snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_CONN_RATE_SECS, r->max_src_conn_rate.seconds); + + snl_add_msg_attr_u16(nw, PF_RT_DNPIPE, r->dnpipe); + snl_add_msg_attr_u16(nw, PF_RT_DNRPIPE, r->dnrpipe); + snl_add_msg_attr_u32(nw, PF_RT_DNFLAGS, r->free_flags); + + snl_add_msg_attr_u32(nw, PF_RT_NR, r->nr); + snl_add_msg_attr_u32(nw, PF_RT_PROB, r->prob); + snl_add_msg_attr_u32(nw, PF_RT_CUID, r->cuid); + snl_add_msg_attr_u32(nw, PF_RT_CPID, r->cpid); + + snl_add_msg_attr_u16(nw, PF_RT_RETURN_ICMP, r->return_icmp); + snl_add_msg_attr_u16(nw, PF_RT_RETURN_ICMP6, r->return_icmp6); + snl_add_msg_attr_u16(nw, PF_RT_MAX_MSS, r->max_mss); + snl_add_msg_attr_u16(nw, PF_RT_SCRUB_FLAGS, r->scrub_flags); + + snl_add_msg_attr_uid(nw, PF_RT_UID, &r->uid); + snl_add_msg_attr_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&r->gid); + + snl_add_msg_attr_u32(nw, PF_RT_RULE_FLAG, r->rule_flag); + snl_add_msg_attr_u8(nw, PF_RT_ACTION, r->action); + snl_add_msg_attr_u8(nw, PF_RT_DIRECTION, r->direction); + snl_add_msg_attr_u8(nw, PF_RT_LOG, r->log); + snl_add_msg_attr_u8(nw, PF_RT_LOGIF, r->logif); + snl_add_msg_attr_u8(nw, PF_RT_QUICK, r->quick); + snl_add_msg_attr_u8(nw, PF_RT_IF_NOT, r->ifnot); + snl_add_msg_attr_u8(nw, PF_RT_MATCH_TAG_NOT, r->match_tag_not); + snl_add_msg_attr_u8(nw, PF_RT_NATPASS, r->natpass); + snl_add_msg_attr_u8(nw, PF_RT_KEEP_STATE, r->keep_state); + snl_add_msg_attr_u8(nw, PF_RT_AF, r->af); + snl_add_msg_attr_u8(nw, PF_RT_PROTO, r->proto); + snl_add_msg_attr_u8(nw, PF_RT_TYPE, r->type); + snl_add_msg_attr_u8(nw, PF_RT_CODE, r->code); + snl_add_msg_attr_u8(nw, PF_RT_FLAGS, r->flags); + snl_add_msg_attr_u8(nw, PF_RT_FLAGSET, r->flagset); + snl_add_msg_attr_u8(nw, PF_RT_MIN_TTL, r->min_ttl); + snl_add_msg_attr_u8(nw, PF_RT_ALLOW_OPTS, r->allow_opts); + snl_add_msg_attr_u8(nw, PF_RT_RT, r->rt); + snl_add_msg_attr_u8(nw, PF_RT_RETURN_TTL, r->return_ttl); + snl_add_msg_attr_u8(nw, PF_RT_TOS, r->tos); + snl_add_msg_attr_u8(nw, PF_RT_SET_TOS, r->set_tos); + + snl_add_msg_attr_u8(nw, PF_RT_ANCHOR_RELATIVE, r->anchor_relative); + snl_add_msg_attr_u8(nw, PF_RT_ANCHOR_WILDCARD, r->anchor_wildcard); + snl_add_msg_attr_u8(nw, PF_RT_FLUSH, r->flush); + snl_add_msg_attr_u8(nw, PF_RT_PRIO, r->prio); + snl_add_msg_attr_u8(nw, PF_RT_SET_PRIO, r->set_prio[0]); + snl_add_msg_attr_u8(nw, PF_RT_SET_PRIO_REPLY, r->set_prio[1]); + + snl_add_msg_attr_ip6(nw, PF_RT_DIVERT_ADDRESS, &r->divert.addr.v6); + snl_add_msg_attr_u16(nw, PF_RT_DIVERT_PORT, r->divert.port); + + snl_end_attr_nested(nw, off); +} + +int +pfctl_add_rule(int dev __unused, const struct pfctl_rule *r, const char *anchor, + const char *anchor_call, uint32_t ticket, uint32_t pool_ticket) +{ + struct snl_writer nw; + struct snl_state ss = {}; + struct snl_errmsg_data e = {}; + struct nlmsghdr *hdr; + uint32_t seq_id; + int family_id; + + snl_init(&ss, NETLINK_GENERIC); + family_id = snl_get_genl_family(&ss, PFNL_FAMILY_NAME); + + snl_init_writer(&ss, &nw); + hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_ADDRULE); + hdr->nlmsg_flags |= NLM_F_DUMP; + snl_add_msg_attr_u32(&nw, PF_ART_TICKET, ticket); + snl_add_msg_attr_u32(&nw, PF_ART_POOL_TICKET, pool_ticket); + snl_add_msg_attr_string(&nw, PF_ART_ANCHOR, anchor); + snl_add_msg_attr_string(&nw, PF_ART_ANCHOR_CALL, anchor_call); + + snl_add_msg_attr_pf_rule(&nw, PF_ART_RULE, r); + + if ((hdr = snl_finalize_msg(&nw)) == NULL) + return (ENXIO); + + seq_id = hdr->nlmsg_seq; + + if (! snl_send_message(&ss, hdr)) { + printf("Send failed\n"); + return (ENXIO); + } + + while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) { + } + + return (e.error); } int diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -2465,6 +2465,10 @@ struct pf_keth_ruleset *pf_find_or_create_keth_ruleset(const char *); void pf_keth_anchor_remove(struct pf_keth_rule *); +int pf_ioctl_addrule(struct pf_krule *, uint32_t, + uint32_t, const char *, const char *, uid_t uid, + pid_t); + void pf_krule_free(struct pf_krule *); #endif diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -2016,10 +2016,10 @@ return (0); } -static int +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) + uid_t uid, pid_t pid) { struct pf_kruleset *ruleset; struct pf_krule *tail; @@ -2045,8 +2045,8 @@ 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; + rule->cuid = uid; + rule->cpid = pid; TAILQ_INIT(&rule->rpool.list); PF_CONFIG_LOCK(); @@ -3076,7 +3076,8 @@ /* Frees rule on error */ error = pf_ioctl_addrule(rule, ticket, pool_ticket, anchor, - anchor_call, td); + anchor_call, td->td_ucred->cr_ruid, + td->td_proc ? td->td_proc->p_pid : 0); nvlist_destroy(nvl); free(nvlpacked, M_NVLIST); @@ -3104,7 +3105,8 @@ /* Frees rule on error */ error = pf_ioctl_addrule(rule, pr->ticket, pr->pool_ticket, - pr->anchor, pr->anchor_call, td); + pr->anchor, pr->anchor_call, td->td_ucred->cr_ruid, + td->td_proc ? td->td_proc->p_pid : 0); break; } diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -40,6 +40,7 @@ PFNL_CMD_GETCREATORS = 2, PFNL_CMD_START = 3, PFNL_CMD_STOP = 4, + PFNL_CMD_ADDRULE = 5, __PFNL_CMD_MAX, }; #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1) @@ -98,6 +99,137 @@ PF_ST_VERSION = 28, /* u64 */ }; +enum pf_addr_type_t { + PF_AT_UNSPEC, + PF_AT_ADDR = 1, /* in6_addr */ + PF_AT_MASK = 2, /* in6_addr */ + PF_AT_IFNAME = 3, /* string */ + PF_AT_TABLENAME = 4, /* string */ + PF_AT_TYPE = 5, /* u8 */ + PF_AT_IFLAGS = 6, /* u8 */ +}; + +enum pfrule_addr_type_t { + PF_RAT_UNSPEC, + PF_RAT_ADDR = 1, /* nested, pf_addr_type_t */ + PF_RAT_SRC_PORT = 2, /* u16 */ + PF_RAT_DST_PORT = 3, /* u16 */ + PF_RAT_NEG = 4, /* u8 */ + PF_RAT_OP = 5, /* u8 */ +}; + +enum pf_labels_type_t { + PF_LT_UNSPEC, + PF_LT_LABEL = 1, /* string */ +}; + +enum pf_mape_portset_type_t +{ + PF_MET_UNSPEC, + PF_MET_OFFSET = 1, /* u8 */ + PF_MET_PSID_LEN = 2, /* u8 */ + PF_MET_PSID = 3, /* u16 */ +}; + +enum pf_rpool_type_t +{ + PF_PT_UNSPEC, + PF_PT_KEY = 1, /* bytes, sizeof(struct pf_poolhashkey) */ + PF_PT_COUNTER = 2, /* in6_addr */ + PF_PT_TBLIDX = 3, /* u32 */ + PF_PT_PROXY_SRC_PORT = 4, /* u16 */ + PF_PT_PROXY_DST_PORT = 5, /* u16 */ + PF_PT_OPTS = 6, /* u8 */ + PF_PT_MAPE = 7, /* nested, pf_mape_portset_type_t */ +}; + +enum pf_timeout_type_t { + PF_TT_UNSPEC, + PF_TT_TIMEOUT = 1, /* u32 */ +}; + +enum pf_rule_uid_type_t { + PF_RUT_UNSPEC, + PF_RUT_UID_LOW = 1, /* u32 */ + PF_RUT_UID_HIGH = 2, /* u32 */ + PF_RUT_OP = 3, /* u8 */ +}; + +enum pf_rule_type_t { + PF_RT_UNSPEC, + PF_RT_SRC = 1, /* nested, pf_rule_addr_type_t */ + PF_RT_DST = 2, /* nested, pf_rule_addr_type_t */ + PF_RT_RIDENTIFIER = 3, /* u32 */ + PF_RT_LABELS = 4, /* nested, pf_labels_type_t */ + PF_RT_IFNAME = 5, /* string */ + PF_RT_QNAME = 6, /* string */ + PF_RT_PQNAME = 7, /* string */ + PF_RT_TAGNAME = 8, /* string */ + PF_RT_MATCH_TAGNAME = 9, /* string */ + PF_RT_OVERLOAD_TBLNAME = 10, /* string */ + PF_RT_RPOOL = 11, /* nested, pf_rpool_type_t */ + PF_RT_OS_FINGERPRINT = 12, /* u32 */ + PF_RT_RTABLEID = 13, /* u32 */ + PF_RT_TIMEOUT = 14, /* nested, pf_timeout_type_t */ + PF_RT_MAX_STATES = 15, /* u32 */ + PF_RT_MAX_SRC_NODES = 16, /* u32 */ + PF_RT_MAX_SRC_STATES = 17, /* u32 */ + PF_RT_MAX_SRC_CONN_RATE_LIMIT = 18, /* u32 */ + PF_RT_MAX_SRC_CONN_RATE_SECS = 19, /* u32 */ + PF_RT_DNPIPE = 20, /* u16 */ + PF_RT_DNRPIPE = 21, /* u16 */ + PF_RT_DNFLAGS = 22, /* u32 */ + PF_RT_NR = 23, /* u32 */ + PF_RT_PROB = 24, /* u32 */ + PF_RT_CUID = 25, /* u32 */ + PF_RT_CPID = 26, /* u32 */ + PF_RT_RETURN_ICMP = 27, /* u16 */ + PF_RT_RETURN_ICMP6 = 28, /* u16 */ + PF_RT_MAX_MSS = 29, /* u16 */ + PF_RT_SCRUB_FLAGS = 30, /* u16 */ + PF_RT_UID = 31, /* nested, pf_rule_uid_type_t */ + PF_RT_GID = 32, /* nested, pf_rule_uid_type_t */ + PF_RT_RULE_FLAG = 33, /* u32 */ + PF_RT_ACTION = 34, /* u8 */ + PF_RT_DIRECTION = 35, /* u8 */ + PF_RT_LOG = 36, /* u8 */ + PF_RT_LOGIF = 37, /* u8 */ + PF_RT_QUICK = 38, /* u8 */ + PF_RT_IF_NOT = 39, /* u8 */ + PF_RT_MATCH_TAG_NOT = 40, /* u8 */ + PF_RT_NATPASS = 41, /* u8 */ + PF_RT_KEEP_STATE = 42, /* u8 */ + PF_RT_AF = 43, /* u8 */ + PF_RT_PROTO = 44, /* u8 */ + PF_RT_TYPE = 45, /* u8 */ + PF_RT_CODE = 46, /* u8 */ + PF_RT_FLAGS = 47, /* u8 */ + PF_RT_FLAGSET = 48, /* u8 */ + PF_RT_MIN_TTL = 49, /* u8 */ + PF_RT_ALLOW_OPTS = 50, /* u8 */ + PF_RT_RT = 51, /* u8 */ + PF_RT_RETURN_TTL = 52, /* u8 */ + PF_RT_TOS = 53, /* u8 */ + PF_RT_SET_TOS = 54, /* u8 */ + PF_RT_ANCHOR_RELATIVE = 55, /* u8 */ + PF_RT_ANCHOR_WILDCARD = 56, /* u8 */ + PF_RT_FLUSH = 57, /* u8 */ + PF_RT_PRIO = 58, /* u8 */ + PF_RT_SET_PRIO = 59, /* u8 */ + PF_RT_SET_PRIO_REPLY = 60, /* u8 */ + PF_RT_DIVERT_ADDRESS = 61, /* in6_addr */ + PF_RT_DIVERT_PORT = 62, /* u16 */ +}; + +enum pf_addrule_type_t { + PF_ART_UNSPEC, + PF_ART_TICKET = 1, /* u32 */ + PF_ART_POOL_TICKET = 2, /* u32 */ + PF_ART_ANCHOR = 3, /* string */ + PF_ART_ANCHOR_CALL = 4, /* string */ + PF_ART_RULE = 5, /* nested, pfrule_type_t */ +}; + #ifdef _KERNEL void pf_nl_register(void); diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -348,7 +348,265 @@ return (pf_stop()); } -static const struct nlhdr_parser *all_parsers[] = { &state_parser }; +#define _OUT(_field) offsetof(struct pf_addr_wrap, _field) +static const struct nlattr_parser nla_p_addr_wrap[] = { + { .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = nlattr_get_in6_addr }, + { .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = nlattr_get_in6_addr }, + { .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = nlattr_get_chara }, + { .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara }, + { .type = PF_AT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 }, + { .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = nlattr_get_uint8 }, +}; +NL_DECLARE_ATTR_PARSER(addr_wrap_parser, nla_p_addr_wrap); +#undef _OUT + +#define _OUT(_field) offsetof(struct pf_rule_addr, _field) +static const struct nlattr_parser nla_p_ruleaddr[] = { + { .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = nlattr_get_nested }, + { .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = nlattr_get_uint16 }, + { .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = nlattr_get_uint16 }, + { .type = PF_RAT_NEG, .off = _OUT(neg), .cb = nlattr_get_uint8 }, + { .type = PF_RAT_OP, .off = _OUT(port_op), .cb = nlattr_get_uint8 }, +}; +NL_DECLARE_ATTR_PARSER(rule_addr_parser, nla_p_ruleaddr); +#undef _OUT + +#define _OUT(_field) offsetof(struct pf_mape_portset, _field) +static const struct nlattr_parser nla_p_mape_portset[] = { + { .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = nlattr_get_uint8 }, + { .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = nlattr_get_uint8 }, + {. type = PF_MET_PSID, .off = _OUT(psid), .cb = nlattr_get_uint16 }, +}; +NL_DECLARE_ATTR_PARSER(mape_portset_parser, nla_p_mape_portset); +#undef _OUT + +struct nl_parsed_labels +{ + char labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE]; + uint32_t i; +}; + +static int +nlattr_get_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt, + const void *arg, void *target) +{ + struct nl_parsed_labels *l = (struct nl_parsed_labels *)target; + int ret; + + if (l->i >= PF_RULE_MAX_LABEL_COUNT) + return (E2BIG); + + ret = nlattr_get_chara(nla, npt, (void *)PF_RULE_LABEL_SIZE, + l->labels[l->i]); + if (ret == 0) + l->i++; + + return (ret); +} + +#define _OUT(_field) offsetof(struct nl_parsed_labels, _field) +static const struct nlattr_parser nla_p_labels[] = { + { .type = PF_LT_LABEL, .off = 0, .cb = nlattr_get_pf_rule_labels }, +}; +NL_DECLARE_ATTR_PARSER(rule_labels_parser, nla_p_labels); +#undef _OUT + +static int +nlattr_get_nested_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target) +{ + struct nl_parsed_labels parsed_labels = { }; + int error; + + /* Assumes target points to the beginning of the structure */ + error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, npt, &parsed_labels); + if (error != 0) + return (error); + + memcpy(target, parsed_labels.labels, sizeof(parsed_labels)); + + return (0); +} + +#define _OUT(_field) offsetof(struct pf_kpool, _field) +static const struct nlattr_parser nla_p_pool[] = { + { .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = nlattr_get_bytes }, + { .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = nlattr_get_in6_addr }, + { .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = nlattr_get_uint32 }, + { .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = nlattr_get_uint16 }, + { .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = nlattr_get_uint16 }, + { .type = PF_PT_OPTS, .off = _OUT(opts), .cb = nlattr_get_uint8 }, + { .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = nlattr_get_nested }, +}; +NL_DECLARE_ATTR_PARSER(pool_parser, nla_p_pool); +#undef _OUT + +#define _OUT(_field) offsetof(struct pf_rule_uid, _field) +static const struct nlattr_parser nla_p_rule_uid[] = { + { .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = nlattr_get_uint32 }, + { .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = nlattr_get_uint32 }, + { .type = PF_RUT_OP, .off = _OUT(op), .cb = nlattr_get_uint8 }, +}; +NL_DECLARE_ATTR_PARSER(rule_uid_parser, nla_p_rule_uid); +#undef _OUT + +struct nl_parsed_timeouts +{ + uint32_t timeouts[PFTM_MAX]; + uint32_t i; +}; + +static int +nlattr_get_pf_timeout(struct nlattr *nla, struct nl_pstate *npt, + const void *arg, void *target) +{ + struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target; + int ret; + + if (t->i >= PFTM_MAX) + return (E2BIG); + + ret = nlattr_get_uint32(nla, npt, NULL, &t->timeouts[t->i]); + if (ret == 0) + t->i++; + + return (ret); +} + +#define _OUT(_field) offsetof(struct nl_parsed_timeout, _field) +static const struct nlattr_parser nla_p_timeouts[] = { + { .type = PF_TT_TIMEOUT, .off = 0, .cb = nlattr_get_pf_timeout }, +}; +NL_DECLARE_ATTR_PARSER(timeout_parser, nla_p_timeouts); +#undef _OUT + +static int +nlattr_get_nested_timeouts(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target) +{ + struct nl_parsed_timeouts parsed_timeouts = { }; + int error; + + /* Assumes target points to the beginning of the structure */ + error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, npt, &parsed_timeouts); + if (error != 0) + return (error); + + memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts)); + + return (0); +} + +#define _OUT(_field) offsetof(struct pf_krule, _field) +static const struct nlattr_parser nla_p_rule[] = { + { .type = PF_RT_SRC, .off = _OUT(src), .arg = &rule_addr_parser,.cb = nlattr_get_nested }, + { .type = PF_RT_DST, .off = _OUT(dst), .arg = &rule_addr_parser,.cb = nlattr_get_nested }, + { .type = PF_RT_RIDENTIFIER, .off = _OUT(ridentifier), .cb = nlattr_get_uint32 }, + { .type = PF_RT_LABELS, .off = _OUT(label), .arg = &rule_labels_parser,.cb = nlattr_get_nested_pf_rule_labels }, + { .type = PF_RT_IFNAME, .off = _OUT(ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara }, + { .type = PF_RT_QNAME, .off = _OUT(qname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara }, + { .type = PF_RT_PQNAME, .off = _OUT(pqname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara }, + { .type = PF_RT_TAGNAME, .off = _OUT(tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara }, + { .type = PF_RT_MATCH_TAGNAME, .off = _OUT(match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara }, + { .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara }, + { .type = PF_RT_RPOOL, .off = _OUT(rpool), .arg = &pool_parser, .cb = nlattr_get_nested }, + { .type = PF_RT_OS_FINGERPRINT, .off = _OUT(os_fingerprint), .cb = nlattr_get_uint32 }, + { .type = PF_RT_RTABLEID, .off = _OUT(rtableid), .cb = nlattr_get_uint32 }, + { .type = PF_RT_TIMEOUT, .off = _OUT(timeout), .arg = &timeout_parser, .cb = nlattr_get_nested_timeouts }, + { .type = PF_RT_MAX_STATES, .off = _OUT(max_states), .cb = nlattr_get_uint32 }, + { .type = PF_RT_MAX_SRC_NODES, .off = _OUT(max_src_nodes), .cb = nlattr_get_uint32 }, + { .type = PF_RT_MAX_SRC_STATES, .off = _OUT(max_src_states), .cb = nlattr_get_uint32 }, + { .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(max_src_conn_rate.limit), .cb = nlattr_get_uint32 }, + { .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(max_src_conn_rate.seconds), .cb = nlattr_get_uint32 }, + { .type = PF_RT_DNPIPE, .off = _OUT(dnpipe), .cb = nlattr_get_uint16 }, + { .type = PF_RT_DNRPIPE, .off = _OUT(dnrpipe), .cb = nlattr_get_uint16 }, + { .type = PF_RT_DNFLAGS, .off = _OUT(free_flags), .cb = nlattr_get_uint32 }, + { .type = PF_RT_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 }, + { .type = PF_RT_PROB, .off = _OUT(prob), .cb = nlattr_get_uint32 }, + { .type = PF_RT_CUID, .off = _OUT(cuid), .cb = nlattr_get_uint32 }, + {. type = PF_RT_CPID, .off = _OUT(cpid), .cb = nlattr_get_uint32 }, + { .type = PF_RT_RETURN_ICMP, .off = _OUT(return_icmp), .cb = nlattr_get_uint16 }, + { .type = PF_RT_RETURN_ICMP6, .off = _OUT(return_icmp6), .cb = nlattr_get_uint16 }, + { .type = PF_RT_MAX_MSS, .off = _OUT(max_mss), .cb = nlattr_get_uint16 }, + { .type = PF_RT_SCRUB_FLAGS, .off = _OUT(scrub_flags), .cb = nlattr_get_uint16 }, + { .type = PF_RT_UID, .off = _OUT(uid), .arg = &rule_uid_parser, .cb = nlattr_get_nested }, + { .type = PF_RT_GID, .off = _OUT(gid), .arg = &rule_uid_parser, .cb = nlattr_get_nested }, + { .type = PF_RT_RULE_FLAG, .off = _OUT(rule_flag), .cb = nlattr_get_uint32 }, + { .type = PF_RT_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 }, + { .type = PF_RT_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 }, + { .type = PF_RT_LOG, .off = _OUT(log), .cb = nlattr_get_uint8 }, + { .type = PF_RT_LOGIF, .off = _OUT(logif), .cb = nlattr_get_uint8 }, + { .type = PF_RT_QUICK, .off = _OUT(quick), .cb = nlattr_get_uint8 }, + { .type = PF_RT_IF_NOT, .off = _OUT(ifnot), .cb = nlattr_get_uint8 }, + { .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(match_tag_not), .cb = nlattr_get_uint8 }, + { .type = PF_RT_NATPASS, .off = _OUT(natpass), .cb = nlattr_get_uint8 }, + { .type = PF_RT_KEEP_STATE, .off = _OUT(keep_state), .cb = nlattr_get_uint8 }, + { .type = PF_RT_AF, .off = _OUT(af), .cb = nlattr_get_uint8 }, + { .type = PF_RT_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 }, + { .type = PF_RT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 }, + { .type = PF_RT_CODE, .off = _OUT(code), .cb = nlattr_get_uint8 }, + { .type = PF_RT_FLAGS, .off = _OUT(flags), .cb = nlattr_get_uint8 }, + { .type = PF_RT_FLAGSET, .off = _OUT(flagset), .cb = nlattr_get_uint8 }, + { .type = PF_RT_MIN_TTL, .off = _OUT(min_ttl), .cb = nlattr_get_uint8 }, + { .type = PF_RT_ALLOW_OPTS, .off = _OUT(allow_opts), .cb = nlattr_get_uint8 }, + { .type = PF_RT_RT, .off = _OUT(rt), .cb = nlattr_get_uint8 }, + { .type = PF_RT_RETURN_TTL, .off = _OUT(return_ttl), .cb = nlattr_get_uint8 }, + { .type = PF_RT_TOS, .off = _OUT(tos), .cb = nlattr_get_uint8 }, + { .type = PF_RT_SET_TOS, .off = _OUT(set_tos), .cb = nlattr_get_uint8 }, + { .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(anchor_relative), .cb = nlattr_get_uint8 }, + { .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(anchor_wildcard), .cb = nlattr_get_uint8 }, + { .type = PF_RT_FLUSH, .off = _OUT(flush), .cb = nlattr_get_uint8 }, + { .type = PF_RT_PRIO, .off = _OUT(prio), .cb = nlattr_get_uint8 }, + { .type = PF_RT_SET_PRIO, .off = _OUT(set_prio[0]), .cb = nlattr_get_uint8 }, + { .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(set_prio[1]), .cb = nlattr_get_uint8 }, + { .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(divert.addr), .cb = nlattr_get_in6_addr }, + { .type = PF_RT_DIVERT_PORT, .off = _OUT(divert.port), .cb = nlattr_get_uint16 }, +}; +NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule); +#undef _OUT +struct nl_parsed_addrule { + struct pf_krule *rule; + uint32_t ticket; + uint32_t pool_ticket; + char *anchor; + char *anchor_call; +}; +#define _IN(_field) offsetof(struct genlmsghdr, _field) +#define _OUT(_field) offsetof(struct nl_parsed_addrule, _field) +static const struct nlattr_parser nla_p_addrule[] = { + { .type = PF_ART_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 }, + { .type = PF_ART_POOL_TICKET, .off = _OUT(pool_ticket), .cb = nlattr_get_uint32 }, + { .type = PF_ART_ANCHOR, .off = _OUT(anchor), .cb = nlattr_get_string }, + { .type = PF_ART_ANCHOR_CALL, .off = _OUT(anchor_call), .cb = nlattr_get_string }, + { .type = PF_ART_RULE, .off = _OUT(rule), .arg = &rule_parser, .cb = nlattr_get_nested_ptr } +}; +static const struct nlfield_parser nlf_p_addrule[] = { +}; +#undef _IN +#undef _OUT +NL_DECLARE_PARSER(addrule_parser, struct genlmsghdr, nlf_p_addrule, nla_p_addrule); + +static int +pf_handle_addrule(struct nlmsghdr *hdr, struct nl_pstate *npt) +{ + int error; + struct nl_parsed_addrule attrs = {}; + + attrs.rule = pf_krule_alloc(); + + error = nl_parse_nlmsg(hdr, &addrule_parser, npt, &attrs); + if (error != 0) + return (error); + + error = pf_ioctl_addrule(attrs.rule, attrs.ticket, attrs.pool_ticket, + attrs.anchor, attrs.anchor_call, nlp_get_cred(npt->nlp)->cr_uid, + hdr->nlmsg_pid); + + if (error != 0) + pf_krule_free(attrs.rule); + + return (error); +} + +static const struct nlhdr_parser *all_parsers[] = { &state_parser, &addrule_parser }; static int family_id; @@ -377,12 +635,20 @@ .cmd_cb = pf_handle_stop, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, }, + { + .cmd_num = PFNL_CMD_ADDRULE, + .cmd_name = "ADDRULE", + .cmd_cb = pf_handle_addrule, + .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, + }, + }; void pf_nl_register(void) { NL_VERIFY_PARSERS(all_parsers); + family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX); genl_register_cmds(PFNL_FAMILY_NAME, pf_cmds, NL_ARRAY_LEN(pf_cmds)); }