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
This commit is contained in:
Kristof Provost 2024-10-07 10:52:01 +02:00
parent e4e0f49742
commit fcdb520c1b
11 changed files with 1522 additions and 180 deletions

View file

@ -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

View file

@ -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

View file

@ -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];

View file

@ -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_ */

204
sys/netpfil/pf/inet_nat64.c Normal file
View file

@ -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);
}

File diff suppressed because it is too large Load diff

View file

@ -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;
};

View file

@ -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);
}

View file

@ -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)
if (pf_state_key_setup(pd, pd->nsport, pd->ndport, skp, nkp))
return (PFRES_MEMORY);
*nkp = pf_state_key_clone(*skp);
if (*nkp == NULL) {
uma_zfree(V_pf_state_key_z, *skp);
*skp = NULL;
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
}

View file

@ -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);

View file

@ -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 {