diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -4559,6 +4559,7 @@ netpfil/pf/pflow.c optional pflow pf inet netpfil/pf/pfsync_nv.c optional pfsync pf inet netpfil/pf/in4_cksum.c optional pf inet +netpfil/pf/inet_nat64.c optional pf inet netsmb/smb_conn.c optional netsmb netsmb/smb_crypt.c optional netsmb netsmb/smb_dev.c optional netsmb diff --git a/sys/modules/pf/Makefile b/sys/modules/pf/Makefile --- a/sys/modules/pf/Makefile +++ b/sys/modules/pf/Makefile @@ -2,7 +2,7 @@ KMOD= pf SRCS= pf.c pf_if.c pf_lb.c pf_osfp.c pf_ioctl.c pf_norm.c pf_table.c \ - pf_ruleset.c pf_nl.c pf_nv.c pf_syncookies.c in4_cksum.c \ + pf_ruleset.c pf_nl.c pf_nv.c pf_syncookies.c in4_cksum.c inet_nat64.c \ bus_if.h device_if.h \ opt_pf.h opt_inet.h opt_inet6.h opt_bpf.h opt_sctp.h opt_global.h \ opt_kern_tls.h diff --git a/sys/net/if_pflog.h b/sys/net/if_pflog.h --- a/sys/net/if_pflog.h +++ b/sys/net/if_pflog.h @@ -51,7 +51,9 @@ uid_t rule_uid; pid_t rule_pid; u_int8_t dir; - u_int8_t pad[3]; + u_int8_t pad1; /* rewritten, on OpenBSD */ + sa_family_t naf; + u_int8_t pad[1]; u_int32_t ridentifier; u_int8_t reserve; /* Appease broken software like Wireshark. */ u_int8_t pad2[3]; diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -864,6 +864,7 @@ u_int8_t flush; u_int8_t prio; u_int8_t set_prio[2]; + sa_family_t naf; struct { struct pf_addr addr; @@ -986,6 +987,10 @@ TAILQ_HEAD(, pf_kstate) states[2]; }; +#define PF_REVERSED_KEY(key, family) \ + ((key[PF_SK_WIRE]->af != key[PF_SK_STACK]->af) && \ + (key[PF_SK_WIRE]->af != (family))) + /* Keep synced with struct pf_kstate. */ struct pf_state_cmp { u_int64_t id; @@ -1630,6 +1635,7 @@ #define PF_VPROTO_FRAGMENT 256 int extoff; sa_family_t af; + sa_family_t naf; u_int8_t proto; u_int8_t tos; u_int8_t ttl; @@ -2429,6 +2435,9 @@ int); int pf_socket_lookup(struct pf_pdesc *); struct pf_state_key *pf_alloc_state_key(int); +int pf_translate(struct pf_pdesc *, struct pf_addr *, u_int16_t, + struct pf_addr *, u_int16_t, u_int16_t, int); +int pf_translate_af(struct pf_pdesc *); void pfr_initialize(void); void pfr_cleanup(void); int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t); @@ -2642,18 +2651,23 @@ u_short pf_map_addr(u_int8_t, struct pf_krule *, struct pf_addr *, struct pf_addr *, - struct pfi_kkif **nkif, struct pf_addr *); + struct pfi_kkif **nkif, 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_ksrc_node **, struct pf_srchash **); + struct pf_ksrc_node **, struct pf_srchash **, + struct pf_kpool *); +int pf_get_transaddr_af(struct pf_krule *, + struct pf_pdesc *); u_short pf_get_translation(struct pf_pdesc *, int, struct pf_state_key **, struct pf_state_key **, struct pf_kanchor_stackframe *, struct pf_krule **, struct pf_udp_mapping **udp_mapping); -struct pf_state_key *pf_state_key_setup(struct pf_pdesc *, - u_int16_t, u_int16_t); +int pf_state_key_setup(struct pf_pdesc *, + u_int16_t, u_int16_t, + struct pf_state_key **sk, struct pf_state_key **nk); struct pf_state_key *pf_state_key_clone(const struct pf_state_key *); void pf_rule_to_actions(struct pf_krule *, struct pf_rule_actions *); @@ -2665,6 +2679,17 @@ struct pfi_kkif *pf_kkif_create(int); void pf_kkif_free(struct pfi_kkif *); void pf_kkif_zero(struct pfi_kkif *); + + +/* NAT64 functions. */ +int inet_nat64(int, const void *, void *, const void *, u_int8_t); +int inet_nat64_inet(const void *, void *, const void *, u_int8_t); +int inet_nat64_inet6(const void *, void *, const void *, u_int8_t); + +int inet_nat46(int, const void *, void *, const void *, u_int8_t); +int inet_nat46_inet(const void *, void *, const void *, u_int8_t); +int inet_nat46_inet6(const void *, void *, const void *, u_int8_t); + #endif /* _KERNEL */ #endif /* _NET_PFVAR_H_ */ diff --git a/sys/netpfil/pf/inet_nat64.c b/sys/netpfil/pf/inet_nat64.c new file mode 100644 --- /dev/null +++ b/sys/netpfil/pf/inet_nat64.c @@ -0,0 +1,204 @@ +/* $OpenBSD: inet_nat64.c,v 1.1 2011/10/13 18:23:40 claudio Exp $ */ +/* $vantronix: inet_nat64.c,v 1.2 2011/02/28 14:57:58 mike Exp $ */ + +/* + * Copyright (c) 2011 Reyk Floeter + * + * 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 + +union inet_nat64_addr { + u_int32_t u32[4]; + u_int8_t u8[16]; +}; + +static u_int32_t +inet_nat64_mask(u_int32_t src, u_int32_t pfx, u_int8_t pfxlen) +{ + u_int32_t u32; + if (pfxlen == 0) + return (src); + else if (pfxlen > 32) + pfxlen = 32; + u32 = + (src & ~htonl(0xffffffff << (32 - pfxlen))) | + (pfx & htonl(0xffffffff << (32 - pfxlen))); + return (u32); + +} + +int +inet_nat64(int af, const void *src, void *dst, + const void *pfx, u_int8_t pfxlen) +{ + switch (af) { + case AF_INET: + return (inet_nat64_inet(src, dst, pfx, pfxlen)); + case AF_INET6: + return (inet_nat64_inet6(src, dst, pfx, pfxlen)); + default: +#ifndef _KERNEL + errno = EAFNOSUPPORT; +#endif + return (-1); + } + /* NOTREACHED */ +} + +int +inet_nat64_inet(const void *src, void *dst, const void *pfx, u_int8_t pfxlen) +{ + const union inet_nat64_addr *s = src; + const union inet_nat64_addr *p = pfx; + union inet_nat64_addr *d = dst; + int i, j; + + switch (pfxlen) { + case 32: + case 40: + case 48: + case 56: + case 64: + case 96: + i = pfxlen / 8; + break; + default: + if (pfxlen < 96 || pfxlen > 128) { +#ifndef _KERNEL + errno = EINVAL; +#endif + return (-1); + } + + /* as an extension, mask out any other bits */ + d->u32[0] = inet_nat64_mask(s->u32[3], p->u32[3], + (u_int8_t)(32 - (128 - pfxlen))); + return (0); + } + + /* fill the octets with the source and skip reserved octet 8 */ + for (j = 0; j < 4; j++) { + if (i == 8) + i++; + d->u8[j] = s->u8[i++]; + } + + return (0); +} + +int +inet_nat64_inet6(const void *src, void *dst, const void *pfx, u_int8_t pfxlen) +{ + const union inet_nat64_addr *s = src; + const union inet_nat64_addr *p = pfx; + union inet_nat64_addr *d = dst; + int i, j; + + /* first copy the prefix octets to the destination */ + *d = *p; + + switch (pfxlen) { + case 32: + case 40: + case 48: + case 56: + case 64: + case 96: + i = pfxlen / 8; + break; + default: + if (pfxlen < 96 || pfxlen > 128) { +#ifndef _KERNEL + errno = EINVAL; +#endif + return (-1); + } + + /* as an extension, mask out any other bits */ + d->u32[3] = inet_nat64_mask(s->u32[0], p->u32[3], + (u_int8_t)(32 - (128 - pfxlen))); + return (0); + } + + /* octet 8 is reserved and must be set to zero */ + d->u8[8] = 0; + + /* fill the other octets with the source and skip octet 8 */ + for (j = 0; j < 4; j++) { + if (i == 8) + i++; + d->u8[i++] = s->u8[j]; + } + + return (0); +} + +int +inet_nat46(int af, const void *src, void *dst, + const void *pfx, u_int8_t pfxlen) +{ + if (pfxlen > 32) { +#ifndef _KERNEL + errno = EINVAL; +#endif + return (-1); + } + + switch (af) { + case AF_INET: + return (inet_nat46_inet(src, dst, pfx, pfxlen)); + case AF_INET6: + return (inet_nat46_inet6(src, dst, pfx, pfxlen)); + default: +#ifndef _KERNEL + errno = EAFNOSUPPORT; +#endif + return (-1); + } + /* NOTREACHED */ +} + +int +inet_nat46_inet(const void *src, void *dst, const void *pfx, u_int8_t pfxlen) +{ + const union inet_nat64_addr *s = src; + const union inet_nat64_addr *p = pfx; + union inet_nat64_addr *d = dst; + + /* set the remaining bits to the source */ + d->u32[0] = inet_nat64_mask(s->u32[3], p->u32[0], pfxlen); + + return (0); +} + +int +inet_nat46_inet6(const void *src, void *dst, const void *pfx, u_int8_t pfxlen) +{ + const union inet_nat64_addr *s = src; + const union inet_nat64_addr *p = pfx; + union inet_nat64_addr *d = dst; + + /* set the initial octets to zero */ + d->u32[0] = d->u32[1] = d->u32[2] = 0; + + /* now set the remaining bits to the source */ + d->u32[3] = inet_nat64_mask(s->u32[0], p->u32[0], pfxlen); + + return (0); +} diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h --- a/sys/netpfil/pf/pf.h +++ b/sys/netpfil/pf/pf.h @@ -49,7 +49,7 @@ enum { PF_INOUT, PF_IN, PF_OUT }; enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT, PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, PF_DEFER, - PF_MATCH }; + PF_MATCH, PF_AFRT }; enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT, PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_MAX }; enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT, @@ -126,7 +126,7 @@ PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, PF_ADDR_TABLE, PF_ADDR_URPFFAILED, - PF_ADDR_RANGE }; + PF_ADDR_RANGE, PF_ADDR_NONE }; #define PF_POOL_TYPEMASK 0x0f #define PF_POOL_STICKYADDR 0x20 #define PF_POOL_ENDPI 0x40 @@ -673,6 +673,7 @@ u_int32_t creation; u_int32_t expire; sa_family_t af; + sa_family_t naf; u_int8_t ruletype; }; 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 @@ -301,7 +301,7 @@ static void pf_change_ap(struct mbuf *, struct pf_addr *, u_int16_t *, u_int16_t *, u_int16_t *, struct pf_addr *, - u_int16_t, u_int8_t, sa_family_t); + u_int16_t, u_int8_t, sa_family_t, sa_family_t); static int pf_modulate_sack(struct pf_pdesc *, struct tcphdr *, struct pf_state_peer *); int pf_icmp_mapping(struct pf_pdesc *, u_int8_t, int *, @@ -310,6 +310,11 @@ struct pf_addr *, struct pf_addr *, u_int16_t, u_int16_t *, u_int16_t *, u_int16_t *, u_int16_t *, u_int8_t, sa_family_t); +int pf_change_icmp_af(struct mbuf *, int, + struct pf_pdesc *, struct pf_pdesc *, + struct pf_addr *, struct pf_addr *, sa_family_t, + sa_family_t); +int pf_translate_icmp_af(int, void *); static void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, sa_family_t, struct pf_krule *, int); static void pf_detach_state(struct pf_kstate *); @@ -607,11 +612,11 @@ if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af)) pf_change_ap(m, pd->src, &th->th_sport, pd->ip_sum, &th->th_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 0, pd->af); + nk->port[pd->sidx], 0, pd->af, pd->naf); if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af)) pf_change_ap(m, pd->dst, &th->th_dport, pd->ip_sum, &th->th_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 0, pd->af); + nk->port[pd->didx], 0, pd->af, pd->naf); m_copyback(m, off, sizeof(*th), (caddr_t)th); break; } @@ -621,11 +626,11 @@ if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af)) pf_change_ap(m, pd->src, &uh->uh_sport, pd->ip_sum, &uh->uh_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, pd->af); + nk->port[pd->sidx], 1, pd->af, pd->naf); if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af)) pf_change_ap(m, pd->dst, &uh->uh_dport, pd->ip_sum, &uh->uh_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 1, pd->af); + nk->port[pd->didx], 1, pd->af, pd->naf); m_copyback(m, off, sizeof(*uh), (caddr_t)uh); break; } @@ -636,12 +641,12 @@ if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af)) { pf_change_ap(m, pd->src, &sh->src_port, pd->ip_sum, &checksum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, pd->af); + nk->port[pd->sidx], 1, pd->af, pd->naf); } if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af)) { pf_change_ap(m, pd->dst, &sh->dest_port, pd->ip_sum, &checksum, &nk->addr[pd->didx], - nk->port[pd->didx], 1, pd->af); + nk->port[pd->didx], 1, pd->af, pd->naf); } break; @@ -1423,7 +1428,12 @@ PF_HASHROW_LOCK(ih); if (si->kif == s->kif && - si->direction == s->direction) { + ((si->key[PF_SK_WIRE]->af == sk->af && + si->direction == s->direction) || + (si->key[PF_SK_WIRE]->af != + si->key[PF_SK_STACK]->af && + sk->af == si->key[PF_SK_STACK]->af && + si->direction != s->direction))) { if (sk->proto == IPPROTO_TCP && si->src.state >= TCPS_FIN_WAIT_2 && si->dst.state >= TCPS_FIN_WAIT_2) { @@ -1652,27 +1662,65 @@ return (0); } -struct pf_state_key * -pf_state_key_setup(struct pf_pdesc *pd, u_int16_t sport, u_int16_t dport) +int +pf_state_key_setup(struct pf_pdesc *pd, u_int16_t sport, u_int16_t dport, + struct pf_state_key **sk, struct pf_state_key **nk) { - struct pf_state_key *sk; - - sk = uma_zalloc(V_pf_state_key_z, M_NOWAIT); - if (sk == NULL) - return (NULL); + *sk = uma_zalloc(V_pf_state_key_z, M_NOWAIT); + if (*sk == NULL) + return (ENOMEM); - if (pf_state_key_addr_setup(pd, (struct pf_state_key_cmp *)sk, + if (pf_state_key_addr_setup(pd, (struct pf_state_key_cmp *)*sk, 0)) { - uma_zfree(V_pf_state_key_z, sk); - return (NULL); + uma_zfree(V_pf_state_key_z, *sk); + *sk = NULL; + return (ENOMEM); } - sk->port[pd->sidx] = sport; - sk->port[pd->didx] = dport; - sk->proto = pd->proto; - sk->af = pd->af; + (*sk)->port[pd->sidx] = sport; + (*sk)->port[pd->didx] = dport; + (*sk)->proto = pd->proto; + (*sk)->af = pd->af; - return (sk); + *nk = pf_state_key_clone(*sk); + if (*nk == NULL) { + uma_zfree(V_pf_state_key_z, *sk); + *sk = NULL; + return (ENOMEM); + } + + if (pd->af != pd->naf) { + (*sk)->port[pd->sidx] = pd->osport; + (*sk)->port[pd->didx] = pd->odport; + + (*nk)->af = pd->naf; + + /* + * We're overwriting an address here, so potentially there's bits of an IPv6 + * address left in here. Clear that out first. + */ + bzero(&(*nk)->addr[0], sizeof((*nk)->addr[0])); + bzero(&(*nk)->addr[1], sizeof((*nk)->addr[1])); + + PF_ACPY(&(*nk)->addr[pd->af == pd->naf ? pd->sidx : pd->didx], + &pd->nsaddr, pd->naf); + PF_ACPY(&(*nk)->addr[pd->af == pd->naf ? pd->didx : pd->sidx], + &pd->ndaddr, pd->naf); + (*nk)->port[pd->af == pd->naf ? pd->sidx : pd->didx] = pd->nsport; + (*nk)->port[pd->af == pd->naf ? pd->didx : pd->sidx] = pd->ndport; + switch (pd->proto) { + case IPPROTO_ICMP: + (*nk)->proto = IPPROTO_ICMPV6; + break; + case IPPROTO_ICMPV6: + (*nk)->proto = IPPROTO_ICMP; + break; + default: + (*nk)->proto = pd->proto; + } + } + + return (0); } struct pf_state_key * @@ -1816,6 +1864,28 @@ } return (s); } + + /* Look through the other list, in case of AF-TO */ + idx = idx == PF_SK_WIRE ? PF_SK_STACK : PF_SK_WIRE; + TAILQ_FOREACH(s, &sk->states[idx], key_list[idx]) { + if (s->key[PF_SK_WIRE]->af == s->key[PF_SK_STACK]->af) + continue; + if (s->kif == V_pfi_all || s->kif == kif || s->orig_kif == kif) { + PF_STATE_LOCK(s); + PF_HASHROW_UNLOCK(kh); + if (__predict_false(s->timeout >= PFTM_MAX)) { + /* + * State is either being processed by + * pf_unlink_state() in an other thread, or + * is scheduled for immediate expiry. + */ + PF_STATE_UNLOCK(s); + return (NULL); + } + return (s); + } + } + PF_HASHROW_UNLOCK(kh); return (NULL); @@ -3024,6 +3094,7 @@ return (0); case PF_ADDR_DYNIFTL: return (aw1->p.dyn->pfid_kt != aw2->p.dyn->pfid_kt); + case PF_ADDR_NONE: case PF_ADDR_NOROUTE: case PF_ADDR_URPFFAILED: return (0); @@ -3123,13 +3194,14 @@ static void pf_change_ap(struct mbuf *m, struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, struct pf_addr *an, u_int16_t pn, u_int8_t u, - sa_family_t af) + sa_family_t af, sa_family_t naf) { struct pf_addr ao; u_int16_t po = *p; PF_ACPY(&ao, a, af); - PF_ACPY(a, an, af); + if (af == naf) + PF_ACPY(a, an, af); if (m->m_pkthdr.csum_flags & (CSUM_DELAY_DATA | CSUM_DELAY_DATA_IPV6)) *pc = ~*pc; @@ -3139,33 +3211,77 @@ switch (af) { #ifdef INET case AF_INET: - *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, - ao.addr16[0], an->addr16[0], 0), - ao.addr16[1], an->addr16[1], 0); - *p = pn; + switch (naf) { + case AF_INET: + *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, + ao.addr16[0], an->addr16[0], 0), + ao.addr16[1], an->addr16[1], 0); + *p = pn; - *pc = pf_cksum_fixup(pf_cksum_fixup(*pc, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u); + *pc = pf_cksum_fixup(pf_cksum_fixup(*pc, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u); - *pc = pf_proto_cksum_fixup(m, *pc, po, pn, u); + *pc = pf_proto_cksum_fixup(m, *pc, po, pn, u); + break; +#ifdef INET6 + case AF_INET6: + *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u), + 0, an->addr16[2], u), + 0, an->addr16[3], u), + 0, an->addr16[4], u), + 0, an->addr16[5], u), + 0, an->addr16[6], u), + 0, an->addr16[7], u), + po, pn, u); + + /* XXXKP TODO *ic checksum? */ + break; +#endif /* INET6 */ + } break; #endif /* INET */ #ifdef INET6 case AF_INET6: - *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( - pf_cksum_fixup(pf_cksum_fixup(*pc, - ao.addr16[0], an->addr16[0], u), - ao.addr16[1], an->addr16[1], u), - ao.addr16[2], an->addr16[2], u), - ao.addr16[3], an->addr16[3], u), - ao.addr16[4], an->addr16[4], u), - ao.addr16[5], an->addr16[5], u), - ao.addr16[6], an->addr16[6], u), - ao.addr16[7], an->addr16[7], u); - - *pc = pf_proto_cksum_fixup(m, *pc, po, pn, u); + switch (naf) { +#ifdef INET + case AF_INET: + *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u), + ao.addr16[2], 0, u), + ao.addr16[3], 0, u), + ao.addr16[4], 0, u), + ao.addr16[5], 0, u), + ao.addr16[6], 0, u), + ao.addr16[7], 0, u), + po, pn, u); + + /* XXXKP TODO *ic checksum? */ + break; +#endif /* INET */ + case AF_INET6: + *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(*pc, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u), + ao.addr16[2], an->addr16[2], u), + ao.addr16[3], an->addr16[3], u), + ao.addr16[4], an->addr16[4], u), + ao.addr16[5], an->addr16[5], u), + ao.addr16[6], an->addr16[6], u), + ao.addr16[7], an->addr16[7], u); + + *pc = pf_proto_cksum_fixup(m, *pc, po, pn, u); + break; + } break; #endif /* INET6 */ } @@ -3314,6 +3430,394 @@ } } +int +pf_translate_af(struct pf_pdesc *pd) +{ +#if defined(INET) && defined(INET6) + struct mbuf *mp; + struct ip *ip4; + struct ip6_hdr *ip6; + struct icmp6_hdr *icmp; + int hlen; + + hlen = pd->naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6); + + /* trim the old header */ + m_adj(pd->m, pd->off); + + /* prepend a new one */ + M_PREPEND(pd->m, hlen, M_NOWAIT); + if (pd->m == NULL) + return (-1); + + switch (pd->naf) { + case AF_INET: + ip4 = mtod(pd->m, struct ip *); + bzero(ip4, hlen); + ip4->ip_v = IPVERSION; + ip4->ip_hl = hlen >> 2; + ip4->ip_len = htons(hlen + (pd->tot_len - pd->off)); + ip_fillid(ip4); + ip4->ip_off = htons(IP_DF); + ip4->ip_ttl = pd->ttl; + ip4->ip_p = pd->proto; + ip4->ip_src = pd->nsaddr.v4; + ip4->ip_dst = pd->ndaddr.v4; + pd->src = (struct pf_addr *)&ip4->ip_src; + pd->dst = (struct pf_addr *)&ip4->ip_dst; + break; + case AF_INET6: + ip6 = mtod(pd->m, struct ip6_hdr *); + bzero(ip6, hlen); + ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_plen = htons(pd->tot_len - pd->off); + ip6->ip6_nxt = pd->proto; + if (!pd->ttl || pd->ttl > IPV6_DEFHLIM) + ip6->ip6_hlim = IPV6_DEFHLIM; + else + ip6->ip6_hlim = pd->ttl; + ip6->ip6_src = pd->nsaddr.v6; + ip6->ip6_dst = pd->ndaddr.v6; + pd->src = (struct pf_addr *)&ip6->ip6_src; + pd->dst = (struct pf_addr *)&ip6->ip6_dst; + break; + default: + return (-1); + } + + /* recalculate icmp/icmp6 checksums */ + if (pd->proto == IPPROTO_ICMP || pd->proto == IPPROTO_ICMPV6) { + int off; + if ((mp = m_pulldown(pd->m, hlen, sizeof(*icmp), &off)) == + NULL) { + pd->m = NULL; + return (-1); + } + icmp = (struct icmp6_hdr *)(mp->m_data + off); + icmp->icmp6_cksum = 0; + icmp->icmp6_cksum = pd->naf == AF_INET ? + in4_cksum(pd->m, 0, hlen, ntohs(ip4->ip_len) - hlen) : + in6_cksum(pd->m, IPPROTO_ICMPV6, hlen, + ntohs(ip6->ip6_plen)); + } +#endif /* INET && INET6 */ + + return (0); +} + +int +pf_change_icmp_af(struct mbuf *m, int off, struct pf_pdesc *pd, + struct pf_pdesc *pd2, struct pf_addr *src, struct pf_addr *dst, + sa_family_t af, sa_family_t naf) +{ +#if defined(INET) && defined(INET6) + struct mbuf *n = NULL; + struct ip *ip4; + struct ip6_hdr *ip6; + int hlen, olen, mlen; + + if (af == naf || (af != AF_INET && af != AF_INET6) || + (naf != AF_INET && naf != AF_INET6)) + return (-1); + + /* split the mbuf chain on the inner ip/ip6 header boundary */ + if ((n = m_split(m, off, M_NOWAIT)) == NULL) + return (-1); + + /* old header */ + olen = pd2->off - off; + /* new header */ + hlen = naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6); + /* data lenght */ + mlen = m->m_pkthdr.len - pd2->off; + + /* trim old header */ + m_adj(n, olen); + + /* prepend a new one */ + M_PREPEND(n, hlen, M_NOWAIT); + if (n == NULL) + return (-1); + + /* translate inner ip/ip6 header */ + switch (naf) { + case AF_INET: + ip4 = mtod(n, struct ip *); + bzero(ip4, sizeof(*ip4)); + ip4->ip_v = IPVERSION; + ip4->ip_hl = sizeof(*ip4) >> 2; + ip4->ip_len = htons(sizeof(*ip4) + mlen); + ip_fillid(ip4); + ip4->ip_off = htons(IP_DF); + ip4->ip_ttl = pd2->ttl; + if (pd2->proto == IPPROTO_ICMPV6) + ip4->ip_p = IPPROTO_ICMP; + else + ip4->ip_p = pd2->proto; + ip4->ip_src = src->v4; + ip4->ip_dst = dst->v4; + ip4->ip_sum = in_cksum(n, ip4->ip_hl << 2); + break; + case AF_INET6: + ip6 = mtod(n, struct ip6_hdr *); + bzero(ip6, sizeof(*ip6)); + ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_plen = htons(mlen); + if (pd2->proto == IPPROTO_ICMP) + ip6->ip6_nxt = IPPROTO_ICMPV6; + else + ip6->ip6_nxt = pd2->proto; + if (!pd2->ttl || pd2->ttl > IPV6_DEFHLIM) + ip6->ip6_hlim = IPV6_DEFHLIM; + else + ip6->ip6_hlim = pd2->ttl; + ip6->ip6_src = src->v6; + ip6->ip6_dst = dst->v6; + break; + } + + /* adjust payload offset and total packet length */ + pd2->off += hlen - olen; + pd->tot_len += hlen - olen; + + /* merge modified inner packet with the original header */ + mlen = n->m_pkthdr.len; + m_cat(m, n); + m->m_pkthdr.len += mlen; +#endif /* INET && INET6 */ + + return (0); +} + +#define PTR_IP(field) (offsetof(struct ip, field)) +#define PTR_IP6(field) (offsetof(struct ip6_hdr, field)) + +int +pf_translate_icmp_af(int af, void *arg) +{ +#if defined(INET) && defined(INET6) + struct icmp *icmp4; + struct icmp6_hdr *icmp6; + u_int32_t mtu; + int32_t ptr = -1; + u_int8_t type; + u_int8_t code; + + switch (af) { + case AF_INET: + icmp6 = arg; + type = icmp6->icmp6_type; + code = icmp6->icmp6_code; + mtu = ntohl(icmp6->icmp6_mtu); + + switch (type) { + case ICMP6_ECHO_REQUEST: + type = ICMP_ECHO; + break; + case ICMP6_ECHO_REPLY: + type = ICMP_ECHOREPLY; + break; + case ICMP6_DST_UNREACH: + type = ICMP_UNREACH; + switch (code) { + case ICMP6_DST_UNREACH_NOROUTE: + case ICMP6_DST_UNREACH_BEYONDSCOPE: + case ICMP6_DST_UNREACH_ADDR: + code = ICMP_UNREACH_HOST; + break; + case ICMP6_DST_UNREACH_ADMIN: + code = ICMP_UNREACH_HOST_PROHIB; + break; + case ICMP6_DST_UNREACH_NOPORT: + code = ICMP_UNREACH_PORT; + break; + default: + return (-1); + } + break; + case ICMP6_PACKET_TOO_BIG: + type = ICMP_UNREACH; + code = ICMP_UNREACH_NEEDFRAG; + mtu -= 20; + break; + case ICMP6_TIME_EXCEEDED: + type = ICMP_TIMXCEED; + break; + case ICMP6_PARAM_PROB: + switch (code) { + case ICMP6_PARAMPROB_HEADER: + type = ICMP_PARAMPROB; + code = ICMP_PARAMPROB_ERRATPTR; + ptr = ntohl(icmp6->icmp6_pptr); + + if (ptr == PTR_IP6(ip6_vfc)) + ; /* preserve */ + else if (ptr == PTR_IP6(ip6_vfc) + 1) + ptr = PTR_IP(ip_tos); + else if (ptr == PTR_IP6(ip6_plen) || + ptr == PTR_IP6(ip6_plen) + 1) + ptr = PTR_IP(ip_len); + else if (ptr == PTR_IP6(ip6_nxt)) + ptr = PTR_IP(ip_p); + else if (ptr == PTR_IP6(ip6_hlim)) + ptr = PTR_IP(ip_ttl); + else if (ptr >= PTR_IP6(ip6_src) && + ptr < PTR_IP6(ip6_dst)) + ptr = PTR_IP(ip_src); + else if (ptr >= PTR_IP6(ip6_dst) && + ptr < sizeof(struct ip6_hdr)) + ptr = PTR_IP(ip_dst); + else { + return (-1); + } + break; + case ICMP6_PARAMPROB_NEXTHEADER: + type = ICMP_UNREACH; + code = ICMP_UNREACH_PROTOCOL; + break; + default: + return (-1); + } + break; + default: + return (-1); + } + if (icmp6->icmp6_type != type) { + icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum, + icmp6->icmp6_type, type, 0); + icmp6->icmp6_type = type; + } + if (icmp6->icmp6_code != code) { + icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum, + icmp6->icmp6_code, code, 0); + icmp6->icmp6_code = code; + } + if (icmp6->icmp6_mtu != htonl(mtu)) { + icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum, + htons(ntohl(icmp6->icmp6_mtu)), htons(mtu), 0); + /* aligns well with a icmpv4 nextmtu */ + icmp6->icmp6_mtu = htonl(mtu); + } + if (ptr >= 0 && icmp6->icmp6_pptr != htonl(ptr)) { + icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum, + htons(ntohl(icmp6->icmp6_pptr)), htons(ptr), 0); + /* icmpv4 pptr is a one most significant byte */ + icmp6->icmp6_pptr = htonl(ptr << 24); + } + break; + case AF_INET6: + icmp4 = arg; + type = icmp4->icmp_type; + code = icmp4->icmp_code; + mtu = ntohs(icmp4->icmp_nextmtu); + + switch (type) { + case ICMP_ECHO: + type = ICMP6_ECHO_REQUEST; + break; + case ICMP_ECHOREPLY: + type = ICMP6_ECHO_REPLY; + break; + case ICMP_UNREACH: + type = ICMP6_DST_UNREACH; + switch (code) { + case ICMP_UNREACH_NET: + case ICMP_UNREACH_HOST: + case ICMP_UNREACH_NET_UNKNOWN: + case ICMP_UNREACH_HOST_UNKNOWN: + case ICMP_UNREACH_ISOLATED: + case ICMP_UNREACH_TOSNET: + case ICMP_UNREACH_TOSHOST: + code = ICMP6_DST_UNREACH_NOROUTE; + break; + case ICMP_UNREACH_PORT: + code = ICMP6_DST_UNREACH_NOPORT; + break; + case ICMP_UNREACH_NET_PROHIB: + case ICMP_UNREACH_HOST_PROHIB: + case ICMP_UNREACH_FILTER_PROHIB: + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + code = ICMP6_DST_UNREACH_ADMIN; + break; + case ICMP_UNREACH_PROTOCOL: + type = ICMP6_PARAM_PROB; + code = ICMP6_PARAMPROB_NEXTHEADER; + ptr = offsetof(struct ip6_hdr, ip6_nxt); + break; + case ICMP_UNREACH_NEEDFRAG: + type = ICMP6_PACKET_TOO_BIG; + code = 0; + mtu += 20; + break; + default: + return (-1); + } + break; + case ICMP_TIMXCEED: + type = ICMP6_TIME_EXCEEDED; + break; + case ICMP_PARAMPROB: + type = ICMP6_PARAM_PROB; + switch (code) { + case ICMP_PARAMPROB_ERRATPTR: + code = ICMP6_PARAMPROB_HEADER; + break; + case ICMP_PARAMPROB_LENGTH: + code = ICMP6_PARAMPROB_HEADER; + break; + default: + return (-1); + } + + ptr = icmp4->icmp_pptr; + if (ptr == 0 || ptr == PTR_IP(ip_tos)) + ; /* preserve */ + else if (ptr == PTR_IP(ip_len) || + ptr == PTR_IP(ip_len) + 1) + ptr = PTR_IP6(ip6_plen); + else if (ptr == PTR_IP(ip_ttl)) + ptr = PTR_IP6(ip6_hlim); + else if (ptr == PTR_IP(ip_p)) + ptr = PTR_IP6(ip6_nxt); + else if (ptr >= PTR_IP(ip_src) && ptr < PTR_IP(ip_dst)) + ptr = PTR_IP6(ip6_src); + else if (ptr >= PTR_IP(ip_dst) && + ptr < sizeof(struct ip)) + ptr = PTR_IP6(ip6_dst); + else { + return (-1); + } + break; + default: + return (-1); + } + if (icmp4->icmp_type != type) { + icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum, + icmp4->icmp_type, type, 0); + icmp4->icmp_type = type; + } + if (icmp4->icmp_code != code) { + icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum, + icmp4->icmp_code, code, 0); + icmp4->icmp_code = code; + } + if (icmp4->icmp_nextmtu != htons(mtu)) { + icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum, + icmp4->icmp_nextmtu, htons(mtu), 0); + icmp4->icmp_nextmtu = htons(mtu); + } + if (ptr >= 0 && icmp4->icmp_void != ptr) { + icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum, + htons(icmp4->icmp_pptr), htons(ptr), 0); + icmp4->icmp_void = htonl(ptr); + } + break; + } +#endif /* INET && INET6 */ + + return (0); +} + /* * Need to modulate the sequence numbers in the TCP SACK option * (credits to Krzysztof Pfaff for report and patch) @@ -5014,7 +5518,7 @@ nk->port[pd->sidx] != pd->nsport) { pf_change_ap(pd->m, pd->src, &th->th_sport, pd->ip_sum, &th->th_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 0, pd->af); + nk->port[pd->sidx], 0, pd->af, pd->naf); pd->sport = &th->th_sport; pd->nsport = th->th_sport; PF_ACPY(&pd->nsaddr, pd->src, pd->af); @@ -5024,9 +5528,9 @@ nk->port[pd->didx] != pd->ndport) { pf_change_ap(pd->m, pd->dst, &th->th_dport, pd->ip_sum, &th->th_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 0, pd->af); - pd->ndport = th->th_dport; + nk->port[pd->didx], 0, pd->af, pd->naf); pd->dport = &th->th_dport; + pd->ndport = th->th_dport; PF_ACPY(&pd->ndaddr, pd->dst, pd->af); } rewrite++; @@ -5040,9 +5544,9 @@ &pd->hdr.udp.uh_sport, pd->ip_sum, &pd->hdr.udp.uh_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, pd->af); - pd->nsport = pd->hdr.udp.uh_sport; + nk->port[pd->sidx], 1, pd->af, pd->naf); pd->sport = &pd->hdr.udp.uh_sport; + pd->nsport = pd->hdr.udp.uh_sport; PF_ACPY(&pd->nsaddr, pd->src, pd->af); } @@ -5052,9 +5556,9 @@ &pd->hdr.udp.uh_dport, pd->ip_sum, &pd->hdr.udp.uh_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 1, pd->af); - pd->ndport = pd->hdr.udp.uh_dport; + nk->port[pd->didx], 1, pd->af, pd->naf); pd->dport = &pd->hdr.udp.uh_dport; + pd->ndport = pd->hdr.udp.uh_dport; PF_ACPY(&pd->ndaddr, pd->dst, pd->af); } rewrite++; @@ -5067,7 +5571,9 @@ pf_change_ap(pd->m, pd->src, &pd->hdr.sctp.src_port, pd->ip_sum, &checksum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, pd->af); + nk->port[pd->sidx], 1, pd->af, pd->naf); + pd->sport = &pd->hdr.sctp.src_port; + pd->nsport = pd->hdr.sctp.src_port; PF_ACPY(&pd->nsaddr, pd->src, pd->af); } if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], pd->af) || @@ -5075,7 +5581,9 @@ pf_change_ap(pd->m, pd->dst, &pd->hdr.sctp.dest_port, pd->ip_sum, &checksum, &nk->addr[pd->didx], - nk->port[pd->didx], 1, pd->af); + nk->port[pd->didx], 1, pd->af, pd->naf); + pd->dport = &pd->hdr.sctp.dest_port; + pd->ndport = pd->hdr.sctp.dest_port; PF_ACPY(&pd->ndaddr, pd->dst, pd->af); } break; @@ -5174,7 +5682,7 @@ r->skip[PF_SKIP_AF]); PF_TEST_ATTRIB(r->proto && r->proto != pd->proto, r->skip[PF_SKIP_PROTO]); - PF_TEST_ATTRIB(PF_MISMATCHAW(&r->src.addr, &pd->nsaddr, pd->af, + PF_TEST_ATTRIB(PF_MISMATCHAW(&r->src.addr, &pd->nsaddr, pd->naf, r->src.neg, pd->kif, M_GETFIB(pd->m)), r->skip[PF_SKIP_SRC_ADDR]); PF_TEST_ATTRIB(PF_MISMATCHAW(&r->dst.addr, &pd->ndaddr, pd->af, @@ -5274,6 +5782,14 @@ pf_counter_u64_add_protected(&r->bytes[pd->dir == PF_OUT], pd->tot_len); pf_counter_u64_critical_exit(); pf_rule_to_actions(r, &pd->act); + if (r->naf) + pd->naf = r->naf; + if (pd->af != pd->naf) { + if (pf_get_transaddr_af(r, pd) == -1) { + REASON_SET(&reason, PFRES_MEMORY); + goto cleanup; + } + } if (r->log || pd->act.log & PF_LOG_MATCHES) PFLOG_PACKET(r->action, PFRES_MATCH, r, a, ruleset, pd, 1); @@ -5306,6 +5822,14 @@ /* apply actions for last matching pass/block rule */ pf_rule_to_actions(r, &pd->act); + if (r->naf) + pd->naf = r->naf; + if (pd->af != pd->naf) { + if (pf_get_transaddr_af(r, pd) == -1) { + REASON_SET(&reason, PFRES_MEMORY); + goto cleanup; + } + } if (r->log || pd->act.log & PF_LOG_MATCHES) { if (rewrite) @@ -5342,7 +5866,7 @@ pd->act.rt = r->rt; /* Don't use REASON_SET, pf_map_addr increases the reason counters */ reason = pf_map_addr_sn(pd->af, r, pd->src, &pd->act.rt_addr, - &pd->act.rt_kif, NULL, &sn, &snh); + &pd->act.rt_kif, NULL, &sn, &snh, &r->rdr); if (reason != 0) goto cleanup; } @@ -5351,6 +5875,8 @@ (!state_icmp && (r->keep_state || nr != NULL || (pd->flags & PFDESC_TCP_NORM)))) { int action; + bool nat64; + action = pf_create_state(r, nr, a, pd, nk, sk, &rewrite, sm, tag, bproto_sum, bip_sum, &match_rules, udp_mapping); @@ -5363,6 +5889,26 @@ pd->act.rtableid); return (action); } + + nat64 = pd->af != pd->naf; + if (nat64) { + struct pf_state_key *_sk; + + if (sk == NULL) + sk = (*sm)->key[pd->dir == PF_IN ? PF_SK_STACK : PF_SK_WIRE]; + if (nk == NULL) + nk = (*sm)->key[pd->dir == PF_IN ? PF_SK_WIRE : PF_SK_STACK]; + if (pd->dir == PF_IN) + _sk = sk; + else + _sk = nk; + rewrite += pf_translate(pd, + &_sk->addr[pd->didx], + _sk->port[pd->didx], + &_sk->addr[pd->sidx], + _sk->port[pd->sidx], + virtual_type, icmp_dir); + } } else { while ((ri = SLIST_FIRST(&match_rules))) { SLIST_REMOVE_HEAD(&match_rules, entry); @@ -5389,7 +5935,10 @@ */ return (PF_DEFER); - return (PF_PASS); + if (rewrite && sk != NULL && nk != NULL && sk->af != nk->af) { + return (PF_AFRT); + } else + return (PF_PASS); cleanup: while ((ri = SLIST_FIRST(&match_rules))) { @@ -5562,10 +6111,9 @@ __func__, nr, sk, nk)); MPASS(pd->sport == NULL || (pd->osport == *pd->sport)); MPASS(pd->dport == NULL || (pd->odport == *pd->dport)); - sk = pf_state_key_setup(pd, pd->osport, pd->odport); - if (sk == NULL) + if (pf_state_key_setup(pd, pd->nsport, pd->ndport, &sk, &nk)) { goto csfailed; - nk = sk; + } } else KASSERT((sk != NULL && nk != NULL), ("%s: nr %p sk %p, nk %p", __func__, nr, sk, nk)); @@ -5670,6 +6218,103 @@ return (PF_DROP); } +int +pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport, + struct pf_addr *daddr, u_int16_t dport, u_int16_t virtual_type, + int icmp_dir) +{ + /* + * pf_translate() implements OpenBSD's "new" NAT approach. + * We don't follow it, because it involves a breaking syntax change + * (removing nat/rdr rules, moving it into regular pf rules.) + * It also moves NAT processing to be done after normal rules evaluation + * whereas in FreeBSD that's done before rules processing. + * + * We adopt the function only for nat64, and keep other NAT processing + * before rules processing. + */ + int rewrite = 0; + int afto = pd->af != pd->naf; + + MPASS(afto); + + switch (pd->proto) { + case IPPROTO_TCP: + if (afto || *pd->sport != sport) { + pf_change_ap(pd->m, pd->src, pd->sport, pd->ip_sum, &pd->hdr.tcp.th_sum, + saddr, sport, 0, pd->af, pd->naf); + rewrite = 1; + } + if (afto || *pd->dport != dport) { + pf_change_ap(pd->m, pd->dst, pd->dport, pd->ip_sum, &pd->hdr.tcp.th_sum, + daddr, dport, 0, pd->af, pd->naf); + rewrite = 1; + } + break; + + case IPPROTO_UDP: + if (afto || *pd->sport != sport) { + pf_change_ap(pd->m, pd->src, pd->sport, pd->ip_sum, &pd->hdr.udp.uh_sum, + saddr, sport, 1, pd->af, pd->naf); + rewrite = 1; + } + if (afto || *pd->dport != dport) { + pf_change_ap(pd->m, pd->dst, pd->dport, pd->ip_sum, &pd->hdr.udp.uh_sum, + daddr, dport, 1, pd->af, pd->naf); + rewrite = 1; + } + break; + +#ifdef INET + case IPPROTO_ICMP: + /* pf_translate() is also used when logging invalid packets */ + if (pd->af != AF_INET) + return (0); + + if (afto) { + if (pf_translate_icmp_af(AF_INET6, &pd->hdr.icmp)) + return (0); + pd->proto = IPPROTO_ICMPV6; + rewrite = 1; + } + if (virtual_type == htons(ICMP_ECHO)) { + u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport; + + if (icmpid != pd->hdr.icmp.icmp_id) { + pd->hdr.icmp.icmp_cksum = pf_cksum_fixup( + pd->hdr.icmp.icmp_cksum, + pd->hdr.icmp.icmp_id, icmpid, 0); + pd->hdr.icmp.icmp_id = icmpid; + /* XXX TODO copyback. */ + rewrite = 1; + } + } + break; +#endif /* INET */ + +#ifdef INET6 + case IPPROTO_ICMPV6: + /* pf_translate() is also used when logging invalid packets */ + if (pd->af != AF_INET6) + return (0); + + if (afto) { + /* ip_sum will be recalculated in pf_translate_af */ + if (pf_translate_icmp_af(AF_INET, &pd->hdr.icmp6)) + return (0); + pd->proto = IPPROTO_ICMP; + rewrite = 1; + } + break; +#endif /* INET6 */ + + default: + break; + } + + return (rewrite); +} + static int pf_tcp_track_full(struct pf_kstate **state, struct pf_pdesc *pd, u_short *reason, int *copyback) @@ -5682,13 +6327,23 @@ int ackskew; if (pd->dir == (*state)->direction) { - src = &(*state)->src; - dst = &(*state)->dst; + if (PF_REVERSED_KEY((*state)->key, pd->af)) { + src = &(*state)->dst; + dst = &(*state)->src; + } else { + src = &(*state)->src; + dst = &(*state)->dst; + } psrc = PF_PEER_SRC; pdst = PF_PEER_DST; } else { - src = &(*state)->dst; - dst = &(*state)->src; + if (PF_REVERSED_KEY((*state)->key, pd->af)) { + src = &(*state)->src; + dst = &(*state)->dst; + } else { + src = &(*state)->dst; + dst = &(*state)->src; + } psrc = PF_PEER_DST; pdst = PF_PEER_SRC; } @@ -6198,7 +6853,7 @@ struct pf_state_key_cmp key; struct tcphdr *th = &pd->hdr.tcp; int copyback = 0; - int action; + int action = PF_PASS; struct pf_state_peer *src, *dst; bzero(&key, sizeof(key)); @@ -6251,26 +6906,47 @@ if (pf_tcp_track_sloppy(state, pd, reason) == PF_DROP) return (PF_DROP); } else { - if (pf_tcp_track_full(state, pd, reason, - ©back) == PF_DROP) + int ret; + + ret = pf_tcp_track_full(state, pd, reason, + ©back); + if (ret == PF_DROP) return (PF_DROP); } /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = (*state)->key[pd->didx]; + struct pf_state_key *nk; + int afto, sidx, didx; - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || - nk->port[pd->sidx] != th->th_sport) + if (PF_REVERSED_KEY((*state)->key, pd->af)) + nk = (*state)->key[pd->sidx]; + else + nk = (*state)->key[pd->didx]; + + afto = pd->af != nk->af; + sidx = afto ? pd->didx : pd->sidx; + didx = afto ? pd->sidx : pd->didx; + + if (afto || PF_ANEQ(pd->src, &nk->addr[sidx], pd->af) || + nk->port[sidx] != th->th_sport) pf_change_ap(pd->m, pd->src, &th->th_sport, - pd->ip_sum, &th->th_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 0, pd->af); + pd->ip_sum, &th->th_sum, &nk->addr[sidx], + nk->port[sidx], 0, pd->af, nk->af); - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || - nk->port[pd->didx] != th->th_dport) + if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) || + nk->port[didx] != th->th_dport) pf_change_ap(pd->m, pd->dst, &th->th_dport, - pd->ip_sum, &th->th_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 0, pd->af); + pd->ip_sum, &th->th_sum, &nk->addr[didx], + nk->port[didx], 0, pd->af, nk->af); + + if (afto) { + PF_ACPY(&pd->nsaddr, &nk->addr[sidx], nk->af); + PF_ACPY(&pd->ndaddr, &nk->addr[didx], nk->af); + pd->naf = nk->af; + action = PF_AFRT; + } + copyback = 1; } @@ -6278,7 +6954,7 @@ if (copyback) m_copyback(pd->m, pd->off, sizeof(*th), (caddr_t)th); - return (PF_PASS); + return (action); } static int @@ -6288,6 +6964,7 @@ struct pf_state_key_cmp key; struct udphdr *uh = &pd->hdr.udp; uint8_t psrc, pdst; + int action = PF_PASS; bzero(&key, sizeof(key)); key.af = pd->af; @@ -6333,23 +7010,41 @@ /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = (*state)->key[pd->didx]; + struct pf_state_key *nk; + int afto, sidx, didx; - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || - nk->port[pd->sidx] != uh->uh_sport) + if (PF_REVERSED_KEY((*state)->key, pd->af)) + nk = (*state)->key[pd->sidx]; + else + nk = (*state)->key[pd->didx]; + + afto = pd->af != nk->af; + sidx = afto ? pd->didx : pd->sidx; + didx = afto ? pd->sidx : pd->didx; + + if (afto || PF_ANEQ(pd->src, &nk->addr[sidx], pd->af) || + nk->port[sidx] != uh->uh_sport) pf_change_ap(pd->m, pd->src, &uh->uh_sport, pd->ip_sum, &uh->uh_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, pd->af); + nk->port[sidx], 1, pd->af, nk->af); - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || - nk->port[pd->didx] != uh->uh_dport) + if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) || + nk->port[didx] != uh->uh_dport) pf_change_ap(pd->m, pd->dst, &uh->uh_dport, pd->ip_sum, &uh->uh_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 1, pd->af); + nk->port[didx], 1, pd->af, nk->af); + + if (afto) { + PF_ACPY(&pd->nsaddr, &nk->addr[sidx], nk->af); + PF_ACPY(&pd->ndaddr, &nk->addr[didx], nk->af); + pd->naf = nk->af; + action = PF_AFRT; + } + m_copyback(pd->m, pd->off, sizeof(*uh), (caddr_t)uh); } - return (PF_PASS); + return (action); } static int @@ -6442,18 +7137,23 @@ uint16_t checksum = 0; struct pf_state_key *nk = (*state)->key[pd->didx]; + if (pd->af != nk->af) { + /* XXX No nat64 for SCTP for now. */ + return (PF_DROP); + } + if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || nk->port[pd->sidx] != pd->hdr.sctp.src_port) { pf_change_ap(pd->m, pd->src, &pd->hdr.sctp.src_port, pd->ip_sum, &checksum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, pd->af); + nk->port[pd->sidx], 1, pd->af, pd->naf); } if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) || nk->port[pd->didx] != pd->hdr.sctp.dest_port) { pf_change_ap(pd->m, pd->dst, &pd->hdr.sctp.dest_port, pd->ip_sum, &checksum, &nk->addr[pd->didx], - nk->port[pd->didx], 1, pd->af); + nk->port[pd->didx], 1, pd->af, pd->naf); } } @@ -6986,22 +7686,42 @@ /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = (*state)->key[pd->didx]; + struct pf_state_key *nk; + int afto, sidx, didx; + + if (PF_REVERSED_KEY((*state)->key, pd->af)) + nk = (*state)->key[pd->sidx]; + else + nk = (*state)->key[pd->didx]; + + afto = pd->af != nk->af; + sidx = afto ? pd->didx : pd->sidx; + didx = afto ? pd->sidx : pd->didx; + iidx = afto ? !iidx : iidx; switch (pd->af) { #ifdef INET case AF_INET: - if (PF_ANEQ(pd->src, - &nk->addr[pd->sidx], AF_INET)) +#ifdef INET6 + if (afto) { + if (pf_translate_icmp_af(AF_INET6, + &pd->hdr.icmp)) + return (PF_DROP); + pd->proto = IPPROTO_ICMPV6; + } +#endif + if (!afto && + PF_ANEQ(pd->src, &nk->addr[sidx], AF_INET)) pf_change_a(&saddr->v4.s_addr, pd->ip_sum, - nk->addr[pd->sidx].v4.s_addr, 0); + nk->addr[sidx].v4.s_addr, + 0); - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], - AF_INET)) + if (!afto && PF_ANEQ(pd->dst, + &nk->addr[didx], AF_INET)) pf_change_a(&daddr->v4.s_addr, pd->ip_sum, - nk->addr[pd->didx].v4.s_addr, 0); + nk->addr[didx].v4.s_addr, 0); if (nk->port[iidx] != pd->hdr.icmp.icmp_id) { @@ -7019,23 +7739,37 @@ #endif /* INET */ #ifdef INET6 case AF_INET6: - if (PF_ANEQ(pd->src, - &nk->addr[pd->sidx], AF_INET6)) +#ifdef INET + if (afto) { + if (pf_translate_icmp_af(AF_INET, + &pd->hdr.icmp6)) + return (PF_DROP); + pd->proto = IPPROTO_ICMP; + } +#endif + if (!afto && + PF_ANEQ(pd->src, &nk->addr[sidx], AF_INET6)) pf_change_a6(saddr, &pd->hdr.icmp6.icmp6_cksum, - &nk->addr[pd->sidx], 0); + &nk->addr[sidx], 0); - if (PF_ANEQ(pd->dst, - &nk->addr[pd->didx], AF_INET6)) + if (!afto && PF_ANEQ(pd->dst, + &nk->addr[didx], AF_INET6)) pf_change_a6(daddr, &pd->hdr.icmp6.icmp6_cksum, - &nk->addr[pd->didx], 0); + &nk->addr[didx], 0); m_copyback(pd->m, pd->off, sizeof(struct icmp6_hdr), (caddr_t )&pd->hdr.icmp6); break; #endif /* INET6 */ } + if (afto) { + PF_ACPY(&pd->nsaddr, &nk->addr[sidx], nk->af); + PF_ACPY(&pd->ndaddr, &nk->addr[didx], nk->af); + pd->naf = nk->af; + return (PF_AFRT); + } } return (PF_PASS); @@ -7166,11 +7900,21 @@ STATE_LOOKUP(&key, *state, pd); if (pd->dir == (*state)->direction) { - src = &(*state)->dst; - dst = &(*state)->src; + if (PF_REVERSED_KEY((*state)->key, pd->af)) { + src = &(*state)->src; + dst = &(*state)->dst; + } else { + src = &(*state)->dst; + dst = &(*state)->src; + } } else { - src = &(*state)->src; - dst = &(*state)->dst; + if (PF_REVERSED_KEY((*state)->key, pd->af)) { + src = &(*state)->dst; + dst = &(*state)->src; + } else { + src = &(*state)->src; + dst = &(*state)->dst; + } } if (src->wscale && dst->wscale) @@ -7217,8 +7961,48 @@ /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = - (*state)->key[pd->didx]; + + struct pf_state_key *nk; + + if (PF_REVERSED_KEY((*state)->key, pd->af)) + nk = (*state)->key[pd->sidx]; + else + nk = (*state)->key[pd->didx]; + +#if defined(INET) && defined(INET6) + int afto, sidx, didx; + + afto = pd->af != nk->af; + sidx = afto ? pd2.didx : pd2.sidx; + didx = afto ? pd2.sidx : pd2.didx; + + if (afto) { + if (pf_translate_icmp_af(nk->af, + &pd->hdr.icmp)) + return (PF_DROP); + m_copyback(pd->m, pd->off, + sizeof(struct icmp6_hdr), + (c_caddr_t)&pd->hdr.icmp6); + if (pf_change_icmp_af(pd->m, ipoff2, pd, + &pd2, &nk->addr[sidx], + &nk->addr[didx], pd->af, + nk->af)) + return (PF_DROP); + if (nk->af == AF_INET) + pd->proto = IPPROTO_ICMP; + else + pd->proto = IPPROTO_ICMPV6; + th.th_sport = nk->port[sidx]; + th.th_dport = nk->port[didx]; + m_copyback(pd2.m, pd2.off, 8, (c_caddr_t)&th); + PF_ACPY(pd->src, + &nk->addr[pd2.sidx], nk->af); + PF_ACPY(pd->dst, + &nk->addr[pd2.didx], nk->af); + pd->naf = nk->af; + return (PF_AFRT); + } +#endif if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || @@ -7289,8 +8073,52 @@ /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = - (*state)->key[pd->didx]; + struct pf_state_key *nk; + + if (PF_REVERSED_KEY((*state)->key, pd->af)) + nk = (*state)->key[pd->sidx]; + else + nk = (*state)->key[pd->didx]; + +#if defined(INET) && defined(INET6) + int afto, sidx, didx; + + afto = pd->af != nk->af; + sidx = afto ? pd2.didx : pd2.sidx; + didx = afto ? pd2.sidx : pd2.didx; + + if (afto) { + if (pf_translate_icmp_af(nk->af, + &pd->hdr.icmp)) + return (PF_DROP); + m_copyback(pd->m, pd->off, + sizeof(struct icmp6_hdr), + (c_caddr_t)&pd->hdr.icmp6); + if (pf_change_icmp_af(pd->m, ipoff2, pd, + &pd2, &nk->addr[sidx], + &nk->addr[didx], pd->af, + nk->af)) + return (PF_DROP); + if (nk->af == AF_INET) + pd->proto = IPPROTO_ICMP; + else + pd->proto = IPPROTO_ICMPV6; + pf_change_ap(pd->m, pd2.src, &uh.uh_sport, + pd->ip_sum, &uh.uh_sum, &nk->addr[pd2.sidx], + nk->port[sidx], 1, pd->af, nk->af); + pf_change_ap(pd->m, pd2.dst, &uh.uh_dport, + pd->ip_sum, &uh.uh_sum, &nk->addr[pd2.didx], + nk->port[didx], 1, pd->af, nk->af); + m_copyback(pd2.m, pd2.off, sizeof(uh), + (c_caddr_t)&uh); + PF_ACPY(&pd->nsaddr, + &nk->addr[pd2.sidx], nk->af); + PF_ACPY(&pd->ndaddr, + &nk->addr[pd2.didx], nk->af); + pd->naf = nk->af; + return (PF_AFRT); + } +#endif if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || @@ -7365,8 +8193,51 @@ /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = - (*state)->key[pd->didx]; + struct pf_state_key *nk; + + if (PF_REVERSED_KEY((*state)->key, pd->af)) + nk = (*state)->key[pd->sidx]; + else + nk = (*state)->key[pd->didx]; + +#if defined(INET) && defined(INET6) + int afto, sidx, didx; + + afto = pd->af != nk->af; + sidx = afto ? pd2.didx : pd2.sidx; + didx = afto ? pd2.sidx : pd2.didx; + iidx = afto ? !iidx : iidx; + + if (afto) { + if (nk->af != AF_INET6) + return (PF_DROP); + if (pf_translate_icmp_af(nk->af, + &pd->hdr.icmp)) + return (PF_DROP); + m_copyback(pd->m, pd->off, + sizeof(struct icmp6_hdr), + (c_caddr_t)&pd->hdr.icmp6); + if (pf_change_icmp_af(pd->m, ipoff2, pd, + &pd2, &nk->addr[sidx], + &nk->addr[didx], pd->af, + nk->af)) + return (PF_DROP); + pd->proto = IPPROTO_ICMPV6; + if (pf_translate_icmp_af(nk->af, &iih)) + return (PF_DROP); + if (virtual_type == htons(ICMP_ECHO) && + nk->port[iidx] != iih->icmp_id) + iih->icmp_id = nk->port[iidx]; + m_copyback(pd2.m, pd2.off, ICMP_MINLEN, + (c_caddr_t)&iih); + PF_ACPY(&pd->nsaddr, + &nk->addr[pd2.sidx], nk->af); + PF_ACPY(&pd->ndaddr, + &nk->addr[pd2.didx], nk->af); + pd->naf = nk->af; + return (PF_AFRT); + } +#endif if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || @@ -7438,8 +8309,52 @@ /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = - (*state)->key[pd->didx]; + struct pf_state_key *nk; + + if (PF_REVERSED_KEY((*state)->key, pd->af)) + nk = (*state)->key[pd->sidx]; + else + nk = (*state)->key[pd->didx]; + +#if defined(INET) && defined(INET6) + int afto, sidx, didx; + + afto = pd->af != nk->af; + sidx = afto ? pd2.didx : pd2.sidx; + didx = afto ? pd2.sidx : pd2.didx; + iidx = afto ? !iidx : iidx; + + if (afto) { + if (nk->af != AF_INET) + return (PF_DROP); + if (pf_translate_icmp_af(nk->af, + &pd->hdr.icmp)) + return (PF_DROP); + m_copyback(pd->m, pd->off, + sizeof(struct icmp6_hdr), + (c_caddr_t)&pd->hdr.icmp6); + if (pf_change_icmp_af(pd->m, ipoff2, pd, + &pd2, &nk->addr[sidx], + &nk->addr[didx], pd->af, + nk->af)) + return (PF_DROP); + pd->proto = IPPROTO_ICMP; + if (pf_translate_icmp_af(nk->af, &iih)) + return (PF_DROP); + if (virtual_type == + htons(ICMP6_ECHO_REQUEST) && + nk->port[iidx] != iih->icmp6_id) + iih->icmp6_id = nk->port[iidx]; + m_copyback(pd2.m, pd2.off, + sizeof(struct icmp6_hdr), (c_caddr_t)&iih); + PF_ACPY(&pd->nsaddr, + &nk->addr[pd2.sidx], nk->af); + PF_ACPY(&pd->ndaddr, + &nk->addr[pd2.didx], nk->af); + pd->naf = nk->af; + return (PF_AFRT); + } +#endif if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || @@ -7532,6 +8447,7 @@ struct pf_state_peer *src, *dst; struct pf_state_key_cmp key; uint8_t psrc, pdst; + int action = PF_PASS; bzero(&key, sizeof(key)); key.af = pd->af; @@ -7575,22 +8491,33 @@ /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = (*state)->key[pd->didx]; + struct pf_state_key *nk; + int afto; + + if (PF_REVERSED_KEY((*state)->key, pd->af)) + nk = (*state)->key[pd->sidx]; + else + nk = (*state)->key[pd->didx]; KASSERT(nk, ("%s: nk is null", __func__)); KASSERT(pd, ("%s: pd is null", __func__)); KASSERT(pd->src, ("%s: pd->src is null", __func__)); KASSERT(pd->dst, ("%s: pd->dst is null", __func__)); + + afto = pd->af != nk->af; + switch (pd->af) { #ifdef INET case AF_INET: - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET)) + if (!afto && + PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET)) pf_change_a(&pd->src->v4.s_addr, pd->ip_sum, nk->addr[pd->sidx].v4.s_addr, 0); - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) + if (!afto && + PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) pf_change_a(&pd->dst->v4.s_addr, pd->ip_sum, nk->addr[pd->didx].v4.s_addr, @@ -7600,15 +8527,25 @@ #endif /* INET */ #ifdef INET6 case AF_INET6: - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET6)) + if (!afto && + PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET6)) PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af); - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET6)) + if (!afto && + PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET6)) PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af); #endif /* INET6 */ } + if (afto) { + PF_ACPY(&pd->nsaddr, + &nk->addr[afto ? pd->didx : pd->sidx], nk->af); + PF_ACPY(&pd->ndaddr, + &nk->addr[afto ? pd->sidx : pd->didx], nk->af); + pd->naf = nk->af; + action = PF_AFRT; + } } - return (PF_PASS); + return (action); } /* @@ -8633,12 +9570,12 @@ u_short *action, u_short *reason, struct pfi_kkif *kif, struct pf_rule_actions *default_actions) { - pd->af = af; pd->dir = dir; pd->kif = kif; pd->m = *m0; pd->sidx = (dir == PF_IN) ? 0 : 1; pd->didx = (dir == PF_IN) ? 1 : 0; + pd->af = pd->naf = af; TAILQ_INIT(&pd->sctp_multihome_jobs); if (default_actions != NULL) @@ -8906,7 +9843,7 @@ &pd->kif->pfik_packets[pd->af == AF_INET6][dir == PF_OUT][action != PF_PASS], 1); - if (action == PF_PASS || r->action == PF_DROP) { + if (action == PF_PASS || action == PF_AFRT || r->action == PF_DROP) { dirndx = (dir == PF_OUT); pf_counter_u64_add_protected(&r->packets[dirndx], 1); pf_counter_u64_add_protected(&r->bytes[dirndx], pd->tot_len); @@ -9128,7 +10065,7 @@ if (action == PF_DROP) goto done; action = pf_test_state_tcp(&s, &pd, &reason); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { if (V_pfsync_update_state_ptr != NULL) V_pfsync_update_state_ptr(s); r = s->rule; @@ -9174,7 +10111,7 @@ case IPPROTO_UDP: { action = pf_test_state_udp(&s, &pd); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { if (V_pfsync_update_state_ptr != NULL) V_pfsync_update_state_ptr(s); r = s->rule; @@ -9190,7 +10127,7 @@ if (action == PF_DROP) goto done; action = pf_test_state_sctp(&s, &pd, &reason); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { if (V_pfsync_update_state_ptr != NULL) V_pfsync_update_state_ptr(s); r = s->rule; @@ -9211,7 +10148,7 @@ goto done; } action = pf_test_state_icmp(&s, &pd, &reason); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { if (V_pfsync_update_state_ptr != NULL) V_pfsync_update_state_ptr(s); r = s->rule; @@ -9231,7 +10168,7 @@ goto done; } action = pf_test_state_icmp(&s, &pd, &reason); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { if (V_pfsync_update_state_ptr != NULL) V_pfsync_update_state_ptr(s); r = s->rule; @@ -9244,7 +10181,7 @@ default: action = pf_test_state_other(&s, &pd); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { if (V_pfsync_update_state_ptr != NULL) V_pfsync_update_state_ptr(s); r = s->rule; @@ -9418,6 +10355,26 @@ m_freem(*m0); *m0 = NULL; break; + case PF_AFRT: + if (pf_translate_af(&pd)) { + if (!pd.m) + *m0 = NULL; + action = PF_DROP; + break; + } + *m0 = pd.m; /* pf_translate_af may change pd.m */ +#ifdef INET + if (pd.naf == AF_INET) + pf_route(m0, r, kif->pfik_ifp, s, &pd, inp); +#endif +#ifdef INET6 + if (pd.naf == AF_INET6) + pf_route6(m0, r, kif->pfik_ifp, s, &pd, inp); +#endif + *m0 = NULL; + action = PF_PASS; + goto out; + break; default: if (pd.act.rt) { switch (af) { 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 @@ -611,6 +611,7 @@ pfi_kkif_unref(rule->rcv_kif); pf_kanchor_remove(rule); pf_empty_kpool(&rule->rdr.list); + pf_empty_kpool(&rule->nat.list); pf_krule_free(rule); } 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 @@ -48,10 +48,20 @@ #include #include +#include #include #include #include +#ifdef INET +#include +#endif + +#ifdef INET6 +#include +#endif + + /* * Limit the amount of work we do to find a free source port for redirects that * introduce a state conflict. @@ -67,7 +77,7 @@ int, struct pf_kanchor_stackframe *); static int pf_get_sport(struct pf_pdesc *, struct pf_krule *, struct pf_addr *, uint16_t *, uint16_t, uint16_t, struct pf_ksrc_node **, - struct pf_srchash **, struct pf_udp_mapping **); + struct pf_srchash **, struct pf_kpool *, struct pf_udp_mapping **); static bool pf_islinklocal(const sa_family_t, const struct pf_addr *); #define mix(a,b,c) \ @@ -220,14 +230,22 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r, struct pf_addr *naddr, uint16_t *nport, uint16_t low, uint16_t high, struct pf_ksrc_node **sn, - struct pf_srchash **sh, struct pf_udp_mapping **udp_mapping) + struct pf_srchash **sh, struct pf_kpool *rpool, + struct pf_udp_mapping **udp_mapping) { struct pf_state_key_cmp key; struct pf_addr init_addr; bzero(&init_addr, sizeof(init_addr)); - MPASS(*udp_mapping == NULL); + if (! TAILQ_EMPTY(&r->nat.list) && + pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL, &init_addr, + sn, sh, &r->nat)) + return (1); + + if (udp_mapping) { + MPASS(*udp_mapping == NULL); + } /* * If we are UDP and have an existing mapping we can get source port @@ -241,26 +259,29 @@ udp_source.af = pd->af; PF_ACPY(&udp_source.addr, &pd->nsaddr, pd->af); udp_source.port = pd->nsport; - *udp_mapping = pf_udp_mapping_find(&udp_source); - if (*udp_mapping) { - PF_ACPY(naddr, &(*udp_mapping)->endpoints[1].addr, pd->af); - *nport = (*udp_mapping)->endpoints[1].port; - /* Try to find a src_node as per pf_map_addr(). */ - if (*sn == NULL && r->rdr.opts & PF_POOL_STICKYADDR && - (r->rdr.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) - *sn = pf_find_src_node(&pd->nsaddr, r, pd->af, sh, false); - if (*sn != NULL) - PF_SRC_NODE_UNLOCK(*sn); - return (0); - } else { - *udp_mapping = pf_udp_mapping_create(pd->af, &pd->nsaddr, - pd->nsport, &init_addr, 0); - if (*udp_mapping == NULL) - return (1); + if (udp_mapping) { + *udp_mapping = pf_udp_mapping_find(&udp_source); + if (*udp_mapping) { + PF_ACPY(naddr, &(*udp_mapping)->endpoints[1].addr, pd->af); + *nport = (*udp_mapping)->endpoints[1].port; + /* Try to find a src_node as per pf_map_addr(). */ + if (*sn == NULL && r->rdr.opts & PF_POOL_STICKYADDR && + (r->rdr.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) + *sn = pf_find_src_node(&pd->nsaddr, r, pd->af, sh, false); + if (*sn != NULL) + PF_SRC_NODE_UNLOCK(*sn); + return (0); + } else { + *udp_mapping = pf_udp_mapping_create(pd->af, &pd->nsaddr, + pd->nsport, &init_addr, 0); + if (*udp_mapping == NULL) + return (1); + } } } - if (pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL, &init_addr, sn, sh)) + if (pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL, &init_addr, + sn, sh, rpool)) goto failed; if (pd->proto == IPPROTO_ICMP) { @@ -281,14 +302,14 @@ #endif /* INET6 */ bzero(&key, sizeof(key)); - key.af = pd->af; + key.af = pd->naf; key.proto = pd->proto; key.port[0] = pd->ndport; PF_ACPY(&key.addr[0], &pd->ndaddr, key.af); do { PF_ACPY(&key.addr[1], naddr, key.af); - if (*udp_mapping) + if (udp_mapping && *udp_mapping) PF_ACPY(&(*udp_mapping)->endpoints[1].addr, naddr, pd->af); /* @@ -317,7 +338,7 @@ } else if (low == high) { key.port[1] = htons(low); if (!pf_find_state_all_exists(&key, PF_IN)) { - if (*udp_mapping != NULL) { + if (udp_mapping && *udp_mapping != NULL) { (*udp_mapping)->endpoints[1].port = htons(low); if (pf_udp_mapping_insert(*udp_mapping) == 0) { *nport = htons(low); @@ -341,7 +362,7 @@ cut = arc4random() % (1 + high - low) + low; /* low <= cut <= high */ for (tmp = cut; tmp <= high && tmp <= 0xffff; ++tmp) { - if (*udp_mapping != NULL) { + if (udp_mapping && *udp_mapping != NULL) { (*udp_mapping)->endpoints[1].port = htons(tmp); if (pf_udp_mapping_insert(*udp_mapping) == 0) { *nport = htons(tmp); @@ -358,7 +379,8 @@ tmp = cut; for (tmp -= 1; tmp >= low && tmp <= 0xffff; --tmp) { if (pd->proto == IPPROTO_UDP && - (r->rdr.opts & PF_POOL_ENDPI)) { + (r->rdr.opts & PF_POOL_ENDPI && + udp_mapping != NULL)) { (*udp_mapping)->endpoints[1].port = htons(tmp); if (pf_udp_mapping_insert(*udp_mapping) == 0) { *nport = htons(tmp); @@ -383,7 +405,7 @@ */ (*sn) = NULL; if (pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL, - &init_addr, sn, sh)) + &init_addr, sn, sh, &r->rdr)) return (1); break; case PF_POOL_NONE: @@ -392,11 +414,14 @@ default: return (1); } - } while (! PF_AEQ(&init_addr, naddr, pd->af) ); + } while (! PF_AEQ(&init_addr, naddr, pd->naf) ); failed: - uma_zfree(V_pf_udp_mapping_z, *udp_mapping); - *udp_mapping = NULL; + if (udp_mapping) { + uma_zfree(V_pf_udp_mapping_z, *udp_mapping); + *udp_mapping = NULL; + } + return (1); /* none available */ } @@ -432,13 +457,15 @@ for (i = cut; i <= ahigh; i++) { low = (i << ashift) | psmask; if (!pf_get_sport(pd, r, - naddr, nport, low, low | highmask, sn, sh, udp_mapping)) + naddr, nport, low, low | highmask, sn, sh, &r->rdr, + udp_mapping)) return (0); } for (i = cut - 1; i > 0; i--) { low = (i << ashift) | psmask; if (!pf_get_sport(pd, r, - naddr, nport, low, low | highmask, sn, sh, udp_mapping)) + naddr, nport, low, low | highmask, sn, sh, &r->rdr, + udp_mapping)) return (0); } return (1); @@ -446,10 +473,10 @@ 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_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr, + struct pf_kpool *rpool) { u_short reason = PFRES_MATCH; - struct pf_kpool *rpool = &r->rdr; struct pf_addr *raddr = NULL, *rmask = NULL; mtx_lock(&rpool->mtx); @@ -621,10 +648,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_ksrc_node **sn, struct pf_srchash **sh) + struct pf_ksrc_node **sn, struct pf_srchash **sh, struct pf_kpool *rpool) { u_short reason = 0; - struct pf_kpool *rpool = &r->rdr; KASSERT(*sn == NULL, ("*sn not NULL")); @@ -667,7 +693,7 @@ * Source node has not been found. Find a new address and store it * in variables given by the caller. */ - if (pf_map_addr(af, r, saddr, naddr, nkif, init_addr) != 0) { + if (pf_map_addr(af, r, saddr, naddr, nkif, init_addr, rpool) != 0) { /* pf_map_addr() sets reason counters on its own */ goto done; } @@ -732,15 +758,8 @@ return (PFRES_MAX); } - *skp = pf_state_key_setup(pd, pd->nsport, pd->ndport); - if (*skp == NULL) - return (PFRES_MEMORY); - *nkp = pf_state_key_clone(*skp); - if (*nkp == NULL) { - uma_zfree(V_pf_state_key_z, *skp); - *skp = NULL; + if (pf_state_key_setup(pd, pd->nsport, pd->ndport, skp, nkp)) return (PFRES_MEMORY); - } naddr = &(*nkp)->addr[1]; nportp = &(*nkp)->port[1]; @@ -767,7 +786,7 @@ goto notrans; } } else if (pf_get_sport(pd, r, naddr, nportp, low, high, &sn, - &sh, udp_mapping)) { + &sh, &r->rdr, udp_mapping)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: NAT proxy port allocation (%u-%u) failed\n", r->rdr.proxy_port[0], r->rdr.proxy_port[1])); @@ -855,7 +874,7 @@ uint16_t cut, low, high, nport; reason = pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL, - NULL, &sn, &sh); + NULL, &sn, &sh, &r->rdr); if (reason != 0) goto notrans; if ((r->rdr.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK) @@ -965,3 +984,132 @@ return (reason); } + +int +pf_get_transaddr_af(struct pf_krule *r, struct pf_pdesc *pd) +{ +#if defined(INET) && defined(INET6) + struct pf_addr ndaddr, nsaddr, naddr; + u_int16_t nport = 0; + int prefixlen = 96; + struct pf_srchash *sh = NULL; + struct pf_ksrc_node *sns = NULL; + + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: af-to %s %s, ", + pd->naf == AF_INET ? "inet" : "inet6", + TAILQ_EMPTY(&r->rdr.list) ? "nat" : "rdr"); + pf_print_host(&pd->nsaddr, pd->nsport, pd->af); + printf(" -> "); + pf_print_host(&pd->ndaddr, pd->ndport, pd->af); + printf("\n"); + } + + if (TAILQ_EMPTY(&r->nat.list)) + panic("pf_get_transaddr_af: no nat pool for source address"); + + /* get source address and port */ + if (pf_get_sport(pd, r, &nsaddr, &nport, + r->nat.proxy_port[0], r->nat.proxy_port[1], &sns, &sh, &r->nat, NULL)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: af-to NAT proxy port allocation (%u-%u) failed", + r->nat.proxy_port[0], r->nat.proxy_port[1])); + return (-1); + } + + if (pd->proto == IPPROTO_ICMPV6 && pd->naf == AF_INET) { + if (pd->dir == PF_IN) { + NTOHS(pd->ndport); + if (pd->ndport == ICMP6_ECHO_REQUEST) + pd->ndport = ICMP_ECHO; + else if (pd->ndport == ICMP6_ECHO_REPLY) + pd->ndport = ICMP_ECHOREPLY; + HTONS(pd->ndport); + } else { + NTOHS(pd->nsport); + if (pd->nsport == ICMP6_ECHO_REQUEST) + pd->nsport = ICMP_ECHO; + else if (pd->nsport == ICMP6_ECHO_REPLY) + pd->nsport = ICMP_ECHOREPLY; + HTONS(pd->nsport); + } + } else if (pd->proto == IPPROTO_ICMP && pd->naf == AF_INET6) { + if (pd->dir == PF_IN) { + NTOHS(pd->ndport); + if (pd->ndport == ICMP_ECHO) + pd->ndport = ICMP6_ECHO_REQUEST; + else if (pd->ndport == ICMP_ECHOREPLY) + pd->ndport = ICMP6_ECHO_REPLY; + HTONS(pd->ndport); + } else { + NTOHS(pd->nsport); + if (pd->nsport == ICMP_ECHO) + pd->nsport = ICMP6_ECHO_REQUEST; + else if (pd->nsport == ICMP_ECHOREPLY) + pd->nsport = ICMP6_ECHO_REPLY; + HTONS(pd->nsport); + } + } + + /* get the destination address and port */ + if (! TAILQ_EMPTY(&r->rdr.list)) { + if (pf_map_addr_sn(pd->naf, r, &nsaddr, &naddr, NULL, NULL, + &sns, NULL, &r->rdr)) + return (-1); + if (r->rdr.proxy_port[0]) + pd->ndport = htons(r->rdr.proxy_port[0]); + + if (pd->naf == AF_INET) { + /* The prefix is the IPv4 rdr address */ + prefixlen = in_mask2len( + (struct in_addr *)&r->rdr.cur->addr.v.a.mask); + inet_nat46(pd->naf, &pd->ndaddr, &ndaddr, &naddr, + prefixlen); + } else { + /* The prefix is the IPv6 rdr address */ + prefixlen = in6_mask2len( + (struct in6_addr *)&r->rdr.cur->addr.v.a.mask, NULL); + inet_nat64(pd->naf, &pd->ndaddr, &ndaddr, &naddr, + prefixlen); + } + } else { + if (pd->naf == AF_INET) { + /* The prefix is the IPv6 dst address */ + prefixlen = in6_mask2len( + (struct in6_addr *)&r->dst.addr.v.a.mask, NULL); + if (prefixlen < 32) + prefixlen = 96; + inet_nat64(pd->naf, &pd->ndaddr, &ndaddr, &pd->ndaddr, + prefixlen); + } else { + /* + * The prefix is the IPv6 nat address + * (that was stored in pd->nsaddr) + */ + prefixlen = in6_mask2len( + (struct in6_addr *)&r->nat.cur->addr.v.a.mask, NULL); + if (prefixlen > 96) + prefixlen = 96; + inet_nat64(pd->naf, &pd->ndaddr, &ndaddr, &nsaddr, + prefixlen); + } + } + + PF_ACPY(&pd->nsaddr, &nsaddr, pd->naf); + PF_ACPY(&pd->ndaddr, &ndaddr, pd->naf); + + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: af-to %s done, prefixlen %d, ", + pd->naf == AF_INET ? "inet" : "inet6", + prefixlen); + pf_print_host(&pd->nsaddr, pd->nsport, pd->naf); + printf(" -> "); + pf_print_host(&pd->ndaddr, pd->ndport, pd->naf); + printf("\n"); + } + + return (0); +#else + return (-1); +#endif +} 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 @@ -263,6 +263,7 @@ PF_RT_RCV_IFNAME = 73, /* string */ PF_RT_MAX_SRC_CONN = 74, /* u32 */ PF_RT_RPOOL_NAT = 75, /* nested, pf_rpool_type_t */ + PF_RT_NAF = 76, /* u8 */ }; enum pf_addrule_type_t { 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 @@ -733,6 +733,7 @@ { .type = PF_RT_RCV_IFNAME, .off = _OUT(rcv_ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara }, { .type = PF_RT_MAX_SRC_CONN, .off = _OUT(max_src_conn), .cb = nlattr_get_uint32 }, { .type = PF_RT_RPOOL_NAT, .off = _OUT(nat), .arg = &pool_parser, .cb = nlattr_get_nested }, + { .type = PF_RT_NAF, .off = _OUT(naf), .cb = nlattr_get_uint8 }, }; NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule); #undef _OUT @@ -960,6 +961,7 @@ 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_NAF, rule->naf); 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);