summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorclaudio <claudio@openbsd.org>2011-10-13 18:23:39 +0000
committerclaudio <claudio@openbsd.org>2011-10-13 18:23:39 +0000
commit97326e01c96e69168e367df5630a19ba8c336f1a (patch)
tree870cc72806275f81888b4d0c56bc437f2c869661
parentI'm sick and tired of people doing misalgned reads and writes to PCI config (diff)
downloadwireguard-openbsd-97326e01c96e69168e367df5630a19ba8c336f1a.tar.xz
wireguard-openbsd-97326e01c96e69168e367df5630a19ba8c336f1a.zip
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@
-rw-r--r--sys/conf/files3
-rw-r--r--sys/net/if_pflog.c125
-rw-r--r--sys/net/if_pflog.h5
-rw-r--r--sys/net/if_pfsync.c22
-rw-r--r--sys/net/pf.c1033
-rw-r--r--sys/net/pf_ioctl.c4
-rw-r--r--sys/net/pf_lb.c144
-rw-r--r--sys/net/pfvar.h13
-rw-r--r--sys/netinet/in.c3
-rw-r--r--sys/netinet/in_var.h6
-rw-r--r--sys/netinet/inet_nat64.c218
-rw-r--r--sys/netinet6/in6.h3
-rw-r--r--sys/netinet6/in6_var.h3
13 files changed, 1402 insertions, 180 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 4446d70a388..7fd9d226097 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.528 2011/10/06 20:49:28 deraadt Exp $
+# $OpenBSD: files,v 1.529 2011/10/13 18:23:39 claudio Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -850,6 +850,7 @@ file netinet/igmp.c inet
file netinet/in.c inet
file netinet/in_pcb.c inet
file netinet/in_proto.c inet
+file netinet/inet_nat64.c inet & pf
file netinet/ip_divert.c inet & pf
file netinet/ip_icmp.c inet
file netinet/ip_id.c inet
diff --git a/sys/net/if_pflog.c b/sys/net/if_pflog.c
index f389a99aae5..f8ae8be545d 100644
--- a/sys/net/if_pflog.c
+++ b/sys/net/if_pflog.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_pflog.c,v 1.43 2011/09/28 17:15:45 bluhm Exp $ */
+/* $OpenBSD: if_pflog.c,v 1.44 2011/10/13 18:23:39 claudio Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
@@ -63,6 +63,7 @@
#ifndef INET
#include <netinet/in.h>
#endif
+#include <netinet/ip6.h>
#include <netinet6/nd6.h>
#include <netinet/icmp6.h>
#endif /* INET6 */
@@ -91,7 +92,7 @@ struct if_clone pflog_cloner =
IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
struct ifnet *pflogifs[PFLOGIFS_MAX]; /* for fast access */
-struct mbuf *mfake = NULL;
+struct mbuf *pflog_mhdr = NULL, *pflog_mptr = NULL;
void
pflogattach(int npflog)
@@ -100,8 +101,12 @@ pflogattach(int npflog)
LIST_INIT(&pflogif_list);
for (i = 0; i < PFLOGIFS_MAX; i++)
pflogifs[i] = NULL;
- if (mfake == NULL)
- mfake = m_get(M_DONTWAIT, MT_HEADER);
+ if (pflog_mhdr == NULL)
+ if ((pflog_mhdr = m_get(M_DONTWAIT, MT_HEADER)) == NULL)
+ panic("pflogattach: no mbuf");
+ if (pflog_mptr == NULL)
+ if ((pflog_mptr = m_get(M_DONTWAIT, MT_DATA)) == NULL)
+ panic("pflogattach: no mbuf");
if_clone_attach(&pflog_cloner);
}
@@ -225,7 +230,6 @@ pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm,
bzero(&hdr, sizeof(hdr));
hdr.length = PFLOG_REAL_HDRLEN;
- hdr.af = pd->af;
hdr.action = rm->action;
hdr.reason = reason;
memcpy(hdr.ifname, pd->kif->pfik_name, sizeof(hdr.ifname));
@@ -253,8 +257,10 @@ pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm,
hdr.rule_pid = rm->cpid;
hdr.dir = pd->dir;
- PF_ACPY(&hdr.saddr, &pd->nsaddr, pd->af);
- PF_ACPY(&hdr.daddr, &pd->ndaddr, pd->af);
+ PF_ACPY(&hdr.saddr, &pd->nsaddr, pd->naf);
+ PF_ACPY(&hdr.daddr, &pd->ndaddr, pd->naf);
+ hdr.af = pd->af;
+ hdr.naf = pd->naf;
hdr.sport = pd->nsport;
hdr.dport = pd->ndport;
@@ -270,11 +276,12 @@ pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm,
void
pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
{
- const struct mbuf *m;
+ struct mbuf *m, *mp, *mhdr, *mptr;
struct pfloghdr *pfloghdr;
u_int count;
- u_char *dst;
+ u_char *dst, *mdst, *cp;
u_short action, reason;
+ int afto, hlen, mlen, off;
union pf_headers {
struct tcphdr tcp;
struct udphdr udp;
@@ -290,14 +297,18 @@ pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
struct pf_addr osaddr, odaddr;
u_int16_t osport = 0, odport = 0;
- m = src_arg;
+ m = (struct mbuf *)src_arg;
dst = dst_arg;
+ mhdr = pflog_mhdr;
+ mptr = pflog_mptr;
+
if (m == NULL)
panic("pflog_bpfcopy got no mbuf");
/* first mbuf holds struct pfloghdr */
pfloghdr = mtod(m, struct pfloghdr *);
+ afto = pfloghdr->af != pfloghdr->naf;
count = min(m->m_len, len);
bcopy(pfloghdr, dst, count);
pfloghdr = (struct pfloghdr *)dst;
@@ -305,36 +316,72 @@ pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
len -= count;
m = m->m_next;
- /* second mbuf is pkthdr */
- if (len > 0) {
- if (m == NULL)
- panic("no second mbuf");
- bcopy(m, mfake, sizeof(*mfake));
- mfake->m_flags &= ~(M_EXT|M_CLUSTER);
- mfake->m_next = NULL;
- mfake->m_nextpkt = NULL;
- mfake->m_data = dst;
- mfake->m_len = len;
- } else
+ if (len <= 0)
return;
- while (len > 0) {
- if (m == 0)
- panic("bpf_mcopy");
- count = min(m->m_len, len);
- bcopy(mtod(m, caddr_t), (caddr_t)dst, count);
- m = m->m_next;
- dst += count;
- len -= count;
+ /* second mbuf is pkthdr */
+ if (m == NULL)
+ panic("no second mbuf");
+
+ /*
+ * temporary mbuf will hold an ip/ip6 header and 8 bytes
+ * of the protocol header
+ */
+ m_inithdr(mhdr);
+ mhdr->m_len = 0; /* XXX not done in m_inithdr() */
+
+ /* offset for a new header */
+ if (afto && pfloghdr->af == AF_INET)
+ mhdr->m_data += sizeof(struct ip6_hdr) -
+ sizeof(struct ip);
+
+ mdst = mtod(mhdr, char *);
+ switch (pfloghdr->af) {
+ case AF_INET: {
+ struct ip *h;
+
+ m_copydata(m, 0, sizeof(*h), mdst);
+ h = (struct ip *)mdst;
+ hlen = h->ip_hl << 2;
+ if (hlen > sizeof(*h))
+ m_copydata(m, sizeof(*h), hlen - sizeof(*h),
+ mdst + sizeof(*h));
+ break;
+ }
+ case AF_INET6: {
+ hlen = sizeof(struct ip6_hdr);
+ m_copydata(m, 0, hlen, mdst);
+ break;
+ }
+ default:
+ /* shouldn't happen ever :-) */
+ m_copydata(m, 0, len, dst);
+ return;
}
- if (mfake->m_flags & M_PKTHDR)
- mfake->m_pkthdr.len = min(mfake->m_pkthdr.len, mfake->m_len);
+ /* copy 8 bytes of the protocol header */
+ m_copydata(m, hlen, 8, mdst + hlen);
+
+ mhdr->m_len += hlen + 8;
+ mhdr->m_pkthdr.len = mhdr->m_len;
+
+ /* create a chain mhdr -> mptr, mptr->m_data = (m->m_data+hlen+8) */
+ mp = m_getptr(m, hlen + 8, &off);
+ if (mp != NULL) {
+ bcopy(mp, mptr, sizeof(*mptr));
+ cp = mtod(mp, char *);
+ mptr->m_data += off;
+ mptr->m_len -= off;
+ mptr->m_flags &= ~M_PKTHDR;
+ mhdr->m_next = mptr;
+ mhdr->m_pkthdr.len += m->m_pkthdr.len - (hlen + 8);
+ }
/* rewrite addresses if needed */
if (pf_setup_pdesc(&pd, &pdhdrs, pfloghdr->af, pfloghdr->dir, NULL,
- &mfake, &action, &reason) == -1)
+ &mhdr, &action, &reason) == -1)
return;
+ pd.naf = pfloghdr->naf;
PF_ACPY(&osaddr, pd.src, pd.af);
PF_ACPY(&odaddr, pd.dst, pd.af);
@@ -346,11 +393,21 @@ pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
if ((pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr,
pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0,
pfloghdr->dir))) {
- m_copyback(pd.m, pd.off, min(pd.m->m_len - pd.off,
- pd.hdrlen), pd.hdr.any, M_NOWAIT);
+ m_copyback(pd.m, pd.off, min(pd.m->m_len - pd.off, pd.hdrlen),
+ pd.hdr.any, M_NOWAIT);
+ if (afto) {
+ PF_ACPY(&pd.nsaddr, &pfloghdr->saddr, pd.naf);
+ PF_ACPY(&pd.ndaddr, &pfloghdr->daddr, pd.naf);
+ }
PF_ACPY(&pfloghdr->saddr, &osaddr, pd.af);
PF_ACPY(&pfloghdr->daddr, &odaddr, pd.af);
pfloghdr->sport = osport;
pfloghdr->dport = odport;
}
+
+ if (afto)
+ pf_translate_af(&pd);
+
+ mlen = min(pd.m->m_pkthdr.len, len);
+ m_copydata(pd.m, 0, mlen, dst);
}
diff --git a/sys/net/if_pflog.h b/sys/net/if_pflog.h
index 7b1c7bead0c..d3a81a7bbdb 100644
--- a/sys/net/if_pflog.h
+++ b/sys/net/if_pflog.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_pflog.h,v 1.21 2011/09/28 17:15:45 bluhm Exp $ */
+/* $OpenBSD: if_pflog.h,v 1.22 2011/10/13 18:23:39 claudio Exp $ */
/*
* Copyright 2001 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
@@ -54,7 +54,8 @@ struct pfloghdr {
pid_t rule_pid;
u_int8_t dir;
u_int8_t rewritten;
- u_int8_t pad[2];
+ sa_family_t naf;
+ u_int8_t pad[1];
struct pf_addr saddr;
struct pf_addr daddr;
u_int16_t sport;
diff --git a/sys/net/if_pfsync.c b/sys/net/if_pfsync.c
index f8525bc8354..1a2912c435e 100644
--- a/sys/net/if_pfsync.c
+++ b/sys/net/if_pfsync.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_pfsync.c,v 1.167 2011/08/03 00:01:30 dlg Exp $ */
+/* $OpenBSD: if_pfsync.c,v 1.168 2011/10/13 18:23:39 claudio Exp $ */
/*
* Copyright (c) 2002 Michael Shalayeff
@@ -426,11 +426,13 @@ pfsync_state_export(struct pfsync_state *sp, struct pf_state *st)
sp->key[PF_SK_WIRE].port[0] = st->key[PF_SK_WIRE]->port[0];
sp->key[PF_SK_WIRE].port[1] = st->key[PF_SK_WIRE]->port[1];
sp->key[PF_SK_WIRE].rdomain = htons(st->key[PF_SK_WIRE]->rdomain);
+ sp->key[PF_SK_WIRE].af = st->key[PF_SK_WIRE]->af;
sp->key[PF_SK_STACK].addr[0] = st->key[PF_SK_STACK]->addr[0];
sp->key[PF_SK_STACK].addr[1] = st->key[PF_SK_STACK]->addr[1];
sp->key[PF_SK_STACK].port[0] = st->key[PF_SK_STACK]->port[0];
sp->key[PF_SK_STACK].port[1] = st->key[PF_SK_STACK]->port[1];
sp->key[PF_SK_STACK].rdomain = htons(st->key[PF_SK_STACK]->rdomain);
+ sp->key[PF_SK_STACK].af = st->key[PF_SK_STACK]->af;
sp->rtableid[PF_SK_WIRE] = htonl(st->rtableid[PF_SK_WIRE]);
sp->rtableid[PF_SK_STACK] = htonl(st->rtableid[PF_SK_STACK]);
sp->proto = st->key[PF_SK_WIRE]->proto;
@@ -532,7 +534,9 @@ pfsync_state_import(struct pfsync_state *sp, int flags)
if ((skw = pf_alloc_state_key(pool_flags)) == NULL)
goto cleanup;
- if (PF_ANEQ(&sp->key[PF_SK_WIRE].addr[0],
+ if ((sp->key[PF_SK_WIRE].af &&
+ (sp->key[PF_SK_WIRE].af != sp->key[PF_SK_STACK].af)) ||
+ PF_ANEQ(&sp->key[PF_SK_WIRE].addr[0],
&sp->key[PF_SK_STACK].addr[0], sp->af) ||
PF_ANEQ(&sp->key[PF_SK_WIRE].addr[1],
&sp->key[PF_SK_STACK].addr[1], sp->af) ||
@@ -556,7 +560,8 @@ pfsync_state_import(struct pfsync_state *sp, int flags)
skw->port[1] = sp->key[PF_SK_WIRE].port[1];
skw->rdomain = ntohs(sp->key[PF_SK_WIRE].rdomain);
skw->proto = sp->proto;
- skw->af = sp->af;
+ if (!(skw->af = sp->key[PF_SK_WIRE].af))
+ skw->af = sp->af;
if (sks != skw) {
sks->addr[0] = sp->key[PF_SK_STACK].addr[0];
sks->addr[1] = sp->key[PF_SK_STACK].addr[1];
@@ -564,7 +569,8 @@ pfsync_state_import(struct pfsync_state *sp, int flags)
sks->port[1] = sp->key[PF_SK_STACK].port[1];
sks->rdomain = ntohs(sp->key[PF_SK_STACK].rdomain);
sks->proto = sp->proto;
- sks->af = sp->af;
+ if (!(sks->af = sp->key[PF_SK_STACK].af))
+ sks->af = sp->af;
}
st->rtableid[PF_SK_WIRE] = ntohl(sp->rtableid[PF_SK_WIRE]);
st->rtableid[PF_SK_STACK] = ntohl(sp->rtableid[PF_SK_STACK]);
@@ -793,17 +799,23 @@ int
pfsync_in_ins(caddr_t buf, int len, int count, int flags)
{
struct pfsync_state *sp;
+ sa_family_t af1, af2;
int i;
for (i = 0; i < count; i++) {
sp = (struct pfsync_state *)(buf + len * i);
+ af1 = sp->key[0].af;
+ af2 = sp->key[1].af;
/* check for invalid values */
if (sp->timeout >= PFTM_MAX ||
sp->src.state > PF_TCPS_PROXY_DST ||
sp->dst.state > PF_TCPS_PROXY_DST ||
sp->direction > PF_OUT ||
- (sp->af != AF_INET && sp->af != AF_INET6)) {
+ (((af1 || af2) &&
+ ((af1 != AF_INET && af1 != AF_INET6) ||
+ (af2 != AF_INET && af2 != AF_INET6))) ||
+ (sp->af != AF_INET && sp->af != AF_INET6))) {
DPFPRINTF(LOG_NOTICE,
"pfsync_input: PFSYNC5_ACT_INS: invalid value");
pfsyncstats.pfsyncs_badval++;
diff --git a/sys/net/pf.c b/sys/net/pf.c
index b7cdbe13610..b17220d99ac 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.783 2011/10/07 14:24:10 henning Exp $ */
+/* $OpenBSD: pf.c,v 1.784 2011/10/13 18:23:39 claudio Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -149,7 +149,7 @@ int pf_check_threshold(struct pf_threshold *);
void pf_change_ap(struct pf_addr *, u_int16_t *,
u_int16_t *, struct pf_addr *, u_int16_t,
- u_int8_t, sa_family_t);
+ u_int8_t, sa_family_t, sa_family_t);
int pf_modulate_sack(struct pf_pdesc *,
struct pf_state_peer *);
#ifdef INET6
@@ -162,6 +162,11 @@ 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_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 *);
void pf_send_tcp(const struct pf_rule *, sa_family_t,
const struct pf_addr *, const struct pf_addr *,
u_int16_t, u_int16_t, u_int32_t, u_int32_t,
@@ -676,7 +681,12 @@ pf_state_key_attach(struct pf_state_key *sk, struct pf_state *s, int idx)
/* key exists. check for same kif, if none, add to key */
TAILQ_FOREACH(si, &cur->states, entry)
if (si->s->kif == s->kif &&
- si->s->direction == s->direction) {
+ ((si->s->key[PF_SK_WIRE]->af == sk->af &&
+ si->s->direction == s->direction) ||
+ (si->s->key[PF_SK_WIRE]->af !=
+ si->s->key[PF_SK_STACK]->af &&
+ sk->af == si->s->key[PF_SK_STACK]->af &&
+ si->s->direction != s->direction))) {
if (sk->proto == IPPROTO_TCP &&
si->s->src.state >= TCPS_FIN_WAIT_2 &&
si->s->dst.state >= TCPS_FIN_WAIT_2) {
@@ -806,7 +816,8 @@ pf_state_key_setup(struct pf_pdesc *pd, struct pf_state_key **skw,
if (rtableid >= 0)
wrdom = rtable_l2(rtableid);
- if (PF_ANEQ(&pd->nsaddr, pd->src, pd->af) ||
+ if (pd->af != pd->naf ||
+ PF_ANEQ(&pd->nsaddr, pd->src, pd->af) ||
PF_ANEQ(&pd->ndaddr, pd->dst, pd->af) ||
pd->nsport != pd->osport || pd->ndport != pd->odport ||
wrdom != pd->rdomain) { /* NAT */
@@ -814,12 +825,26 @@ pf_state_key_setup(struct pf_pdesc *pd, struct pf_state_key **skw,
pool_put(&pf_state_key_pl, sk1);
return (ENOMEM);
}
- PF_ACPY(&sk2->addr[pd->sidx], &pd->nsaddr, pd->af);
- PF_ACPY(&sk2->addr[pd->didx], &pd->ndaddr, pd->af);
- sk2->port[pd->sidx] = pd->nsport;
- sk2->port[pd->didx] = pd->ndport;
- sk2->proto = pd->proto;
- sk2->af = pd->af;
+ PF_ACPY(&sk2->addr[pd->af == pd->naf ? pd->sidx : pd->didx],
+ &pd->nsaddr, pd->naf);
+ PF_ACPY(&sk2->addr[pd->af == pd->naf ? pd->didx : pd->sidx],
+ &pd->ndaddr, pd->naf);
+ sk2->port[pd->af == pd->naf ? pd->sidx : pd->didx] = pd->nsport;
+ sk2->port[pd->af == pd->naf ? pd->didx : pd->sidx] = pd->ndport;
+ if (pd->af != pd->naf) {
+ switch (pd->proto) {
+ case IPPROTO_ICMP:
+ sk2->proto = IPPROTO_ICMPV6;
+ break;
+ case IPPROTO_ICMPV6:
+ sk2->proto = IPPROTO_ICMP;
+ break;
+ default:
+ sk2->proto = pd->proto;
+ }
+ } else
+ sk2->proto = pd->proto;
+ sk2->af = pd->naf;
sk2->rdomain = wrdom;
} else
sk2 = sk1;
@@ -965,8 +990,12 @@ pf_find_state(struct pfi_kif *kif, struct pf_state_key_cmp *key, u_int dir,
/* list is sorted, if-bound states before floating ones */
TAILQ_FOREACH(si, &sk->states, entry)
if ((si->s->kif == pfi_all || si->s->kif == kif) &&
- sk == (dir == PF_IN ? si->s->key[PF_SK_WIRE] :
- si->s->key[PF_SK_STACK]))
+ ((si->s->key[PF_SK_WIRE]->af == si->s->key[PF_SK_STACK]->af
+ && sk == (dir == PF_IN ? si->s->key[PF_SK_WIRE] :
+ si->s->key[PF_SK_STACK])) ||
+ (si->s->key[PF_SK_WIRE]->af != si->s->key[PF_SK_STACK]->af
+ && dir == PF_IN && (sk == si->s->key[PF_SK_STACK] ||
+ sk == si->s->key[PF_SK_WIRE]))))
return (si->s);
return (NULL);
@@ -1523,44 +1552,86 @@ pf_cksum_fixup(u_int16_t cksum, u_int16_t old, u_int16_t new, u_int8_t udp)
void
pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *pc,
- struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af)
+ struct pf_addr *an, u_int16_t pn, u_int8_t u, 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, naf);
+
*p = pn;
switch (af) {
#ifdef INET
case AF_INET:
- *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc,
- ao.addr16[0], an->addr16[0], u),
- ao.addr16[1], an->addr16[1], u),
- po, pn, u);
+ switch (naf) {
+ case AF_INET:
+ *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc,
+ ao.addr16[0], an->addr16[0], u),
+ ao.addr16[1], an->addr16[1], u),
+ 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);
+ 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(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),
- 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);
+ 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(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),
+ po, pn, u);
+ break;
+ }
break;
#endif /* INET6 */
}
}
-
/* Changes a u_int32_t. Uses a void * so there are no align restrictions */
void
pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u)
@@ -1857,6 +1928,390 @@ pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa,
}
}
+int
+pf_translate_af(struct pf_pdesc *pd)
+{
+#if INET && 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 */
+ if ((M_PREPEND(pd->m, hlen, M_DONTWAIT)) == 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));
+ ip4->ip_id = htons(ip_randomid());
+ 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;
+ 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;
+ 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 INET && 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_DONTWAIT)) == 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 */
+ if ((M_PREPEND(n, hlen, M_DONTWAIT)) == 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);
+ ip4->ip_id = htons(ip_randomid());
+ 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 INET && 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)
@@ -2815,8 +3270,8 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm,
r->skip[PF_SKIP_AF].ptr);
PF_TEST_ATTRIB((r->proto && r->proto != pd->proto),
r->skip[PF_SKIP_PROTO].ptr);
- PF_TEST_ATTRIB((PF_MISMATCHAW(&r->src.addr, &pd->nsaddr, pd->af,
- r->src.neg, pd->kif, act.rtableid)),
+ PF_TEST_ATTRIB((PF_MISMATCHAW(&r->src.addr, &pd->nsaddr,
+ pd->naf, r->src.neg, pd->kif, act.rtableid)),
r->skip[PF_SKIP_SRC_ADDR].ptr);
PF_TEST_ATTRIB((PF_MISMATCHAW(&r->dst.addr, &pd->ndaddr, pd->af,
r->dst.neg, NULL, act.rtableid)),
@@ -2915,6 +3370,8 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm,
/* order is irrelevant */
SLIST_INSERT_HEAD(&rules, ri, entry);
pf_rule_to_actions(r, &act);
+ if (r->naf)
+ pd->naf = r->naf;
if (pf_get_transaddr(r, pd, sns, &nr) == -1) {
REASON_SET(&reason, PFRES_MEMORY);
goto cleanup;
@@ -2948,6 +3405,8 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm,
/* apply actions for last matching pass/block rule */
pf_rule_to_actions(r, &act);
+ if (r->naf)
+ pd->naf = r->naf;
if (pf_get_transaddr(r, pd, sns, &nr) == -1) {
REASON_SET(&reason, PFRES_MEMORY);
goto cleanup;
@@ -3035,8 +3494,10 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm,
else
sk = skw;
rewrite += pf_translate(pd,
- &sk->addr[pd->sidx], sk->port[pd->sidx],
- &sk->addr[pd->didx], sk->port[pd->didx],
+ &sk->addr[pd->af == pd->naf ? pd->sidx : pd->didx],
+ sk->port[pd->af == pd->naf ? pd->sidx : pd->didx],
+ &sk->addr[pd->af == pd->naf ? pd->didx : pd->sidx],
+ sk->port[pd->af == pd->naf ? pd->didx : pd->sidx],
virtual_type, icmp_dir);
}
} else {
@@ -3067,7 +3528,10 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, struct pf_state **sm,
if (r->rule_flag & PFRULE_ONCE)
pf_purge_rule(ruleset, r);
- return (PF_PASS);
+ if (rewrite && skw->af != sks->af) {
+ return (PF_AFRT);
+ } else
+ return (PF_PASS);
cleanup:
while ((ri = SLIST_FIRST(&rules))) {
@@ -3281,33 +3745,38 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport,
* -pd is not fully set up
*/
int rewrite = 0;
+ int afto = pd->af != pd->naf;
- if (PF_ANEQ(daddr, pd->dst, pd->af))
+ if (afto || PF_ANEQ(daddr, pd->dst, pd->af))
pd->destchg = 1;
switch (pd->proto) {
case IPPROTO_TCP:
- if (PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport) {
+ if (afto || PF_ANEQ(saddr, pd->src, pd->af) ||
+ *pd->sport != sport) {
pf_change_ap(pd->src, pd->sport, &pd->hdr.tcp->th_sum,
- saddr, sport, 0, pd->af);
+ saddr, sport, 0, pd->af, pd->naf);
rewrite = 1;
}
- if (PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) {
+ if (afto || PF_ANEQ(daddr, pd->dst, pd->af) ||
+ *pd->dport != dport) {
pf_change_ap(pd->dst, pd->dport, &pd->hdr.tcp->th_sum,
- daddr, dport, 0, pd->af);
+ daddr, dport, 0, pd->af, pd->naf);
rewrite = 1;
}
break;
case IPPROTO_UDP:
- if (PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport) {
+ if (afto || PF_ANEQ(saddr, pd->src, pd->af) ||
+ *pd->sport != sport) {
pf_change_ap(pd->src, pd->sport, &pd->hdr.udp->uh_sum,
- saddr, sport, 1, pd->af);
+ saddr, sport, 1, pd->af, pd->naf);
rewrite = 1;
}
- if (PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) {
+ if (afto || PF_ANEQ(daddr, pd->dst, pd->af) ||
+ *pd->dport != dport) {
pf_change_ap(pd->dst, pd->dport, &pd->hdr.udp->uh_sum,
- daddr, dport, 1, pd->af);
+ daddr, dport, 1, pd->af, pd->naf);
rewrite = 1;
}
break;
@@ -3318,15 +3787,22 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport,
if (pd->af != AF_INET)
return (0);
- if (PF_ANEQ(saddr, pd->src, pd->af)) {
- pf_change_a(&pd->src->v4.s_addr, NULL,
- saddr->v4.s_addr, 0);
- rewrite = 1;
- }
- if (PF_ANEQ(daddr, pd->dst, pd->af)) {
- pf_change_a(&pd->dst->v4.s_addr, NULL,
- daddr->v4.s_addr, 0);
+ if (afto) {
+ if (pf_translate_icmp_af(AF_INET6, pd->hdr.icmp))
+ return (0);
+ pd->proto = IPPROTO_ICMPV6;
rewrite = 1;
+ } else {
+ if (PF_ANEQ(saddr, pd->src, pd->af)) {
+ pf_change_a(&pd->src->v4.s_addr, NULL,
+ saddr->v4.s_addr, 0);
+ rewrite = 1;
+ }
+ if (PF_ANEQ(daddr, pd->dst, pd->af)) {
+ pf_change_a(&pd->dst->v4.s_addr, NULL,
+ daddr->v4.s_addr, 0);
+ rewrite = 1;
+ }
}
if (virtual_type == htons(ICMP_ECHO)) {
u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport;
@@ -3348,15 +3824,23 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport,
if (pd->af != AF_INET6)
return (0);
- if (PF_ANEQ(saddr, pd->src, pd->af)) {
- pf_change_a6(pd->src, &pd->hdr.icmp6->icmp6_cksum,
- saddr, 0);
- rewrite = 1;
- }
- if (PF_ANEQ(daddr, pd->dst, pd->af)) {
- pf_change_a6(pd->dst, &pd->hdr.icmp6->icmp6_cksum,
- daddr, 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;
+ } else {
+ if (PF_ANEQ(saddr, pd->src, pd->af)) {
+ pf_change_a6(pd->src,
+ &pd->hdr.icmp6->icmp6_cksum, saddr, 0);
+ rewrite = 1;
+ }
+ if (PF_ANEQ(daddr, pd->dst, pd->af)) {
+ pf_change_a6(pd->dst,
+ &pd->hdr.icmp6->icmp6_cksum, daddr, 0);
+ rewrite = 1;
+ }
}
break;
#endif /* INET6 */
@@ -3365,12 +3849,12 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport,
switch (pd->af) {
#ifdef INET
case AF_INET:
- if (PF_ANEQ(saddr, pd->src, pd->af)) {
+ if (!afto && PF_ANEQ(saddr, pd->src, pd->af)) {
pf_change_a(&pd->src->v4.s_addr, NULL,
saddr->v4.s_addr, 0);
rewrite = 1;
}
- if (PF_ANEQ(daddr, pd->dst, pd->af)) {
+ if (!afto && PF_ANEQ(daddr, pd->dst, pd->af)) {
pf_change_a(&pd->dst->v4.s_addr, NULL,
daddr->v4.s_addr, 0);
rewrite = 1;
@@ -3379,11 +3863,11 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport,
#endif /* INET */
#ifdef INET6
case AF_INET6:
- if (PF_ANEQ(saddr, pd->src, pd->af)) {
+ if (!afto && PF_ANEQ(saddr, pd->src, pd->af)) {
pf_change_a6(pd->src, NULL, saddr, 0);
rewrite = 1;
}
- if (PF_ANEQ(daddr, pd->dst, pd->af)) {
+ if (!afto && PF_ANEQ(daddr, pd->dst, pd->af)) {
pf_change_a6(pd->dst, NULL, daddr, 0);
rewrite = 1;
}
@@ -3795,6 +4279,7 @@ pf_test_state_tcp(struct pf_pdesc *pd, struct pf_state **state, u_short *reason)
int copyback = 0;
struct pf_state_peer *src, *dst;
struct pf_state_key *sk;
+ int action = PF_PASS;
key.af = pd->af;
key.proto = IPPROTO_TCP;
@@ -3926,27 +4411,54 @@ pf_test_state_tcp(struct pf_pdesc *pd, struct pf_state **state, u_short *reason)
if (pf_tcp_track_sloppy(pd, src, dst, state, reason) == PF_DROP)
return (PF_DROP);
} else {
- if (pf_tcp_track_full(pd, src, dst, state, reason, &copyback)
- == PF_DROP)
+ int ret;
+
+ if (PF_REVERSED_KEY((*state)->key, pd->af))
+ ret = pf_tcp_track_full(pd, dst, src, state,
+ reason, &copyback);
+ else
+ ret = pf_tcp_track_full(pd, src, dst, state,
+ 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->src, &th->th_sport, &th->th_sum,
- &nk->addr[pd->sidx], nk->port[pd->sidx], 0, pd->af);
- if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
+ &nk->addr[sidx], nk->port[sidx], 0, pd->af, nk->af);
+
+ if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) ||
pd->rdomain != nk->rdomain)
pd->destchg = 1;
- 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->dst, &th->th_dport, &th->th_sum,
- &nk->addr[pd->didx], nk->port[pd->didx], 0, pd->af);
+ &nk->addr[didx], nk->port[didx], 0, pd->af,
+ nk->af);
pd->m->m_pkthdr.rdomain = nk->rdomain;
+
+ 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;
}
@@ -3954,7 +4466,7 @@ pf_test_state_tcp(struct pf_pdesc *pd, struct pf_state **state, u_short *reason)
if (copyback)
m_copyback(pd->m, pd->off, sizeof(*th), th, M_NOWAIT);
- return (PF_PASS);
+ return (action);
}
int
@@ -3963,6 +4475,7 @@ pf_test_state_udp(struct pf_pdesc *pd, struct pf_state **state)
struct pf_state_peer *src, *dst;
struct pf_state_key_cmp key;
struct udphdr *uh = pd->hdr.udp;
+ int action = PF_PASS;
key.af = pd->af;
key.proto = IPPROTO_UDP;
@@ -4004,24 +4517,43 @@ pf_test_state_udp(struct pf_pdesc *pd, struct pf_state **state)
/* 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->src, &uh->uh_sport, &uh->uh_sum,
- &nk->addr[pd->sidx], nk->port[pd->sidx], 1, pd->af);
- if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
+ &nk->addr[sidx], nk->port[sidx], 1, pd->af, nk->af);
+
+ if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) ||
pd->rdomain != nk->rdomain)
pd->destchg = 1;
- 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->dst, &uh->uh_dport, &uh->uh_sum,
- &nk->addr[pd->didx], nk->port[pd->didx], 1, pd->af);
+ &nk->addr[didx], nk->port[didx], 1, pd->af, nk->af);
pd->m->m_pkthdr.rdomain = nk->rdomain;
+
+ 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), uh, M_NOWAIT);
}
- return (PF_PASS);
+ return (action);
}
int
@@ -4133,7 +4665,18 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
/* 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;
if (pd->rdomain != nk->rdomain)
pd->destchg = 1;
@@ -4142,15 +4685,23 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
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, NULL,
- 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, NULL,
- nk->addr[pd->didx].v4.s_addr, 0);
+ nk->addr[didx].v4.s_addr, 0);
pd->destchg = 1;
}
@@ -4170,17 +4721,25 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
#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);
pd->destchg = 1;
}
@@ -4190,6 +4749,12 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
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);
@@ -4305,11 +4870,21 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
STATE_LOOKUP(pd2.kif, &key, pd2.dir, *state, pd2.m);
if (pd2.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)
@@ -4358,9 +4933,48 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
/* 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 ? pd2.didx : pd2.sidx;
+ didx = afto ? pd2.sidx : pd2.didx;
+
+#if INET && INET6
+ 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),
+ pd->hdr.icmp6, M_NOWAIT);
+ 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, &th,
+ M_NOWAIT);
+ pd->m->m_pkthdr.rdomain = nk->rdomain;
+ pd->destchg = 1;
+ 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) ||
nk->port[pd2.sidx] != th.th_sport)
@@ -4433,8 +5047,52 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
/* 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 ? pd2.didx : pd2.sidx;
+ didx = afto ? pd2.sidx : pd2.didx;
+
+#if INET && INET6
+ 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),
+ pd->hdr.icmp6, M_NOWAIT);
+ 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(pd2.src, &uh.uh_sport,
+ &uh.uh_sum, &nk->addr[pd2.sidx],
+ nk->port[sidx], 1, pd->af, nk->af);
+ pf_change_ap(pd2.dst, &uh.uh_dport,
+ &uh.uh_sum, &nk->addr[pd2.didx],
+ nk->port[didx], 1, pd->af, nk->af);
+ m_copyback(pd2.m, pd2.off, sizeof(uh),
+ &uh, M_NOWAIT);
+ pd->m->m_pkthdr.rdomain = nk->rdomain;
+ pd->destchg = 1;
+ 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) ||
@@ -4511,8 +5169,51 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
/* 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 ? pd2.didx : pd2.sidx;
+ didx = afto ? pd2.sidx : pd2.didx;
+ iidx = afto ? !iidx : iidx;
+
+#ifdef INET6
+ 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),
+ pd->hdr.icmp6, M_NOWAIT);
+ 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,
+ &iih, M_NOWAIT);
+ pd->m->m_pkthdr.rdomain = nk->rdomain;
+ pd->destchg = 1;
+ 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) ||
@@ -4585,8 +5286,53 @@ pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state,
/* 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 ? pd2.didx : pd2.sidx;
+ didx = afto ? pd2.sidx : pd2.didx;
+ iidx = afto ? !iidx : iidx;
+
+#ifdef INET
+ 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),
+ pd->hdr.icmp6, M_NOWAIT);
+ 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), &iih,
+ M_NOWAIT);
+ pd->m->m_pkthdr.rdomain = nk->rdomain;
+ pd->destchg = 1;
+ 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) ||
@@ -4691,6 +5437,7 @@ pf_test_state_other(struct pf_pdesc *pd, struct pf_state **state)
{
struct pf_state_peer *src, *dst;
struct pf_state_key_cmp key;
+ int action = PF_PASS;
key.af = pd->af;
key.proto = pd->proto;
@@ -4730,7 +5477,15 @@ pf_test_state_other(struct pf_pdesc *pd, struct pf_state **state)
/* 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];
+
+ afto = pd->af != nk->af;
KASSERT(nk);
KASSERT(pd);
@@ -4740,11 +5495,13 @@ pf_test_state_other(struct pf_pdesc *pd, struct pf_state **state)
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, NULL,
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, NULL,
nk->addr[pd->didx].v4.s_addr,
0);
@@ -4754,10 +5511,12 @@ pf_test_state_other(struct pf_pdesc *pd, struct pf_state **state)
#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);
pd->destchg = 1;
}
@@ -4767,9 +5526,19 @@ pf_test_state_other(struct pf_pdesc *pd, struct pf_state **state)
if (pd->rdomain != nk->rdomain)
pd->destchg = 1;
+ 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->destchg = 1;
+ pd->naf = nk->af;
+ action = PF_AFRT;
+ }
+
pd->m->m_pkthdr.rdomain = nk->rdomain;
}
- return (PF_PASS);
+ return (action);
}
/*
@@ -5555,12 +6324,12 @@ pf_setup_pdesc(struct pf_pdesc *pd, void *pdhdrs, sa_family_t af, int dir,
{
bzero(pd, sizeof(*pd));
pd->hdr.any = pdhdrs;
- pd->af = af;
pd->dir = dir;
pd->kif = kif; /* kif is NULL when called by pflog */
pd->m = *m0;
pd->sidx = (dir == PF_IN) ? 0 : 1;
pd->didx = (dir == PF_IN) ? 1 : 0;
+ pd->af = pd->naf = af;
switch (pd->af) {
#ifdef INET
@@ -5607,6 +6376,7 @@ pf_setup_pdesc(struct pf_pdesc *pd, void *pdhdrs, sa_family_t af, int dir,
pd->tot_len = ntohs(h->ip_len);
pd->tos = h->ip_tos;
pd->rdomain = rtable_l2(pd->m->m_pkthdr.rdomain);
+ pd->ttl = h->ip_ttl;
if (h->ip_hl > 5) /* has options */
pd->badopts++;
@@ -5688,6 +6458,7 @@ pf_setup_pdesc(struct pf_pdesc *pd, void *pdhdrs, sa_family_t af, int dir,
pd->virtual_proto = pd->proto = nxt;
pd->tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr);
pd->tos = 0;
+ pd->ttl = h->ip6_hlim;
pd->rdomain = 0;
if (fragoff != 0)
@@ -5804,7 +6575,7 @@ pf_counters_inc(int action, struct pf_pdesc *pd, struct pf_state *s,
pd->kif->pfik_packets[pd->af == AF_INET6][pd->dir == PF_OUT]
[action != PF_PASS]++;
- if (action == PF_PASS || r->action == PF_DROP) {
+ if (action == PF_PASS || action == PF_AFRT || r->action == PF_DROP) {
dirndx = (pd->dir == PF_OUT);
r->packets[dirndx]++;
r->bytes[dirndx] += pd->tot_len;
@@ -5921,7 +6692,7 @@ pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0,
if (action == PF_DROP)
goto done;
action = pf_test_state_tcp(&pd, &s, &reason);
- if (action == PF_PASS) {
+ if (action == PF_PASS || action == PF_AFRT) {
#if NPFSYNC > 0
pfsync_update_state(s);
#endif /* NPFSYNC */
@@ -5942,7 +6713,7 @@ pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0,
case IPPROTO_UDP: {
action = pf_test_state_udp(&pd, &s);
- if (action == PF_PASS) {
+ if (action == PF_PASS || action == PF_AFRT) {
#if NPFSYNC > 0
pfsync_update_state(s);
#endif /* NPFSYNC */
@@ -5963,7 +6734,7 @@ pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0,
goto done;
}
action = pf_test_state_icmp(&pd, &s, &reason);
- if (action == PF_PASS) {
+ if (action == PF_PASS || action == PF_AFRT) {
#if NPFSYNC > 0
pfsync_update_state(s);
#endif /* NPFSYNC */
@@ -5984,7 +6755,7 @@ pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0,
goto done;
}
action = pf_test_state_icmp(&pd, &s, &reason);
- if (action == PF_PASS) {
+ if (action == PF_PASS || action == PF_AFRT) {
#if NPFSYNC > 0
pfsync_update_state(s);
#endif /* NPFSYNC */
@@ -5998,7 +6769,7 @@ pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0,
default:
action = pf_test_state_other(&pd, &s);
- if (action == PF_PASS) {
+ if (action == PF_PASS || action == PF_AFRT) {
#if NPFSYNC > 0
pfsync_update_state(s);
#endif /* NPFSYNC */
@@ -6130,6 +6901,22 @@ done:
*m0 = NULL;
action = PF_PASS;
break;
+ case PF_AFRT:
+ if (pf_translate_af(&pd)) {
+ if (!pd.m)
+ *m0 = NULL;
+ action = PF_DROP;
+ break;
+ }
+ if (pd.naf == AF_INET)
+ pf_route(&pd.m, r, dir, kif->pfik_ifp, s);
+#ifdef INET6
+ if (pd.naf == AF_INET6)
+ pf_route6(&pd.m, r, dir, kif->pfik_ifp, s);
+#endif
+ *m0 = NULL;
+ action = PF_PASS;
+ break;
default:
/* pf_route can free the mbuf causing *m0 to become NULL */
if (r->rt) {
diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c
index fcd7cefdd33..2392481a84d 100644
--- a/sys/net/pf_ioctl.c
+++ b/sys/net/pf_ioctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_ioctl.c,v 1.243 2011/10/07 17:10:08 henning Exp $ */
+/* $OpenBSD: pf_ioctl.c,v 1.244 2011/10/13 18:23:40 claudio Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -2520,6 +2520,8 @@ pf_rule_copyin(struct pf_rule *from, struct pf_rule *to,
pf_pool_copyin(&from->rdr, &to->rdr);
pf_pool_copyin(&from->route, &to->route);
+ to->naf = from->naf;
+
if (pf_kif_setup(to->ifname, &to->kif))
return (EINVAL);
if (pf_kif_setup(to->rcv_ifname, &to->rcv_kif))
diff --git a/sys/net/pf_lb.c b/sys/net/pf_lb.c
index bd9e78f7745..6082cb905ef 100644
--- a/sys/net/pf_lb.c
+++ b/sys/net/pf_lb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_lb.c,v 1.18 2011/09/18 11:17:57 miod Exp $ */
+/* $OpenBSD: pf_lb.c,v 1.19 2011/10/13 18:23:40 claudio Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -103,6 +103,8 @@ void pf_hash(struct pf_addr *, struct pf_addr *,
int pf_get_sport(struct pf_pdesc *, struct pf_rule *,
struct pf_addr *, u_int16_t *, u_int16_t,
u_int16_t, struct pf_src_node **);
+int pf_get_transaddr_af(struct pf_rule *,
+ struct pf_pdesc *, struct pf_src_node **);
int pf_islinklocal(sa_family_t, struct pf_addr *);
#define mix(a,b,c) \
@@ -172,7 +174,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
u_int16_t cut;
bzero(&init_addr, sizeof(init_addr));
- if (pf_map_addr(pd->af, r, &pd->nsaddr, naddr, &init_addr, sn, &r->nat,
+ if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr, &init_addr, sn, &r->nat,
PF_SN_NAT))
return (1);
@@ -186,7 +188,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
}
do {
- key.af = pd->af;
+ key.af = pd->naf;
key.proto = pd->proto;
key.rdomain = pd->rdomain;
PF_ACPY(&key.addr[0], &pd->ndaddr, key.af);
@@ -251,7 +253,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
case PF_POOL_RANDOM:
case PF_POOL_ROUNDROBIN:
case PF_POOL_LEASTSTATES:
- if (pf_map_addr(pd->af, r, &pd->nsaddr, naddr,
+ if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr,
&init_addr, sn, &r->nat, PF_SN_NAT))
return (1);
break;
@@ -261,7 +263,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
default:
return (1);
}
- } while (! PF_AEQ(&init_addr, naddr, pd->af) );
+ } while (! PF_AEQ(&init_addr, naddr, pd->naf) );
return (1); /* none available */
}
@@ -580,6 +582,9 @@ pf_get_transaddr(struct pf_rule *r, struct pf_pdesc *pd,
struct pf_addr naddr;
u_int16_t nport = 0;
+ if (pd->af != pd->naf)
+ return (pf_get_transaddr_af(r, pd, sns));
+
if (r->nat.addr.type != PF_ADDR_NONE) {
/* XXX is this right? what if rtable is changed at the same
* XXX time? where do I need to figure out the sport? */
@@ -628,6 +633,135 @@ pf_get_transaddr(struct pf_rule *r, struct pf_pdesc *pd,
}
int
+pf_get_transaddr_af(struct pf_rule *r, struct pf_pdesc *pd,
+ struct pf_src_node **sns)
+{
+ struct pf_addr ndaddr, nsaddr, naddr;
+ u_int16_t nport = 0;
+ int prefixlen = 96;
+
+ if (pf_status.debug >= LOG_NOTICE) {
+ log(LOG_NOTICE, "pf: af-to %s %s, ",
+ pd->naf == AF_INET ? "inet" : "inet6",
+ r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr");
+ pf_print_host(&pd->nsaddr, pd->nsport, pd->af);
+ addlog(" -> ");
+ pf_print_host(&pd->ndaddr, pd->ndport, pd->af);
+ addlog("\n");
+ }
+
+ if (r->nat.addr.type == PF_ADDR_NONE)
+ 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)) {
+ DPFPRINTF(LOG_NOTICE,
+ "pf: af-to NAT proxy port allocation (%u-%u) failed",
+ r->nat.proxy_port[0],
+ r->nat.proxy_port[1]);
+ return (-1);
+ }
+ pd->nsport = nport;
+
+ 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 (r->rdr.addr.type != PF_ADDR_NONE) {
+ if (pf_map_addr(pd->naf, r, &nsaddr, &naddr, NULL, sns,
+ &r->rdr, PF_SN_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.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.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.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 (pf_status.debug >= LOG_NOTICE) {
+ log(LOG_NOTICE, "pf: af-to %s %s done, prefixlen %d, ",
+ pd->naf == AF_INET ? "inet" : "inet6",
+ r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr",
+ prefixlen);
+ pf_print_host(&pd->nsaddr, pd->nsport, pd->naf);
+ addlog(" -> ");
+ pf_print_host(&pd->ndaddr, pd->ndport, pd->naf);
+ addlog("\n");
+ }
+
+ return (0);
+}
+
+int
pf_postprocess_addr(struct pf_state *cur) {
struct pf_rule *nr;
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index fe1ec5f0e4f..96a8fed3a96 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfvar.h,v 1.353 2011/10/07 17:10:08 henning Exp $ */
+/* $OpenBSD: pfvar.h,v 1.354 2011/10/13 18:23:40 claudio Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -61,7 +61,7 @@ struct ip6_hdr;
enum { PF_INOUT, PF_IN, PF_OUT, PF_FWD };
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_DIVERT, PF_RT };
+ PF_MATCH, PF_DIVERT, PF_RT, PF_AFRT };
enum { PF_TRANS_RULESET, PF_TRANS_ALTQ, PF_TRANS_TABLE };
enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT,
PF_OP_LE, PF_OP_GT, PF_OP_GE, PF_OP_XRG, PF_OP_RRG };
@@ -651,7 +651,7 @@ struct pf_rule {
u_int8_t flush;
#define PF_PRIO_NOTSET 0xff
u_int8_t prio[2];
- u_int8_t pad;
+ sa_family_t naf;
struct {
struct pf_addr addr;
@@ -712,6 +712,7 @@ struct pf_src_node {
u_int32_t creation;
u_int32_t expire;
sa_family_t af;
+ sa_family_t naf;
u_int8_t type;
};
@@ -789,6 +790,9 @@ struct pf_state_key {
struct pf_state_key *reverse;
struct inpcb *inp;
};
+#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_state, used in RB_FIND */
struct pf_state_cmp {
@@ -1256,8 +1260,10 @@ struct pf_pdesc {
u_int16_t virtual_proto;
#define PF_VPROTO_FRAGMENT 256
sa_family_t af;
+ sa_family_t naf;
u_int8_t proto;
u_int8_t tos;
+ u_int8_t ttl;
u_int8_t dir; /* direction */
u_int8_t sidx; /* key index for source */
u_int8_t didx; /* key index for destination */
@@ -1824,6 +1830,7 @@ void pf_pkt_addr_changed(struct mbuf *);
int pf_state_key_attach(struct pf_state_key *, struct pf_state *, 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);
int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t);
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index 9167486dd95..a611aae8f0c 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in.c,v 1.68 2011/07/06 01:57:37 dlg Exp $ */
+/* $OpenBSD: in.c,v 1.69 2011/10/13 18:23:40 claudio Exp $ */
/* $NetBSD: in.c,v 1.26 1996/02/13 23:41:39 christos Exp $ */
/*
@@ -87,7 +87,6 @@
#ifdef INET
-int in_mask2len(struct in_addr *);
void in_len2mask(struct in_addr *, int);
int in_lifaddr_ioctl(struct socket *, u_long, caddr_t,
struct ifnet *);
diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h
index ec3530d1d6a..e87aee999f2 100644
--- a/sys/netinet/in_var.h
+++ b/sys/netinet/in_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_var.h,v 1.16 2010/11/17 19:25:49 henning Exp $ */
+/* $OpenBSD: in_var.h,v 1.17 2011/10/13 18:23:40 claudio Exp $ */
/* $NetBSD: in_var.h,v 1.16 1996/02/13 23:42:15 christos Exp $ */
/*
@@ -212,6 +212,10 @@ struct in_multi *in_addmulti(struct in_addr *, struct ifnet *);
void in_delmulti(struct in_multi *);
void in_ifscrub(struct ifnet *, struct in_ifaddr *);
int in_control(struct socket *, u_long, caddr_t, struct ifnet *);
+
+int inet_nat64(int, const void *, void *, const void *, u_int8_t);
+int inet_nat46(int, const void *, void *, const void *, u_int8_t);
+int in_mask2len(struct in_addr *);
#endif
diff --git a/sys/netinet/inet_nat64.c b/sys/netinet/inet_nat64.c
new file mode 100644
index 00000000000..c184525977d
--- /dev/null
+++ b/sys/netinet/inet_nat64.c
@@ -0,0 +1,218 @@
+/* $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>
+#ifdef _KERNEL
+#include <sys/mbuf.h>
+#include <netinet/in.h>
+#else
+#include <netinet/in.h>
+#include <errno.h>
+#endif
+
+union inet_nat64_addr {
+ u_int32_t u32[4];
+ u_int8_t u8[16];
+};
+
+u_int32_t inet_nat64_mask(u_int32_t, u_int32_t, u_int8_t);
+
+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);
+
+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/netinet6/in6.h b/sys/netinet6/in6.h
index d4cef9a013b..4b55f0e1f03 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6.h,v 1.53 2011/05/02 13:48:38 mikeb Exp $ */
+/* $OpenBSD: in6.h,v 1.54 2011/10/13 18:23:40 claudio Exp $ */
/* $KAME: in6.h,v 1.83 2001/03/29 02:55:07 jinmei Exp $ */
/*
@@ -787,6 +787,7 @@ int in6_addrscope(struct in6_addr *);
struct in6_ifaddr *in6_ifawithscope(struct ifnet *, struct in6_addr *);
extern void in6_if_up(struct ifnet *);
void in6_get_rand_ifid(struct ifnet *, struct in6_addr *);
+int in6_mask2len(struct in6_addr *, u_char *);
#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
#define sin6tosa(sin6) ((struct sockaddr *)(sin6))
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 6c485d39f85..55f85009a71 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in6_var.h,v 1.33 2011/07/26 21:19:51 bluhm Exp $ */
+/* $OpenBSD: in6_var.h,v 1.34 2011/10/13 18:23:40 claudio Exp $ */
/* $KAME: in6_var.h,v 1.55 2001/02/16 12:49:45 itojun Exp $ */
/*
@@ -572,7 +572,6 @@ struct in6_multi *in6_addmulti(struct in6_addr *, struct ifnet *, int *);
void in6_delmulti(struct in6_multi *);
struct in6_multi_mship *in6_joingroup(struct ifnet *, struct in6_addr *, int *);
int in6_leavegroup(struct in6_multi_mship *);
-int in6_mask2len(struct in6_addr *, u_char *);
int in6_control(struct socket *, u_long, caddr_t, struct ifnet *,
struct proc *);
int in6_update_ifa(struct ifnet *, struct in6_aliasreq *,