diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -143,9 +143,18 @@ 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 @@ 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 @@ 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/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -1940,6 +1940,7 @@ { .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 @@ { .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/sbin/pfctl/parse.y b/sbin/pfctl/parse.y --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -4852,7 +4852,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* * 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 --- a/sbin/pfctl/pf_print_state.c +++ b/sbin/pfctl/pf_print_state.c @@ -113,10 +113,11 @@ 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 @@ 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 @@ 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 @@ 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 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -1038,7 +1038,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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.h b/sbin/pfctl/pfctl_parser.h --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -286,7 +286,7 @@ 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_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/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -429,10 +429,9 @@ } 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 @@ 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_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 @@ 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 @@ 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 @@ 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/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -629,6 +629,7 @@ 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 @@ 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 @@ 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 @@ 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 --- a/sys/netpfil/pf/if_pfsync.c +++ b/sys/netpfil/pf/if_pfsync.c @@ -529,6 +529,7 @@ 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 @@ } 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 @@ 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 @@ 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 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -401,7 +401,7 @@ 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 @@ 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 @@ (*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 @@ * 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 @@ /* 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 @@ 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 @@ (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 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -2661,6 +2661,7 @@ 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 @@ 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 --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -345,8 +345,8 @@ } } - 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 @@ * 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ } 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,13 @@ 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 +667,12 @@ 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 +680,7 @@ 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 +699,14 @@ 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 +716,14 @@ 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, + &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 +734,8 @@ 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 +743,9 @@ } } 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, + &rpool->tblidx, &rpool->counter, wanted_af, pf_islinklocal, true)) { - /* table contains no address of type 'af' */ + /* table contains no address of type 'wanted_af' */ if (rpool->cur != acur) goto try_next; reason = PFRES_MAPFAILED; @@ -737,14 +754,18 @@ } 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 +780,9 @@ } 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 +793,31 @@ */ 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 +829,7 @@ * 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 +839,7 @@ 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 +1021,9 @@ 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 +1187,8 @@ /* 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.h b/sys/netpfil/pf/pf_nl.h --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -135,6 +135,7 @@ 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 @@ 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 */ }; diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -178,7 +178,7 @@ 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 @@ 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 @@ 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/tests/sys/netpfil/pf/nat64.sh b/tests/sys/netpfil/pf/nat64.sh --- a/tests/sys/netpfil/pf/nat64.sh +++ b/tests/sys/netpfil/pf/nat64.sh @@ -916,6 +916,15 @@ atf_check -s exit:0 -o ignore \ ping6 -c 3 64:ff9b::192.0.2.2 + + states=$(mktemp) || exit 1 + jexec rtr pfctl -qvvss | normalize_pfctl_s > $states + + for state_regexp in \ + "${epair}b ipv6-icmp 192.0.2.1:.* \(2001:db8::2\[[0-9]+\]\) -> 192.0.2.2:8 \(64:ff9b::c000:202\[[0-9]+\]\).*4:2 pkts.*route-to: 192.0.2.2@${epair_link}a" \ + ; do + grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" + done } route_to_cleanup() diff --git a/tests/sys/netpfil/pf/src_track.sh b/tests/sys/netpfil/pf/src_track.sh --- a/tests/sys/netpfil/pf/src_track.sh +++ b/tests/sys/netpfil/pf/src_track.sh @@ -503,6 +503,66 @@ pft_cleanup } +atf_test_case "mixed_af" "cleanup" +mixed_af_head() +{ + atf_set descr 'Test mixed address family source tracking' + atf_set require.user root +} + +mixed_af_body() +{ + setup_router_server_nat64 + + # Clients will connect from another network behind the router. + # This allows for using multiple source addresses. + jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester} + + jexec router pfctl -e + pft_set_rules router \ + "set reassemble yes" \ + "set state-policy if-bound" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server1}a ${net_server1_4_host_server}) \ + } sticky-address \ + inet6 proto tcp from any to 64:ff9b::/96 \ + af-to inet from ${net_clients_4}.0/${net_clients_4_mask} round-robin sticky-address" + + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a \ + --replyif ${epair_tester}a \ + --fromaddr 2001:db8:44::1 \ + --to 64:ff9b::192.0.2.100 \ + --ping-type=tcp3way \ + --send-sport=4201 + + states=$(mktemp) || exit 1 + jexec router pfctl -qvvss | normalize_pfctl_s > $states + nodes=$(mktemp) || exit 1 + jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes + + # States are checked for proper route-to information. + # The route-to gateway is IPv4. + for state_regexp in \ + "${epair_tester}b tcp 203.0.113.0:4201 \(2001:db8:44::1\[4201\]\) -> 192.0.2.100:9 \(64:ff9b::c000:264\[9\]\) .* route-to: 198.51.100.18@${epair_server1}a" \ + ; do + grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'" + done + + # Source nodes map IPv6 source address onto IPv4 gateway and IPv4 SNAT address. + for node_regexp in \ + '2001:db8:44::1 -> 203.0.113.0 .* states 1, .* NAT/RDR sticky-address' \ + '2001:db8:44::1 -> 198.51.100.18 .* states 1, .* route sticky-address' \ + ; do + grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'" + done +} + +mixed_af_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "source_track" @@ -512,4 +572,5 @@ atf_add_test_case "max_src_states_global" atf_add_test_case "sn_types_compat" atf_add_test_case "sn_types_pass" + atf_add_test_case "mixed_af" }