diff options
author | 2011-10-13 18:23:39 +0000 | |
---|---|---|
committer | 2011-10-13 18:23:39 +0000 | |
commit | 97326e01c96e69168e367df5630a19ba8c336f1a (patch) | |
tree | 870cc72806275f81888b4d0c56bc437f2c869661 | |
parent | I'm sick and tired of people doing misalgned reads and writes to PCI config (diff) | |
download | wireguard-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/files | 3 | ||||
-rw-r--r-- | sys/net/if_pflog.c | 125 | ||||
-rw-r--r-- | sys/net/if_pflog.h | 5 | ||||
-rw-r--r-- | sys/net/if_pfsync.c | 22 | ||||
-rw-r--r-- | sys/net/pf.c | 1033 | ||||
-rw-r--r-- | sys/net/pf_ioctl.c | 4 | ||||
-rw-r--r-- | sys/net/pf_lb.c | 144 | ||||
-rw-r--r-- | sys/net/pfvar.h | 13 | ||||
-rw-r--r-- | sys/netinet/in.c | 3 | ||||
-rw-r--r-- | sys/netinet/in_var.h | 6 | ||||
-rw-r--r-- | sys/netinet/inet_nat64.c | 218 | ||||
-rw-r--r-- | sys/netinet6/in6.h | 3 | ||||
-rw-r--r-- | sys/netinet6/in6_var.h | 3 |
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, ©back) - == PF_DROP) + int ret; + + if (PF_REVERSED_KEY((*state)->key, pd->af)) + ret = pf_tcp_track_full(pd, dst, src, state, + reason, ©back); + else + ret = pf_tcp_track_full(pd, src, dst, state, + reason, ©back); + if (ret == PF_DROP) return (PF_DROP); } /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) { - struct pf_state_key *nk = (*state)->key[pd->didx]; + struct pf_state_key *nk; + int afto, sidx, didx; - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || - nk->port[pd->sidx] != th->th_sport) + if (PF_REVERSED_KEY((*state)->key, pd->af)) + nk = (*state)->key[pd->sidx]; + else + nk = (*state)->key[pd->didx]; + + afto = pd->af != nk->af; + sidx = afto ? pd->didx : pd->sidx; + didx = afto ? pd->sidx : pd->didx; + + if (afto || PF_ANEQ(pd->src, &nk->addr[sidx], pd->af) || + nk->port[sidx] != th->th_sport) pf_change_ap(pd->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 *, |