From nobody Tue Dec 17 10:07:43 2024 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4YCCDH2f6Rz5h0hQ; Tue, 17 Dec 2024 10:07:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4YCCDH1WD1z49Qr; Tue, 17 Dec 2024 10:07:43 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1734430063; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=H1yC6RJxwgcOzaMz2q/6Fi3Y+pYj+f2Ju44xzRn+9UM=; b=jr7TwsHqe7wEHRflZQz53sOL9Z9Zhw+A8KRU8v7cNJvgYiuLSUsgAcIxw6aQxeqG9bmuWH CjdrlpilfkA/McPvyk+2w7Yzhapy5iQOG9plhCt0whirTGLXbBBZYMkvvxcni59KTsB8n/ 6H4RJbxColoSM+1ckmWC4bBPHK5Au56FPwg+Hr8R4ZyCSNrEAtKUHEL6i8MK39hOc9rnRd Bf4zWm4cB6re4pVtuw1BM3wmJc3WCaQkXC/NmzLRz5D5R/9ey2M/lYRmoZ7hRBjihvpxbq IV79ry0mJqXkW9muJonDQ50J2Jd7ETKWY5tcflbdM6R3U1WRZn3ht/xbtLs8Qw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1734430063; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=H1yC6RJxwgcOzaMz2q/6Fi3Y+pYj+f2Ju44xzRn+9UM=; b=oKImpBJrUP/y6V5UcicFSTpSHexlSL47I6APBUTBHtcbhq+kN4a4k1LyWamLK5kAM2mHdt BRxTtGFD2ydVBTFzwtI0ACUZCyKK5qbeVDqyaABrRetB16OY47kCta0nGbHKqQ/Ox6c9/s 5VCoTu/a2ltDwwIJJBik/zYorFkQOwvOahF/l8VduThFil5DGiZdegf8A3Yf4ybXd1qGsA v2N9KkfXnrn8V/TLu5VTgJ90NA63zDS/34+CXtCPks8r0R2oCTSVUUVXjkBHmLWU3ghyuw Iwv5ua2d+24WY9HEThunuRfJwrABmg3xZSqAdPzB012qn6qMnumCSP+tmU4wTQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1734430063; a=rsa-sha256; cv=none; b=fwiuYdRL2+zuyoCP7J/tlY1Uk6nfBEawn/Ru1B2+/zEm3g0IkHxTvZSvvpuzW7jKj1Mt0j Bru9dwNbSqf218iCvXhW2pQafHvaqnzLipDmfCqfGtDelVYlifAhAZ6JGgjHOyTkaVq+rP xU2pDJrQbhqSA+Qrarybwu1RTtclt8F9Q8sSlUeTvZomg8BOIU6PmDm13BGEd/0ilbVcKK tU7Ds6LoRjAEGQAuCheidPIWd9+iEFNd9DHLIhZHV2o1eqPR7CksCjdpBrWM5CLkkgbLPH h9C9M6SzZksdToHcvcdFttyLsoMTLzDRfCOtDHnIp9oQ4urahVosCgP8Ulxa3A== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4YCCDH16FtzJvW; Tue, 17 Dec 2024 10:07:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 4BHA7hWL023373; Tue, 17 Dec 2024 10:07:43 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 4BHA7huu023370; Tue, 17 Dec 2024 10:07:43 GMT (envelope-from git) Date: Tue, 17 Dec 2024 10:07:43 GMT Message-Id: <202412171007.4BHA7huu023370@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: fcdb520c1b4e - main - pf: nat64 List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: fcdb520c1b4e1a5d5a2e54cb916dccbc848d32ba Auto-Submitted: auto-generated The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=fcdb520c1b4e1a5d5a2e54cb916dccbc848d32ba commit fcdb520c1b4e1a5d5a2e54cb916dccbc848d32ba Author: Kristof Provost AuthorDate: 2024-10-07 08:52:01 +0000 Commit: Kristof Provost CommitDate: 2024-12-17 10:07:12 +0000 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 , 97326e01c9 Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D47786 --- sys/conf/files | 1 + sys/modules/pf/Makefile | 2 +- sys/net/if_pflog.h | 4 +- sys/net/pfvar.h | 33 +- sys/netpfil/pf/inet_nat64.c | 204 ++++++++ sys/netpfil/pf/pf.c | 1211 ++++++++++++++++++++++++++++++++++++++----- sys/netpfil/pf/pf.h | 5 +- sys/netpfil/pf/pf_ioctl.c | 1 + sys/netpfil/pf/pf_lb.c | 240 +++++++-- sys/netpfil/pf/pf_nl.c | 2 + sys/netpfil/pf/pf_nl.h | 1 + 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 + * + * 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.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: *** 1551 LINES SKIPPED ***