diff --git a/contrib/pf/ftp-proxy/filter.c b/contrib/pf/ftp-proxy/filter.c index 7893be97f9b2..e3276f88489e 100644 --- a/contrib/pf/ftp-proxy/filter.c +++ b/contrib/pf/ftp-proxy/filter.c @@ -1,405 +1,405 @@ /* $OpenBSD: filter.c,v 1.8 2008/06/13 07:25:26 claudio Exp $ */ /* * Copyright (c) 2004, 2005 Camiel Dobbelaar, * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "filter.h" /* From netinet/in.h, but only _KERNEL_ gets them. */ #define satosin(sa) ((struct sockaddr_in *)(sa)) #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE }; int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *, u_int16_t); int server_lookup4(struct sockaddr_in *, struct sockaddr_in *, struct sockaddr_in *); int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *, struct sockaddr_in6 *); static struct pfioc_pooladdr pfp; static struct pfctl_rule pfrule; static char pfanchor[PF_ANCHOR_NAME_SIZE]; static char pfanchor_call[PF_ANCHOR_NAME_SIZE]; static uint32_t pfticket; static uint32_t pfpool_ticket; static struct pfioc_trans pft; static struct pfioc_trans_e pfte[TRANS_SIZE]; static int rule_log; static struct pfctl_handle *pfh = NULL; static const char *qname, *tagname; int add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, struct sockaddr *dst, u_int16_t d_port) { if (!src || !dst || !d_port) { errno = EINVAL; return (-1); } if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1) return (-1); pfrule.direction = dir; if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call, pfticket, pfpool_ticket)) return (-1); return (0); } int add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low, u_int16_t nat_range_high) { if (!src || !dst || !d_port || !nat || !nat_range_low || (src->sa_family != nat->sa_family)) { errno = EINVAL; return (-1); } if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1) return (-1); if (nat->sa_family == AF_INET) { memcpy(&pfp.addr.addr.v.a.addr.v4, &satosin(nat)->sin_addr.s_addr, 4); memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); } else { memcpy(&pfp.addr.addr.v.a.addr.v6, &satosin6(nat)->sin6_addr.s6_addr, 16); memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); } if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1) return (-1); pfrule.rpool.proxy_port[0] = nat_range_low; pfrule.rpool.proxy_port[1] = nat_range_high; if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call, pfticket, pfpool_ticket)) return (-1); return (0); } int add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) { if (!src || !dst || !d_port || !rdr || !rdr_port || (src->sa_family != rdr->sa_family)) { errno = EINVAL; return (-1); } if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1) return (-1); if (rdr->sa_family == AF_INET) { memcpy(&pfp.addr.addr.v.a.addr.v4, &satosin(rdr)->sin_addr.s_addr, 4); memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); } else { memcpy(&pfp.addr.addr.v.a.addr.v6, &satosin6(rdr)->sin6_addr.s6_addr, 16); memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); } if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1) return (-1); pfrule.rpool.proxy_port[0] = rdr_port; if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call, pfticket, pfpool_ticket)) return (-1); return (0); } int do_commit(void) { if (ioctl(pfctl_fd(pfh), DIOCXCOMMIT, &pft) == -1) return (-1); return (0); } int do_rollback(void) { if (ioctl(pfctl_fd(pfh), DIOCXROLLBACK, &pft) == -1) return (-1); return (0); } void init_filter(const char *opt_qname, const char *opt_tagname, int opt_verbose) { struct pfctl_status *status; qname = opt_qname; tagname = opt_tagname; if (opt_verbose == 1) rule_log = PF_LOG; else if (opt_verbose == 2) rule_log = PF_LOG_ALL; pfh = pfctl_open(PF_DEVICE); if (pfh == NULL) err(1, "pfctl_open"); status = pfctl_get_status_h(pfh); if (status == NULL) err(1, "DIOCGETSTATUS"); if (!status->running) errx(1, "pf is disabled"); pfctl_free_status(status); } int prepare_commit(u_int32_t id) { char an[PF_ANCHOR_NAME_SIZE]; int i; memset(&pft, 0, sizeof pft); pft.size = TRANS_SIZE; pft.esize = sizeof pfte[0]; pft.array = pfte; snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, getpid(), id); for (i = 0; i < TRANS_SIZE; i++) { memset(&pfte[i], 0, sizeof pfte[0]); strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); switch (i) { case TRANS_FILTER: pfte[i].rs_num = PF_RULESET_FILTER; break; case TRANS_NAT: pfte[i].rs_num = PF_RULESET_NAT; break; case TRANS_RDR: pfte[i].rs_num = PF_RULESET_RDR; break; default: errno = EINVAL; return (-1); } } if (ioctl(pfctl_fd(pfh), DIOCXBEGIN, &pft) == -1) return (-1); return (0); } int prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, struct sockaddr *dst, u_int16_t d_port) { char an[PF_ANCHOR_NAME_SIZE]; if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || (src->sa_family != dst->sa_family)) { errno = EPROTONOSUPPORT; return (-1); } memset(&pfp, 0, sizeof pfp); memset(&pfrule, 0, sizeof pfrule); snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, getpid(), id); strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); strlcpy(pfanchor, an, PF_ANCHOR_NAME_SIZE); switch (rs_num) { case PF_RULESET_FILTER: pfticket = pfte[TRANS_FILTER].ticket; break; case PF_RULESET_NAT: pfticket = pfte[TRANS_NAT].ticket; break; case PF_RULESET_RDR: pfticket = pfte[TRANS_RDR].ticket; break; default: errno = EINVAL; return (-1); } if (ioctl(pfctl_fd(pfh), DIOCBEGINADDRS, &pfp) == -1) return (-1); pfpool_ticket = pfp.ticket; /* Generic for all rule types. */ pfrule.af = src->sa_family; pfrule.proto = IPPROTO_TCP; pfrule.src.addr.type = PF_ADDR_ADDRMASK; pfrule.dst.addr.type = PF_ADDR_ADDRMASK; if (src->sa_family == AF_INET) { memcpy(&pfrule.src.addr.v.a.addr.v4, &satosin(src)->sin_addr.s_addr, 4); memset(&pfrule.src.addr.v.a.mask.addr8, 255, 4); memcpy(&pfrule.dst.addr.v.a.addr.v4, &satosin(dst)->sin_addr.s_addr, 4); memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 4); } else { memcpy(&pfrule.src.addr.v.a.addr.v6, &satosin6(src)->sin6_addr.s6_addr, 16); memset(&pfrule.src.addr.v.a.mask.addr8, 255, 16); memcpy(&pfrule.dst.addr.v.a.addr.v6, &satosin6(dst)->sin6_addr.s6_addr, 16); memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 16); } pfrule.dst.port_op = PF_OP_EQ; pfrule.dst.port[0] = htons(d_port); switch (rs_num) { case PF_RULESET_FILTER: /* * pass [quick] [log] inet[6] proto tcp \ * from $src to $dst port = $d_port flags S/SA keep state * (max 1) [queue qname] [tag tagname] */ pfrule.action = PF_PASS; pfrule.quick = 1; pfrule.log = rule_log; pfrule.keep_state = 1; pfrule.flags = TH_SYN; pfrule.flagset = (TH_SYN|TH_ACK); pfrule.max_states = 1; if (qname != NULL) strlcpy(pfrule.qname, qname, sizeof pfrule.qname); if (tagname != NULL) { pfrule.quick = 0; strlcpy(pfrule.tagname, tagname, sizeof pfrule.tagname); } break; case PF_RULESET_NAT: /* * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat */ pfrule.action = PF_NAT; break; case PF_RULESET_RDR: /* * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr */ pfrule.action = PF_RDR; break; default: errno = EINVAL; return (-1); } return (0); } int server_lookup(struct sockaddr *client, struct sockaddr *proxy, struct sockaddr *server) { if (client->sa_family == AF_INET) return (server_lookup4(satosin(client), satosin(proxy), satosin(server))); if (client->sa_family == AF_INET6) return (server_lookup6(satosin6(client), satosin6(proxy), satosin6(server))); errno = EPROTONOSUPPORT; return (-1); } int server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, struct sockaddr_in *server) { - struct pfioc_natlook pnl; - - memset(&pnl, 0, sizeof pnl); - pnl.direction = PF_OUT; - pnl.af = AF_INET; - pnl.proto = IPPROTO_TCP; - memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4); - memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4); - pnl.sport = client->sin_port; - pnl.dport = proxy->sin_port; - - if (ioctl(pfctl_fd(pfh), DIOCNATLOOK, &pnl) == -1) + struct pfctl_natlook_key k = {}; + struct pfctl_natlook r = {}; + + k.direction = PF_OUT; + k.af = AF_INET; + k.proto = IPPROTO_TCP; + memcpy(&k.saddr.v4, &client->sin_addr.s_addr, sizeof(k.saddr.v4)); + memcpy(&k.daddr.v4, &proxy->sin_addr.s_addr, sizeof(k.daddr.v4)); + k.sport = client->sin_port; + k.dport = proxy->sin_port; + + if (pfctl_natlook(pfh, &k, &r)) return (-1); memset(server, 0, sizeof(struct sockaddr_in)); server->sin_len = sizeof(struct sockaddr_in); server->sin_family = AF_INET; - memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4, - sizeof server->sin_addr.s_addr); - server->sin_port = pnl.rdport; - + memcpy(&server->sin_addr.s_addr, &r.daddr.v4, + sizeof(server->sin_addr.s_addr)); + server->sin_port = r.dport; + return (0); } int server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, struct sockaddr_in6 *server) { - struct pfioc_natlook pnl; - - memset(&pnl, 0, sizeof pnl); - pnl.direction = PF_OUT; - pnl.af = AF_INET6; - pnl.proto = IPPROTO_TCP; - memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6); - memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6); - pnl.sport = client->sin6_port; - pnl.dport = proxy->sin6_port; - - if (ioctl(pfctl_fd(pfh), DIOCNATLOOK, &pnl) == -1) + struct pfctl_natlook_key k = {}; + struct pfctl_natlook r = {}; + + k.direction = PF_OUT; + k.af = AF_INET6; + k.proto = IPPROTO_TCP; + memcpy(&k.saddr.v6, &client->sin6_addr.s6_addr, sizeof(k.saddr.v6)); + memcpy(&k.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof(k.daddr.v6)); + k.sport = client->sin6_port; + k.dport = proxy->sin6_port; + + if (pfctl_natlook(pfh, &k, &r)) return (-1); memset(server, 0, sizeof(struct sockaddr_in6)); server->sin6_len = sizeof(struct sockaddr_in6); server->sin6_family = AF_INET6; - memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6, - sizeof server->sin6_addr); - server->sin6_port = pnl.rdport; + memcpy(&server->sin6_addr.s6_addr, &r.daddr.v6, + sizeof(server->sin6_addr)); + server->sin6_port = r.dport; return (0); } diff --git a/contrib/pf/tftp-proxy/filter.c b/contrib/pf/tftp-proxy/filter.c index 1e6d54303996..3cf2e1fcf3ab 100644 --- a/contrib/pf/tftp-proxy/filter.c +++ b/contrib/pf/tftp-proxy/filter.c @@ -1,416 +1,416 @@ /* $OpenBSD: filter.c,v 1.1 2005/12/28 19:07:07 jcs Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 2004, 2005 Camiel Dobbelaar, * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "filter.h" /* From netinet/in.h, but only _KERNEL_ gets them. */ #define satosin(sa) ((struct sockaddr_in *)(sa)) #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE }; int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *, u_int16_t, u_int8_t); int server_lookup4(struct sockaddr_in *, struct sockaddr_in *, struct sockaddr_in *, u_int8_t); int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *, struct sockaddr_in6 *, u_int8_t); static struct pfioc_pooladdr pfp; static struct pfctl_rule pfrule; static uint32_t pfticket; static uint32_t pfpool_ticket; static char pfanchor[PF_ANCHOR_NAME_SIZE]; static char pfanchor_call[PF_ANCHOR_NAME_SIZE]; static struct pfioc_trans pft; static struct pfioc_trans_e pfte[TRANS_SIZE]; static int rule_log; static struct pfctl_handle *pfh = NULL; static char *qname; int add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, struct sockaddr *dst, u_int16_t d_port, u_int8_t proto) { if (!src || !dst || !d_port || !proto) { errno = EINVAL; return (-1); } if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port, proto) == -1) return (-1); pfrule.direction = dir; if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call, pfticket, pfpool_ticket)) return (-1); return (0); } int add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low, u_int16_t nat_range_high, u_int8_t proto) { if (!src || !dst || !d_port || !nat || !nat_range_low || !proto || (src->sa_family != nat->sa_family)) { errno = EINVAL; return (-1); } if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port, proto) == -1) return (-1); if (nat->sa_family == AF_INET) { memcpy(&pfp.addr.addr.v.a.addr.v4, &satosin(nat)->sin_addr.s_addr, 4); memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); } else { memcpy(&pfp.addr.addr.v.a.addr.v6, &satosin6(nat)->sin6_addr.s6_addr, 16); memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); } if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1) return (-1); pfrule.rpool.proxy_port[0] = nat_range_low; pfrule.rpool.proxy_port[1] = nat_range_high; if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call, pfticket, pfpool_ticket)) return (-1); return (0); } int add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto) { if (!src || !dst || !d_port || !rdr || !rdr_port || !proto || (src->sa_family != rdr->sa_family)) { errno = EINVAL; return (-1); } if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port, proto) == -1) return (-1); if (rdr->sa_family == AF_INET) { memcpy(&pfp.addr.addr.v.a.addr.v4, &satosin(rdr)->sin_addr.s_addr, 4); memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); } else { memcpy(&pfp.addr.addr.v.a.addr.v6, &satosin6(rdr)->sin6_addr.s6_addr, 16); memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); } if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1) return (-1); pfrule.rpool.proxy_port[0] = rdr_port; if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call, pfticket, pfpool_ticket)) return (-1); return (0); } int do_commit(void) { if (ioctl(pfctl_fd(pfh), DIOCXCOMMIT, &pft) == -1) return (-1); return (0); } int do_rollback(void) { if (ioctl(pfctl_fd(pfh), DIOCXROLLBACK, &pft) == -1) return (-1); return (0); } void init_filter(char *opt_qname, int opt_verbose) { struct pfctl_status *status; qname = opt_qname; if (opt_verbose == 1) rule_log = PF_LOG; else if (opt_verbose == 2) rule_log = PF_LOG_ALL; pfh = pfctl_open(PF_DEVICE); if (pfh == NULL) { syslog(LOG_ERR, "can't pfctl_open()"); exit(1); } status = pfctl_get_status_h(pfh); if (status == NULL) { syslog(LOG_ERR, "DIOCGETSTATUS"); exit(1); } if (!status->running) { syslog(LOG_ERR, "pf is disabled"); exit(1); } pfctl_free_status(status); } int prepare_commit(u_int32_t id) { char an[PF_ANCHOR_NAME_SIZE]; int i; memset(&pft, 0, sizeof pft); pft.size = TRANS_SIZE; pft.esize = sizeof pfte[0]; pft.array = pfte; snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, getpid(), id); for (i = 0; i < TRANS_SIZE; i++) { memset(&pfte[i], 0, sizeof pfte[0]); strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); switch (i) { case TRANS_FILTER: pfte[i].rs_num = PF_RULESET_FILTER; break; case TRANS_NAT: pfte[i].rs_num = PF_RULESET_NAT; break; case TRANS_RDR: pfte[i].rs_num = PF_RULESET_RDR; break; default: errno = EINVAL; return (-1); } } if (ioctl(pfctl_fd(pfh), DIOCXBEGIN, &pft) == -1) return (-1); return (0); } int prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, struct sockaddr *dst, u_int16_t d_port, u_int8_t proto) { char an[PF_ANCHOR_NAME_SIZE]; if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || (src->sa_family != dst->sa_family)) { errno = EPROTONOSUPPORT; return (-1); } memset(&pfp, 0, sizeof pfp); memset(&pfrule, 0, sizeof pfrule); snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, getpid(), id); strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); strlcpy(pfanchor, an, PF_ANCHOR_NAME_SIZE); switch (rs_num) { case PF_RULESET_FILTER: pfticket = pfte[TRANS_FILTER].ticket; break; case PF_RULESET_NAT: pfticket = pfte[TRANS_NAT].ticket; break; case PF_RULESET_RDR: pfticket = pfte[TRANS_RDR].ticket; break; default: errno = EINVAL; return (-1); } if (ioctl(pfctl_fd(pfh), DIOCBEGINADDRS, &pfp) == -1) return (-1); pfpool_ticket = pfp.ticket; /* Generic for all rule types. */ pfrule.af = src->sa_family; pfrule.proto = proto; pfrule.src.addr.type = PF_ADDR_ADDRMASK; pfrule.dst.addr.type = PF_ADDR_ADDRMASK; if (src->sa_family == AF_INET) { memcpy(&pfrule.src.addr.v.a.addr.v4, &satosin(src)->sin_addr.s_addr, 4); memset(&pfrule.src.addr.v.a.mask.addr8, 255, 4); memcpy(&pfrule.dst.addr.v.a.addr.v4, &satosin(dst)->sin_addr.s_addr, 4); memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 4); } else { memcpy(&pfrule.src.addr.v.a.addr.v6, &satosin6(src)->sin6_addr.s6_addr, 16); memset(&pfrule.src.addr.v.a.mask.addr8, 255, 16); memcpy(&pfrule.dst.addr.v.a.addr.v6, &satosin6(dst)->sin6_addr.s6_addr, 16); memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 16); } pfrule.dst.port_op = PF_OP_EQ; pfrule.dst.port[0] = htons(d_port); switch (rs_num) { case PF_RULESET_FILTER: /* * pass quick [log] inet[6] proto tcp \ * from $src to $dst port = $d_port flags S/SAFR keep state * (max 1) [queue qname] */ pfrule.action = PF_PASS; pfrule.quick = 1; pfrule.log = rule_log; pfrule.keep_state = 1; #ifdef __FreeBSD__ pfrule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0); pfrule.flagset = (proto == IPPROTO_TCP ? (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0); #else pfrule.flags = (proto == IPPROTO_TCP ? TH_SYN : NULL); pfrule.flagset = (proto == IPPROTO_TCP ? (TH_SYN|TH_ACK|TH_FIN|TH_RST) : NULL); #endif pfrule.max_states = 1; if (qname != NULL) strlcpy(pfrule.qname, qname, sizeof pfrule.qname); break; case PF_RULESET_NAT: /* * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat */ pfrule.action = PF_NAT; break; case PF_RULESET_RDR: /* * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr */ pfrule.action = PF_RDR; break; default: errno = EINVAL; return (-1); } return (0); } int server_lookup(struct sockaddr *client, struct sockaddr *proxy, struct sockaddr *server, u_int8_t proto) { if (client->sa_family == AF_INET) return (server_lookup4(satosin(client), satosin(proxy), satosin(server), proto)); if (client->sa_family == AF_INET6) return (server_lookup6(satosin6(client), satosin6(proxy), satosin6(server), proto)); errno = EPROTONOSUPPORT; return (-1); } int server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, struct sockaddr_in *server, u_int8_t proto) { - struct pfioc_natlook pnl; - - memset(&pnl, 0, sizeof pnl); - pnl.direction = PF_OUT; - pnl.af = AF_INET; - pnl.proto = proto; - memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4); - memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4); - pnl.sport = client->sin_port; - pnl.dport = proxy->sin_port; - - if (ioctl(pfctl_fd(pfh), DIOCNATLOOK, &pnl) == -1) + struct pfctl_natlook_key k = {}; + struct pfctl_natlook r = {}; + + k.direction = PF_OUT; + k.af = AF_INET; + k.proto = proto; + memcpy(&k.saddr.v4, &client->sin_addr.s_addr, sizeof(k.saddr.v4)); + memcpy(&k.daddr.v4, &proxy->sin_addr.s_addr, sizeof(k.daddr.v4)); + k.sport = client->sin_port; + k.dport = proxy->sin_port; + + if (pfctl_natlook(pfh, &k, &r)) return (-1); memset(server, 0, sizeof(struct sockaddr_in)); server->sin_len = sizeof(struct sockaddr_in); server->sin_family = AF_INET; - memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4, + memcpy(&server->sin_addr.s_addr, &r.daddr.v4, sizeof server->sin_addr.s_addr); - server->sin_port = pnl.rdport; + server->sin_port = r.dport; return (0); } int server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, struct sockaddr_in6 *server, u_int8_t proto) { - struct pfioc_natlook pnl; - - memset(&pnl, 0, sizeof pnl); - pnl.direction = PF_OUT; - pnl.af = AF_INET6; - pnl.proto = proto; - memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6); - memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6); - pnl.sport = client->sin6_port; - pnl.dport = proxy->sin6_port; - - if (ioctl(pfctl_fd(pfh), DIOCNATLOOK, &pnl) == -1) + struct pfctl_natlook_key k = {}; + struct pfctl_natlook r = {}; + + k.direction = PF_OUT; + k.af = AF_INET6; + k.proto = proto; + memcpy(&k.saddr.v6, &client->sin6_addr.s6_addr, sizeof k.saddr.v6); + memcpy(&k.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof k.daddr.v6); + k.sport = client->sin6_port; + k.dport = proxy->sin6_port; + + if (pfctl_natlook(pfh, &k, &r)) return (-1); memset(server, 0, sizeof(struct sockaddr_in6)); server->sin6_len = sizeof(struct sockaddr_in6); server->sin6_family = AF_INET6; - memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6, + memcpy(&server->sin6_addr.s6_addr, &r.daddr.v6, sizeof server->sin6_addr); - server->sin6_port = pnl.rdport; + server->sin6_port = r.dport; return (0); } diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c index 771097a33dab..d5cd6c4bda60 100644 --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -1,2437 +1,2492 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021 Rubicon Communications, LLC (Netgate) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - 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 COPYRIGHT HOLDERS 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 * COPYRIGHT HOLDERS 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libpfctl.h" struct pfctl_handle { int fd; struct snl_state ss; }; const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = { "never", "always", "adaptive" }; static int _pfctl_clear_states(int , const struct pfctl_kill *, unsigned int *, uint64_t); struct pfctl_handle * pfctl_open(const char *pf_device) { struct pfctl_handle *h; h = calloc(1, sizeof(struct pfctl_handle)); h->fd = -1; h->fd = open(pf_device, O_RDWR); if (h->fd < 0) goto error; if (!snl_init(&h->ss, NETLINK_GENERIC)) goto error; return (h); error: close(h->fd); snl_free(&h->ss); free(h); return (NULL); } void pfctl_close(struct pfctl_handle *h) { close(h->fd); snl_free(&h->ss); free(h); } int pfctl_fd(struct pfctl_handle *h) { return (h->fd); } static int pfctl_do_netlink_cmd(struct pfctl_handle *h, uint cmd) { struct snl_errmsg_data e = {}; struct snl_writer nw; struct nlmsghdr *hdr; uint32_t seq_id; int family_id; family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME); if (family_id == 0) return (ENOTSUP); snl_init_writer(&h->ss, &nw); hdr = snl_create_genl_msg_request(&nw, family_id, cmd); hdr = snl_finalize_msg(&nw); if (hdr == NULL) return (ENOMEM); seq_id = hdr->nlmsg_seq; snl_send_message(&h->ss, hdr); while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) { } return (e.error); } static int pfctl_do_ioctl(int dev, uint cmd, size_t size, nvlist_t **nvl) { struct pfioc_nv nv; void *data; size_t nvlen; int ret; data = nvlist_pack(*nvl, &nvlen); if (nvlen > size) size = nvlen; retry: nv.data = malloc(size); if (nv.data == NULL) { ret = ENOMEM; goto out; } memcpy(nv.data, data, nvlen); nv.len = nvlen; nv.size = size; ret = ioctl(dev, cmd, &nv); if (ret == -1 && errno == ENOSPC) { size *= 2; free(nv.data); goto retry; } nvlist_destroy(*nvl); *nvl = NULL; if (ret == 0) { *nvl = nvlist_unpack(nv.data, nv.len, 0); if (*nvl == NULL) { ret = EIO; goto out; } } else { ret = errno; } out: free(data); free(nv.data); return (ret); } static void pf_nvuint_8_array(const nvlist_t *nvl, const char *name, size_t maxelems, uint8_t *numbers, size_t *nelems) { const uint64_t *tmp; size_t elems; tmp = nvlist_get_number_array(nvl, name, &elems); assert(elems <= maxelems); for (size_t i = 0; i < elems; i++) numbers[i] = tmp[i]; if (nelems) *nelems = elems; } static void pf_nvuint_16_array(const nvlist_t *nvl, const char *name, size_t maxelems, uint16_t *numbers, size_t *nelems) { const uint64_t *tmp; size_t elems; tmp = nvlist_get_number_array(nvl, name, &elems); assert(elems <= maxelems); for (size_t i = 0; i < elems; i++) numbers[i] = tmp[i]; if (nelems) *nelems = elems; } static void pf_nvuint_32_array(const nvlist_t *nvl, const char *name, size_t maxelems, uint32_t *numbers, size_t *nelems) { const uint64_t *tmp; size_t elems; tmp = nvlist_get_number_array(nvl, name, &elems); for (size_t i = 0; i < elems && i < maxelems; i++) numbers[i] = tmp[i]; if (nelems) *nelems = elems; } static void pf_nvuint_64_array(const nvlist_t *nvl, const char *name, size_t maxelems, uint64_t *numbers, size_t *nelems) { const uint64_t *tmp; size_t elems; tmp = nvlist_get_number_array(nvl, name, &elems); assert(elems <= maxelems); for (size_t i = 0; i < elems; i++) numbers[i] = tmp[i]; if (nelems) *nelems = elems; } int pfctl_startstop(struct pfctl_handle *h, int start) { return (pfctl_do_netlink_cmd(h, start ? PFNL_CMD_START : PFNL_CMD_STOP)); } static void _pfctl_get_status_counters(const nvlist_t *nvl, struct pfctl_status_counters *counters) { const uint64_t *ids, *counts; const char *const *names; size_t id_len, counter_len, names_len; ids = nvlist_get_number_array(nvl, "ids", &id_len); counts = nvlist_get_number_array(nvl, "counters", &counter_len); names = nvlist_get_string_array(nvl, "names", &names_len); assert(id_len == counter_len); assert(counter_len == names_len); TAILQ_INIT(counters); for (size_t i = 0; i < id_len; i++) { struct pfctl_status_counter *c; c = malloc(sizeof(*c)); if (c == NULL) continue; c->id = ids[i]; c->counter = counts[i]; c->name = strdup(names[i]); TAILQ_INSERT_TAIL(counters, c, entry); } } #define _OUT(_field) offsetof(struct pfctl_status_counter, _field) static const struct snl_attr_parser ap_counter[] = { { .type = PF_C_COUNTER, .off = _OUT(counter), .cb = snl_attr_get_uint64 }, { .type = PF_C_NAME, .off = _OUT(name), .cb = snl_attr_get_string }, { .type = PF_C_ID, .off = _OUT(id), .cb = snl_attr_get_uint32 }, }; SNL_DECLARE_ATTR_PARSER(counter_parser, ap_counter); #undef _OUT static bool snl_attr_get_counters(struct snl_state *ss, struct nlattr *nla, const void *arg __unused, void *target) { struct pfctl_status_counter counter = {}; struct pfctl_status_counter *c; bool error; error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &counter_parser, &counter); if (! error) return (error); c = malloc(sizeof(*c)); if (c == NULL) return (false); c->id = counter.id; c->counter = counter.counter; c->name = strdup(counter.name); TAILQ_INSERT_TAIL((struct pfctl_status_counters *)target, c, entry); return (error); } struct snl_uint64_array { uint64_t *array; size_t count; size_t max; }; static bool snl_attr_get_uint64_element(struct snl_state *ss, struct nlattr *nla, const void *arg, void *target) { bool error; uint64_t value; struct snl_uint64_array *t = (struct snl_uint64_array *)target; if (t->count >= t->max) return (false); error = snl_attr_get_uint64(ss, nla, arg, &value); if (! error) return (error); t->array[t->count++] = value; return (true); } static const struct snl_attr_parser ap_array[] = { { .cb = snl_attr_get_uint64_element }, }; SNL_DECLARE_ATTR_PARSER(array_parser, ap_array); static bool snl_attr_get_uint64_array(struct snl_state *ss, struct nlattr *nla, const void *arg, void *target) { struct snl_uint64_array a = { .array = target, .count = 0, .max = (size_t)arg, }; bool error; error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &array_parser, &a); if (! error) return (error); return (true); } #define _OUT(_field) offsetof(struct pfctl_status, _field) static const struct snl_attr_parser ap_getstatus[] = { { .type = PF_GS_IFNAME, .off = _OUT(ifname), .arg_u32 = IFNAMSIZ, .cb = snl_attr_copy_string }, { .type = PF_GS_RUNNING, .off = _OUT(running), .cb = snl_attr_get_bool }, { .type = PF_GS_SINCE, .off = _OUT(since), .cb = snl_attr_get_uint32 }, { .type = PF_GS_DEBUG, .off = _OUT(debug), .cb = snl_attr_get_uint32 }, { .type = PF_GS_HOSTID, .off = _OUT(hostid), .cb = snl_attr_get_uint32 }, { .type = PF_GS_STATES, .off = _OUT(states), .cb = snl_attr_get_uint32 }, { .type = PF_GS_SRC_NODES, .off = _OUT(src_nodes), .cb = snl_attr_get_uint32 }, { .type = PF_GS_REASSEMBLE, .off = _OUT(reass), .cb = snl_attr_get_uint32 }, { .type = PF_GS_SYNCOOKIES_ACTIVE, .off = _OUT(syncookies_active), .cb = snl_attr_get_uint32 }, { .type = PF_GS_COUNTERS, .off = _OUT(counters), .cb = snl_attr_get_counters }, { .type = PF_GS_LCOUNTERS, .off = _OUT(lcounters), .cb = snl_attr_get_counters }, { .type = PF_GS_FCOUNTERS, .off = _OUT(fcounters), .cb = snl_attr_get_counters }, { .type = PF_GS_SCOUNTERS, .off = _OUT(scounters), .cb = snl_attr_get_counters }, { .type = PF_GS_CHKSUM, .off = _OUT(pf_chksum), .arg_u32 = PF_MD5_DIGEST_LENGTH, .cb = snl_attr_get_bytes }, { .type = PF_GS_BCOUNTERS, .off = _OUT(bcounters), .arg_u32 = 2 * 2, .cb = snl_attr_get_uint64_array }, { .type = PF_GS_PCOUNTERS, .off = _OUT(pcounters), .arg_u32 = 2 * 2 * 2, .cb = snl_attr_get_uint64_array }, }; static struct snl_field_parser fp_getstatus[] = {}; SNL_DECLARE_PARSER(getstatus_parser, struct genlmsghdr, fp_getstatus, ap_getstatus); #undef _OUT struct pfctl_status * pfctl_get_status_h(struct pfctl_handle *h __unused) { struct pfctl_status *status; struct snl_errmsg_data e = {}; struct nlmsghdr *hdr; struct snl_writer nw; uint32_t seq_id; int family_id; family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME); if (family_id == 0) return (NULL); snl_init_writer(&h->ss, &nw); hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GET_STATUS); hdr->nlmsg_flags |= NLM_F_DUMP; hdr = snl_finalize_msg(&nw); if (hdr == NULL) { return (NULL); } seq_id = hdr->nlmsg_seq; if (! snl_send_message(&h->ss, hdr)) return (NULL); status = calloc(1, sizeof(*status)); if (status == NULL) return (NULL); TAILQ_INIT(&status->counters); TAILQ_INIT(&status->lcounters); TAILQ_INIT(&status->fcounters); TAILQ_INIT(&status->scounters); while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) { if (! snl_parse_nlmsg(&h->ss, hdr, &getstatus_parser, status)) continue; } return (status); } struct pfctl_status * pfctl_get_status(int dev) { struct pfctl_status *status; nvlist_t *nvl; size_t len; const void *chksum; status = calloc(1, sizeof(*status)); if (status == NULL) return (NULL); nvl = nvlist_create(0); if (pfctl_do_ioctl(dev, DIOCGETSTATUSNV, 4096, &nvl)) { nvlist_destroy(nvl); free(status); return (NULL); } status->running = nvlist_get_bool(nvl, "running"); status->since = nvlist_get_number(nvl, "since"); status->debug = nvlist_get_number(nvl, "debug"); status->hostid = ntohl(nvlist_get_number(nvl, "hostid")); status->states = nvlist_get_number(nvl, "states"); status->src_nodes = nvlist_get_number(nvl, "src_nodes"); status->syncookies_active = nvlist_get_bool(nvl, "syncookies_active"); status->reass = nvlist_get_number(nvl, "reass"); strlcpy(status->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ); chksum = nvlist_get_binary(nvl, "chksum", &len); assert(len == PF_MD5_DIGEST_LENGTH); memcpy(status->pf_chksum, chksum, len); _pfctl_get_status_counters(nvlist_get_nvlist(nvl, "counters"), &status->counters); _pfctl_get_status_counters(nvlist_get_nvlist(nvl, "lcounters"), &status->lcounters); _pfctl_get_status_counters(nvlist_get_nvlist(nvl, "fcounters"), &status->fcounters); _pfctl_get_status_counters(nvlist_get_nvlist(nvl, "scounters"), &status->scounters); pf_nvuint_64_array(nvl, "pcounters", 2 * 2 * 2, (uint64_t *)status->pcounters, NULL); pf_nvuint_64_array(nvl, "bcounters", 2 * 2, (uint64_t *)status->bcounters, NULL); nvlist_destroy(nvl); return (status); } int pfctl_clear_status(struct pfctl_handle *h) { return (pfctl_do_netlink_cmd(h, PFNL_CMD_CLEAR_STATUS)); } static uint64_t _pfctl_status_counter(struct pfctl_status_counters *counters, uint64_t id) { struct pfctl_status_counter *c; TAILQ_FOREACH(c, counters, entry) { if (c->id == id) return (c->counter); } return (0); } uint64_t pfctl_status_counter(struct pfctl_status *status, int id) { return (_pfctl_status_counter(&status->counters, id)); } uint64_t pfctl_status_lcounter(struct pfctl_status *status, int id) { return (_pfctl_status_counter(&status->lcounters, id)); } uint64_t pfctl_status_fcounter(struct pfctl_status *status, int id) { return (_pfctl_status_counter(&status->fcounters, id)); } uint64_t pfctl_status_scounter(struct pfctl_status *status, int id) { return (_pfctl_status_counter(&status->scounters, id)); } void pfctl_free_status(struct pfctl_status *status) { struct pfctl_status_counter *c, *tmp; if (status == NULL) return; TAILQ_FOREACH_SAFE(c, &status->counters, entry, tmp) { free(c->name); free(c); } TAILQ_FOREACH_SAFE(c, &status->lcounters, entry, tmp) { free(c->name); free(c); } TAILQ_FOREACH_SAFE(c, &status->fcounters, entry, tmp) { free(c->name); free(c); } TAILQ_FOREACH_SAFE(c, &status->scounters, entry, tmp) { free(c->name); free(c); } free(status); } static void pfctl_nv_add_addr(nvlist_t *nvparent, const char *name, const struct pf_addr *addr) { nvlist_t *nvl = nvlist_create(0); nvlist_add_binary(nvl, "addr", addr, sizeof(*addr)); nvlist_add_nvlist(nvparent, name, nvl); nvlist_destroy(nvl); } static void pf_nvaddr_to_addr(const nvlist_t *nvl, struct pf_addr *addr) { size_t len; const void *data; data = nvlist_get_binary(nvl, "addr", &len); assert(len == sizeof(struct pf_addr)); memcpy(addr, data, len); } static void pfctl_nv_add_addr_wrap(nvlist_t *nvparent, const char *name, const struct pf_addr_wrap *addr) { nvlist_t *nvl = nvlist_create(0); nvlist_add_number(nvl, "type", addr->type); nvlist_add_number(nvl, "iflags", addr->iflags); if (addr->type == PF_ADDR_DYNIFTL) nvlist_add_string(nvl, "ifname", addr->v.ifname); if (addr->type == PF_ADDR_TABLE) nvlist_add_string(nvl, "tblname", addr->v.tblname); pfctl_nv_add_addr(nvl, "addr", &addr->v.a.addr); pfctl_nv_add_addr(nvl, "mask", &addr->v.a.mask); nvlist_add_nvlist(nvparent, name, nvl); nvlist_destroy(nvl); } static void pf_nvaddr_wrap_to_addr_wrap(const nvlist_t *nvl, struct pf_addr_wrap *addr) { bzero(addr, sizeof(*addr)); addr->type = nvlist_get_number(nvl, "type"); addr->iflags = nvlist_get_number(nvl, "iflags"); if (addr->type == PF_ADDR_DYNIFTL) { strlcpy(addr->v.ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ); addr->p.dyncnt = nvlist_get_number(nvl, "dyncnt"); } if (addr->type == PF_ADDR_TABLE) { strlcpy(addr->v.tblname, nvlist_get_string(nvl, "tblname"), PF_TABLE_NAME_SIZE); addr->p.tblcnt = nvlist_get_number(nvl, "tblcnt"); } pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), &addr->v.a.addr); pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "mask"), &addr->v.a.mask); } static void pfctl_nv_add_rule_addr(nvlist_t *nvparent, const char *name, const struct pf_rule_addr *addr) { uint64_t ports[2]; nvlist_t *nvl = nvlist_create(0); pfctl_nv_add_addr_wrap(nvl, "addr", &addr->addr); ports[0] = addr->port[0]; ports[1] = addr->port[1]; nvlist_add_number_array(nvl, "port", ports, 2); nvlist_add_number(nvl, "neg", addr->neg); nvlist_add_number(nvl, "port_op", addr->port_op); nvlist_add_nvlist(nvparent, name, nvl); nvlist_destroy(nvl); } static void pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr) { pf_nvaddr_wrap_to_addr_wrap(nvlist_get_nvlist(nvl, "addr"), &addr->addr); pf_nvuint_16_array(nvl, "port", 2, addr->port, NULL); addr->neg = nvlist_get_number(nvl, "neg"); addr->port_op = nvlist_get_number(nvl, "port_op"); } static void pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape) { mape->offset = nvlist_get_number(nvl, "offset"); mape->psidlen = nvlist_get_number(nvl, "psidlen"); mape->psid = nvlist_get_number(nvl, "psid"); } static void pf_nvpool_to_pool(const nvlist_t *nvl, struct pfctl_pool *pool) { size_t len; const void *data; data = nvlist_get_binary(nvl, "key", &len); assert(len == sizeof(pool->key)); memcpy(&pool->key, data, len); pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "counter"), &pool->counter); pool->tblidx = nvlist_get_number(nvl, "tblidx"); pf_nvuint_16_array(nvl, "proxy_port", 2, pool->proxy_port, NULL); pool->opts = nvlist_get_number(nvl, "opts"); if (nvlist_exists_nvlist(nvl, "mape")) pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"), &pool->mape); } static void pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid) { pf_nvuint_32_array(nvl, "uid", 2, uid->uid, NULL); uid->op = nvlist_get_number(nvl, "op"); } static void pf_nvdivert_to_divert(const nvlist_t *nvl, struct pfctl_rule *rule) { pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), &rule->divert.addr); rule->divert.port = nvlist_get_number(nvl, "port"); } static void pf_nvrule_to_rule(const nvlist_t *nvl, struct pfctl_rule *rule) { const uint64_t *skip; const char *const *labels; size_t skipcount, labelcount; rule->nr = nvlist_get_number(nvl, "nr"); pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"), &rule->src); pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"), &rule->dst); skip = nvlist_get_number_array(nvl, "skip", &skipcount); assert(skip); assert(skipcount == PF_SKIP_COUNT); for (int i = 0; i < PF_SKIP_COUNT; i++) rule->skip[i].nr = skip[i]; labels = nvlist_get_string_array(nvl, "labels", &labelcount); assert(labelcount <= PF_RULE_MAX_LABEL_COUNT); for (size_t i = 0; i < labelcount; i++) strlcpy(rule->label[i], labels[i], PF_RULE_LABEL_SIZE); rule->ridentifier = nvlist_get_number(nvl, "ridentifier"); strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ); strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE); strlcpy(rule->pqname, nvlist_get_string(nvl, "pqname"), PF_QNAME_SIZE); strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"), PF_TAG_NAME_SIZE); strlcpy(rule->match_tagname, nvlist_get_string(nvl, "match_tagname"), PF_TAG_NAME_SIZE); strlcpy(rule->overload_tblname, nvlist_get_string(nvl, "overload_tblname"), PF_TABLE_NAME_SIZE); pf_nvpool_to_pool(nvlist_get_nvlist(nvl, "rpool"), &rule->rpool); rule->evaluations = nvlist_get_number(nvl, "evaluations"); pf_nvuint_64_array(nvl, "packets", 2, rule->packets, NULL); pf_nvuint_64_array(nvl, "bytes", 2, rule->bytes, NULL); if (nvlist_exists_number(nvl, "timestamp")) { rule->last_active_timestamp = nvlist_get_number(nvl, "timestamp"); } rule->os_fingerprint = nvlist_get_number(nvl, "os_fingerprint"); rule->rtableid = nvlist_get_number(nvl, "rtableid"); pf_nvuint_32_array(nvl, "timeout", PFTM_MAX, rule->timeout, NULL); rule->max_states = nvlist_get_number(nvl, "max_states"); rule->max_src_nodes = nvlist_get_number(nvl, "max_src_nodes"); rule->max_src_states = nvlist_get_number(nvl, "max_src_states"); rule->max_src_conn = nvlist_get_number(nvl, "max_src_conn"); rule->max_src_conn_rate.limit = nvlist_get_number(nvl, "max_src_conn_rate.limit"); rule->max_src_conn_rate.seconds = nvlist_get_number(nvl, "max_src_conn_rate.seconds"); rule->qid = nvlist_get_number(nvl, "qid"); rule->pqid = nvlist_get_number(nvl, "pqid"); rule->dnpipe = nvlist_get_number(nvl, "dnpipe"); rule->dnrpipe = nvlist_get_number(nvl, "dnrpipe"); rule->free_flags = nvlist_get_number(nvl, "dnflags"); rule->prob = nvlist_get_number(nvl, "prob"); rule->cuid = nvlist_get_number(nvl, "cuid"); rule->cpid = nvlist_get_number(nvl, "cpid"); rule->return_icmp = nvlist_get_number(nvl, "return_icmp"); rule->return_icmp6 = nvlist_get_number(nvl, "return_icmp6"); rule->max_mss = nvlist_get_number(nvl, "max_mss"); rule->scrub_flags = nvlist_get_number(nvl, "scrub_flags"); pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "uid"), &rule->uid); pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "gid"), (struct pf_rule_uid *)&rule->gid); rule->rule_flag = nvlist_get_number(nvl, "rule_flag"); rule->action = nvlist_get_number(nvl, "action"); rule->direction = nvlist_get_number(nvl, "direction"); rule->log = nvlist_get_number(nvl, "log"); rule->logif = nvlist_get_number(nvl, "logif"); rule->quick = nvlist_get_number(nvl, "quick"); rule->ifnot = nvlist_get_number(nvl, "ifnot"); rule->match_tag_not = nvlist_get_number(nvl, "match_tag_not"); rule->natpass = nvlist_get_number(nvl, "natpass"); rule->keep_state = nvlist_get_number(nvl, "keep_state"); rule->af = nvlist_get_number(nvl, "af"); rule->proto = nvlist_get_number(nvl, "proto"); rule->type = nvlist_get_number(nvl, "type"); rule->code = nvlist_get_number(nvl, "code"); rule->flags = nvlist_get_number(nvl, "flags"); rule->flagset = nvlist_get_number(nvl, "flagset"); rule->min_ttl = nvlist_get_number(nvl, "min_ttl"); rule->allow_opts = nvlist_get_number(nvl, "allow_opts"); rule->rt = nvlist_get_number(nvl, "rt"); rule->return_ttl = nvlist_get_number(nvl, "return_ttl"); rule->tos = nvlist_get_number(nvl, "tos"); rule->set_tos = nvlist_get_number(nvl, "set_tos"); rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative"); rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard"); rule->flush = nvlist_get_number(nvl, "flush"); rule->prio = nvlist_get_number(nvl, "prio"); pf_nvuint_8_array(nvl, "set_prio", 2, rule->set_prio, NULL); pf_nvdivert_to_divert(nvlist_get_nvlist(nvl, "divert"), rule); rule->states_cur = nvlist_get_number(nvl, "states_cur"); rule->states_tot = nvlist_get_number(nvl, "states_tot"); rule->src_nodes = nvlist_get_number(nvl, "src_nodes"); } static void pfctl_nveth_addr_to_eth_addr(const nvlist_t *nvl, struct pfctl_eth_addr *addr) { static const u_int8_t EMPTY_MAC[ETHER_ADDR_LEN] = { 0 }; size_t len; const void *data; data = nvlist_get_binary(nvl, "addr", &len); assert(len == sizeof(addr->addr)); memcpy(addr->addr, data, sizeof(addr->addr)); data = nvlist_get_binary(nvl, "mask", &len); assert(len == sizeof(addr->mask)); memcpy(addr->mask, data, sizeof(addr->mask)); addr->neg = nvlist_get_bool(nvl, "neg"); /* To make checks for 'is this address set?' easier. */ addr->isset = memcmp(addr->addr, EMPTY_MAC, ETHER_ADDR_LEN) != 0; } static nvlist_t * pfctl_eth_addr_to_nveth_addr(const struct pfctl_eth_addr *addr) { nvlist_t *nvl; nvl = nvlist_create(0); if (nvl == NULL) return (NULL); nvlist_add_bool(nvl, "neg", addr->neg); nvlist_add_binary(nvl, "addr", &addr->addr, ETHER_ADDR_LEN); nvlist_add_binary(nvl, "mask", &addr->mask, ETHER_ADDR_LEN); return (nvl); } static void pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule) { const char *const *labels; size_t labelcount, i; rule->nr = nvlist_get_number(nvl, "nr"); rule->quick = nvlist_get_bool(nvl, "quick"); strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ); rule->ifnot = nvlist_get_bool(nvl, "ifnot"); rule->direction = nvlist_get_number(nvl, "direction"); rule->proto = nvlist_get_number(nvl, "proto"); strlcpy(rule->match_tagname, nvlist_get_string(nvl, "match_tagname"), PF_TAG_NAME_SIZE); rule->match_tag = nvlist_get_number(nvl, "match_tag"); rule->match_tag_not = nvlist_get_bool(nvl, "match_tag_not"); labels = nvlist_get_string_array(nvl, "labels", &labelcount); assert(labelcount <= PF_RULE_MAX_LABEL_COUNT); for (i = 0; i < labelcount; i++) strlcpy(rule->label[i], labels[i], PF_RULE_LABEL_SIZE); rule->ridentifier = nvlist_get_number(nvl, "ridentifier"); pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "src"), &rule->src); pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "dst"), &rule->dst); pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "ipsrc"), &rule->ipsrc); pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "ipdst"), &rule->ipdst); rule->evaluations = nvlist_get_number(nvl, "evaluations"); rule->packets[0] = nvlist_get_number(nvl, "packets-in"); rule->packets[1] = nvlist_get_number(nvl, "packets-out"); rule->bytes[0] = nvlist_get_number(nvl, "bytes-in"); rule->bytes[1] = nvlist_get_number(nvl, "bytes-out"); if (nvlist_exists_number(nvl, "timestamp")) { rule->last_active_timestamp = nvlist_get_number(nvl, "timestamp"); } strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE); strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"), PF_TAG_NAME_SIZE); rule->dnpipe = nvlist_get_number(nvl, "dnpipe"); rule->dnflags = nvlist_get_number(nvl, "dnflags"); rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative"); rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard"); strlcpy(rule->bridge_to, nvlist_get_string(nvl, "bridge_to"), IFNAMSIZ); rule->action = nvlist_get_number(nvl, "action"); } int pfctl_get_eth_rulesets_info(int dev, struct pfctl_eth_rulesets_info *ri, const char *path) { nvlist_t *nvl; int ret; bzero(ri, sizeof(*ri)); nvl = nvlist_create(0); nvlist_add_string(nvl, "path", path); if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULESETS, 256, &nvl)) != 0) goto out; ri->nr = nvlist_get_number(nvl, "nr"); out: nvlist_destroy(nvl); return (ret); } int pfctl_get_eth_ruleset(int dev, const char *path, int nr, struct pfctl_eth_ruleset_info *ri) { nvlist_t *nvl; int ret; bzero(ri, sizeof(*ri)); nvl = nvlist_create(0); nvlist_add_string(nvl, "path", path); nvlist_add_number(nvl, "nr", nr); if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULESET, 1024, &nvl)) != 0) goto out; ri->nr = nvlist_get_number(nvl, "nr"); strlcpy(ri->path, nvlist_get_string(nvl, "path"), MAXPATHLEN); strlcpy(ri->name, nvlist_get_string(nvl, "name"), PF_ANCHOR_NAME_SIZE); out: nvlist_destroy(nvl); return (ret); } int pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules, const char *path) { nvlist_t *nvl; int ret; bzero(rules, sizeof(*rules)); nvl = nvlist_create(0); nvlist_add_string(nvl, "anchor", path); if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULES, 1024, &nvl)) != 0) goto out; rules->nr = nvlist_get_number(nvl, "nr"); rules->ticket = nvlist_get_number(nvl, "ticket"); out: nvlist_destroy(nvl); return (ret); } int pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket, const char *path, struct pfctl_eth_rule *rule, bool clear, char *anchor_call) { nvlist_t *nvl; int ret; nvl = nvlist_create(0); nvlist_add_string(nvl, "anchor", path); nvlist_add_number(nvl, "ticket", ticket); nvlist_add_number(nvl, "nr", nr); nvlist_add_bool(nvl, "clear", clear); if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULE, 4096, &nvl)) != 0) goto out; pfctl_nveth_rule_to_eth_rule(nvl, rule); if (anchor_call) strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"), MAXPATHLEN); out: nvlist_destroy(nvl); return (ret); } int pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor, const char *anchor_call, uint32_t ticket) { struct pfioc_nv nv; nvlist_t *nvl, *addr; void *packed; int error = 0; size_t labelcount, size; nvl = nvlist_create(0); nvlist_add_number(nvl, "ticket", ticket); nvlist_add_string(nvl, "anchor", anchor); nvlist_add_string(nvl, "anchor_call", anchor_call); nvlist_add_number(nvl, "nr", r->nr); nvlist_add_bool(nvl, "quick", r->quick); nvlist_add_string(nvl, "ifname", r->ifname); nvlist_add_bool(nvl, "ifnot", r->ifnot); nvlist_add_number(nvl, "direction", r->direction); nvlist_add_number(nvl, "proto", r->proto); nvlist_add_string(nvl, "match_tagname", r->match_tagname); nvlist_add_bool(nvl, "match_tag_not", r->match_tag_not); addr = pfctl_eth_addr_to_nveth_addr(&r->src); if (addr == NULL) { nvlist_destroy(nvl); return (ENOMEM); } nvlist_add_nvlist(nvl, "src", addr); nvlist_destroy(addr); addr = pfctl_eth_addr_to_nveth_addr(&r->dst); if (addr == NULL) { nvlist_destroy(nvl); return (ENOMEM); } nvlist_add_nvlist(nvl, "dst", addr); nvlist_destroy(addr); pfctl_nv_add_rule_addr(nvl, "ipsrc", &r->ipsrc); pfctl_nv_add_rule_addr(nvl, "ipdst", &r->ipdst); labelcount = 0; while (labelcount < PF_RULE_MAX_LABEL_COUNT && r->label[labelcount][0] != 0) { nvlist_append_string_array(nvl, "labels", r->label[labelcount]); labelcount++; } nvlist_add_number(nvl, "ridentifier", r->ridentifier); nvlist_add_string(nvl, "qname", r->qname); nvlist_add_string(nvl, "tagname", r->tagname); nvlist_add_number(nvl, "dnpipe", r->dnpipe); nvlist_add_number(nvl, "dnflags", r->dnflags); nvlist_add_string(nvl, "bridge_to", r->bridge_to); nvlist_add_number(nvl, "action", r->action); packed = nvlist_pack(nvl, &size); if (packed == NULL) { nvlist_destroy(nvl); return (ENOMEM); } nv.len = size; nv.size = size; nv.data = packed; if (ioctl(dev, DIOCADDETHRULE, &nv) != 0) error = errno; free(packed); nvlist_destroy(nvl); return (error); } static void snl_add_msg_attr_addr_wrap(struct snl_writer *nw, uint32_t type, const struct pf_addr_wrap *addr) { int off; off = snl_add_msg_attr_nested(nw, type); 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); 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); 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 (i < PF_RULE_MAX_LABEL_COUNT && labels[i][0] != 0) { snl_add_msg_attr_string(nw, PF_LT_LABEL, labels[i]); i++; } 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); 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); 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); for (int i = 0; i < PFTM_MAX; i++) snl_add_msg_attr_u32(nw, PF_TT_TIMEOUT, timeouts[i]); snl_end_attr_nested(nw, off); } static void snl_add_msg_attr_uid(struct snl_writer *nw, uint32_t type, const struct pf_rule_uid *uid) { int off; 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 pfctl_handle *h; int ret; h = pfctl_open(PF_DEVICE); if (h == NULL) return (ENODEV); ret = pfctl_add_rule_h(h, r, anchor, anchor_call, ticket, pool_ticket); pfctl_close(h); return (ret); } int pfctl_add_rule_h(struct pfctl_handle *h, 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_errmsg_data e = {}; struct nlmsghdr *hdr; uint32_t seq_id; int family_id; family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME); if (family_id == 0) return (ENOTSUP); snl_init_writer(&h->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(&h->ss, hdr)) return (ENXIO); while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) { } return (e.error); } #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct pfctl_rules_info, _field) static struct snl_attr_parser ap_getrules[] = { { .type = PF_GR_NR, .off = _OUT(nr), .cb = snl_attr_get_uint32 }, { .type = PF_GR_TICKET, .off = _OUT(ticket), .cb = snl_attr_get_uint32 }, }; static struct snl_field_parser fp_getrules[] = { }; #undef _IN #undef _OUT SNL_DECLARE_PARSER(getrules_parser, struct genlmsghdr, fp_getrules, ap_getrules); int pfctl_get_rules_info_h(struct pfctl_handle *h, struct pfctl_rules_info *rules, uint32_t ruleset, const char *path) { struct snl_errmsg_data e = {}; struct nlmsghdr *hdr; struct snl_writer nw; uint32_t seq_id; int family_id; family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME); if (family_id == 0) return (ENOTSUP); snl_init_writer(&h->ss, &nw); hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETRULES); hdr->nlmsg_flags |= NLM_F_DUMP; snl_add_msg_attr_string(&nw, PF_GR_ANCHOR, path); snl_add_msg_attr_u8(&nw, PF_GR_ACTION, ruleset); hdr = snl_finalize_msg(&nw); if (hdr == NULL) return (ENOMEM); seq_id = hdr->nlmsg_seq; if (! snl_send_message(&h->ss, hdr)) return (ENXIO); while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) { if (! snl_parse_nlmsg(&h->ss, hdr, &getrules_parser, rules)) continue; } return (e.error); } int pfctl_get_rules_info(int dev __unused, struct pfctl_rules_info *rules, uint32_t ruleset, const char *path) { struct pfctl_handle *h; int error; h = pfctl_open(PF_DEVICE); if (h == NULL) return (ENOTSUP); error = pfctl_get_rules_info_h(h, rules, ruleset, path); pfctl_close(h); return (error); } int pfctl_get_rule_h(struct pfctl_handle *h, uint32_t nr, uint32_t ticket, const char *anchor, uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call) { return (pfctl_get_clear_rule_h(h, nr, ticket, anchor, ruleset, rule, anchor_call, false)); } int pfctl_get_rule(int dev, uint32_t nr, uint32_t ticket, const char *anchor, uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call) { return (pfctl_get_clear_rule(dev, nr, ticket, anchor, ruleset, rule, anchor_call, false)); } #define _OUT(_field) offsetof(struct pf_addr_wrap, _field) static const struct snl_attr_parser ap_addr_wrap[] = { { .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = snl_attr_get_in6_addr }, { .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = snl_attr_get_in6_addr }, { .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = snl_attr_copy_string }, { .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = snl_attr_copy_string }, { .type = PF_AT_TYPE, .off = _OUT(type), .cb = snl_attr_get_uint8 }, { .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = snl_attr_get_uint8 }, { .type = PF_AT_TBLCNT, .off = _OUT(p.tblcnt), .cb = snl_attr_get_uint32 }, { .type = PF_AT_DYNCNT, .off = _OUT(p.dyncnt), .cb = snl_attr_get_uint32 }, }; SNL_DECLARE_ATTR_PARSER(addr_wrap_parser, ap_addr_wrap); #undef _OUT #define _OUT(_field) offsetof(struct pf_rule_addr, _field) static struct snl_attr_parser ap_rule_addr[] = { { .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = snl_attr_get_nested }, { .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = snl_attr_get_uint16 }, { .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = snl_attr_get_uint16 }, { .type = PF_RAT_NEG, .off = _OUT(neg), .cb = snl_attr_get_uint8 }, { .type = PF_RAT_OP, .off = _OUT(port_op), .cb = snl_attr_get_uint8 }, }; #undef _OUT SNL_DECLARE_ATTR_PARSER(rule_addr_parser, ap_rule_addr); struct snl_parsed_labels { char labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE]; uint32_t i; }; static bool snl_attr_get_pf_rule_labels(struct snl_state *ss, struct nlattr *nla, const void *arg __unused, void *target) { struct snl_parsed_labels *l = (struct snl_parsed_labels *)target; bool ret; if (l->i >= PF_RULE_MAX_LABEL_COUNT) return (E2BIG); ret = snl_attr_copy_string(ss, nla, (void *)PF_RULE_LABEL_SIZE, l->labels[l->i]); if (ret) l->i++; return (ret); } #define _OUT(_field) offsetof(struct nl_parsed_labels, _field) static const struct snl_attr_parser ap_labels[] = { { .type = PF_LT_LABEL, .off = 0, .cb = snl_attr_get_pf_rule_labels }, }; SNL_DECLARE_ATTR_PARSER(rule_labels_parser, ap_labels); #undef _OUT static bool snl_attr_get_nested_pf_rule_labels(struct snl_state *ss, struct nlattr *nla, const void *arg __unused, void *target) { struct snl_parsed_labels parsed_labels = { }; bool error; /* Assumes target points to the beginning of the structure */ error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, &parsed_labels); if (! error) return (error); memcpy(target, parsed_labels.labels, sizeof(parsed_labels.labels)); return (true); } #define _OUT(_field) offsetof(struct pf_mape_portset, _field) static const struct snl_attr_parser ap_mape_portset[] = { { .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = snl_attr_get_uint8 }, { .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = snl_attr_get_uint8 }, {. type = PF_MET_PSID, .off = _OUT(psid), .cb = snl_attr_get_uint16 }, }; SNL_DECLARE_ATTR_PARSER(mape_portset_parser, ap_mape_portset); #undef _OUT #define _OUT(_field) offsetof(struct pfctl_pool, _field) static const struct snl_attr_parser ap_pool[] = { { .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = snl_attr_get_bytes }, { .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = snl_attr_get_in6_addr }, { .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = snl_attr_get_uint32 }, { .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = snl_attr_get_uint16 }, { .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = snl_attr_get_uint16 }, { .type = PF_PT_OPTS, .off = _OUT(opts), .cb = snl_attr_get_uint8 }, { .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = snl_attr_get_nested }, }; SNL_DECLARE_ATTR_PARSER(pool_parser, ap_pool); #undef _OUT struct nl_parsed_timeouts { uint32_t timeouts[PFTM_MAX]; uint32_t i; }; static bool snl_attr_get_pf_timeout(struct snl_state *ss, struct nlattr *nla, const void *arg __unused, void *target) { struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target; bool ret; if (t->i >= PFTM_MAX) return (E2BIG); ret = snl_attr_get_uint32(ss, nla, NULL, &t->timeouts[t->i]); if (ret) t->i++; return (ret); } #define _OUT(_field) offsetof(struct nl_parsed_timeout, _field) static const struct snl_attr_parser ap_timeouts[] = { { .type = PF_TT_TIMEOUT, .off = 0, .cb = snl_attr_get_pf_timeout }, }; SNL_DECLARE_ATTR_PARSER(timeout_parser, ap_timeouts); #undef _OUT static bool snl_attr_get_nested_timeouts(struct snl_state *ss, struct nlattr *nla, const void *arg __unused, void *target) { struct nl_parsed_timeouts parsed_timeouts = { }; bool error; /* Assumes target points to the beginning of the structure */ error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, &parsed_timeouts); if (! error) return (error); memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts)); return (true); } #define _OUT(_field) offsetof(struct pf_rule_uid, _field) static const struct snl_attr_parser ap_rule_uid[] = { { .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = snl_attr_get_uint32 }, { .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = snl_attr_get_uint32 }, { .type = PF_RUT_OP, .off = _OUT(op), .cb = snl_attr_get_uint8 }, }; SNL_DECLARE_ATTR_PARSER(rule_uid_parser, ap_rule_uid); #undef _OUT struct pfctl_nl_get_rule { struct pfctl_rule r; char anchor_call[MAXPATHLEN]; }; #define _OUT(_field) offsetof(struct pfctl_nl_get_rule, _field) static struct snl_attr_parser ap_getrule[] = { { .type = PF_RT_SRC, .off = _OUT(r.src), .arg = &rule_addr_parser,.cb = snl_attr_get_nested }, { .type = PF_RT_DST, .off = _OUT(r.dst), .arg = &rule_addr_parser,.cb = snl_attr_get_nested }, { .type = PF_RT_RIDENTIFIER, .off = _OUT(r.ridentifier), .cb = snl_attr_get_uint32 }, { .type = PF_RT_LABELS, .off = _OUT(r.label), .arg = &rule_labels_parser,.cb = snl_attr_get_nested_pf_rule_labels }, { .type = PF_RT_IFNAME, .off = _OUT(r.ifname), .arg = (void *)IFNAMSIZ, .cb = snl_attr_copy_string }, { .type = PF_RT_QNAME, .off = _OUT(r.qname), .arg = (void *)PF_QNAME_SIZE, .cb = snl_attr_copy_string }, { .type = PF_RT_PQNAME, .off = _OUT(r.pqname), .arg = (void *)PF_QNAME_SIZE, .cb = snl_attr_copy_string }, { .type = PF_RT_TAGNAME, .off = _OUT(r.tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = snl_attr_copy_string }, { .type = PF_RT_MATCH_TAGNAME, .off = _OUT(r.match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = snl_attr_copy_string }, { .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(r.overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = snl_attr_copy_string }, { .type = PF_RT_RPOOL, .off = _OUT(r.rpool), .arg = &pool_parser, .cb = snl_attr_get_nested }, { .type = PF_RT_OS_FINGERPRINT, .off = _OUT(r.os_fingerprint), .cb = snl_attr_get_uint32 }, { .type = PF_RT_RTABLEID, .off = _OUT(r.rtableid), .cb = snl_attr_get_uint32 }, { .type = PF_RT_TIMEOUT, .off = _OUT(r.timeout), .arg = &timeout_parser, .cb = snl_attr_get_nested_timeouts }, { .type = PF_RT_MAX_STATES, .off = _OUT(r.max_states), .cb = snl_attr_get_uint32 }, { .type = PF_RT_MAX_SRC_NODES, .off = _OUT(r.max_src_nodes), .cb = snl_attr_get_uint32 }, { .type = PF_RT_MAX_SRC_STATES, .off = _OUT(r.max_src_states), .cb = snl_attr_get_uint32 }, { .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(r.max_src_conn_rate.limit), .cb = snl_attr_get_uint32 }, { .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(r.max_src_conn_rate.seconds), .cb = snl_attr_get_uint32 }, { .type = PF_RT_DNPIPE, .off = _OUT(r.dnpipe), .cb = snl_attr_get_uint16 }, { .type = PF_RT_DNRPIPE, .off = _OUT(r.dnrpipe), .cb = snl_attr_get_uint16 }, { .type = PF_RT_DNFLAGS, .off = _OUT(r.free_flags), .cb = snl_attr_get_uint32 }, { .type = PF_RT_NR, .off = _OUT(r.nr), .cb = snl_attr_get_uint32 }, { .type = PF_RT_PROB, .off = _OUT(r.prob), .cb = snl_attr_get_uint32 }, { .type = PF_RT_CUID, .off = _OUT(r.cuid), .cb = snl_attr_get_uint32 }, {. type = PF_RT_CPID, .off = _OUT(r.cpid), .cb = snl_attr_get_uint32 }, { .type = PF_RT_RETURN_ICMP, .off = _OUT(r.return_icmp), .cb = snl_attr_get_uint16 }, { .type = PF_RT_RETURN_ICMP6, .off = _OUT(r.return_icmp6), .cb = snl_attr_get_uint16 }, { .type = PF_RT_MAX_MSS, .off = _OUT(r.max_mss), .cb = snl_attr_get_uint16 }, { .type = PF_RT_SCRUB_FLAGS, .off = _OUT(r.scrub_flags), .cb = snl_attr_get_uint16 }, { .type = PF_RT_UID, .off = _OUT(r.uid), .arg = &rule_uid_parser, .cb = snl_attr_get_nested }, { .type = PF_RT_GID, .off = _OUT(r.gid), .arg = &rule_uid_parser, .cb = snl_attr_get_nested }, { .type = PF_RT_RULE_FLAG, .off = _OUT(r.rule_flag), .cb = snl_attr_get_uint32 }, { .type = PF_RT_ACTION, .off = _OUT(r.action), .cb = snl_attr_get_uint8 }, { .type = PF_RT_DIRECTION, .off = _OUT(r.direction), .cb = snl_attr_get_uint8 }, { .type = PF_RT_LOG, .off = _OUT(r.log), .cb = snl_attr_get_uint8 }, { .type = PF_RT_LOGIF, .off = _OUT(r.logif), .cb = snl_attr_get_uint8 }, { .type = PF_RT_QUICK, .off = _OUT(r.quick), .cb = snl_attr_get_uint8 }, { .type = PF_RT_IF_NOT, .off = _OUT(r.ifnot), .cb = snl_attr_get_uint8 }, { .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(r.match_tag_not), .cb = snl_attr_get_uint8 }, { .type = PF_RT_NATPASS, .off = _OUT(r.natpass), .cb = snl_attr_get_uint8 }, { .type = PF_RT_KEEP_STATE, .off = _OUT(r.keep_state), .cb = snl_attr_get_uint8 }, { .type = PF_RT_AF, .off = _OUT(r.af), .cb = snl_attr_get_uint8 }, { .type = PF_RT_PROTO, .off = _OUT(r.proto), .cb = snl_attr_get_uint8 }, { .type = PF_RT_TYPE, .off = _OUT(r.type), .cb = snl_attr_get_uint8 }, { .type = PF_RT_CODE, .off = _OUT(r.code), .cb = snl_attr_get_uint8 }, { .type = PF_RT_FLAGS, .off = _OUT(r.flags), .cb = snl_attr_get_uint8 }, { .type = PF_RT_FLAGSET, .off = _OUT(r.flagset), .cb = snl_attr_get_uint8 }, { .type = PF_RT_MIN_TTL, .off = _OUT(r.min_ttl), .cb = snl_attr_get_uint8 }, { .type = PF_RT_ALLOW_OPTS, .off = _OUT(r.allow_opts), .cb = snl_attr_get_uint8 }, { .type = PF_RT_RT, .off = _OUT(r.rt), .cb = snl_attr_get_uint8 }, { .type = PF_RT_RETURN_TTL, .off = _OUT(r.return_ttl), .cb = snl_attr_get_uint8 }, { .type = PF_RT_TOS, .off = _OUT(r.tos), .cb = snl_attr_get_uint8 }, { .type = PF_RT_SET_TOS, .off = _OUT(r.set_tos), .cb = snl_attr_get_uint8 }, { .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(r.anchor_relative), .cb = snl_attr_get_uint8 }, { .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(r.anchor_wildcard), .cb = snl_attr_get_uint8 }, { .type = PF_RT_FLUSH, .off = _OUT(r.flush), .cb = snl_attr_get_uint8 }, { .type = PF_RT_PRIO, .off = _OUT(r.prio), .cb = snl_attr_get_uint8 }, { .type = PF_RT_SET_PRIO, .off = _OUT(r.set_prio[0]), .cb = snl_attr_get_uint8 }, { .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(r.set_prio[1]), .cb = snl_attr_get_uint8 }, { .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(r.divert.addr), .cb = snl_attr_get_in6_addr }, { .type = PF_RT_DIVERT_PORT, .off = _OUT(r.divert.port), .cb = snl_attr_get_uint16 }, { .type = PF_RT_PACKETS_IN, .off = _OUT(r.packets[0]), .cb = snl_attr_get_uint64 }, { .type = PF_RT_PACKETS_OUT, .off = _OUT(r.packets[1]), .cb = snl_attr_get_uint64 }, { .type = PF_RT_BYTES_IN, .off = _OUT(r.bytes[0]), .cb = snl_attr_get_uint64 }, { .type = PF_RT_BYTES_OUT, .off = _OUT(r.bytes[1]), .cb = snl_attr_get_uint64 }, { .type = PF_RT_EVALUATIONS, .off = _OUT(r.evaluations), .cb = snl_attr_get_uint64 }, { .type = PF_RT_TIMESTAMP, .off = _OUT(r.last_active_timestamp), .cb = snl_attr_get_uint64 }, { .type = PF_RT_STATES_CUR, .off = _OUT(r.states_cur), .cb = snl_attr_get_uint64 }, { .type = PF_RT_STATES_TOTAL, .off = _OUT(r.states_tot), .cb = snl_attr_get_uint64 }, { .type = PF_RT_SRC_NODES, .off = _OUT(r.src_nodes), .cb = snl_attr_get_uint64 }, { .type = PF_RT_ANCHOR_CALL, .off = _OUT(anchor_call), .arg = (void*)MAXPATHLEN, .cb = snl_attr_copy_string }, }; static struct snl_field_parser fp_getrule[] = {}; #undef _OUT SNL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, fp_getrule, ap_getrule); int pfctl_get_clear_rule_h(struct pfctl_handle *h, uint32_t nr, uint32_t ticket, const char *anchor, uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call, bool clear) { struct pfctl_nl_get_rule attrs = {}; struct snl_errmsg_data e = {}; struct nlmsghdr *hdr; struct snl_writer nw; uint32_t seq_id; int family_id; family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME); if (family_id == 0) return (ENOTSUP); snl_init_writer(&h->ss, &nw); hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETRULE); hdr->nlmsg_flags |= NLM_F_DUMP; snl_add_msg_attr_string(&nw, PF_GR_ANCHOR, anchor); snl_add_msg_attr_u8(&nw, PF_GR_ACTION, ruleset); snl_add_msg_attr_u32(&nw, PF_GR_NR, nr); snl_add_msg_attr_u32(&nw, PF_GR_TICKET, ticket); snl_add_msg_attr_u8(&nw, PF_GR_CLEAR, clear); hdr = snl_finalize_msg(&nw); if (hdr == NULL) return (ENOMEM); seq_id = hdr->nlmsg_seq; if (! snl_send_message(&h->ss, hdr)) return (ENXIO); while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) { if (! snl_parse_nlmsg(&h->ss, hdr, &getrule_parser, &attrs)) continue; } memcpy(rule, &attrs.r, sizeof(attrs.r)); strlcpy(anchor_call, attrs.anchor_call, MAXPATHLEN); return (e.error); } int pfctl_get_clear_rule(int dev, uint32_t nr, uint32_t ticket, const char *anchor, uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call, bool clear) { nvlist_t *nvl; int ret; nvl = nvlist_create(0); if (nvl == 0) return (ENOMEM); nvlist_add_number(nvl, "nr", nr); nvlist_add_number(nvl, "ticket", ticket); nvlist_add_string(nvl, "anchor", anchor); nvlist_add_number(nvl, "ruleset", ruleset); if (clear) nvlist_add_bool(nvl, "clear_counter", true); if ((ret = pfctl_do_ioctl(dev, DIOCGETRULENV, 8192, &nvl)) != 0) goto out; pf_nvrule_to_rule(nvlist_get_nvlist(nvl, "rule"), rule); if (anchor_call) strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"), MAXPATHLEN); out: nvlist_destroy(nvl); return (ret); } int pfctl_set_keepcounters(int dev, bool keep) { struct pfioc_nv nv; nvlist_t *nvl; int ret; nvl = nvlist_create(0); nvlist_add_bool(nvl, "keep_counters", keep); nv.data = nvlist_pack(nvl, &nv.len); nv.size = nv.len; nvlist_destroy(nvl); ret = ioctl(dev, DIOCKEEPCOUNTERS, &nv); free(nv.data); return (ret); } struct pfctl_creator { uint32_t id; }; #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct pfctl_creator, _field) static struct snl_attr_parser ap_creators[] = { { .type = PF_ST_CREATORID, .off = _OUT(id), .cb = snl_attr_get_uint32 }, }; static struct snl_field_parser fp_creators[] = { }; #undef _IN #undef _OUT SNL_DECLARE_PARSER(creator_parser, struct genlmsghdr, fp_creators, ap_creators); static int pfctl_get_creators_nl(struct snl_state *ss, uint32_t *creators, size_t *len) { int family_id = snl_get_genl_family(ss, PFNL_FAMILY_NAME); size_t i = 0; struct nlmsghdr *hdr; struct snl_writer nw; if (family_id == 0) return (ENOTSUP); snl_init_writer(ss, &nw); hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETCREATORS); hdr->nlmsg_flags |= NLM_F_DUMP; hdr = snl_finalize_msg(&nw); if (hdr == NULL) return (ENOMEM); uint32_t seq_id = hdr->nlmsg_seq; snl_send_message(ss, hdr); struct snl_errmsg_data e = {}; while ((hdr = snl_read_reply_multi(ss, seq_id, &e)) != NULL) { struct pfctl_creator c; bzero(&c, sizeof(c)); if (!snl_parse_nlmsg(ss, hdr, &creator_parser, &c)) continue; creators[i] = c.id; i++; if (i > *len) return (E2BIG); } *len = i; return (0); } int pfctl_get_creatorids(struct pfctl_handle *h, uint32_t *creators, size_t *len) { int error; error = pfctl_get_creators_nl(&h->ss, creators, len); return (error); } static inline bool snl_attr_get_pfaddr(struct snl_state *ss __unused, struct nlattr *nla, const void *arg __unused, void *target) { memcpy(target, NLA_DATA(nla), NLA_DATA_LEN(nla)); return (true); } static inline bool snl_attr_store_ifname(struct snl_state *ss __unused, struct nlattr *nla, const void *arg __unused, void *target) { size_t maxlen = NLA_DATA_LEN(nla); if (strnlen((char *)NLA_DATA(nla), maxlen) < maxlen) { strlcpy(target, (char *)NLA_DATA(nla), maxlen); return (true); } return (false); } #define _OUT(_field) offsetof(struct pfctl_state_peer, _field) static const struct snl_attr_parser nla_p_speer[] = { { .type = PF_STP_SEQLO, .off = _OUT(seqlo), .cb = snl_attr_get_uint32 }, { .type = PF_STP_SEQHI, .off = _OUT(seqhi), .cb = snl_attr_get_uint32 }, { .type = PF_STP_SEQDIFF, .off = _OUT(seqdiff), .cb = snl_attr_get_uint32 }, { .type = PF_STP_STATE, .off = _OUT(state), .cb = snl_attr_get_uint8 }, { .type = PF_STP_WSCALE, .off = _OUT(wscale), .cb = snl_attr_get_uint8 }, }; SNL_DECLARE_ATTR_PARSER(speer_parser, nla_p_speer); #undef _OUT #define _OUT(_field) offsetof(struct pf_state_key_export, _field) static const struct snl_attr_parser nla_p_skey[] = { { .type = PF_STK_ADDR0, .off = _OUT(addr[0]), .cb = snl_attr_get_pfaddr }, { .type = PF_STK_ADDR1, .off = _OUT(addr[1]), .cb = snl_attr_get_pfaddr }, { .type = PF_STK_PORT0, .off = _OUT(port[0]), .cb = snl_attr_get_uint16 }, { .type = PF_STK_PORT1, .off = _OUT(port[1]), .cb = snl_attr_get_uint16 }, }; SNL_DECLARE_ATTR_PARSER(skey_parser, nla_p_skey); #undef _OUT #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct pfctl_state, _field) static struct snl_attr_parser ap_state[] = { { .type = PF_ST_ID, .off = _OUT(id), .cb = snl_attr_get_uint64 }, { .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = snl_attr_get_uint32 }, { .type = PF_ST_IFNAME, .off = _OUT(ifname), .cb = snl_attr_store_ifname }, { .type = PF_ST_ORIG_IFNAME, .off = _OUT(orig_ifname), .cb = snl_attr_store_ifname }, { .type = PF_ST_KEY_WIRE, .off = _OUT(key[0]), .arg = &skey_parser, .cb = snl_attr_get_nested }, { .type = PF_ST_KEY_STACK, .off = _OUT(key[1]), .arg = &skey_parser, .cb = snl_attr_get_nested }, { .type = PF_ST_PEER_SRC, .off = _OUT(src), .arg = &speer_parser, .cb = snl_attr_get_nested }, { .type = PF_ST_PEER_DST, .off = _OUT(dst), .arg = &speer_parser, .cb = snl_attr_get_nested }, { .type = PF_ST_RT_ADDR, .off = _OUT(rt_addr), .cb = snl_attr_get_pfaddr }, { .type = PF_ST_RULE, .off = _OUT(rule), .cb = snl_attr_get_uint32 }, { .type = PF_ST_ANCHOR, .off = _OUT(anchor), .cb = snl_attr_get_uint32 }, { .type = PF_ST_NAT_RULE, .off = _OUT(nat_rule), .cb = snl_attr_get_uint32 }, { .type = PF_ST_CREATION, .off = _OUT(creation), .cb = snl_attr_get_uint32 }, { .type = PF_ST_EXPIRE, .off = _OUT(expire), .cb = snl_attr_get_uint32 }, { .type = PF_ST_PACKETS0, .off = _OUT(packets[0]), .cb = snl_attr_get_uint64 }, { .type = PF_ST_PACKETS1, .off = _OUT(packets[1]), .cb = snl_attr_get_uint64 }, { .type = PF_ST_BYTES0, .off = _OUT(bytes[0]), .cb = snl_attr_get_uint64 }, { .type = PF_ST_BYTES1, .off = _OUT(bytes[1]), .cb = snl_attr_get_uint64 }, { .type = PF_ST_AF, .off = _OUT(key[0].af), .cb = snl_attr_get_uint8 }, { .type = PF_ST_PROTO, .off = _OUT(key[0].proto), .cb = snl_attr_get_uint8 }, { .type = PF_ST_DIRECTION, .off = _OUT(direction), .cb = snl_attr_get_uint8 }, { .type = PF_ST_LOG, .off = _OUT(log), .cb = snl_attr_get_uint8 }, { .type = PF_ST_STATE_FLAGS, .off = _OUT(state_flags), .cb = snl_attr_get_uint16 }, { .type = PF_ST_SYNC_FLAGS, .off = _OUT(sync_flags), .cb = snl_attr_get_uint8 }, { .type = PF_ST_RTABLEID, .off = _OUT(rtableid), .cb = snl_attr_get_int32 }, { .type = PF_ST_MIN_TTL, .off = _OUT(min_ttl), .cb = snl_attr_get_uint8 }, { .type = PF_ST_MAX_MSS, .off = _OUT(max_mss), .cb = snl_attr_get_uint16 }, { .type = PF_ST_DNPIPE, .off = _OUT(dnpipe), .cb = snl_attr_get_uint16 }, { .type = PF_ST_DNRPIPE, .off = _OUT(dnrpipe), .cb = snl_attr_get_uint16 }, { .type = PF_ST_RT, .off = _OUT(rt), .cb = snl_attr_get_uint8 }, { .type = PF_ST_RT_IFNAME, .off = _OUT(rt_ifname), .cb = snl_attr_store_ifname }, }; static struct snl_field_parser fp_state[] = { }; #undef _IN #undef _OUT SNL_DECLARE_PARSER(state_parser, struct genlmsghdr, fp_state, ap_state); static const struct snl_hdr_parser *all_parsers[] = { &state_parser, &skey_parser, &speer_parser, &creator_parser, &getrules_parser }; static int pfctl_get_states_nl(struct pfctl_state_filter *filter, struct snl_state *ss, pfctl_get_state_fn f, void *arg) { SNL_VERIFY_PARSERS(all_parsers); int family_id = snl_get_genl_family(ss, PFNL_FAMILY_NAME); int ret; struct nlmsghdr *hdr; struct snl_writer nw; if (family_id == 0) return (ENOTSUP); snl_init_writer(ss, &nw); hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETSTATES); hdr->nlmsg_flags |= NLM_F_DUMP; snl_add_msg_attr_string(&nw, PF_ST_IFNAME, filter->ifname); snl_add_msg_attr_u16(&nw, PF_ST_PROTO, filter->proto); snl_add_msg_attr_u8(&nw, PF_ST_AF, filter->af); snl_add_msg_attr_ip6(&nw, PF_ST_FILTER_ADDR, &filter->addr.v6); snl_add_msg_attr_ip6(&nw, PF_ST_FILTER_MASK, &filter->mask.v6); hdr = snl_finalize_msg(&nw); if (hdr == NULL) return (ENOMEM); uint32_t seq_id = hdr->nlmsg_seq; snl_send_message(ss, hdr); struct snl_errmsg_data e = {}; while ((hdr = snl_read_reply_multi(ss, seq_id, &e)) != NULL) { struct pfctl_state s; bzero(&s, sizeof(s)); if (!snl_parse_nlmsg(ss, hdr, &state_parser, &s)) continue; s.key[1].af = s.key[0].af; s.key[1].proto = s.key[0].proto; ret = f(&s, arg); if (ret != 0) return (ret); } return (0); } int pfctl_get_states_iter(pfctl_get_state_fn f, void *arg) { struct pfctl_state_filter filter = {}; return (pfctl_get_filtered_states_iter(&filter, f, arg)); } int pfctl_get_filtered_states_iter(struct pfctl_state_filter *filter, pfctl_get_state_fn f, void *arg) { struct snl_state ss = {}; int error; snl_init(&ss, NETLINK_GENERIC); error = pfctl_get_states_nl(filter, &ss, f, arg); snl_free(&ss); return (error); } static int pfctl_append_states(struct pfctl_state *s, void *arg) { struct pfctl_state *new; struct pfctl_states *states = (struct pfctl_states *)arg; new = malloc(sizeof(*s)); if (new == NULL) return (ENOMEM); memcpy(new, s, sizeof(*s)); TAILQ_INSERT_TAIL(&states->states, new, entry); return (0); } int pfctl_get_states(int dev __unused, struct pfctl_states *states) { int ret; bzero(states, sizeof(*states)); TAILQ_INIT(&states->states); ret = pfctl_get_states_iter(pfctl_append_states, states); if (ret != 0) { pfctl_free_states(states); return (ret); } return (0); } void pfctl_free_states(struct pfctl_states *states) { struct pfctl_state *s, *tmp; TAILQ_FOREACH_SAFE(s, &states->states, entry, tmp) { free(s); } bzero(states, sizeof(*states)); } struct pfctl_nl_clear_states { uint32_t killed; }; #define _OUT(_field) offsetof(struct pfctl_nl_clear_states, _field) static struct snl_attr_parser ap_clear_states[] = { { .type = PF_CS_KILLED, .off = _OUT(killed), .cb = snl_attr_get_uint32 }, }; static struct snl_field_parser fp_clear_states[] = {}; #undef _OUT SNL_DECLARE_PARSER(clear_states_parser, struct genlmsghdr, fp_clear_states, ap_clear_states); static int _pfctl_clear_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill, unsigned int *killed, int cmd) { struct snl_writer nw; struct snl_errmsg_data e = {}; struct pfctl_nl_clear_states attrs = {}; struct nlmsghdr *hdr; uint32_t seq_id; int family_id; family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME); if (family_id == 0) return (ENOTSUP); snl_init_writer(&h->ss, &nw); hdr = snl_create_genl_msg_request(&nw, family_id, cmd); hdr->nlmsg_flags |= NLM_F_DUMP; snl_add_msg_attr_u64(&nw, PF_CS_CMP_ID, kill->cmp.id); snl_add_msg_attr_u32(&nw, PF_CS_CMP_CREATORID, htonl(kill->cmp.creatorid)); snl_add_msg_attr_u8(&nw, PF_CS_CMP_DIR, kill->cmp.direction); snl_add_msg_attr_u8(&nw, PF_CS_AF, kill->af); snl_add_msg_attr_u8(&nw, PF_CS_PROTO, kill->proto); snl_add_msg_attr_rule_addr(&nw, PF_CS_SRC, &kill->src); snl_add_msg_attr_rule_addr(&nw, PF_CS_DST, &kill->dst); snl_add_msg_attr_rule_addr(&nw, PF_CS_RT_ADDR, &kill->rt_addr); snl_add_msg_attr_string(&nw, PF_CS_IFNAME, kill->ifname); snl_add_msg_attr_string(&nw, PF_CS_LABEL, kill->label); snl_add_msg_attr_bool(&nw, PF_CS_KILL_MATCH, kill->kill_match); snl_add_msg_attr_bool(&nw, PF_CS_NAT, kill->nat); if ((hdr = snl_finalize_msg(&nw)) == NULL) return (ENXIO); seq_id = hdr->nlmsg_seq; if (! snl_send_message(&h->ss, hdr)) return (ENXIO); while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) { if (! snl_parse_nlmsg(&h->ss, hdr, &clear_states_parser, &attrs)) continue; } if (killed) *killed = attrs.killed; return (e.error); } int pfctl_clear_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill, unsigned int *killed) { return(_pfctl_clear_states_h(h, kill, killed, PFNL_CMD_CLRSTATES)); } int pfctl_kill_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill, unsigned int *killed) { return(_pfctl_clear_states_h(h, kill, killed, PFNL_CMD_KILLSTATES)); } static int _pfctl_clear_states(int dev __unused, const struct pfctl_kill *kill, unsigned int *killed, uint64_t cmd) { struct pfctl_handle *h; int ret; h = pfctl_open(PF_DEVICE); if (h == NULL) return (ENODEV); ret = _pfctl_clear_states_h(h, kill, killed, cmd); pfctl_close(h); return (ret); } int pfctl_clear_states(int dev __unused, const struct pfctl_kill *kill, unsigned int *killed) { return (_pfctl_clear_states(dev, kill, killed, PFNL_CMD_CLRSTATES)); } int pfctl_kill_states(int dev __unused, const struct pfctl_kill *kill, unsigned int *killed) { return (_pfctl_clear_states(dev, kill, killed, PFNL_CMD_KILLSTATES)); } int pfctl_clear_rules(int dev, const char *anchorname) { struct pfioc_trans trans; struct pfioc_trans_e transe[2]; int ret; bzero(&trans, sizeof(trans)); bzero(&transe, sizeof(transe)); transe[0].rs_num = PF_RULESET_SCRUB; if (strlcpy(transe[0].anchor, anchorname, sizeof(transe[0].anchor)) >= sizeof(transe[0].anchor)) return (E2BIG); transe[1].rs_num = PF_RULESET_FILTER; if (strlcpy(transe[1].anchor, anchorname, sizeof(transe[1].anchor)) >= sizeof(transe[1].anchor)) return (E2BIG); trans.size = 2; trans.esize = sizeof(transe[0]); trans.array = transe; ret = ioctl(dev, DIOCXBEGIN, &trans); if (ret != 0) return (ret); return ioctl(dev, DIOCXCOMMIT, &trans); } int pfctl_clear_nat(int dev, const char *anchorname) { struct pfioc_trans trans; struct pfioc_trans_e transe[3]; int ret; bzero(&trans, sizeof(trans)); bzero(&transe, sizeof(transe)); transe[0].rs_num = PF_RULESET_NAT; if (strlcpy(transe[0].anchor, anchorname, sizeof(transe[0].anchor)) >= sizeof(transe[0].anchor)) return (E2BIG); transe[1].rs_num = PF_RULESET_BINAT; if (strlcpy(transe[1].anchor, anchorname, sizeof(transe[1].anchor)) >= sizeof(transe[0].anchor)) return (E2BIG); transe[2].rs_num = PF_RULESET_RDR; if (strlcpy(transe[2].anchor, anchorname, sizeof(transe[2].anchor)) >= sizeof(transe[2].anchor)) return (E2BIG); trans.size = 3; trans.esize = sizeof(transe[0]); trans.array = transe; ret = ioctl(dev, DIOCXBEGIN, &trans); if (ret != 0) return (ret); return ioctl(dev, DIOCXCOMMIT, &trans); } int pfctl_clear_eth_rules(int dev, const char *anchorname) { struct pfioc_trans trans; struct pfioc_trans_e transe; int ret; bzero(&trans, sizeof(trans)); bzero(&transe, sizeof(transe)); transe.rs_num = PF_RULESET_ETH; if (strlcpy(transe.anchor, anchorname, sizeof(transe.anchor)) >= sizeof(transe.anchor)) return (E2BIG); trans.size = 1; trans.esize = sizeof(transe); trans.array = &transe; ret = ioctl(dev, DIOCXBEGIN, &trans); if (ret != 0) return (ret); return ioctl(dev, DIOCXCOMMIT, &trans); } static int pfctl_get_limit(int dev, const int index, uint *limit) { struct pfioc_limit pl; bzero(&pl, sizeof(pl)); pl.index = index; if (ioctl(dev, DIOCGETLIMIT, &pl) == -1) return (errno); *limit = pl.limit; return (0); } int pfctl_set_syncookies(int dev, const struct pfctl_syncookies *s) { struct pfioc_nv nv; nvlist_t *nvl; int ret; uint state_limit; uint64_t lim, hi, lo; ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit); if (ret != 0) return (ret); lim = state_limit; hi = lim * s->highwater / 100; lo = lim * s->lowwater / 100; if (lo == hi) hi++; nvl = nvlist_create(0); nvlist_add_bool(nvl, "enabled", s->mode != PFCTL_SYNCOOKIES_NEVER); nvlist_add_bool(nvl, "adaptive", s->mode == PFCTL_SYNCOOKIES_ADAPTIVE); nvlist_add_number(nvl, "highwater", hi); nvlist_add_number(nvl, "lowwater", lo); nv.data = nvlist_pack(nvl, &nv.len); nv.size = nv.len; nvlist_destroy(nvl); nvl = NULL; ret = ioctl(dev, DIOCSETSYNCOOKIES, &nv); free(nv.data); return (ret); } int pfctl_get_syncookies(int dev, struct pfctl_syncookies *s) { nvlist_t *nvl; int ret; uint state_limit; bool enabled, adaptive; ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit); if (ret != 0) return (ret); bzero(s, sizeof(*s)); nvl = nvlist_create(0); if ((ret = pfctl_do_ioctl(dev, DIOCGETSYNCOOKIES, 256, &nvl)) != 0) { ret = errno; goto out; } enabled = nvlist_get_bool(nvl, "enabled"); adaptive = nvlist_get_bool(nvl, "adaptive"); if (enabled) { if (adaptive) s->mode = PFCTL_SYNCOOKIES_ADAPTIVE; else s->mode = PFCTL_SYNCOOKIES_ALWAYS; } else { s->mode = PFCTL_SYNCOOKIES_NEVER; } s->highwater = nvlist_get_number(nvl, "highwater") * 100 / state_limit; s->lowwater = nvlist_get_number(nvl, "lowwater") * 100 / state_limit; s->halfopen_states = nvlist_get_number(nvl, "halfopen_states"); out: nvlist_destroy(nvl); return (ret); } int pfctl_table_add_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr, int size, int *nadd, int flags) { struct pfioc_table io; if (tbl == NULL || size < 0 || (size && addr == NULL)) { return (EINVAL); } bzero(&io, sizeof io); io.pfrio_flags = flags; io.pfrio_table = *tbl; io.pfrio_buffer = addr; io.pfrio_esize = sizeof(*addr); io.pfrio_size = size; if (ioctl(dev, DIOCRADDADDRS, &io)) return (errno); if (nadd != NULL) *nadd = io.pfrio_nadd; return (0); } int pfctl_table_del_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr, int size, int *ndel, int flags) { struct pfioc_table io; if (tbl == NULL || size < 0 || (size && addr == NULL)) { return (EINVAL); } bzero(&io, sizeof io); io.pfrio_flags = flags; io.pfrio_table = *tbl; io.pfrio_buffer = addr; io.pfrio_esize = sizeof(*addr); io.pfrio_size = size; if (ioctl(dev, DIOCRDELADDRS, &io)) return (errno); if (ndel != NULL) *ndel = io.pfrio_ndel; return (0); } int pfctl_table_set_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr, int size, int *size2, int *nadd, int *ndel, int *nchange, int flags) { struct pfioc_table io; if (tbl == NULL || size < 0 || (size && addr == NULL)) { return (EINVAL); } bzero(&io, sizeof io); io.pfrio_flags = flags; io.pfrio_table = *tbl; io.pfrio_buffer = addr; io.pfrio_esize = sizeof(*addr); io.pfrio_size = size; io.pfrio_size2 = (size2 != NULL) ? *size2 : 0; if (ioctl(dev, DIOCRSETADDRS, &io)) return (-1); if (nadd != NULL) *nadd = io.pfrio_nadd; if (ndel != NULL) *ndel = io.pfrio_ndel; if (nchange != NULL) *nchange = io.pfrio_nchange; if (size2 != NULL) *size2 = io.pfrio_size2; return (0); } int pfctl_table_get_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr, int *size, int flags) { struct pfioc_table io; if (tbl == NULL || size == NULL || *size < 0 || (*size && addr == NULL)) { return (EINVAL); } bzero(&io, sizeof io); io.pfrio_flags = flags; io.pfrio_table = *tbl; io.pfrio_buffer = addr; io.pfrio_esize = sizeof(*addr); io.pfrio_size = *size; if (ioctl(dev, DIOCRGETADDRS, &io)) return (-1); *size = io.pfrio_size; return (0); } int pfctl_set_statusif(struct pfctl_handle *h, const char *ifname) { struct snl_writer nw; struct snl_errmsg_data e = {}; struct nlmsghdr *hdr; uint32_t seq_id; int family_id; family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME); if (family_id == 0) return (ENOTSUP); snl_init_writer(&h->ss, &nw); hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_SET_STATUSIF); snl_add_msg_attr_string(&nw, PF_SS_IFNAME, ifname); if ((hdr = snl_finalize_msg(&nw)) == NULL) return (ENXIO); seq_id = hdr->nlmsg_seq; if (! snl_send_message(&h->ss, hdr)) return (ENXIO); while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) { } return (e.error); } + +#define _IN(_field) offsetof(struct genlmsghdr, _field) +#define _OUT(_field) offsetof(struct pfctl_natlook, _field) +static struct snl_attr_parser ap_natlook[] = { + { .type = PF_NL_SRC_ADDR, .off = _OUT(saddr), .cb = snl_attr_get_in6_addr }, + { .type = PF_NL_DST_ADDR, .off = _OUT(daddr), .cb = snl_attr_get_in6_addr }, + { .type = PF_NL_SRC_PORT, .off = _OUT(sport), .cb = snl_attr_get_uint16 }, + { .type = PF_NL_DST_PORT, .off = _OUT(dport), .cb = snl_attr_get_uint16 }, +}; +static struct snl_field_parser fp_natlook[] = {}; +#undef _IN +#undef _OUT +SNL_DECLARE_PARSER(natlook_parser, struct genlmsghdr, fp_natlook, ap_natlook); + +int +pfctl_natlook(struct pfctl_handle *h, const struct pfctl_natlook_key *k, + struct pfctl_natlook *r) +{ + struct snl_writer nw; + struct snl_errmsg_data e = {}; + struct nlmsghdr *hdr; + uint32_t seq_id; + int family_id; + + family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME); + if (family_id == 0) + return (ENOTSUP); + + snl_init_writer(&h->ss, &nw); + hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_NATLOOK); + hdr->nlmsg_flags |= NLM_F_DUMP; + + snl_add_msg_attr_u8(&nw, PF_NL_AF, k->af); + snl_add_msg_attr_u8(&nw, PF_NL_DIRECTION, k->direction); + snl_add_msg_attr_u8(&nw, PF_NL_PROTO, k->proto); + snl_add_msg_attr_ip6(&nw, PF_NL_SRC_ADDR, &k->saddr.v6); + snl_add_msg_attr_ip6(&nw, PF_NL_DST_ADDR, &k->daddr.v6); + snl_add_msg_attr_u16(&nw, PF_NL_SRC_PORT, k->sport); + snl_add_msg_attr_u16(&nw, PF_NL_DST_PORT, k->dport); + + if ((hdr = snl_finalize_msg(&nw)) == NULL) + return (ENXIO); + + seq_id = hdr->nlmsg_seq; + + if (! snl_send_message(&h->ss, hdr)) + return (ENXIO); + + while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) { + if (! snl_parse_nlmsg(&h->ss, hdr, &natlook_parser, r)) + continue; + } + + return (e.error); +} diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h index e130fe0fe842..e7625252638c 100644 --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -1,478 +1,496 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021 Rubicon Communications, LLC (Netgate) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - 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 COPYRIGHT HOLDERS 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 * COPYRIGHT HOLDERS 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 _PFCTL_IOCTL_H_ #define _PFCTL_IOCTL_H_ #include struct pfctl_anchor; struct pfctl_eth_anchor; struct pfctl_status_counter { uint64_t id; uint64_t counter; char *name; TAILQ_ENTRY(pfctl_status_counter) entry; }; TAILQ_HEAD(pfctl_status_counters, pfctl_status_counter); struct pfctl_status { bool running; uint32_t since; uint32_t debug; uint32_t hostid; uint64_t states; uint64_t src_nodes; char ifname[IFNAMSIZ]; uint8_t pf_chksum[PF_MD5_DIGEST_LENGTH]; bool syncookies_active; uint32_t reass; struct pfctl_status_counters counters; struct pfctl_status_counters lcounters; struct pfctl_status_counters fcounters; struct pfctl_status_counters scounters; uint64_t pcounters[2][2][2]; uint64_t bcounters[2][2]; }; struct pfctl_eth_rulesets_info { uint32_t nr; }; struct pfctl_eth_rules_info { uint32_t nr; uint32_t ticket; }; struct pfctl_eth_addr { uint8_t addr[ETHER_ADDR_LEN]; uint8_t mask[ETHER_ADDR_LEN]; bool neg; bool isset; }; struct pfctl_eth_rule { uint32_t nr; char label[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE]; uint32_t ridentifier; bool quick; /* Filter */ char ifname[IFNAMSIZ]; uint8_t ifnot; uint8_t direction; uint16_t proto; struct pfctl_eth_addr src, dst; struct pf_rule_addr ipsrc, ipdst; char match_tagname[PF_TAG_NAME_SIZE]; uint16_t match_tag; bool match_tag_not; /* Stats */ uint64_t evaluations; uint64_t packets[2]; uint64_t bytes[2]; time_t last_active_timestamp; /* Action */ char qname[PF_QNAME_SIZE]; char tagname[PF_TAG_NAME_SIZE]; uint16_t dnpipe; uint32_t dnflags; char bridge_to[IFNAMSIZ]; uint8_t action; struct pfctl_eth_anchor *anchor; uint8_t anchor_relative; uint8_t anchor_wildcard; TAILQ_ENTRY(pfctl_eth_rule) entries; }; TAILQ_HEAD(pfctl_eth_rules, pfctl_eth_rule); struct pfctl_eth_ruleset_info { uint32_t nr; char name[PF_ANCHOR_NAME_SIZE]; char path[MAXPATHLEN]; }; struct pfctl_eth_ruleset { struct pfctl_eth_rules rules; struct pfctl_eth_anchor *anchor; }; struct pfctl_eth_anchor { struct pfctl_eth_anchor *parent; char name[PF_ANCHOR_NAME_SIZE]; char path[MAXPATHLEN]; struct pfctl_eth_ruleset ruleset; int refcnt; /* anchor rules */ int match; /* XXX: used for pfctl black magic */ }; struct pfctl_pool { struct pf_palist list; struct pf_pooladdr *cur; struct pf_poolhashkey key; struct pf_addr counter; struct pf_mape_portset mape; int tblidx; uint16_t proxy_port[2]; uint8_t opts; }; struct pfctl_rules_info { uint32_t nr; uint32_t ticket; }; struct pfctl_rule { struct pf_rule_addr src; struct pf_rule_addr dst; union pf_rule_ptr skip[PF_SKIP_COUNT]; char label[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE]; uint32_t ridentifier; char ifname[IFNAMSIZ]; char qname[PF_QNAME_SIZE]; char pqname[PF_QNAME_SIZE]; char tagname[PF_TAG_NAME_SIZE]; char match_tagname[PF_TAG_NAME_SIZE]; char overload_tblname[PF_TABLE_NAME_SIZE]; TAILQ_ENTRY(pfctl_rule) entries; struct pfctl_pool rpool; uint64_t evaluations; uint64_t packets[2]; uint64_t bytes[2]; time_t last_active_timestamp; struct pfi_kif *kif; struct pfctl_anchor *anchor; struct pfr_ktable *overload_tbl; pf_osfp_t os_fingerprint; int rtableid; uint32_t timeout[PFTM_MAX]; uint32_t max_states; uint32_t max_src_nodes; uint32_t max_src_states; uint32_t max_src_conn; struct { uint32_t limit; uint32_t seconds; } max_src_conn_rate; uint32_t qid; uint32_t pqid; uint16_t dnpipe; uint16_t dnrpipe; uint32_t free_flags; uint32_t nr; uint32_t prob; uid_t cuid; pid_t cpid; uint64_t states_cur; uint64_t states_tot; uint64_t src_nodes; uint16_t return_icmp; uint16_t return_icmp6; uint16_t max_mss; uint16_t tag; uint16_t match_tag; uint16_t scrub_flags; struct pf_rule_uid uid; struct pf_rule_gid gid; uint32_t rule_flag; uint8_t action; uint8_t direction; uint8_t log; uint8_t logif; uint8_t quick; uint8_t ifnot; uint8_t match_tag_not; uint8_t natpass; uint8_t keep_state; sa_family_t af; uint8_t proto; uint8_t type; uint8_t code; uint8_t flags; uint8_t flagset; uint8_t min_ttl; uint8_t allow_opts; uint8_t rt; uint8_t return_ttl; uint8_t tos; uint8_t set_tos; uint8_t anchor_relative; uint8_t anchor_wildcard; uint8_t flush; uint8_t prio; uint8_t set_prio[2]; struct { struct pf_addr addr; uint16_t port; } divert; }; TAILQ_HEAD(pfctl_rulequeue, pfctl_rule); struct pfctl_ruleset { struct { struct pfctl_rulequeue queues[2]; struct { struct pfctl_rulequeue *ptr; struct pfctl_rule **ptr_array; uint32_t rcount; uint32_t ticket; int open; } active, inactive; } rules[PF_RULESET_MAX]; struct pfctl_anchor *anchor; uint32_t tticket; int tables; int topen; }; RB_HEAD(pfctl_anchor_global, pfctl_anchor); RB_HEAD(pfctl_anchor_node, pfctl_anchor); struct pfctl_anchor { RB_ENTRY(pfctl_anchor) entry_global; RB_ENTRY(pfctl_anchor) entry_node; struct pfctl_anchor *parent; struct pfctl_anchor_node children; char name[PF_ANCHOR_NAME_SIZE]; char path[MAXPATHLEN]; struct pfctl_ruleset ruleset; int refcnt; /* anchor rules */ int match; /* XXX: used for pfctl black magic */ }; RB_PROTOTYPE(pfctl_anchor_global, pfctl_anchor, entry_global, pf_anchor_compare); RB_PROTOTYPE(pfctl_anchor_node, pfctl_anchor, entry_node, pf_anchor_compare); struct pfctl_state_cmp { uint64_t id; uint32_t creatorid; uint8_t direction; }; struct pfctl_kill { struct pfctl_state_cmp cmp; sa_family_t af; int proto; struct pf_rule_addr src; struct pf_rule_addr dst; struct pf_rule_addr rt_addr; char ifname[IFNAMSIZ]; char label[PF_RULE_LABEL_SIZE]; bool kill_match; bool nat; }; struct pfctl_state_peer { uint32_t seqlo; uint32_t seqhi; uint32_t seqdiff; uint8_t state; uint8_t wscale; }; struct pfctl_state_key { struct pf_addr addr[2]; uint16_t port[2]; sa_family_t af; uint8_t proto; }; struct pfctl_state { TAILQ_ENTRY(pfctl_state) entry; uint64_t id; uint32_t creatorid; uint8_t direction; struct pfctl_state_peer src; struct pfctl_state_peer dst; uint32_t rule; uint32_t anchor; uint32_t nat_rule; struct pf_addr rt_addr; struct pfctl_state_key key[2]; /* addresses stack and wire */ char ifname[IFNAMSIZ]; char orig_ifname[IFNAMSIZ]; uint64_t packets[2]; uint64_t bytes[2]; uint32_t creation; uint32_t expire; uint32_t pfsync_time; uint16_t state_flags; uint32_t sync_flags; uint16_t qid; uint16_t pqid; uint16_t dnpipe; uint16_t dnrpipe; uint8_t log; int32_t rtableid; uint8_t min_ttl; uint8_t set_tos; uint16_t max_mss; uint8_t set_prio[2]; uint8_t rt; char rt_ifname[IFNAMSIZ]; }; TAILQ_HEAD(pfctl_statelist, pfctl_state); struct pfctl_states { struct pfctl_statelist states; }; enum pfctl_syncookies_mode { PFCTL_SYNCOOKIES_NEVER, PFCTL_SYNCOOKIES_ALWAYS, PFCTL_SYNCOOKIES_ADAPTIVE }; extern const char* PFCTL_SYNCOOKIES_MODE_NAMES[]; struct pfctl_syncookies { enum pfctl_syncookies_mode mode; uint8_t highwater; /* Percent */ uint8_t lowwater; /* Percent */ uint32_t halfopen_states; }; #define PF_DEVICE "/dev/pf" struct pfctl_handle; struct pfctl_handle *pfctl_open(const char *pf_device); void pfctl_close(struct pfctl_handle *); int pfctl_fd(struct pfctl_handle *); int pfctl_startstop(struct pfctl_handle *h, int start); struct pfctl_status* pfctl_get_status_h(struct pfctl_handle *h); struct pfctl_status* pfctl_get_status(int dev); int pfctl_clear_status(struct pfctl_handle *h); uint64_t pfctl_status_counter(struct pfctl_status *status, int id); uint64_t pfctl_status_lcounter(struct pfctl_status *status, int id); uint64_t pfctl_status_fcounter(struct pfctl_status *status, int id); uint64_t pfctl_status_scounter(struct pfctl_status *status, int id); void pfctl_free_status(struct pfctl_status *status); int pfctl_get_eth_rulesets_info(int dev, struct pfctl_eth_rulesets_info *ri, const char *path); int pfctl_get_eth_ruleset(int dev, const char *path, int nr, struct pfctl_eth_ruleset_info *ri); int pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules, const char *path); int pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket, const char *path, struct pfctl_eth_rule *rule, bool clear, char *anchor_call); int pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor, const char *anchor_call, uint32_t ticket); int pfctl_get_rules_info_h(struct pfctl_handle *h, struct pfctl_rules_info *rules, uint32_t ruleset, const char *path); int pfctl_get_rules_info(int dev, struct pfctl_rules_info *rules, uint32_t ruleset, const char *path); int pfctl_get_rule(int dev, uint32_t nr, uint32_t ticket, const char *anchor, uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call); int pfctl_get_rule_h(struct pfctl_handle *h, uint32_t nr, uint32_t ticket, const char *anchor, uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call); int pfctl_get_clear_rule(int dev, uint32_t nr, uint32_t ticket, const char *anchor, uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call, bool clear); int pfctl_get_clear_rule_h(struct pfctl_handle *h, uint32_t nr, uint32_t ticket, const char *anchor, uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call, bool clear); 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); int pfctl_add_rule_h(struct pfctl_handle *h, const struct pfctl_rule *r, const char *anchor, const char *anchor_call, uint32_t ticket, uint32_t pool_ticket); int pfctl_set_keepcounters(int dev, bool keep); int pfctl_get_creatorids(struct pfctl_handle *h, uint32_t *creators, size_t *len); struct pfctl_state_filter { char ifname[IFNAMSIZ]; uint16_t proto; sa_family_t af; struct pf_addr addr; struct pf_addr mask; }; typedef int (*pfctl_get_state_fn)(struct pfctl_state *, void *); int pfctl_get_states_iter(pfctl_get_state_fn f, void *arg); int pfctl_get_filtered_states_iter(struct pfctl_state_filter *filter, pfctl_get_state_fn f, void *arg); int pfctl_get_states(int dev, struct pfctl_states *states); void pfctl_free_states(struct pfctl_states *states); int pfctl_clear_states(int dev, const struct pfctl_kill *kill, unsigned int *killed); int pfctl_kill_states(int dev, const struct pfctl_kill *kill, unsigned int *killed); int pfctl_clear_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill, unsigned int *killed); int pfctl_kill_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill, unsigned int *killed); int pfctl_clear_rules(int dev, const char *anchorname); int pfctl_clear_nat(int dev, const char *anchorname); int pfctl_clear_eth_rules(int dev, const char *anchorname); int pfctl_set_syncookies(int dev, const struct pfctl_syncookies *s); int pfctl_get_syncookies(int dev, struct pfctl_syncookies *s); int pfctl_table_add_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr, int size, int *nadd, int flags); int pfctl_table_del_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr, int size, int *ndel, int flags); int pfctl_table_set_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr, int size, int *size2, int *nadd, int *ndel, int *nchange, int flags); int pfctl_table_get_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr, int *size, int flags); int pfctl_set_statusif(struct pfctl_handle *h, const char *ifname); +struct pfctl_natlook_key { + sa_family_t af; + uint8_t direction; + uint8_t proto; + struct pf_addr saddr; + struct pf_addr daddr; + uint16_t sport; + uint16_t dport; +}; +struct pfctl_natlook { + struct pf_addr saddr; + struct pf_addr daddr; + uint16_t sport; + uint16_t dport; +}; +int pfctl_natlook(struct pfctl_handle *h, + const struct pfctl_natlook_key *k, struct pfctl_natlook *r); + #endif diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c index 67a785e54d6f..b9dc7358ae28 100644 --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -1,1335 +1,1441 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023 Alexander V. Chernikov * Copyright (c) 2023 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 +#include "opt_inet.h" +#include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_MOD_NAME nl_pf #define DEBUG_MAX_LEVEL LOG_DEBUG3 #include _DECLARE_DEBUG(LOG_DEBUG); struct nl_parsed_state { uint8_t version; uint32_t id; uint32_t creatorid; char ifname[IFNAMSIZ]; uint16_t proto; sa_family_t af; struct pf_addr addr; struct pf_addr mask; }; #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct nl_parsed_state, _field) static const struct nlattr_parser nla_p_state[] = { { .type = PF_ST_ID, .off = _OUT(id), .cb = nlattr_get_uint32 }, { .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = nlattr_get_uint32 }, { .type = PF_ST_IFNAME, .arg = (const void *)IFNAMSIZ, .off = _OUT(ifname), .cb = nlattr_get_chara }, { .type = PF_ST_AF, .off = _OUT(proto), .cb = nlattr_get_uint8 }, { .type = PF_ST_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint16 }, { .type = PF_ST_FILTER_ADDR, .off = _OUT(addr), .cb = nlattr_get_in6_addr }, { .type = PF_ST_FILTER_MASK, .off = _OUT(mask), .cb = nlattr_get_in6_addr }, }; static const struct nlfield_parser nlf_p_generic[] = { { .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 }, }; #undef _IN #undef _OUT NL_DECLARE_PARSER(state_parser, struct genlmsghdr, nlf_p_generic, nla_p_state); static void dump_addr(struct nl_writer *nw, int attr, const struct pf_addr *addr, int af) { switch (af) { case AF_INET: nlattr_add(nw, attr, 4, &addr->v4); break; case AF_INET6: nlattr_add(nw, attr, 16, &addr->v6); break; }; } static bool dump_state_peer(struct nl_writer *nw, int attr, const struct pf_state_peer *peer) { int off = nlattr_add_nested(nw, attr); if (off == 0) return (false); nlattr_add_u32(nw, PF_STP_SEQLO, peer->seqlo); nlattr_add_u32(nw, PF_STP_SEQHI, peer->seqhi); nlattr_add_u32(nw, PF_STP_SEQDIFF, peer->seqdiff); nlattr_add_u16(nw, PF_STP_MAX_WIN, peer->max_win); nlattr_add_u16(nw, PF_STP_MSS, peer->mss); nlattr_add_u8(nw, PF_STP_STATE, peer->state); nlattr_add_u8(nw, PF_STP_WSCALE, peer->wscale); if (peer->scrub != NULL) { struct pf_state_scrub *sc = peer->scrub; uint16_t pfss_flags = sc->pfss_flags & PFSS_TIMESTAMP; nlattr_add_u16(nw, PF_STP_PFSS_FLAGS, pfss_flags); nlattr_add_u32(nw, PF_STP_PFSS_TS_MOD, sc->pfss_ts_mod); nlattr_add_u8(nw, PF_STP_PFSS_TTL, sc->pfss_ttl); nlattr_add_u8(nw, PF_STP_SCRUB_FLAG, PFSYNC_SCRUB_FLAG_VALID); } nlattr_set_len(nw, off); return (true); } static bool dump_state_key(struct nl_writer *nw, int attr, const struct pf_state_key *key) { int off = nlattr_add_nested(nw, attr); if (off == 0) return (false); dump_addr(nw, PF_STK_ADDR0, &key->addr[0], key->af); dump_addr(nw, PF_STK_ADDR1, &key->addr[1], key->af); nlattr_add_u16(nw, PF_STK_PORT0, key->port[0]); nlattr_add_u16(nw, PF_STK_PORT1, key->port[1]); nlattr_set_len(nw, off); return (true); } static int dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s, struct nl_pstate *npt) { struct nl_writer *nw = npt->nw; int error = 0; int af; struct pf_state_key *key; if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) goto enomem; struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); ghdr_new->cmd = PFNL_CMD_GETSTATES; ghdr_new->version = 0; ghdr_new->reserved = 0; nlattr_add_u64(nw, PF_ST_VERSION, PF_STATE_VERSION); key = s->key[PF_SK_WIRE]; if (!dump_state_key(nw, PF_ST_KEY_WIRE, key)) goto enomem; key = s->key[PF_SK_STACK]; if (!dump_state_key(nw, PF_ST_KEY_STACK, key)) goto enomem; af = s->key[PF_SK_WIRE]->af; nlattr_add_u8(nw, PF_ST_PROTO, s->key[PF_SK_WIRE]->proto); nlattr_add_u8(nw, PF_ST_AF, af); nlattr_add_string(nw, PF_ST_IFNAME, s->kif->pfik_name); nlattr_add_string(nw, PF_ST_ORIG_IFNAME, s->orig_kif->pfik_name); dump_addr(nw, PF_ST_RT_ADDR, &s->rt_addr, af); nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - (s->creation / 1000)); uint32_t expire = pf_state_expires(s); if (expire > time_uptime) expire = expire - time_uptime; nlattr_add_u32(nw, PF_ST_EXPIRE, expire); nlattr_add_u8(nw, PF_ST_DIRECTION, s->direction); nlattr_add_u8(nw, PF_ST_LOG, s->act.log); nlattr_add_u8(nw, PF_ST_TIMEOUT, s->timeout); nlattr_add_u16(nw, PF_ST_STATE_FLAGS, s->state_flags); uint8_t sync_flags = 0; if (s->src_node) sync_flags |= PFSYNC_FLAG_SRCNODE; if (s->nat_src_node) sync_flags |= PFSYNC_FLAG_NATSRCNODE; nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags); nlattr_add_u64(nw, PF_ST_ID, s->id); nlattr_add_u32(nw, PF_ST_CREATORID, htonl(s->creatorid)); nlattr_add_u32(nw, PF_ST_RULE, s->rule.ptr ? s->rule.ptr->nr : -1); nlattr_add_u32(nw, PF_ST_ANCHOR, s->anchor.ptr ? s->anchor.ptr->nr : -1); nlattr_add_u32(nw, PF_ST_NAT_RULE, s->nat_rule.ptr ? s->nat_rule.ptr->nr : -1); nlattr_add_u64(nw, PF_ST_PACKETS0, s->packets[0]); nlattr_add_u64(nw, PF_ST_PACKETS1, s->packets[1]); nlattr_add_u64(nw, PF_ST_BYTES0, s->bytes[0]); nlattr_add_u64(nw, PF_ST_BYTES1, s->bytes[1]); nlattr_add_u32(nw, PF_ST_RTABLEID, s->act.rtableid); nlattr_add_u8(nw, PF_ST_MIN_TTL, s->act.min_ttl); nlattr_add_u16(nw, PF_ST_MAX_MSS, s->act.max_mss); nlattr_add_u16(nw, PF_ST_DNPIPE, s->act.dnpipe); nlattr_add_u16(nw, PF_ST_DNRPIPE, s->act.dnrpipe); nlattr_add_u8(nw, PF_ST_RT, s->rt); if (s->rt_kif != NULL) nlattr_add_string(nw, PF_ST_RT_IFNAME, s->rt_kif->pfik_name); if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src)) goto enomem; if (!dump_state_peer(nw, PF_ST_PEER_DST, &s->dst)) goto enomem; if (nlmsg_end(nw)) return (0); enomem: error = ENOMEM; nlmsg_abort(nw); return (error); } static int handle_dumpstates(struct nlpcb *nlp, struct nl_parsed_state *attrs, struct nlmsghdr *hdr, struct nl_pstate *npt) { int error = 0; hdr->nlmsg_flags |= NLM_F_MULTI; for (int i = 0; i <= pf_hashmask; i++) { struct pf_idhash *ih = &V_pf_idhash[i]; struct pf_kstate *s; if (LIST_EMPTY(&ih->states)) continue; PF_HASHROW_LOCK(ih); LIST_FOREACH(s, &ih->states, entry) { sa_family_t af = s->key[PF_SK_WIRE]->af; if (s->timeout == PFTM_UNLINKED) continue; /* Filter */ if (attrs->creatorid != 0 && s->creatorid != attrs->creatorid) continue; if (attrs->ifname[0] != 0 && strncmp(attrs->ifname, s->kif->pfik_name, IFNAMSIZ) != 0) continue; if (attrs->proto != 0 && s->key[PF_SK_WIRE]->proto != attrs->proto) continue; if (attrs->af != 0 && af != attrs->af) continue; if (pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[0], &attrs->mask, &attrs->addr, af) && pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[1], &attrs->mask, &attrs->addr, af) && pf_match_addr(1, &s->key[PF_SK_STACK]->addr[0], &attrs->mask, &attrs->addr, af) && pf_match_addr(1, &s->key[PF_SK_STACK]->addr[1], &attrs->mask, &attrs->addr, af)) continue; error = dump_state(nlp, hdr, s, npt); if (error != 0) break; } PF_HASHROW_UNLOCK(ih); } if (!nlmsg_end_dump(npt->nw, error, hdr)) { NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); return (ENOMEM); } return (error); } static int handle_getstate(struct nlpcb *nlp, struct nl_parsed_state *attrs, struct nlmsghdr *hdr, struct nl_pstate *npt) { struct pf_kstate *s = pf_find_state_byid(attrs->id, attrs->creatorid); if (s == NULL) return (ENOENT); return (dump_state(nlp, hdr, s, npt)); } static int dump_creatorid(struct nlpcb *nlp, const struct nlmsghdr *hdr, uint32_t creator, struct nl_pstate *npt) { struct nl_writer *nw = npt->nw; if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) goto enomem; struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); ghdr_new->cmd = PFNL_CMD_GETCREATORS; ghdr_new->version = 0; ghdr_new->reserved = 0; nlattr_add_u32(nw, PF_ST_CREATORID, htonl(creator)); if (nlmsg_end(nw)) return (0); enomem: nlmsg_abort(nw); return (ENOMEM); } static int pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate *npt) { int error; struct nl_parsed_state attrs = {}; error = nl_parse_nlmsg(hdr, &state_parser, npt, &attrs); if (error != 0) return (error); if (attrs.id != 0) error = handle_getstate(npt->nlp, &attrs, hdr, npt); else error = handle_dumpstates(npt->nlp, &attrs, hdr, npt); return (error); } static int pf_handle_getcreators(struct nlmsghdr *hdr, struct nl_pstate *npt) { uint32_t creators[16]; int error = 0; bzero(creators, sizeof(creators)); for (int i = 0; i < pf_hashmask; i++) { struct pf_idhash *ih = &V_pf_idhash[i]; struct pf_kstate *s; if (LIST_EMPTY(&ih->states)) continue; PF_HASHROW_LOCK(ih); LIST_FOREACH(s, &ih->states, entry) { int j; if (s->timeout == PFTM_UNLINKED) continue; for (j = 0; j < nitems(creators); j++) { if (creators[j] == s->creatorid) break; if (creators[j] == 0) { creators[j] = s->creatorid; break; } } if (j == nitems(creators)) printf("Warning: too many creators!\n"); } PF_HASHROW_UNLOCK(ih); } hdr->nlmsg_flags |= NLM_F_MULTI; for (int i = 0; i < nitems(creators); i++) { if (creators[i] == 0) break; error = dump_creatorid(npt->nlp, hdr, creators[i], npt); } if (!nlmsg_end_dump(npt->nw, error, hdr)) { NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); return (ENOMEM); } return (error); } static int pf_handle_start(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused) { return (pf_start()); } static int pf_handle_stop(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused) { return (pf_stop()); } #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 static bool nlattr_add_addr_wrap(struct nl_writer *nw, int attrtype, struct pf_addr_wrap *a) { int off = nlattr_add_nested(nw, attrtype); int num; nlattr_add_in6_addr(nw, PF_AT_ADDR, &a->v.a.addr.v6); nlattr_add_in6_addr(nw, PF_AT_MASK, &a->v.a.mask.v6); nlattr_add_u8(nw, PF_AT_TYPE, a->type); nlattr_add_u8(nw, PF_AT_IFLAGS, a->iflags); if (a->type == PF_ADDR_DYNIFTL) { nlattr_add_string(nw, PF_AT_IFNAME, a->v.ifname); num = 0; if (a->p.dyn != NULL) num = a->p.dyn->pfid_acnt4 + a->p.dyn->pfid_acnt6; nlattr_add_u32(nw, PF_AT_DYNCNT, num); } else if (a->type == PF_ADDR_TABLE) { struct pfr_ktable *kt; nlattr_add_string(nw, PF_AT_TABLENAME, a->v.tblname); num = -1; kt = a->p.tbl; if ((kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) kt = kt->pfrkt_root; if (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) num = kt->pfrkt_cnt; nlattr_add_u32(nw, PF_AT_TBLCNT, num); } nlattr_set_len(nw, off); return (true); } #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 static bool nlattr_add_rule_addr(struct nl_writer *nw, int attrtype, struct pf_rule_addr *r) { int off = nlattr_add_nested(nw, attrtype); nlattr_add_addr_wrap(nw, PF_RAT_ADDR, &r->addr); nlattr_add_u16(nw, PF_RAT_SRC_PORT, r->port[0]); nlattr_add_u16(nw, PF_RAT_DST_PORT, r->port[1]); nlattr_add_u8(nw, PF_RAT_NEG, r->neg); nlattr_add_u8(nw, PF_RAT_OP, r->port_op); nlattr_set_len(nw, off); return (true); } #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 static bool nlattr_add_mape_portset(struct nl_writer *nw, int attrtype, const struct pf_mape_portset *m) { int off = nlattr_add_nested(nw, attrtype); nlattr_add_u8(nw, PF_MET_OFFSET, m->offset); nlattr_add_u8(nw, PF_MET_PSID_LEN, m->psidlen); nlattr_add_u16(nw, PF_MET_PSID, m->psid); nlattr_set_len(nw, off); return (true); } 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); } static bool nlattr_add_labels(struct nl_writer *nw, int attrtype, const struct pf_krule *r) { int off = nlattr_add_nested(nw, attrtype); int i = 0; while (r->label[i][0] != 0 && i < PF_RULE_MAX_LABEL_COUNT) { nlattr_add_string(nw, PF_LT_LABEL, r->label[i]); i++; } nlattr_set_len(nw, off); return (true); } #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 static bool nlattr_add_pool(struct nl_writer *nw, int attrtype, const struct pf_kpool *pool) { int off = nlattr_add_nested(nw, attrtype); nlattr_add(nw, PF_PT_KEY, sizeof(struct pf_poolhashkey), &pool->key); nlattr_add_in6_addr(nw, PF_PT_COUNTER, (const struct in6_addr *)&pool->counter); nlattr_add_u32(nw, PF_PT_TBLIDX, pool->tblidx); nlattr_add_u16(nw, PF_PT_PROXY_SRC_PORT, pool->proxy_port[0]); nlattr_add_u16(nw, PF_PT_PROXY_DST_PORT, pool->proxy_port[1]); nlattr_add_u8(nw, PF_PT_OPTS, pool->opts); nlattr_add_mape_portset(nw, PF_PT_MAPE, &pool->mape); nlattr_set_len(nw, off); return (true); } #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 static bool nlattr_add_rule_uid(struct nl_writer *nw, int attrtype, const struct pf_rule_uid *u) { int off = nlattr_add_nested(nw, attrtype); nlattr_add_u32(nw, PF_RUT_UID_LOW, u->uid[0]); nlattr_add_u32(nw, PF_RUT_UID_HIGH, u->uid[1]); nlattr_add_u8(nw, PF_RUT_OP, u->op); nlattr_set_len(nw, off); return (true); } 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); } static bool nlattr_add_timeout(struct nl_writer *nw, int attrtype, uint32_t *timeout) { int off = nlattr_add_nested(nw, attrtype); for (int i = 0; i < PFTM_MAX; i++) nlattr_add_u32(nw, PF_RT_TIMEOUT, timeout[i]); nlattr_set_len(nw, off); return (true); } #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) { pf_free_rule(attrs.rule); 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); return (error); } #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct pfioc_rule, _field) static const struct nlattr_parser nla_p_getrules[] = { { .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara }, { .type = PF_GR_ACTION, .off = _OUT(rule.action), .cb = nlattr_get_uint8 }, }; static const struct nlfield_parser nlf_p_getrules[] = { }; #undef _IN #undef _OUT NL_DECLARE_PARSER(getrules_parser, struct genlmsghdr, nlf_p_getrules, nla_p_getrules); static int pf_handle_getrules(struct nlmsghdr *hdr, struct nl_pstate *npt) { struct pfioc_rule attrs = {}; int error; struct nl_writer *nw = npt->nw; struct genlmsghdr *ghdr_new; error = nl_parse_nlmsg(hdr, &getrules_parser, npt, &attrs); if (error != 0) return (error); if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) return (ENOMEM); ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); ghdr_new->cmd = PFNL_CMD_GETRULES; ghdr_new->version = 0; ghdr_new->reserved = 0; error = pf_ioctl_getrules(&attrs); if (error != 0) goto out; nlattr_add_u32(nw, PF_GR_NR, attrs.nr); nlattr_add_u32(nw, PF_GR_TICKET, attrs.ticket); if (!nlmsg_end(nw)) { error = ENOMEM; goto out; } return (0); out: nlmsg_abort(nw); return (error); } struct nl_parsed_get_rule { char anchor[MAXPATHLEN]; uint8_t action; uint32_t nr; uint32_t ticket; uint8_t clear; }; #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct nl_parsed_get_rule, _field) static const struct nlattr_parser nla_p_getrule[] = { { .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara }, { .type = PF_GR_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 }, { .type = PF_GR_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 }, { .type = PF_GR_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 }, { .type = PF_GR_CLEAR, .off = _OUT(clear), .cb = nlattr_get_uint8 }, }; static const struct nlfield_parser nlf_p_getrule[] = { }; #undef _IN #undef _OUT NL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, nlf_p_getrule, nla_p_getrule); static int pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate *npt) { char anchor_call[MAXPATHLEN]; struct nl_parsed_get_rule attrs = {}; struct nl_writer *nw = npt->nw; struct genlmsghdr *ghdr_new; struct pf_kruleset *ruleset; struct pf_krule *rule; int rs_num; int error; error = nl_parse_nlmsg(hdr, &getrule_parser, npt, &attrs); if (error != 0) return (error); if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) return (ENOMEM); ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); ghdr_new->cmd = PFNL_CMD_GETRULE; ghdr_new->version = 0; ghdr_new->reserved = 0; PF_RULES_WLOCK(); ruleset = pf_find_kruleset(attrs.anchor); if (ruleset == NULL) { PF_RULES_WUNLOCK(); error = ENOENT; goto out; } rs_num = pf_get_ruleset_number(attrs.action); if (rs_num >= PF_RULESET_MAX) { PF_RULES_WUNLOCK(); error = EINVAL; goto out; } if (attrs.ticket != ruleset->rules[rs_num].active.ticket) { PF_RULES_WUNLOCK(); error = EBUSY; goto out; } rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); while ((rule != NULL) && (rule->nr != attrs.nr)) rule = TAILQ_NEXT(rule, entries); if (rule == NULL) { PF_RULES_WUNLOCK(); error = EBUSY; goto out; } nlattr_add_rule_addr(nw, PF_RT_SRC, &rule->src); nlattr_add_rule_addr(nw, PF_RT_DST, &rule->dst); nlattr_add_u32(nw, PF_RT_RIDENTIFIER, rule->ridentifier); nlattr_add_labels(nw, PF_RT_LABELS, rule); nlattr_add_string(nw, PF_RT_IFNAME, rule->ifname); nlattr_add_string(nw, PF_RT_QNAME, rule->qname); nlattr_add_string(nw, PF_RT_PQNAME, rule->pqname); nlattr_add_string(nw, PF_RT_TAGNAME, rule->tagname); nlattr_add_string(nw, PF_RT_MATCH_TAGNAME, rule->match_tagname); nlattr_add_string(nw, PF_RT_OVERLOAD_TBLNAME, rule->overload_tblname); nlattr_add_pool(nw, PF_RT_RPOOL, &rule->rpool); nlattr_add_u32(nw, PF_RT_OS_FINGERPRINT, rule->os_fingerprint); nlattr_add_u32(nw, PF_RT_RTABLEID, rule->rtableid); nlattr_add_timeout(nw, PF_RT_TIMEOUT, rule->timeout); nlattr_add_u32(nw, PF_RT_MAX_STATES, rule->max_states); nlattr_add_u32(nw, PF_RT_MAX_SRC_NODES, rule->max_src_nodes); nlattr_add_u32(nw, PF_RT_MAX_SRC_STATES, rule->max_src_states); nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_LIMIT, rule->max_src_conn_rate.limit); nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_SECS, rule->max_src_conn_rate.seconds); nlattr_add_u16(nw, PF_RT_DNPIPE, rule->dnpipe); nlattr_add_u16(nw, PF_RT_DNRPIPE, rule->dnrpipe); nlattr_add_u32(nw, PF_RT_DNFLAGS, rule->free_flags); nlattr_add_u32(nw, PF_RT_NR, rule->nr); nlattr_add_u32(nw, PF_RT_PROB, rule->prob); nlattr_add_u32(nw, PF_RT_CUID, rule->cuid); nlattr_add_u32(nw, PF_RT_CPID, rule->cpid); nlattr_add_u16(nw, PF_RT_RETURN_ICMP, rule->return_icmp); nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6); nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6); nlattr_add_u16(nw, PF_RT_MAX_MSS, rule->max_mss); nlattr_add_u16(nw, PF_RT_SCRUB_FLAGS, rule->scrub_flags); nlattr_add_rule_uid(nw, PF_RT_UID, &rule->uid); nlattr_add_rule_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&rule->gid); nlattr_add_u32(nw, PF_RT_RULE_FLAG, rule->rule_flag); nlattr_add_u8(nw, PF_RT_ACTION, rule->action); nlattr_add_u8(nw, PF_RT_DIRECTION, rule->direction); nlattr_add_u8(nw, PF_RT_LOG, rule->log); nlattr_add_u8(nw, PF_RT_LOGIF, rule->logif); nlattr_add_u8(nw, PF_RT_QUICK, rule->quick); nlattr_add_u8(nw, PF_RT_IF_NOT, rule->ifnot); nlattr_add_u8(nw, PF_RT_MATCH_TAG_NOT, rule->match_tag_not); nlattr_add_u8(nw, PF_RT_NATPASS, rule->natpass); nlattr_add_u8(nw, PF_RT_KEEP_STATE, rule->keep_state); nlattr_add_u8(nw, PF_RT_AF, rule->af); nlattr_add_u8(nw, PF_RT_PROTO, rule->proto); nlattr_add_u8(nw, PF_RT_TYPE, rule->type); nlattr_add_u8(nw, PF_RT_CODE, rule->code); nlattr_add_u8(nw, PF_RT_FLAGS, rule->flags); nlattr_add_u8(nw, PF_RT_FLAGSET, rule->flagset); nlattr_add_u8(nw, PF_RT_MIN_TTL, rule->min_ttl); nlattr_add_u8(nw, PF_RT_ALLOW_OPTS, rule->allow_opts); nlattr_add_u8(nw, PF_RT_RT, rule->rt); nlattr_add_u8(nw, PF_RT_RETURN_TTL, rule->return_ttl); nlattr_add_u8(nw, PF_RT_TOS, rule->tos); nlattr_add_u8(nw, PF_RT_SET_TOS, rule->set_tos); nlattr_add_u8(nw, PF_RT_ANCHOR_RELATIVE, rule->anchor_relative); nlattr_add_u8(nw, PF_RT_ANCHOR_WILDCARD, rule->anchor_wildcard); nlattr_add_u8(nw, PF_RT_FLUSH, rule->flush); nlattr_add_u8(nw, PF_RT_PRIO, rule->prio); nlattr_add_u8(nw, PF_RT_SET_PRIO, rule->set_prio[0]); nlattr_add_u8(nw, PF_RT_SET_PRIO_REPLY, rule->set_prio[1]); nlattr_add_in6_addr(nw, PF_RT_DIVERT_ADDRESS, &rule->divert.addr.v6); nlattr_add_u16(nw, PF_RT_DIVERT_PORT, rule->divert.port); nlattr_add_u64(nw, PF_RT_PACKETS_IN, pf_counter_u64_fetch(&rule->packets[0])); nlattr_add_u64(nw, PF_RT_PACKETS_OUT, pf_counter_u64_fetch(&rule->packets[1])); nlattr_add_u64(nw, PF_RT_BYTES_IN, pf_counter_u64_fetch(&rule->bytes[0])); nlattr_add_u64(nw, PF_RT_BYTES_OUT, pf_counter_u64_fetch(&rule->bytes[1])); nlattr_add_u64(nw, PF_RT_EVALUATIONS, pf_counter_u64_fetch(&rule->evaluations)); nlattr_add_u64(nw, PF_RT_TIMESTAMP, pf_get_timestamp(rule)); nlattr_add_u64(nw, PF_RT_STATES_CUR, counter_u64_fetch(rule->states_cur)); nlattr_add_u64(nw, PF_RT_STATES_TOTAL, counter_u64_fetch(rule->states_tot)); nlattr_add_u64(nw, PF_RT_SRC_NODES, counter_u64_fetch(rule->src_nodes)); error = pf_kanchor_copyout(ruleset, rule, anchor_call, sizeof(anchor_call)); MPASS(error == 0); nlattr_add_string(nw, PF_RT_ANCHOR_CALL, anchor_call); if (attrs.clear) pf_krule_clear_counters(rule); PF_RULES_WUNLOCK(); if (!nlmsg_end(nw)) { error = ENOMEM; goto out; } return (0); out: nlmsg_abort(nw); return (error); } #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct pf_kstate_kill, _field) static const struct nlattr_parser nla_p_clear_states[] = { { .type = PF_CS_CMP_ID, .off = _OUT(psk_pfcmp.id), .cb = nlattr_get_uint64 }, { .type = PF_CS_CMP_CREATORID, .off = _OUT(psk_pfcmp.creatorid), .cb = nlattr_get_uint32 }, { .type = PF_CS_CMP_DIR, .off = _OUT(psk_pfcmp.direction), .cb = nlattr_get_uint8 }, { .type = PF_CS_AF, .off = _OUT(psk_af), .cb = nlattr_get_uint8 }, { .type = PF_CS_PROTO, .off = _OUT(psk_proto), .cb = nlattr_get_uint8 }, { .type = PF_CS_SRC, .off = _OUT(psk_src), .arg = &rule_addr_parser, .cb = nlattr_get_nested }, { .type = PF_CS_DST, .off = _OUT(psk_dst), .arg = &rule_addr_parser, .cb = nlattr_get_nested }, { .type = PF_CS_RT_ADDR, .off = _OUT(psk_rt_addr), .arg = &rule_addr_parser, .cb = nlattr_get_nested }, { .type = PF_CS_IFNAME, .off = _OUT(psk_ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara }, { .type = PF_CS_LABEL, .off = _OUT(psk_label), .arg = (void *)PF_RULE_LABEL_SIZE, .cb = nlattr_get_chara }, { .type = PF_CS_KILL_MATCH, .off = _OUT(psk_kill_match), .cb = nlattr_get_bool }, { .type = PF_CS_NAT, .off = _OUT(psk_nat), .cb = nlattr_get_bool }, }; static const struct nlfield_parser nlf_p_clear_states[] = {}; #undef _IN #undef _OUT NL_DECLARE_PARSER(clear_states_parser, struct genlmsghdr, nlf_p_clear_states, nla_p_clear_states); static int pf_handle_killclear_states(struct nlmsghdr *hdr, struct nl_pstate *npt, int cmd) { struct pf_kstate_kill kill = {}; struct epoch_tracker et; struct nl_writer *nw = npt->nw; struct genlmsghdr *ghdr_new; int error; unsigned int killed = 0; error = nl_parse_nlmsg(hdr, &clear_states_parser, npt, &kill); if (error != 0) return (error); if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) return (ENOMEM); ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); ghdr_new->cmd = cmd; ghdr_new->version = 0; ghdr_new->reserved = 0; NET_EPOCH_ENTER(et); if (cmd == PFNL_CMD_KILLSTATES) pf_killstates(&kill, &killed); else killed = pf_clear_states(&kill); NET_EPOCH_EXIT(et); nlattr_add_u32(nw, PF_CS_KILLED, killed); if (! nlmsg_end(nw)) { error = ENOMEM; goto out; } return (0); out: nlmsg_abort(nw); return (error); } static int pf_handle_clear_states(struct nlmsghdr *hdr, struct nl_pstate *npt) { return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_CLRSTATES)); } static int pf_handle_kill_states(struct nlmsghdr *hdr, struct nl_pstate *npt) { return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_KILLSTATES)); } struct nl_parsed_set_statusif { char ifname[IFNAMSIZ]; }; #define _IN(_field) offsetof(struct genlmsghdr, _field) #define _OUT(_field) offsetof(struct nl_parsed_set_statusif, _field) static const struct nlattr_parser nla_p_set_statusif[] = { { .type = PF_SS_IFNAME, .off = _OUT(ifname), .arg = (const void *)IFNAMSIZ, .cb = nlattr_get_chara }, }; static const struct nlfield_parser nlf_p_set_statusif[] = {}; #undef _IN #undef _OUT NL_DECLARE_PARSER(set_statusif_parser, struct genlmsghdr, nlf_p_set_statusif, nla_p_set_statusif); static int pf_handle_set_statusif(struct nlmsghdr *hdr, struct nl_pstate *npt) { int error; struct nl_parsed_set_statusif attrs = {}; error = nl_parse_nlmsg(hdr, &set_statusif_parser, npt, &attrs); if (error != 0) return (error); PF_RULES_WLOCK(); strlcpy(V_pf_status.ifname, attrs.ifname, IFNAMSIZ); PF_RULES_WUNLOCK(); return (0); } static bool nlattr_add_counters(struct nl_writer *nw, int attr, size_t number, char **names, counter_u64_t *counters) { for (int i = 0; i < number; i++) { int off = nlattr_add_nested(nw, attr); nlattr_add_u32(nw, PF_C_ID, i); nlattr_add_string(nw, PF_C_NAME, names[i]); nlattr_add_u64(nw, PF_C_COUNTER, counter_u64_fetch(counters[i])); nlattr_set_len(nw, off); } return (true); } static bool nlattr_add_fcounters(struct nl_writer *nw, int attr, size_t number, char **names, struct pf_counter_u64 *counters) { for (int i = 0; i < number; i++) { int off = nlattr_add_nested(nw, attr); nlattr_add_u32(nw, PF_C_ID, i); nlattr_add_string(nw, PF_C_NAME, names[i]); nlattr_add_u64(nw, PF_C_COUNTER, pf_counter_u64_fetch(&counters[i])); nlattr_set_len(nw, off); } return (true); } static bool nlattr_add_u64_array(struct nl_writer *nw, int attr, size_t number, uint64_t *array) { int off = nlattr_add_nested(nw, attr); for (size_t i = 0; i < number; i++) nlattr_add_u64(nw, 0, array[i]); nlattr_set_len(nw, off); return (true); } static int pf_handle_get_status(struct nlmsghdr *hdr, struct nl_pstate *npt) { struct pf_status s; struct nl_writer *nw = npt->nw; struct genlmsghdr *ghdr_new; char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES; char *pf_lcounter[KLCNT_MAX+1] = KLCNT_NAMES; char *pf_fcounter[FCNT_MAX+1] = FCNT_NAMES; int error; PF_RULES_RLOCK_TRACKER; if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) return (ENOMEM); ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); ghdr_new->cmd = PFNL_CMD_GET_STATUS; ghdr_new->version = 0; ghdr_new->reserved = 0; PF_RULES_RLOCK(); nlattr_add_string(nw, PF_GS_IFNAME, V_pf_status.ifname); nlattr_add_bool(nw, PF_GS_RUNNING, V_pf_status.running); nlattr_add_u32(nw, PF_GS_SINCE, V_pf_status.since); nlattr_add_u32(nw, PF_GS_DEBUG, V_pf_status.debug); nlattr_add_u32(nw, PF_GS_HOSTID, ntohl(V_pf_status.hostid)); nlattr_add_u32(nw, PF_GS_STATES, V_pf_status.states); nlattr_add_u32(nw, PF_GS_SRC_NODES, V_pf_status.src_nodes); nlattr_add_u32(nw, PF_GS_REASSEMBLE, V_pf_status.reass); nlattr_add_u32(nw, PF_GS_SYNCOOKIES_ACTIVE, V_pf_status.syncookies_active); nlattr_add_counters(nw, PF_GS_COUNTERS, PFRES_MAX, pf_reasons, V_pf_status.counters); nlattr_add_counters(nw, PF_GS_LCOUNTERS, KLCNT_MAX, pf_lcounter, V_pf_status.lcounters); nlattr_add_fcounters(nw, PF_GS_FCOUNTERS, FCNT_MAX, pf_fcounter, V_pf_status.fcounters); nlattr_add_counters(nw, PF_GS_SCOUNTERS, SCNT_MAX, pf_fcounter, V_pf_status.scounters); pfi_update_status(V_pf_status.ifname, &s); nlattr_add_u64_array(nw, PF_GS_BCOUNTERS, 2 * 2, (uint64_t *)s.bcounters); nlattr_add_u64_array(nw, PF_GS_PCOUNTERS, 2 * 2 * 2, (uint64_t *)s.pcounters); nlattr_add(nw, PF_GS_CHKSUM, PF_MD5_DIGEST_LENGTH, V_pf_status.pf_chksum); PF_RULES_RUNLOCK(); if (!nlmsg_end(nw)) { error = ENOMEM; goto out; } return (0); out: nlmsg_abort(nw); return (error); } static int pf_handle_clear_status(struct nlmsghdr *hdr, struct nl_pstate *npt) { pf_ioctl_clear_status(); return (0); } +struct pf_nl_natlook { + sa_family_t af; + uint8_t direction; + uint8_t proto; + struct pf_addr src; + struct pf_addr dst; + uint16_t sport; + uint16_t dport; +}; + +#define _IN(_field) offsetof(struct genlmsghdr, _field) +#define _OUT(_field) offsetof(struct pf_nl_natlook, _field) +static const struct nlattr_parser nla_p_natlook[] = { + { .type = PF_NL_AF, .off = _OUT(af), .cb = nlattr_get_uint8 }, + { .type = PF_NL_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 }, + { .type = PF_NL_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 }, + { .type = PF_NL_SRC_ADDR, .off = _OUT(src), .cb = nlattr_get_in6_addr }, + { .type = PF_NL_DST_ADDR, .off = _OUT(dst), .cb = nlattr_get_in6_addr }, + { .type = PF_NL_SRC_PORT, .off = _OUT(sport), .cb = nlattr_get_uint16 }, + { .type = PF_NL_DST_PORT, .off = _OUT(dport), .cb = nlattr_get_uint16 }, +}; +static const struct nlfield_parser nlf_p_natlook[] = {}; +#undef _IN +#undef _OUT +NL_DECLARE_PARSER(natlook_parser, struct genlmsghdr, nlf_p_natlook, nla_p_natlook); + +static int +pf_handle_natlook(struct nlmsghdr *hdr, struct nl_pstate *npt) +{ + struct pf_nl_natlook attrs = {}; + struct pf_state_key_cmp key = {}; + struct nl_writer *nw = npt->nw; + struct pf_state_key *sk; + struct pf_kstate *state; + struct genlmsghdr *ghdr_new; + int error, m; + int sidx, didx; + + error = nl_parse_nlmsg(hdr, &natlook_parser, npt, &attrs); + if (error != 0) + return (error); + + if (attrs.proto == 0 || + PF_AZERO(&attrs.src, attrs.af) || + PF_AZERO(&attrs.dst, attrs.af) || + ((attrs.proto == IPPROTO_TCP || attrs.proto == IPPROTO_UDP) && + (attrs.sport == 0 || attrs.dport == 0))) + return (EINVAL); + + /* NATLOOK src and dst are reversed, so reverse sidx/didx */ + sidx = (attrs.direction == PF_IN) ? 1 : 0; + didx = (attrs.direction == PF_IN) ? 0 : 1; + + key.af = attrs.af; + key.proto = attrs.proto; + PF_ACPY(&key.addr[sidx], &attrs.src, attrs.af); + key.port[sidx] = attrs.sport; + PF_ACPY(&key.addr[didx], &attrs.dst, attrs.af); + key.port[didx] = attrs.dport; + + state = pf_find_state_all(&key, attrs.direction, &m); + if (state == NULL) + return (ENOENT); + if (m > 1) { + PF_STATE_UNLOCK(state); + return (E2BIG); + } + + if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) { + PF_STATE_UNLOCK(state); + return (ENOMEM); + } + + ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); + ghdr_new->cmd = PFNL_CMD_NATLOOK; + ghdr_new->version = 0; + ghdr_new->reserved = 0; + + sk = state->key[sidx]; + + nlattr_add_in6_addr(nw, PF_NL_SRC_ADDR, &sk->addr[sidx].v6); + nlattr_add_in6_addr(nw, PF_NL_DST_ADDR, &sk->addr[didx].v6); + nlattr_add_u16(nw, PF_NL_SRC_PORT, sk->port[sidx]); + nlattr_add_u16(nw, PF_NL_DST_PORT, sk->port[didx]); + + PF_STATE_UNLOCK(state); + + if (!nlmsg_end(nw)) { + nlmsg_abort(nw); + return (ENOMEM); + } + + return (0); +} + static const struct nlhdr_parser *all_parsers[] = { &state_parser, &addrule_parser, &getrules_parser, &clear_states_parser, &set_statusif_parser, + &natlook_parser, }; static int family_id; static const struct genl_cmd pf_cmds[] = { { .cmd_num = PFNL_CMD_GETSTATES, .cmd_name = "GETSTATES", .cmd_cb = pf_handle_getstates, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_GETCREATORS, .cmd_name = "GETCREATORS", .cmd_cb = pf_handle_getcreators, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_START, .cmd_name = "START", .cmd_cb = pf_handle_start, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_STOP, .cmd_name = "STOP", .cmd_cb = pf_handle_stop, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .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, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_GETRULES, .cmd_name = "GETRULES", .cmd_cb = pf_handle_getrules, .cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_GETRULE, .cmd_name = "GETRULE", .cmd_cb = pf_handle_getrule, .cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_CLRSTATES, .cmd_name = "CLRSTATES", .cmd_cb = pf_handle_clear_states, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_KILLSTATES, .cmd_name = "KILLSTATES", .cmd_cb = pf_handle_kill_states, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_SET_STATUSIF, .cmd_name = "SETSTATUSIF", .cmd_cb = pf_handle_set_statusif, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_GET_STATUS, .cmd_name = "GETSTATUS", .cmd_cb = pf_handle_get_status, .cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, { .cmd_num = PFNL_CMD_CLEAR_STATUS, .cmd_name = "CLEARSTATUS", .cmd_cb = pf_handle_clear_status, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, .cmd_priv = PRIV_NETINET_PF, }, + { + .cmd_num = PFNL_CMD_NATLOOK, + .cmd_name = "NATLOOK", + .cmd_cb = pf_handle_natlook, + .cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, + .cmd_priv = PRIV_NETINET_PF, + }, }; 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)); } void pf_nl_unregister(void) { genl_unregister_family(PFNL_FAMILY_NAME); } diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h index 10440eaf6366..7c2ef3712dc7 100644 --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -1,326 +1,338 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023 Alexander V. Chernikov * Copyright (c) 2023 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 _NETPFIL_PF_PF_NL_H_ #define _NETPFIL_PF_PF_NL_H_ /* Genetlink family */ #define PFNL_FAMILY_NAME "pfctl" /* available commands */ enum { PFNL_CMD_UNSPEC = 0, PFNL_CMD_GETSTATES = 1, PFNL_CMD_GETCREATORS = 2, PFNL_CMD_START = 3, PFNL_CMD_STOP = 4, PFNL_CMD_ADDRULE = 5, PFNL_CMD_GETRULES = 6, PFNL_CMD_GETRULE = 7, PFNL_CMD_CLRSTATES = 8, PFNL_CMD_KILLSTATES = 9, PFNL_CMD_SET_STATUSIF = 10, PFNL_CMD_GET_STATUS = 11, PFNL_CMD_CLEAR_STATUS = 12, + PFNL_CMD_NATLOOK = 13, __PFNL_CMD_MAX, }; #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1) enum pfstate_key_type_t { PF_STK_UNSPEC, PF_STK_ADDR0 = 1, /* ip */ PF_STK_ADDR1 = 2, /* ip */ PF_STK_PORT0 = 3, /* u16 */ PF_STK_PORT1 = 4, /* u16 */ }; enum pfstate_peer_type_t { PF_STP_UNSPEC, PF_STP_PFSS_FLAGS = 1, /* u16 */ PF_STP_PFSS_TTL = 2, /* u8 */ PF_STP_SCRUB_FLAG = 3, /* u8 */ PF_STP_PFSS_TS_MOD = 4, /* u32 */ PF_STP_SEQLO = 5, /* u32 */ PF_STP_SEQHI = 6, /* u32 */ PF_STP_SEQDIFF = 7, /* u32 */ PF_STP_MAX_WIN = 8, /* u16 */ PF_STP_MSS = 9, /* u16 */ PF_STP_STATE = 10, /* u8 */ PF_STP_WSCALE = 11, /* u8 */ }; enum pfstate_type_t { PF_ST_UNSPEC, PF_ST_ID = 1, /* u32, state id */ PF_ST_CREATORID = 2, /* u32, */ PF_ST_IFNAME = 3, /* string */ PF_ST_ORIG_IFNAME = 4, /* string */ PF_ST_KEY_WIRE = 5, /* nested, pfstate_key_type_t */ PF_ST_KEY_STACK = 6, /* nested, pfstate_key_type_t */ PF_ST_PEER_SRC = 7, /* nested, pfstate_peer_type_t*/ PF_ST_PEER_DST = 8, /* nested, pfstate_peer_type_t */ PF_ST_RT_ADDR = 9, /* ip */ PF_ST_RULE = 10, /* u32 */ PF_ST_ANCHOR = 11, /* u32 */ PF_ST_NAT_RULE = 12, /* u32 */ PF_ST_CREATION = 13, /* u32 */ PF_ST_EXPIRE = 14, /* u32 */ PF_ST_PACKETS0 = 15, /* u64 */ PF_ST_PACKETS1 = 16, /* u64 */ PF_ST_BYTES0 = 17, /* u64 */ PF_ST_BYTES1 = 18, /* u64 */ PF_ST_AF = 19, /* u8 */ PF_ST_PROTO = 21, /* u8 */ PF_ST_DIRECTION = 22, /* u8 */ PF_ST_LOG = 23, /* u8 */ PF_ST_TIMEOUT = 24, /* u8 */ PF_ST_STATE_FLAGS = 25, /* u8 */ PF_ST_SYNC_FLAGS = 26, /* u8 */ PF_ST_UPDATES = 27, /* u8 */ PF_ST_VERSION = 28, /* u64 */ PF_ST_FILTER_ADDR = 29, /* in6_addr */ PF_ST_FILTER_MASK = 30, /* in6_addr */ PF_ST_RTABLEID = 31, /* i32 */ PF_ST_MIN_TTL = 32, /* u8 */ PF_ST_MAX_MSS = 33, /* u16 */ PF_ST_DNPIPE = 34, /* u16 */ PF_ST_DNRPIPE = 35, /* u16 */ PF_ST_RT = 36, /* u8 */ PF_ST_RT_IFNAME = 37, /* string */ }; 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 */ PF_AT_TBLCNT = 7, /* u32 */ PF_AT_DYNCNT = 8, /* u32 */ }; 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 */ PF_RT_PACKETS_IN = 63, /* u64 */ PF_RT_PACKETS_OUT = 64, /* u64 */ PF_RT_BYTES_IN = 65, /* u64 */ PF_RT_BYTES_OUT = 66, /* u64 */ PF_RT_EVALUATIONS = 67, /* u64 */ PF_RT_TIMESTAMP = 68, /* u64 */ PF_RT_STATES_CUR = 69, /* u64 */ PF_RT_STATES_TOTAL = 70, /* u64 */ PF_RT_SRC_NODES = 71, /* u64 */ PF_RT_ANCHOR_CALL = 72, /* string */ }; 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 */ }; enum pf_getrules_type_t { PF_GR_UNSPEC, PF_GR_ANCHOR = 1, /* string */ PF_GR_ACTION = 2, /* u8 */ PF_GR_NR = 3, /* u32 */ PF_GR_TICKET = 4, /* u32 */ PF_GR_CLEAR = 5, /* u8 */ }; enum pf_clear_states_type_t { PF_CS_UNSPEC, PF_CS_CMP_ID = 1, /* u64 */ PF_CS_CMP_CREATORID = 2, /* u32 */ PF_CS_CMP_DIR = 3, /* u8 */ PF_CS_AF = 4, /* u8 */ PF_CS_PROTO = 5, /* u8 */ PF_CS_SRC = 6, /* nested, pf_addr_wrap */ PF_CS_DST = 7, /* nested, pf_addr_wrap */ PF_CS_RT_ADDR = 8, /* nested, pf_addr_wrap */ PF_CS_IFNAME = 9, /* string */ PF_CS_LABEL = 10, /* string */ PF_CS_KILL_MATCH = 11, /* bool */ PF_CS_NAT = 12, /* bool */ PF_CS_KILLED = 13, /* u32 */ }; enum pf_set_statusif_types_t { PF_SS_UNSPEC, PF_SS_IFNAME = 1, /* string */ }; enum pf_counter_types_t { PF_C_UNSPEC, PF_C_COUNTER = 1, /* u64 */ PF_C_NAME = 2, /* string */ PF_C_ID = 3, /* u32 */ }; enum pf_get_status_types_t { PF_GS_UNSPEC, PF_GS_IFNAME = 1, /* string */ PF_GS_RUNNING = 2, /* bool */ PF_GS_SINCE = 3, /* u32 */ PF_GS_DEBUG = 4, /* u32 */ PF_GS_HOSTID = 5, /* u32 */ PF_GS_STATES = 6, /* u32 */ PF_GS_SRC_NODES = 7, /* u32 */ PF_GS_REASSEMBLE = 8, /* u32 */ PF_GS_SYNCOOKIES_ACTIVE = 9, /* bool */ PF_GS_COUNTERS = 10, /* nested, */ PF_GS_LCOUNTERS = 11, /* nested, */ PF_GS_FCOUNTERS = 12, /* nested, */ PF_GS_SCOUNTERS = 13, /* nested, */ PF_GS_CHKSUM = 14, /* byte array */ PF_GS_PCOUNTERS = 15, /* u64 array */ PF_GS_BCOUNTERS = 16, /* u64 array */ }; +enum pf_natlook_types_t { + PF_NL_UNSPEC, + PF_NL_AF = 1, /* u8 */ + PF_NL_DIRECTION = 2, /* u8 */ + PF_NL_PROTO = 3, /* u8 */ + PF_NL_SRC_ADDR = 4, /* in6_addr */ + PF_NL_DST_ADDR = 5, /* in6_addr */ + PF_NL_SRC_PORT = 6, /* u16 */ + PF_NL_DST_PORT = 7, /* u16 */ +}; + #ifdef _KERNEL void pf_nl_register(void); void pf_nl_unregister(void); #endif #endif