aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2024-10-07 08:52:01 +0000
committerKristof Provost <kp@FreeBSD.org>2024-12-17 10:07:12 +0000
commitfcdb520c1b4e1a5d5a2e54cb916dccbc848d32ba (patch)
tree25e70cf572ca97af7e505bf3840d47ec15123c0c
parente4e0f497429c635a02897d86c4eb5ec649cc2df8 (diff)
pf: nat64
Since the IPv6 madness is not enough introduce NAT64 -- which is actually "af-to" a generic IP version translator for pf(4). Not everything perfect yet but lets fix these things in the tree. Insane amount of work done by sperreault@, mikeb@ and reyk@. Looked over by mcbride@ henning@ and myself at eurobsdcon. OK mcbride@ and general put it in from deraadt@ Obtained from: OpenBSD, claudio <claudio@openbsd.org>, 97326e01c9 Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D47786
-rw-r--r--sys/conf/files1
-rw-r--r--sys/modules/pf/Makefile2
-rw-r--r--sys/net/if_pflog.h4
-rw-r--r--sys/net/pfvar.h33
-rw-r--r--sys/netpfil/pf/inet_nat64.c204
-rw-r--r--sys/netpfil/pf/pf.c1211
-rw-r--r--sys/netpfil/pf/pf.h5
-rw-r--r--sys/netpfil/pf/pf_ioctl.c1
-rw-r--r--sys/netpfil/pf/pf_lb.c240
-rw-r--r--sys/netpfil/pf/pf_nl.c2
-rw-r--r--sys/netpfil/pf/pf_nl.h1
11 files changed, 1523 insertions, 181 deletions
diff --git a/sys/conf/files b/sys/conf/files
index c1b7aac99c4c..428a2805768c 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4559,6 +4559,7 @@ netpfil/pf/pf_table.c optional pf inet
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
index 4a12730f3610..ad9790704cf1 100644
--- 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
index b2052d5bd5f3..9734ca245eda 100644
--- a/sys/net/if_pflog.h
+++ b/sys/net/if_pflog.h
@@ -51,7 +51,9 @@ struct pfloghdr {
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
index 232fa404e1d9..094bc38c4a1b 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -864,6 +864,7 @@ struct pf_krule {
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 @@ struct pf_state_key {
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 @@ struct pf_pdesc {
#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 pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kkif *,
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 @@ int pf_step_out_of_keth_anchor(struct pf_keth_anchor_stackframe *,
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 @@ void pf_scrub(struct pf_pdesc *);
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
index 000000000000..7f62814c2383
--- /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 <reyk@vantronix.net>
+ *
+ * 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 <sys/param.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+
+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.c b/sys/netpfil/pf/pf.c
index 95000bf0fd48..860f9a8fce64 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -301,7 +301,7 @@ static int pf_check_threshold(struct pf_threshold *);
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 @@ static void pf_change_icmp(struct pf_addr *, u_int16_t *,
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 @@ pf_packet_rework_nat(struct mbuf *m, struct pf_pdesc *pd, int off,
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 @@ pf_packet_rework_nat(struct mbuf *m, struct pf_pdesc *pd, int off,
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 @@ pf_packet_rework_nat(struct mbuf *m, struct pf_pdesc *pd, int off,
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 @@ keyattach:
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 @@ copy:
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 @@ pf_find_state(struct pfi_kkif *kif, const struct pf_state_key_cmp *key,
}
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 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2)
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 @@ pf_proto_cksum_fixup(struct mbuf *m, u_int16_t cksum, u_int16_t old,
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 @@ pf_change_ap(struct mbuf *m, struct pf_addr *a, u_int16_t *p, u_int16_t *ic,
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 @@ pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa,
}
}
+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 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
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 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
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 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
&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 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
&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_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
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_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
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 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
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_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
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 @@ nextrule:
/* 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 @@ nextrule:
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 @@ nextrule:
(!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 @@ nextrule:
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 @@ nextrule:
*/
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 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
__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 @@ drop:
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 @@ pf_tcp_track_full(struct pf_kstate **state, struct pf_pdesc *pd,
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 @@ pf_test_state_tcp(struct pf_kstate **state, struct pf_pdesc *pd,
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 @@ pf_test_state_tcp(struct pf_kstate **state, struct pf_pdesc *pd,
if (pf_tcp_track_sloppy(state, pd, reason) == PF_DROP)
return (PF_DROP);
} else {
- if (pf_tcp_track_full(state, pd, reason,
- &copyback) == PF_DROP)
+ int ret;
+
+ ret = pf_tcp_track_full(state, pd, reason,
+ &copyback);
+ 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 @@ pf_test_state_tcp(struct pf_kstate **state, struct pf_pdesc *pd,
if (copyback)
m_copyback(pd->m, pd->off, sizeof(*th), (caddr_t)th);
- return (PF_PASS);
+ return (action);
}
static int
@@ -6288,6 +6964,7 @@ pf_test_state_udp(struct pf_kstate **state, struct pf_pdesc *pd)
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 @@ pf_test_state_udp(struct pf_kstate **state, struct pf_pdesc *pd)
/* 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 @@ pf_test_state_sctp(struct pf_kstate **state, struct pf_pdesc *pd,
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 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
/* 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 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
#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 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
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 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
/* 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 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
/* 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 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
/* 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 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
/* 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 @@ pf_test_state_other(struct pf_kstate **state, struct pf_pdesc *pd)
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 @@ pf_test_state_other(struct pf_kstate **state, struct pf_pdesc *pd)
/* 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 @@ pf_test_state_other(struct pf_kstate **state, struct pf_pdesc *pd)
#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 @@ pf_setup_pdesc(sa_family_t af, int dir, struct pf_pdesc *pd, struct mbuf **m0,
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 @@ pf_counters_inc(int action, struct pf_pdesc *pd,
&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 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
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 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
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 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
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 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
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 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
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 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
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 @@ done:
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.h b/sys/netpfil/pf/pf.h
index 77d4ea6c5e70..0d4d0a6a6f32 100644
--- 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 @@ enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM,
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 @@ struct pf_src_node {
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_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 35af04eda837..188681329b43 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -611,6 +611,7 @@ pf_free_rule(struct pf_krule *rule)
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
index c216ea9f7214..0f08226c1c0d 100644
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -48,10 +48,20 @@
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_var.h>
#include <net/vnet.h>
#include <net/pfvar.h>
#include <net/if_pflog.h>
+#ifdef INET
+#include <netinet/in_var.h>
+#endif
+
+#ifdef INET6
+#include <netinet6/in6_var.h>
+#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 @@ static struct pf_krule *pf_match_translation(struct pf_pdesc *,
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 @@ static int
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 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
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 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
#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 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
} 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 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
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 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
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 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
*/
(*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 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
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 @@ pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r,
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 @@ pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r,
u_short
pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
- struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr)
+ struct pf_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 @@ done_pool_mtx:
u_short
pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr,
- struct pf_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 @@ pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
* Source node has not been found. Find a new address and store it
* in variables given by the caller.
*/
- if (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 @@ pf_get_translation(struct pf_pdesc *pd, int off,
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 @@ pf_get_translation(struct pf_pdesc *pd, int off,
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 @@ pf_get_translation(struct pf_pdesc *pd, int off,
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 @@ notrans:
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.c b/sys/netpfil/pf/pf_nl.c
index 52d77034c4b7..d2a050140dbc 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -733,6 +733,7 @@ static const struct nlattr_parser nla_p_rule[] = {
{ .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 @@ pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
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);
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index a317bb47f713..096b9913d4a6 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -263,6 +263,7 @@ enum pf_rule_type_t {
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 {