The branch main has been updated by ks: URL: https://cgit.FreeBSD.org/src/commit/?id=d2761422eb0a811976390743921b3f15e9d8331a
commit d2761422eb0a811976390743921b3f15e9d8331a Author: Kajetan Staszkiewicz <k...@freebsd.org> AuthorDate: 2025-07-31 14:28:30 +0000 Commit: Kajetan Staszkiewicz <k...@freebsd.org> CommitDate: 2025-08-01 10:11:30 +0000 pf: Use different address family for source and redirection address The function pf_map_addr() and source tracking operate on a single address family. This made sense before introducing address family translation. When combining af-to with route-to or with sticky-address, the next-hop or the NAT address are of different address family than the source address. For example in NAT64 scenaro an IPv6 source address is translated to an IPv4 address and routed over IPv4 gateway. Make source nodes dual-AF, that is have a separate source AF and redirection AF. Store route AF in struct pf_kstate, export it to pfctl. When loading rules with redirection pools with pfctl store address family of each address. When printing states don't deduce next-hop's address family from af-to, use the one stored in state. Reviewed by: kp Approved by: kp Sponsored by: InnoGames GmbH Differential Revision: https://reviews.freebsd.org/D51659 --- lib/libpfctl/libpfctl.c | 3 +- lib/libpfctl/libpfctl.h | 16 ++++- sbin/pfctl/parse.y | 14 ++-- sbin/pfctl/pf_print_state.c | 13 ++-- sbin/pfctl/pfctl.c | 25 +++---- sbin/pfctl/pfctl_parser.c | 28 +++----- sbin/pfctl/pfctl_parser.h | 4 +- sys/net/pfvar.h | 15 ++-- sys/netpfil/pf/if_pfsync.c | 12 ++++ sys/netpfil/pf/pf.c | 21 +++--- sys/netpfil/pf/pf_ioctl.c | 2 + sys/netpfil/pf/pf_lb.c | 139 +++++++++++++++++++++++--------------- sys/netpfil/pf/pf_nl.c | 5 +- sys/netpfil/pf/pf_nl.h | 3 +- tests/sys/netpfil/pf/nat64.sh | 9 +++ tests/sys/netpfil/pf/src_track.sh | 61 +++++++++++++++++ 16 files changed, 252 insertions(+), 118 deletions(-) diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c index d8e60075e103..104777352d8b 100644 --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -1940,6 +1940,7 @@ static struct snl_attr_parser ap_state[] = { { .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 }, { .type = PF_ST_SRC_NODE_FLAGS, .off = _OUT(src_node_flags), .cb = snl_attr_get_uint8 }, + { .type = PF_ST_RT_AF, .off = _OUT(rt_af), .cb = snl_attr_get_uint8 }, }; #undef _IN #undef _OUT @@ -3043,7 +3044,7 @@ static struct snl_attr_parser ap_srcnode[] = { { .type = PF_SN_CREATION, .off = _OUT(creation), .cb = snl_attr_get_uint64 }, { .type = PF_SN_EXPIRE, .off = _OUT(expire), .cb = snl_attr_get_uint64 }, { .type = PF_SN_CONNECTION_RATE, .off = _OUT(conn_rate), .arg = &pfctl_threshold_parser, .cb = snl_attr_get_nested }, - { .type = PF_SN_NAF, .off = _OUT(naf), .cb = snl_attr_get_uint8 }, + { .type = PF_SN_RAF, .off = _OUT(raf), .cb = snl_attr_get_uint8 }, { .type = PF_SN_NODE_TYPE, .off = _OUT(type), .cb = snl_attr_get_uint8 }, }; #undef _OUT diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h index 98a80758ca74..116f9243fda9 100644 --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -143,9 +143,18 @@ struct pfctl_eth_anchor { int match; /* XXX: used for pfctl black magic */ }; +struct pfctl_pooladdr { + struct pf_addr_wrap addr; + TAILQ_ENTRY(pfctl_pooladdr) entries; + char ifname[IFNAMSIZ]; + sa_family_t af; +}; + +TAILQ_HEAD(pfctl_palist, pfctl_pooladdr); + struct pfctl_pool { - struct pf_palist list; - struct pf_pooladdr *cur; + struct pfctl_palist list; + struct pfctl_pooladdr *cur; struct pf_poolhashkey key; struct pf_addr counter; struct pf_mape_portset mape; @@ -383,6 +392,7 @@ struct pfctl_state { uint8_t set_prio[2]; uint8_t rt; char rt_ifname[IFNAMSIZ]; + sa_family_t rt_af; uint8_t src_node_flags; }; @@ -414,7 +424,7 @@ struct pfctl_src_node { uint32_t states; uint32_t conn; sa_family_t af; - sa_family_t naf; + sa_family_t raf; uint8_t ruletype; uint64_t creation; uint64_t expire; diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index ddb0f5dc6565..00c36b218055 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -4852,7 +4852,7 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag tagged rtable binat_redirspec { struct pfctl_rule binat; - struct pf_pooladdr *pa; + struct pfctl_pooladdr *pa; if (check_rulestate(PFCTL_STATE_NAT)) YYERROR; @@ -5011,11 +5011,12 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag YYERROR; } - pa = calloc(1, sizeof(struct pf_pooladdr)); + pa = calloc(1, sizeof(struct pfctl_pooladdr)); if (pa == NULL) err(1, "binat: calloc"); pa->addr = $13->host->addr; pa->ifname[0] = 0; + pa->af = $13->host->af; TAILQ_INSERT_TAIL(&binat.rdr.list, pa, entries); @@ -6115,7 +6116,7 @@ int apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs) { struct node_host *h; - struct pf_pooladdr *pa; + struct pfctl_pooladdr *pa; if (rs == NULL) return 0; @@ -6155,10 +6156,11 @@ apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs) sizeof(struct pf_poolhashkey)); for (h = rs->host; h != NULL; h = h->next) { - pa = calloc(1, sizeof(struct pf_pooladdr)); + pa = calloc(1, sizeof(struct pfctl_pooladdr)); if (pa == NULL) err(1, "%s: calloc", __func__); pa->addr = h->addr; + pa->af = h->af; if (h->ifname != NULL) { if (strlcpy(pa->ifname, h->ifname, sizeof(pa->ifname)) >= sizeof(pa->ifname)) @@ -6175,7 +6177,7 @@ int check_binat_redirspec(struct node_host *src_host, struct pfctl_rule *r, sa_family_t af) { - struct pf_pooladdr *nat_pool = TAILQ_FIRST(&(r->nat.list)); + struct pfctl_pooladdr *nat_pool = TAILQ_FIRST(&(r->nat.list)); int error = 0; /* XXX: FreeBSD allows syntax like "{ host1 host2 }" for redirection @@ -6248,7 +6250,7 @@ add_binat_rdr_rule( /* * We're copying the whole rule, but we must re-init redir pools. - * FreeBSD uses lists of pf_pooladdr, we can't just overwrite them. + * FreeBSD uses lists of pfctl_pooladdr, we can't just overwrite them. */ bcopy(binat_rule, rdr_rule, sizeof(struct pfctl_rule)); TAILQ_INIT(&(rdr_rule->rdr.list)); diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c index 5abdc2de419d..417ff70de975 100644 --- a/sbin/pfctl/pf_print_state.c +++ b/sbin/pfctl/pf_print_state.c @@ -113,10 +113,11 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose) if (addr->type != PF_ADDR_RANGE && !(PF_AZERO(&addr->v.a.addr, AF_INET6) && PF_AZERO(&addr->v.a.mask, AF_INET6))) { - int bits = unmask(&addr->v.a.mask); - - if (bits < (af == AF_INET ? 32 : 128)) - printf("/%d", bits); + if (af == AF_INET || af == AF_INET6) { + int bits = unmask(&addr->v.a.mask); + if (bits < (af == AF_INET ? 32 : 128)) + printf("/%d", bits); + } } } @@ -228,7 +229,6 @@ print_state(struct pfctl_state *s, int opts) struct pfctl_state_key *key, *sk, *nk; const char *protoname; int min, sec; - sa_family_t af; uint8_t proto; int afto = (s->key[PF_SK_STACK].af != s->key[PF_SK_WIRE].af); int idx; @@ -242,7 +242,6 @@ print_state(struct pfctl_state *s, int opts) key = s->key; #endif - af = s->key[PF_SK_WIRE].af; proto = s->key[PF_SK_WIRE].proto; if (s->direction == PF_OUT) { @@ -430,7 +429,7 @@ print_state(struct pfctl_state *s, int opts) default: printf(" gateway: "); } - print_host(&s->rt_addr, 0, af, opts); + print_host(&s->rt_addr, 0, s->rt_af, opts); if (s->rt_ifname[0]) printf("@%s", s->rt_ifname); } diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index 2015e0a09549..9aa50a73ba04 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -1038,7 +1038,7 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr, u_int32_t ticket, int r_action, const char *anchorname, int which) { struct pfioc_pooladdr pp; - struct pf_pooladdr *pa; + struct pfctl_pooladdr *pa; u_int32_t pnr, mpnr; int ret; @@ -1054,10 +1054,11 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr, warnc(ret, "DIOCGETADDR"); return (-1); } - pa = calloc(1, sizeof(struct pf_pooladdr)); + pa = calloc(1, sizeof(struct pfctl_pooladdr)); if (pa == NULL) err(1, "calloc"); - bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr)); + bcopy(&pp.addr, pa, sizeof(struct pfctl_pooladdr)); + pa->af = pp.af; TAILQ_INSERT_TAIL(&pool->list, pa, entries); } @@ -1067,7 +1068,7 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr, void pfctl_move_pool(struct pfctl_pool *src, struct pfctl_pool *dst) { - struct pf_pooladdr *pa; + struct pfctl_pooladdr *pa; while ((pa = TAILQ_FIRST(&src->list)) != NULL) { TAILQ_REMOVE(&src->list, pa, entries); @@ -1078,7 +1079,7 @@ pfctl_move_pool(struct pfctl_pool *src, struct pfctl_pool *dst) void pfctl_clear_pool(struct pfctl_pool *pool) { - struct pf_pooladdr *pa; + struct pfctl_pooladdr *pa; while ((pa = TAILQ_FIRST(&pool->list)) != NULL) { TAILQ_REMOVE(&pool->list, pa, entries); @@ -1794,14 +1795,14 @@ pfctl_show_creators(int opts) /* callbacks for rule/nat/rdr/addr */ int -pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, sa_family_t af, int which) +pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, int which) { - struct pf_pooladdr *pa; + struct pfctl_pooladdr *pa; int ret; - pf->paddr.af = af; TAILQ_FOREACH(pa, &p->list, entries) { - memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr)); + memcpy(&pf->paddr.addr, pa, sizeof(struct pfctl_pooladdr)); + pf->paddr.af = pa->af; if ((pf->opts & PF_OPT_NOACTION) == 0) { if ((ret = pfctl_add_addr(pf->h, &pf->paddr, which)) != 0) errc(1, ret, "DIOCADDADDR"); @@ -2165,11 +2166,11 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth) errc(1, error, "DIOCBEGINADDRS"); } - if (pfctl_add_pool(pf, &r->rdr, r->af, PF_RDR)) + if (pfctl_add_pool(pf, &r->rdr, PF_RDR)) return (1); - if (pfctl_add_pool(pf, &r->nat, r->naf ? r->naf : r->af, PF_NAT)) + if (pfctl_add_pool(pf, &r->nat, PF_NAT)) return (1); - if (pfctl_add_pool(pf, &r->route, r->af, PF_RT)) + if (pfctl_add_pool(pf, &r->route, PF_RT)) return (1); error = pfctl_add_rule_h(pf->h, r, anchor, name, ticket, pf->paddr.ticket); diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index f2eb75135609..18b78a150c28 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -429,10 +429,9 @@ print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst, } void -print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2, - sa_family_t af, int id) +print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2, int id) { - struct pf_pooladdr *pooladdr; + struct pfctl_pooladdr *pooladdr; if ((TAILQ_FIRST(&pool->list) != NULL) && TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL) @@ -442,15 +441,15 @@ print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2, case PF_NAT: case PF_RDR: case PF_BINAT: - print_addr(&pooladdr->addr, af, 0); + print_addr(&pooladdr->addr, pooladdr->af, 0); break; case PF_PASS: case PF_MATCH: - if (PF_AZERO(&pooladdr->addr.v.a.addr, af)) + if (PF_AZERO(&pooladdr->addr.v.a.addr, pooladdr->af)) printf("%s", pooladdr->ifname); else { printf("(%s ", pooladdr->ifname); - print_addr(&pooladdr->addr, af, 0); + print_addr(&pooladdr->addr, pooladdr->af, 0); printf(")"); } break; @@ -674,7 +673,7 @@ print_src_node(struct pfctl_src_node *sn, int opts) print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2); printf(" -> "); aw.v.a.addr = sn->raddr; - print_addr(&aw, sn->naf ? sn->naf : sn->af, opts & PF_OPT_VERBOSE2); + print_addr(&aw, sn->raf, opts & PF_OPT_VERBOSE2); printf(" ( states %u, connections %u, rate %u.%u/%us )\n", sn->states, sn->conn, sn->conn_rate.count / 1000, (sn->conn_rate.count % 1000) / 100, sn->conn_rate.seconds); @@ -952,10 +951,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer else if (r->rt == PF_DUPTO) printf(" dup-to"); printf(" "); - print_pool(&r->rdr, 0, 0, r->af, PF_PASS); - print_pool(&r->route, 0, 0, - r->rule_flag & PFRULE_AFTO && r->rt != PF_REPLYTO ? r->naf : r->af, - PF_PASS); + print_pool(&r->route, 0, 0, PF_PASS); } if (r->af) { if (r->af == AF_INET) @@ -1252,17 +1248,16 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer if (r->action == PF_NAT || r->action == PF_BINAT || r->action == PF_RDR) { printf(" -> "); print_pool(&r->rdr, r->rdr.proxy_port[0], - r->rdr.proxy_port[1], r->af, r->action); + r->rdr.proxy_port[1], r->action); } else { if (!TAILQ_EMPTY(&r->nat.list)) { if (r->rule_flag & PFRULE_AFTO) { - printf(" af-to %s from ", r->naf == AF_INET ? "inet" : "inet6"); + printf(" af-to %s from ", r->naf == AF_INET ? "inet" : (r->naf == AF_INET6 ? "inet6" : "? ")); } else { printf(" nat-to "); } print_pool(&r->nat, r->nat.proxy_port[0], - r->nat.proxy_port[1], r->naf ? r->naf : r->af, - PF_NAT); + r->nat.proxy_port[1], PF_NAT); } if (!TAILQ_EMPTY(&r->rdr.list)) { if (r->rule_flag & PFRULE_AFTO) { @@ -1271,8 +1266,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer printf(" rdr-to "); } print_pool(&r->rdr, r->rdr.proxy_port[0], - r->rdr.proxy_port[1], r->naf ? r->naf : r->af, - PF_RDR); + r->rdr.proxy_port[1], PF_RDR); } } } diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index 7a3c0c2a523f..721950967661 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -286,7 +286,7 @@ int pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *); int pfctl_append_eth_rule(struct pfctl *, struct pfctl_eth_rule *, const char *); int pfctl_add_altq(struct pfctl *, struct pf_altq *); -int pfctl_add_pool(struct pfctl *, struct pfctl_pool *, sa_family_t, int); +int pfctl_add_pool(struct pfctl *, struct pfctl_pool *, int); void pfctl_move_pool(struct pfctl_pool *, struct pfctl_pool *); void pfctl_clear_pool(struct pfctl_pool *); @@ -304,7 +304,7 @@ int parse_config(char *, struct pfctl *); int parse_flags(char *); int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *); -void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, sa_family_t, int); +void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, int); void print_src_node(struct pfctl_src_node *, int); void print_eth_rule(struct pfctl_eth_rule *, const char *, int); void print_rule(struct pfctl_rule *, const char *, int, int); diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index d55afe750869..8dae95c2cc2e 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -629,6 +629,7 @@ struct pf_kpooladdr { struct pf_addr_wrap addr; TAILQ_ENTRY(pf_kpooladdr) entries; char ifname[IFNAMSIZ]; + sa_family_t af; struct pfi_kkif *kif; }; @@ -656,6 +657,7 @@ struct pf_rule_actions { uint16_t max_mss; uint16_t dnpipe; uint16_t dnrpipe; /* Reverse direction pipe */ + sa_family_t rt_af; uint8_t log; uint8_t set_tos; uint8_t min_ttl; @@ -911,7 +913,7 @@ struct pf_ksrc_node { u_int32_t creation; u_int32_t expire; sa_family_t af; - sa_family_t naf; + sa_family_t raf; u_int8_t ruletype; pf_sn_types_t type; struct mtx *lock; @@ -2717,14 +2719,15 @@ int pf_step_out_of_keth_anchor(struct pf_keth_anchor_stackframe *, struct pf_keth_rule **, struct pf_keth_rule **, int *); -u_short pf_map_addr(u_int8_t, struct pf_krule *, +u_short pf_map_addr(sa_family_t, struct pf_krule *, struct pf_addr *, struct pf_addr *, - struct pfi_kkif **nkif, struct pf_addr *, - struct pf_kpool *); + struct pfi_kkif **nkif, sa_family_t *, + struct pf_addr *, struct pf_kpool *); u_short pf_map_addr_sn(u_int8_t, struct pf_krule *, struct pf_addr *, struct pf_addr *, - struct pfi_kkif **nkif, struct pf_addr *, - struct pf_kpool *, pf_sn_types_t); + sa_family_t *, struct pfi_kkif **nkif, + struct pf_addr *, struct pf_kpool *, + pf_sn_types_t); int pf_get_transaddr_af(struct pf_krule *, struct pf_pdesc *); u_short pf_get_translation(struct pf_test_ctx *); diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c index ee10a997c977..e34c08c8c4db 100644 --- a/sys/netpfil/pf/if_pfsync.c +++ b/sys/netpfil/pf/if_pfsync.c @@ -529,6 +529,7 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version) struct pfi_kkif *rt_kif = NULL; struct pf_kpooladdr *rpool_first; int error; + sa_family_t rt_af = 0; uint8_t rt = 0; int n = 0; @@ -602,6 +603,11 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version) } rt = r->rt; rt_kif = rpool_first->kif; + /* + * Guess the AF of the route address, FreeBSD 13 does + * not support af-to so it should be safe. + */ + rt_af = r->af; } else if (!PF_AZERO(&sp->pfs_1301.rt_addr, sp->pfs_1301.af)) { /* * Ruleset different, routing *supposedly* requested, @@ -627,6 +633,11 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version) return ((flags & PFSYNC_SI_IOCTL) ? EINVAL : 0); } rt = sp->pfs_1400.rt; + /* + * Guess the AF of the route address, FreeBSD 13 does + * not support af-to so it should be safe. + */ + rt_af = sp->pfs_1400.af; } break; } @@ -706,6 +717,7 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version) st->act.rt = rt; st->act.rt_kif = rt_kif; + st->act.rt_af = rt_af; switch (msg_version) { case PFSYNC_MSG_VERSION_1301: diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 79c298c18b46..f6ee02626624 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -401,7 +401,7 @@ static void pf_overload_task(void *v, int pending); static u_short pf_insert_src_node(struct pf_ksrc_node *[PF_SN_MAX], struct pf_srchash *[PF_SN_MAX], struct pf_krule *, struct pf_addr *, sa_family_t, struct pf_addr *, - struct pfi_kkif *, pf_sn_types_t); + struct pfi_kkif *, sa_family_t, pf_sn_types_t); static u_int pf_purge_expired_states(u_int, int); static void pf_purge_unlinked_rules(void); static int pf_mtag_uminit(void *, int, int); @@ -1017,7 +1017,7 @@ static u_short pf_insert_src_node(struct pf_ksrc_node *sns[PF_SN_MAX], struct pf_srchash *snhs[PF_SN_MAX], struct pf_krule *rule, struct pf_addr *src, sa_family_t af, struct pf_addr *raddr, - struct pfi_kkif *rkif, pf_sn_types_t sn_type) + struct pfi_kkif *rkif, sa_family_t raf, pf_sn_types_t sn_type) { u_short reason = 0; struct pf_krule *r_track = rule; @@ -1089,8 +1089,9 @@ pf_insert_src_node(struct pf_ksrc_node *sns[PF_SN_MAX], (*sn)->rule = r_track; pf_addrcpy(&(*sn)->addr, src, af); if (raddr != NULL) - pf_addrcpy(&(*sn)->raddr, raddr, af); + pf_addrcpy(&(*sn)->raddr, raddr, raf); (*sn)->rkif = rkif; + (*sn)->raf = raf; LIST_INSERT_HEAD(&(*sh)->nodes, *sn, entry); (*sn)->creation = time_uptime; (*sn)->ruletype = rule->action; @@ -5907,9 +5908,13 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, * it is applied only from the last pass rule. */ pd->act.rt = r->rt; + if (r->rt == PF_REPLYTO) + pd->act.rt_af = pd->af; + else + pd->act.rt_af = pd->naf; if ((transerror = pf_map_addr_sn(pd->af, r, pd->src, - &pd->act.rt_addr, &pd->act.rt_kif, NULL, &(r->route), - PF_SN_ROUTE)) != PFRES_MATCH) { + &pd->act.rt_addr, &pd->act.rt_af, &pd->act.rt_kif, NULL, + &(r->route), PF_SN_ROUTE)) != PFRES_MATCH) { REASON_SET(&ctx.reason, transerror); goto cleanup; } @@ -6039,7 +6044,7 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx, /* src node for limits */ if ((r->rule_flag & PFRULE_SRCTRACK) && (sn_reason = pf_insert_src_node(sns, snhs, r, pd->src, pd->af, - NULL, NULL, PF_SN_LIMIT)) != 0) { + NULL, NULL, pd->af, PF_SN_LIMIT)) != 0) { REASON_SET(&ctx->reason, sn_reason); goto csfailed; } @@ -6047,7 +6052,7 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx, if (r->rt) { if ((r->route.opts & PF_POOL_STICKYADDR) && (sn_reason = pf_insert_src_node(sns, snhs, r, pd->src, - pd->af, &pd->act.rt_addr, pd->act.rt_kif, + pd->af, &pd->act.rt_addr, pd->act.rt_kif, pd->act.rt_af, PF_SN_ROUTE)) != 0) { REASON_SET(&ctx->reason, sn_reason); goto csfailed; @@ -6066,7 +6071,7 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx, (sn_reason = pf_insert_src_node(sns, snhs, ctx->nr, ctx->sk ? &(ctx->sk->addr[pd->sidx]) : pd->src, pd->af, ctx->nk ? &(ctx->nk->addr[1]) : &(pd->nsaddr), NULL, - PF_SN_NAT)) != 0 ) { + pd->naf, PF_SN_NAT)) != 0 ) { REASON_SET(&ctx->reason, sn_reason); goto csfailed; } diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 9abc07c36788..178ee01649c6 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -2661,6 +2661,7 @@ pf_ioctl_add_addr(struct pf_nl_pooladdr *pp) PF_RULES_WUNLOCK(); goto out; } + pa->af = pp->af; switch (pp->which) { case PF_NAT: TAILQ_INSERT_TAIL(&V_pf_pabuf[0], pa, entries); @@ -2742,6 +2743,7 @@ pf_ioctl_get_addr(struct pf_nl_pooladdr *pp) return (EBUSY); } pf_kpooladdr_to_pooladdr(pa, &pp->addr); + pp->af = pa->af; pf_addr_copyout(&pp->addr.addr); PF_RULES_RUNLOCK(); diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c index ea0d6facf695..bc9e1dc72902 100644 --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -345,8 +345,8 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r, struct pf_addr *naddr, } } - if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL, &init_addr, - rpool, sn_type)) + if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, &(pd->naf), NULL, + &init_addr, rpool, sn_type)) goto failed; if (pd->proto == IPPROTO_ICMP) { @@ -470,8 +470,8 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r, struct pf_addr *naddr, * pick a different source address since we're out * of free port choices for the current one. */ - if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL, - &init_addr, rpool, sn_type)) + if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, + &(pd->naf), NULL, &init_addr, rpool, sn_type)) return (1); break; case PF_POOL_NONE: @@ -501,8 +501,8 @@ pf_islinklocal(const sa_family_t af, const struct pf_addr *addr) static int pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r, - struct pf_addr *naddr, uint16_t *nport, - struct pf_udp_mapping **udp_mapping, struct pf_kpool *rpool) + struct pf_addr *naddr, uint16_t *nport, struct pf_udp_mapping **udp_mapping, + struct pf_kpool *rpool) { uint16_t psmask, low, highmask; uint16_t i, ahigh, cut; @@ -535,15 +535,22 @@ pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r, } u_short -pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, - struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr, - struct pf_kpool *rpool) +pf_map_addr(sa_family_t saf, struct pf_krule *r, struct pf_addr *saddr, + struct pf_addr *naddr, struct pfi_kkif **nkif, sa_family_t *naf, + struct pf_addr *init_addr, struct pf_kpool *rpool) { u_short reason = PFRES_MATCH; struct pf_addr *raddr = NULL, *rmask = NULL; struct pfr_ktable *kt; uint64_t hashidx; int cnt; + sa_family_t wanted_af; + + KASSERT(saf != 0, ("%s: saf == 0", __func__)); + KASSERT(naf != NULL, ("%s: naf = NULL", __func__)); + KASSERT((*naf) != 0, ("%s: *naf = 0", __func__)); + + wanted_af = (*naf); mtx_lock(&rpool->mtx); /* Find the route using chosen algorithm. Store the found route @@ -553,7 +560,7 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, goto done_pool_mtx; } if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { - switch (af) { + switch (wanted_af) { #ifdef INET case AF_INET: if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 && @@ -577,7 +584,7 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, break; #endif /* INET6 */ default: - unhandled_af(af); + unhandled_af(wanted_af); } } else if (rpool->cur->addr.type == PF_ADDR_TABLE) { if (!PF_POOL_DYNTYPE(rpool->opts)) { @@ -587,14 +594,24 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, } else { raddr = &rpool->cur->addr.v.a.addr; rmask = &rpool->cur->addr.v.a.mask; + /* + * For single addresses check their address family. Unless they + * have none, which happens when addresses are added with + * the old ioctl mechanism. In such case trust that the address + * has the proper AF. + */ + if (rpool->cur->af && rpool->cur->af != wanted_af) { + reason = PFRES_MAPFAILED; + goto done_pool_mtx; + } } switch (rpool->opts & PF_POOL_TYPEMASK) { case PF_POOL_NONE: - pf_addrcpy(naddr, raddr, af); + pf_addrcpy(naddr, raddr, wanted_af); break; case PF_POOL_BITMASK: - pf_poolmask(naddr, raddr, rmask, saddr, af); + pf_poolmask(naddr, raddr, rmask, saddr, wanted_af); break; case PF_POOL_RANDOM: if (rpool->cur->addr.type == PF_ADDR_TABLE || @@ -615,13 +632,14 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, rpool->tblidx = (int)arc4random_uniform(cnt); memset(&rpool->counter, 0, sizeof(rpool->counter)); if (pfr_pool_get(kt, &rpool->tblidx, &rpool->counter, - af, pf_islinklocal, false)) { + wanted_af, pf_islinklocal, false)) { reason = PFRES_MAPFAILED; goto done_pool_mtx; /* unsupported */ } - pf_addrcpy(naddr, &rpool->counter, af); - } else if (init_addr != NULL && PF_AZERO(init_addr, af)) { - switch (af) { + pf_addrcpy(naddr, &rpool->counter, wanted_af); + } else if (init_addr != NULL && PF_AZERO(init_addr, + wanted_af)) { + switch (wanted_af) { #ifdef INET case AF_INET: rpool->counter.addr32[0] = arc4random(); @@ -650,12 +668,14 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, break; #endif /* INET6 */ } - pf_poolmask(naddr, raddr, rmask, &rpool->counter, af); - pf_addrcpy(init_addr, naddr, af); + pf_poolmask(naddr, raddr, rmask, &rpool->counter, + wanted_af); + pf_addrcpy(init_addr, naddr, wanted_af); } else { - pf_addr_inc(&rpool->counter, af); - pf_poolmask(naddr, raddr, rmask, &rpool->counter, af); + pf_addr_inc(&rpool->counter, wanted_af); + pf_poolmask(naddr, raddr, rmask, &rpool->counter, + wanted_af); } break; case PF_POOL_SRCHASH: @@ -663,7 +683,8 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, unsigned char hash[16]; hashidx = - pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af); + pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, + wanted_af); if (rpool->cur->addr.type == PF_ADDR_TABLE || rpool->cur->addr.type == PF_ADDR_DYNIFTL) { if (rpool->cur->addr.type == PF_ADDR_TABLE) @@ -682,14 +703,14 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, rpool->tblidx = (int)(hashidx % cnt); memset(&rpool->counter, 0, sizeof(rpool->counter)); if (pfr_pool_get(kt, &rpool->tblidx, &rpool->counter, - af, pf_islinklocal, false)) { + wanted_af, pf_islinklocal, false)) { reason = PFRES_MAPFAILED; goto done_pool_mtx; /* unsupported */ } - pf_addrcpy(naddr, &rpool->counter, af); + pf_addrcpy(naddr, &rpool->counter, wanted_af); } else { pf_poolmask(naddr, raddr, rmask, - (struct pf_addr *)&hash, af); + (struct pf_addr *)&hash, wanted_af); } break; } @@ -699,14 +720,16 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, if (rpool->cur->addr.type == PF_ADDR_TABLE) { if (!pfr_pool_get(rpool->cur->addr.p.tbl, - &rpool->tblidx, &rpool->counter, af, NULL, true)) + &rpool->tblidx, &rpool->counter, wanted_af, + NULL, true)) goto get_addr; } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, - &rpool->tblidx, &rpool->counter, af, pf_islinklocal, - true)) + &rpool->tblidx, &rpool->counter, wanted_af, + pf_islinklocal, true)) goto get_addr; - } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af)) + } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, + wanted_af)) goto get_addr; try_next: @@ -717,8 +740,9 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, rpool->tblidx = -1; if (rpool->cur->addr.type == PF_ADDR_TABLE) { if (pfr_pool_get(rpool->cur->addr.p.tbl, - &rpool->tblidx, &rpool->counter, af, NULL, true)) { - /* table contains no address of type 'af' */ + &rpool->tblidx, &rpool->counter, wanted_af, NULL, + true)) { + /* table contains no address of type 'wanted_af' */ if (rpool->cur != acur) goto try_next; reason = PFRES_MAPFAILED; @@ -726,9 +750,9 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, } } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, - &rpool->tblidx, &rpool->counter, af, pf_islinklocal, - true)) { - /* table contains no address of type 'af' */ + &rpool->tblidx, &rpool->counter, wanted_af, + pf_islinklocal, true)) { + /* interface has no address of type 'wanted_af' */ if (rpool->cur != acur) goto try_next; reason = PFRES_MAPFAILED; @@ -737,14 +761,18 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, } else { raddr = &rpool->cur->addr.v.a.addr; rmask = &rpool->cur->addr.v.a.mask; - pf_addrcpy(&rpool->counter, raddr, af); + if (rpool->cur->af && rpool->cur->af != wanted_af) { + reason = PFRES_MAPFAILED; + goto done_pool_mtx; + } + pf_addrcpy(&rpool->counter, raddr, wanted_af); } get_addr: - pf_addrcpy(naddr, &rpool->counter, af); - if (init_addr != NULL && PF_AZERO(init_addr, af)) - pf_addrcpy(init_addr, naddr, af); - pf_addr_inc(&rpool->counter, af); + pf_addrcpy(naddr, &rpool->counter, wanted_af); + if (init_addr != NULL && PF_AZERO(init_addr, wanted_af)) + pf_addrcpy(init_addr, naddr, wanted_af); + pf_addr_inc(&rpool->counter, wanted_af); break; } } @@ -759,9 +787,9 @@ done_pool_mtx: } u_short -pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, - struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr, - struct pf_kpool *rpool, pf_sn_types_t sn_type) +pf_map_addr_sn(sa_family_t saf, struct pf_krule *r, struct pf_addr *saddr, + struct pf_addr *naddr, sa_family_t *naf, struct pfi_kkif **nkif, + struct pf_addr *init_addr, struct pf_kpool *rpool, pf_sn_types_t sn_type) { struct pf_ksrc_node *sn = NULL; struct pf_srchash *sh = NULL; @@ -772,27 +800,31 @@ pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, */ if (rpool->opts & PF_POOL_STICKYADDR && (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) - sn = pf_find_src_node(saddr, r, af, &sh, sn_type, false); + sn = pf_find_src_node(saddr, r, saf, &sh, sn_type, false); if (sn != NULL) { PF_SRC_NODE_LOCK_ASSERT(sn); + (*naf) = sn->raf; /* If the supplied address is the same as the current one we've * been asked before, so tell the caller that there's no other * address to be had. */ - if (PF_AEQ(naddr, &(sn->raddr), af)) { + + if (PF_AEQ(naddr, &(sn->raddr), *naf)) { + printf("%s: no more addresses\n", __func__); reason = PFRES_MAPFAILED; goto done; } - pf_addrcpy(naddr, &(sn->raddr), af); + pf_addrcpy(naddr, &(sn->raddr), *naf); + if (nkif) *nkif = sn->rkif; if (V_pf_status.debug >= PF_DEBUG_NOISY) { printf("%s: src tracking maps ", __func__); - pf_print_host(saddr, 0, af); + pf_print_host(saddr, 0, saf); printf(" to "); - pf_print_host(naddr, 0, af); + pf_print_host(naddr, 0, *naf); if (nkif) printf("@%s", (*nkif)->pfik_name); printf("\n"); @@ -804,7 +836,7 @@ pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, * Source node has not been found. Find a new address and store it * in variables given by the caller. */ - if ((reason = pf_map_addr(af, r, saddr, naddr, nkif, init_addr, + if ((reason = pf_map_addr(saf, r, saddr, naddr, nkif, naf, init_addr, rpool)) != 0) { if (V_pf_status.debug >= PF_DEBUG_MISC) printf("%s: pf_map_addr has failed\n", __func__); @@ -814,7 +846,7 @@ pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, if (V_pf_status.debug >= PF_DEBUG_NOISY && (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { printf("%s: selected address ", __func__); - pf_print_host(naddr, 0, af); + pf_print_host(naddr, 0, *naf); if (nkif) printf("@%s", (*nkif)->pfik_name); printf("\n"); @@ -996,8 +1028,9 @@ pf_get_transaddr(struct pf_test_ctx *ctx, struct pf_krule *r, int tries; uint16_t cut, low, high, nport; - reason = pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL, - NULL, rpool, PF_SN_NAT); + reason = pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, + &(pd->naf), NULL, NULL, rpool, PF_SN_NAT); + if (reason != 0) goto notrans; if ((rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK) @@ -1161,8 +1194,8 @@ pf_get_transaddr_af(struct pf_krule *r, struct pf_pdesc *pd) /* get the destination address and port */ if (! TAILQ_EMPTY(&r->rdr.list)) { - if (pf_map_addr_sn(pd->naf, r, &nsaddr, &naddr, NULL, NULL, - &r->rdr, PF_SN_NAT)) + if (pf_map_addr_sn(pd->naf, r, &nsaddr, &naddr, &(pd->naf), + NULL, NULL, &r->rdr, PF_SN_NAT)) return (-1); if (r->rdr.proxy_port[0]) pd->ndport = htons(r->rdr.proxy_port[0]); diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c index 73933c022ca2..c5de1e84a287 100644 --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -178,7 +178,7 @@ dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s, 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->act.rt_addr, af); + dump_addr(nw, PF_ST_RT_ADDR, &s->act.rt_addr, s->act.rt_af); nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - (s->creation / 1000)); uint32_t expire = pf_state_expires(s); if (expire > time_uptime) @@ -224,6 +224,7 @@ dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s, if (s->sns[PF_SN_ROUTE] != NULL) src_node_flags |= PFSTATE_SRC_NODE_ROUTE; nlattr_add_u8(nw, PF_ST_SRC_NODE_FLAGS, src_node_flags); + nlattr_add_u8(nw, PF_ST_RT_AF, s->act.rt_af); if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src)) goto enomem; @@ -1761,7 +1762,7 @@ pf_handle_get_srcnodes(struct nlmsghdr *hdr, struct nl_pstate *npt) nlattr_add_u32(nw, PF_SN_STATES, n->states); nlattr_add_u32(nw, PF_SN_CONNECTIONS, n->conn); nlattr_add_u8(nw, PF_SN_AF, n->af); - nlattr_add_u8(nw, PF_SN_NAF, n->naf); + nlattr_add_u8(nw, PF_SN_RAF, n->raf); nlattr_add_u8(nw, PF_SN_RULE_TYPE, n->ruletype); nlattr_add_u64(nw, PF_SN_CREATION, secs - n->creation); diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h index 929c20e4c582..d263a0b22deb 100644 --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -135,6 +135,7 @@ enum pfstate_type_t { PF_ST_RT = 36, /* u8 */ PF_ST_RT_IFNAME = 37, /* string */ PF_ST_SRC_NODE_FLAGS = 38, /* u8 */ + PF_ST_RT_AF = 39, /* u8 */ }; enum pf_addr_type_t { @@ -433,7 +434,7 @@ enum pf_srcnodes_types_t { PF_SN_CREATION = 12, /* u64 */ PF_SN_EXPIRE = 13, /* u64 */ PF_SN_CONNECTION_RATE = 14, /* nested, pf_threshold */ - PF_SN_NAF = 15, /* u8 */ + PF_SN_RAF = 15, /* u8 */ PF_SN_NODE_TYPE = 16, /* u8 */ *** 99 LINES SKIPPED ***