Index: conf/files =================================================================== RCS file: /home/cvs/src/sys/conf/files,v retrieving revision 1.512 diff -u -p -r1.512 files --- conf/files 7 Apr 2011 13:42:53 -0000 1.512 +++ conf/files 14 Apr 2011 09:19:03 -0000 @@ -851,6 +851,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 Index: net/if_pflog.c =================================================================== RCS file: /home/cvs/src/sys/net/if_pflog.c,v retrieving revision 1.33 diff -u -p -r1.33 if_pflog.c --- net/if_pflog.c 7 Dec 2010 11:39:40 -0000 1.33 +++ net/if_pflog.c 11 May 2011 10:07:51 -0000 @@ -63,6 +63,7 @@ #ifndef INET #include #endif +#include #include #include #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); } @@ -226,7 +231,6 @@ pflog_packet(struct pfi_kif *kif, struct bzero(&hdr, sizeof(hdr)); hdr.length = PFLOG_REAL_HDRLEN; - hdr.af = af; hdr.action = rm->action; hdr.reason = reason; memcpy(hdr.ifname, kif->pfik_name, sizeof(hdr.ifname)); @@ -254,8 +258,10 @@ pflog_packet(struct pfi_kif *kif, struct hdr.rule_pid = rm->cpid; hdr.dir = 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; @@ -271,12 +277,12 @@ pflog_packet(struct pfi_kif *kif, struct 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 off = 0, hdrlen = 0; + int afto, hlen, mlen, off = 0, hdrlen = 0; union { struct tcphdr tcp; struct udphdr udp; @@ -290,14 +296,18 @@ pflog_bpfcopy(const void *src_arg, void 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,38 +315,72 @@ pflog_bpfcopy(const void *src_arg, void 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"); + + /* mhdr will hold an ip/ip6 header and 8 bytes of the protocol header */ + m_inithdr(mhdr); + mhdr->m_len = 0; + mhdr->m_pkthdr.len = 0; + + /* reserve space for header expansion (ip -> ip6) */ + 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 */ memset(&pd, 0, sizeof(pd)); pd.hdr.any = &pf_hdrs; - if (pf_setup_pdesc(pfloghdr->af, pfloghdr->dir, &pd, mfake, &action, + if (pf_setup_pdesc(pfloghdr->af, pfloghdr->dir, &pd, mhdr, &action, &reason, NULL, NULL, NULL, NULL, &off, &hdrlen) == -1) return; + pd.naf = pfloghdr->naf; PF_ACPY(&osaddr, pd.src, pd.af); PF_ACPY(&odaddr, pd.dst, pd.af); @@ -348,11 +392,27 @@ pflog_bpfcopy(const void *src_arg, void if ((pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr, pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0, pfloghdr->dir))) { - m_copyback(mfake, off, min(mfake->m_len - off, hdrlen), - pd.hdr.any, M_NOWAIT); + m_copyback(mhdr, off, min(mhdr->m_len - off, 8), 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; } + + pd.tot_len = min(pd.tot_len, len); + pd.tot_len -= mhdr->m_data - mhdr->m_pktdat; + + if (afto) + pf_translate_af(pd.naf, &mhdr, off, &pd); + + mlen = min(mhdr->m_pkthdr.len, len); + m_copydata(mhdr, 0, mlen, dst); + len -= mlen; + if (len > 0) + bzero(dst + mlen, len); } Index: net/if_pflog.h =================================================================== RCS file: /home/cvs/src/sys/net/if_pflog.h,v retrieving revision 1.17 diff -u -p -r1.17 if_pflog.h --- net/if_pflog.h 21 Sep 2010 11:05:10 -0000 1.17 +++ net/if_pflog.h 24 Mar 2011 19:59:08 -0000 @@ -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; Index: net/if_pfsync.c =================================================================== RCS file: /home/cvs/src/sys/net/if_pfsync.c,v retrieving revision 1.163 diff -u -p -r1.163 if_pfsync.c --- net/if_pfsync.c 10 May 2011 01:10:08 -0000 1.163 +++ net/if_pfsync.c 10 May 2011 21:09:41 -0000 @@ -426,11 +426,13 @@ pfsync_state_export(struct pfsync_state 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; @@ -527,7 +529,9 @@ pfsync_state_import(struct pfsync_state 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) || @@ -551,7 +555,8 @@ pfsync_state_import(struct pfsync_state 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]; @@ -559,7 +564,8 @@ pfsync_state_import(struct pfsync_state 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]); @@ -787,17 +793,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++; Index: net/pf.c =================================================================== RCS file: /home/cvs/src/sys/net/pf.c,v retrieving revision 1.742 diff -u -p -r1.742 pf.c --- net/pf.c 24 Apr 2011 19:36:54 -0000 1.742 +++ net/pf.c 11 May 2011 12:13:57 -0000 @@ -146,7 +146,7 @@ int pf_check_threshold(struct pf_thre 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 mbuf *, int, struct pf_pdesc *, struct tcphdr *, struct pf_state_peer *); #ifdef INET6 @@ -159,6 +159,11 @@ void pf_change_icmp(struct pf_addr *, 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, 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, @@ -683,7 +688,11 @@ pf_state_key_attach(struct pf_state_key /* 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[0]->af == sk->af && + si->s->direction == s->direction) || + (si->s->key[0]->af != si->s->key[1]->af && + sk->af == si->s->key[1]->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) { @@ -813,7 +822,8 @@ pf_state_key_setup(struct pf_pdesc *pd, 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 */ @@ -821,12 +831,26 @@ pf_state_key_setup(struct pf_pdesc *pd, 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; @@ -980,8 +1004,12 @@ pf_find_state(struct pfi_kif *kif, struc /* 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); @@ -1535,44 +1563,86 @@ pf_cksum_fixup(u_int16_t cksum, u_int16_ 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) @@ -1874,6 +1944,390 @@ pf_change_icmp(struct pf_addr *ia, u_int } } +int +pf_translate_af(int af, struct mbuf **m0, int off, struct pf_pdesc *pd) +{ +#if INET && INET6 + struct mbuf *m = *m0, *mp = NULL; + struct ip *ip4; + struct ip6_hdr *ip6; + struct icmp6_hdr *icmp; + int hlen; + + hlen = af == AF_INET ? sizeof(*ip4) : sizeof(*ip6); + + /* trim the old header */ + m_adj(m, off); + + /* prepend a new one */ + if ((M_PREPEND(m, hlen, M_DONTWAIT)) == NULL) { + *m0 = NULL; + return (-1); + } + + switch (af) { + case AF_INET: + ip4 = mtod(m, struct ip *); + bzero(ip4, hlen); + ip4->ip_v = IPVERSION; + ip4->ip_hl = hlen >> 2; + ip4->ip_len = htons(hlen + (pd->tot_len - 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(m, struct ip6_hdr *); + bzero(ip6, hlen); + ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_plen = htons(pd->tot_len - 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) { + if ((mp = m_pulldown(m, hlen, sizeof(*icmp), &off)) == NULL) { + *m0 = NULL; + return (-1); + } + icmp = (struct icmp6_hdr *)(mp->m_data + off); + icmp->icmp6_cksum = 0; + icmp->icmp6_cksum = af == AF_INET ? + in4_cksum(m, 0, hlen, ntohs(ip4->ip_len) - hlen) : + in6_cksum(m, IPPROTO_ICMPV6, hlen, ntohs(ip6->ip6_plen)); + } + + *m0 = m; +#endif /* INET && INET6 */ + + return (0); +} + +int +pf_change_icmp_af(struct mbuf *m, int off, int *poff, 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 = *poff - off; + /* new header */ + hlen = naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6); + /* data lenght */ + mlen = m->m_pkthdr.len - *poff; + + /* 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 */ + *poff += 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; + default: + return (-1); + } +#endif /* INET && INET6 */ + + return (0); +} /* * Need to modulate the sequence numbers in the TCP SACK option @@ -2727,7 +3181,6 @@ pf_test_rule(struct pf_rule **rm, struct struct ifqueue *ifq, int hdrlen) { struct pf_rule *lastr = NULL; - sa_family_t af = pd->af; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; struct pf_rule_slist rules; @@ -2815,18 +3268,18 @@ pf_test_rule(struct pf_rule **rm, struct else if (r->onrdomain >= 0 && (r->onrdomain == pd->rdomain) == r->ifnot) r = r->skip[PF_SKIP_RDOM].ptr; - else if (r->af && r->af != af) + else if (r->af && r->af != pd->af) r = r->skip[PF_SKIP_AF].ptr; else if (r->proto && r->proto != pd->proto) r = r->skip[PF_SKIP_PROTO].ptr; - else if (PF_MISMATCHAW(&r->src.addr, &pd->nsaddr, af, + else if (PF_MISMATCHAW(&r->src.addr, &pd->nsaddr, pd->naf, r->src.neg, kif, act.rtableid)) r = r->skip[PF_SKIP_SRC_ADDR].ptr; /* tcp/udp only. port_op always 0 in other cases */ else if (r->src.port_op && !pf_match_port(r->src.port_op, r->src.port[0], r->src.port[1], pd->nsport)) r = r->skip[PF_SKIP_SRC_PORT].ptr; - else if (PF_MISMATCHAW(&r->dst.addr, &pd->ndaddr, af, + else if (PF_MISMATCHAW(&r->dst.addr, &pd->ndaddr, pd->af, r->dst.neg, NULL, act.rtableid)) r = r->skip[PF_SKIP_DST_ADDR].ptr; /* tcp/udp only. port_op always 0 in other cases */ @@ -2886,6 +3339,8 @@ pf_test_rule(struct pf_rule **rm, struct /* 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) == -1) { REASON_SET(&reason, @@ -2893,7 +3348,7 @@ pf_test_rule(struct pf_rule **rm, struct goto cleanup; } if (r->log || act.log & PF_LOG_MATCHES) - PFLOG_PACKET(kif, h, m, af, + PFLOG_PACKET(kif, h, m, pd->af, direction, reason, r, a, ruleset, pd); } else { @@ -2902,7 +3357,7 @@ pf_test_rule(struct pf_rule **rm, struct *am = a; *rsm = ruleset; if (act.log & PF_LOG_MATCHES) - PFLOG_PACKET(kif, h, m, af, + PFLOG_PACKET(kif, h, m, pd->af, direction, reason, r, a, ruleset, pd); } @@ -2924,6 +3379,8 @@ pf_test_rule(struct pf_rule **rm, struct /* 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) == -1) { REASON_SET(&reason, PFRES_MEMORY); goto cleanup; @@ -2931,7 +3388,7 @@ pf_test_rule(struct pf_rule **rm, struct REASON_SET(&reason, PFRES_MATCH); if (r->log || act.log & PF_LOG_MATCHES) - PFLOG_PACKET(kif, h, m, af, direction, reason, + PFLOG_PACKET(kif, h, m, pd->af, direction, reason, r, a, ruleset, pd); if ((r->action == PF_DROP) && @@ -2947,7 +3404,7 @@ pf_test_rule(struct pf_rule **rm, struct struct ip *h4; struct ip6_hdr *h6; - switch (af) { + switch (pd->af) { case AF_INET: h4 = mtod(m, struct ip *); len = ntohs(h4->ip_len) - off; @@ -2958,27 +3415,28 @@ pf_test_rule(struct pf_rule **rm, struct break; } - if (pf_check_proto_cksum(m, off, len, IPPROTO_TCP, af)) + if (pf_check_proto_cksum(m, off, len, IPPROTO_TCP, + pd->af)) REASON_SET(&reason, PFRES_PROTCKSUM); else { if (th->th_flags & TH_SYN) ack++; if (th->th_flags & TH_FIN) ack++; - pf_send_tcp(r, af, pd->dst, + pf_send_tcp(r, pd->af, pd->dst, pd->src, th->th_dport, th->th_sport, ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0, r->return_ttl, 1, 0, pd->rdomain, pd->eh, kif->pfik_ifp); } - } else if (pd->proto != IPPROTO_ICMP && af == AF_INET && + } else if (pd->proto != IPPROTO_ICMP && pd->af == AF_INET && r->return_icmp) pf_send_icmp(m, r->return_icmp >> 8, - r->return_icmp & 255, af, r, pd->rdomain); - else if (pd->proto != IPPROTO_ICMPV6 && af == AF_INET6 && + r->return_icmp & 255, pd->af, r, pd->rdomain); + else if (pd->proto != IPPROTO_ICMPV6 && pd->af == AF_INET6 && r->return_icmp6) pf_send_icmp(m, r->return_icmp6 >> 8, - r->return_icmp6 & 255, af, r, pd->rdomain); + r->return_icmp6 & 255, pd->af, r, pd->rdomain); } if (r->action == PF_DROP) @@ -2989,7 +3447,7 @@ pf_test_rule(struct pf_rule **rm, struct rtable_l2(act.rtableid) != pd->rdomain) pd->destchg = 1; - if (r->action == PF_PASS && af == AF_INET && ! r->allow_opts) { + if (r->action == PF_PASS && pd->af == AF_INET && ! r->allow_opts) { struct ip *h4 = mtod(m, struct ip *); if (h4->ip_hl > 5) { @@ -3024,8 +3482,10 @@ pf_test_rule(struct pf_rule **rm, struct 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 { @@ -3053,7 +3513,10 @@ pf_test_rule(struct pf_rule **rm, struct } #endif - return (PF_PASS); + if (rewrite && skw->af != sks->af) { + return (PF_AFRT); + } else + return (PF_PASS); cleanup: while ((ri = SLIST_FIRST(&rules))) { @@ -3266,33 +3729,38 @@ pf_translate(struct pf_pdesc *pd, struct * -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; @@ -3303,15 +3771,22 @@ pf_translate(struct pf_pdesc *pd, struct 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; @@ -3333,15 +3808,34 @@ pf_translate(struct pf_pdesc *pd, struct 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); + 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; + } } - if (PF_ANEQ(daddr, pd->dst, pd->af)) { - pf_change_a6(pd->dst, &pd->hdr.icmp6->icmp6_cksum, - daddr, 0); - rewrite = 1; + if (virtual_type == htons(ICMP6_ECHO_REQUEST)) { + u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport; + + if (icmpid != pd->hdr.icmp6->icmp6_id) { + pd->hdr.icmp6->icmp6_cksum = pf_cksum_fixup( + pd->hdr.icmp6->icmp6_cksum, + pd->hdr.icmp6->icmp6_id, icmpid, 0); + pd->hdr.icmp6->icmp6_id = icmpid; + rewrite = 1; + } } break; #endif /* INET6 */ @@ -3350,12 +3844,12 @@ pf_translate(struct pf_pdesc *pd, struct 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; @@ -3364,11 +3858,11 @@ pf_translate(struct pf_pdesc *pd, struct #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; } @@ -3871,6 +4365,7 @@ pf_test_state_tcp(struct pf_state **stat 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; @@ -4002,27 +4497,52 @@ pf_test_state_tcp(struct pf_state **stat if (pf_tcp_track_sloppy(src, dst, state, pd, reason) == PF_DROP) return (PF_DROP); } else { - if (pf_tcp_track_full(src, dst, state, kif, m, off, pd, reason, - ©back) == PF_DROP) - return (PF_DROP); + int ret; + + if (PF_REVERSED_KEY((*state)->key, pd->af)) + ret = pf_tcp_track_full(dst, src, state, kif, m, off, + pd, reason, ©back); + else + ret = pf_tcp_track_full(src, dst, state, kif, m, off, + pd, reason, ©back); + if (ret == PF_DROP) + return (ret); } /* 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) - pf_change_ap(pd->dst, &th->th_dport, &th->th_sum, - &nk->addr[pd->didx], nk->port[pd->didx], 0, pd->af); + 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[didx], nk->port[didx], 0, pd->af, nk->af); 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); + action = PF_AFRT; + } + copyback = 1; } @@ -4030,7 +4550,7 @@ pf_test_state_tcp(struct pf_state **stat if (copyback) m_copyback(m, off, sizeof(*th), th, M_NOWAIT); - return (PF_PASS); + return (action); } int @@ -4040,6 +4560,7 @@ pf_test_state_udp(struct pf_state **stat 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; @@ -4081,24 +4602,42 @@ pf_test_state_udp(struct pf_state **stat /* 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]; - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) || - nk->port[pd->sidx] != uh->uh_sport) + 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); 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); + action = PF_AFRT; + } + m_copyback(m, off, sizeof(*uh), uh, M_NOWAIT); } - return (PF_PASS); + return (action); } int @@ -4211,7 +4750,18 @@ pf_test_state_icmp(struct pf_state **sta /* 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; @@ -4220,13 +4770,21 @@ pf_test_state_icmp(struct pf_state **sta switch (pd->af) { #ifdef INET case AF_INET: - if (PF_ANEQ(pd->src, +#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[pd->sidx], AF_INET)) pf_change_a(&saddr->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(&daddr->v4.s_addr, NULL, nk->addr[pd->didx].v4.s_addr, 0); pd->destchg = 1; @@ -4248,13 +4806,21 @@ pf_test_state_icmp(struct pf_state **sta #endif /* INET */ #ifdef INET6 case AF_INET6: - if (PF_ANEQ(pd->src, +#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[pd->sidx], AF_INET6)) pf_change_a6(saddr, &pd->hdr.icmp6->icmp6_cksum, &nk->addr[pd->sidx], 0); - if (PF_ANEQ(pd->dst, + if (!afto && PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET6)) { pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, @@ -4262,12 +4828,28 @@ pf_test_state_icmp(struct pf_state **sta pd->destchg = 1; } + if (nk->port[iidx] != + pd->hdr.icmp6->icmp6_id) { + pd->hdr.icmp6->icmp6_cksum = + pf_cksum_fixup( + pd->hdr.icmp6->icmp6_cksum, + pd->hdr.icmp6->icmp6_id, + nk->port[iidx], 0); + pd->hdr.icmp6->icmp6_id = + nk->port[iidx]; + } + m_copyback(m, off, sizeof(struct icmp6_hdr), pd->hdr.icmp6, M_NOWAIT); break; #endif /* INET6 */ } + if (afto) { + PF_ACPY(&pd->nsaddr, &nk->addr[sidx], nk->af); + PF_ACPY(&pd->ndaddr, &nk->addr[didx], nk->af); + return (PF_AFRT); + } } return (PF_PASS); @@ -4462,8 +5044,46 @@ pf_test_state_icmp(struct pf_state **sta /* 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(m, off, + sizeof(struct icmp6_hdr), + pd->hdr.icmp6, M_NOWAIT); + if (pf_change_icmp_af(m, ipoff2, &off2, + 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(m, off2, 8, &th, M_NOWAIT); + 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); + return (PF_AFRT); + } +#endif if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || @@ -4537,8 +5157,51 @@ pf_test_state_icmp(struct pf_state **sta /* 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(m, off, + sizeof(struct icmp6_hdr), + pd->hdr.icmp6, M_NOWAIT); + if (pf_change_icmp_af(m, ipoff2, &off2, + 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(m, off2, sizeof(uh), &uh, + M_NOWAIT); + 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); + return (PF_AFRT); + } +#endif if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || @@ -4609,8 +5272,50 @@ pf_test_state_icmp(struct pf_state **sta /* 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(m, off, + sizeof(struct icmp6_hdr), + pd->hdr.icmp6, M_NOWAIT); + if (pf_change_icmp_af(m, ipoff2, &off2, + 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(m, off2, ICMP_MINLEN, &iih, + M_NOWAIT); + 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); + return (PF_AFRT); + } +#endif if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || @@ -4679,8 +5384,52 @@ pf_test_state_icmp(struct pf_state **sta /* 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(m, off, + sizeof(struct icmp6_hdr), + pd->hdr.icmp6, M_NOWAIT); + if (pf_change_icmp_af(m, ipoff2, &off2, + 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(m, off2, + sizeof(struct icmp6_hdr), &iih, + M_NOWAIT); + 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); + return (PF_AFRT); + } +#endif if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || @@ -4785,6 +5534,7 @@ pf_test_state_other(struct pf_state **st { struct pf_state_peer *src, *dst; struct pf_state_key_cmp key; + int action = PF_PASS; key.af = pd->af; key.proto = pd->proto; @@ -4824,7 +5574,15 @@ pf_test_state_other(struct pf_state **st /* 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); @@ -4834,11 +5592,13 @@ pf_test_state_other(struct pf_state **st 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); @@ -4848,10 +5608,12 @@ pf_test_state_other(struct pf_state **st #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; } @@ -4861,9 +5623,18 @@ pf_test_state_other(struct pf_state **st 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; + action = PF_AFRT; + } + m->m_pkthdr.rdomain = nk->rdomain; } - return (PF_PASS); + return (action); } /* @@ -5500,8 +6271,9 @@ pf_setup_pdesc(sa_family_t af, int dir, pd->dir = dir; pd->sidx = (dir == PF_IN) ? 0 : 1; pd->didx = (dir == PF_IN) ? 1 : 0; - pd->af = AF_INET; + pd->af = pd->naf = AF_INET; pd->tos = h->ip_tos; + pd->ttl = h->ip_ttl; pd->tot_len = ntohs(h->ip_len); pd->rdomain = rtable_l2(m->m_pkthdr.rdomain); @@ -5565,8 +6337,9 @@ pf_setup_pdesc(sa_family_t af, int dir, pd->dir = dir; pd->sidx = (dir == PF_IN) ? 0 : 1; pd->didx = (dir == PF_IN) ? 1 : 0; - pd->af = AF_INET6; + pd->af = pd->naf = AF_INET6; pd->tos = 0; + pd->ttl = h->ip6_hlim; pd->tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr); *off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr); pd->proto = h->ip6_nxt; @@ -5711,7 +6484,7 @@ pf_counters_inc(int dir, int action, str += pd->tot_len; kif->pfik_packets[pd->af == AF_INET6][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 = (dir == PF_OUT); r->packets[dirndx]++; r->bytes[dirndx] += pd->tot_len; @@ -5843,7 +6616,7 @@ pf_test(int dir, struct ifnet *ifp, stru goto done; action = pf_test_state_tcp(&s, dir, kif, m, off, &pd, &reason); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { #if NPFSYNC > 0 pfsync_update_state(s); #endif /* NPFSYNC */ @@ -5865,7 +6638,7 @@ pf_test(int dir, struct ifnet *ifp, stru case IPPROTO_UDP: { action = pf_test_state_udp(&s, dir, kif, m, off, &pd); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { #if NPFSYNC > 0 pfsync_update_state(s); #endif /* NPFSYNC */ @@ -5881,7 +6654,7 @@ pf_test(int dir, struct ifnet *ifp, stru case IPPROTO_ICMP: { action = pf_test_state_icmp(&s, dir, kif, m, off, &pd, &reason); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { #if NPFSYNC > 0 pfsync_update_state(s); #endif /* NPFSYNC */ @@ -5903,7 +6676,7 @@ pf_test(int dir, struct ifnet *ifp, stru default: action = pf_test_state_other(&s, dir, kif, m, &pd); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { #if NPFSYNC > 0 pfsync_update_state(s); #endif /* NPFSYNC */ @@ -6021,6 +6794,17 @@ done: *m0 = NULL; action = PF_PASS; break; + case PF_AFRT: + if (pf_translate_af(AF_INET6, &m, off, &pd)) { + if (!m) + *m0 = NULL; + action = PF_DROP; + break; + } + pf_route6(&m, r, dir, kif->pfik_ifp, s); + *m0 = NULL; + action = PF_PASS; + break; default: /* pf_route can free the mbuf causing *m0 to become NULL */ if (r->rt) @@ -6134,7 +6918,7 @@ pf_test6(int fwdir, struct ifnet *ifp, s goto done; action = pf_test_state_tcp(&s, dir, kif, m, off, &pd, &reason); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { #if NPFSYNC > 0 pfsync_update_state(s); #endif /* NPFSYNC */ @@ -6156,7 +6940,7 @@ pf_test6(int fwdir, struct ifnet *ifp, s case IPPROTO_UDP: { action = pf_test_state_udp(&s, dir, kif, m, off, &pd); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { #if NPFSYNC > 0 pfsync_update_state(s); #endif /* NPFSYNC */ @@ -6179,7 +6963,7 @@ pf_test6(int fwdir, struct ifnet *ifp, s case IPPROTO_ICMPV6: { action = pf_test_state_icmp(&s, dir, kif, m, off, &pd, &reason); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { #if NPFSYNC > 0 pfsync_update_state(s); #endif /* NPFSYNC */ @@ -6194,7 +6978,7 @@ pf_test6(int fwdir, struct ifnet *ifp, s default: action = pf_test_state_other(&s, dir, kif, m, &pd); - if (action == PF_PASS) { + if (action == PF_PASS || action == PF_AFRT) { #if NPFSYNC > 0 pfsync_update_state(s); #endif /* NPFSYNC */ @@ -6305,6 +7089,17 @@ done: break; case PF_DIVERT: divert6_packet(m, dir); + *m0 = NULL; + action = PF_PASS; + break; + case PF_AFRT: + if (pf_translate_af(AF_INET, &m, off, &pd)) { + if (!m) + *m0 = NULL; + action = PF_DROP; + break; + } + pf_route(&m, r, dir, kif->pfik_ifp, s); *m0 = NULL; action = PF_PASS; break; Index: net/pf_ioctl.c =================================================================== RCS file: /home/cvs/src/sys/net/pf_ioctl.c,v retrieving revision 1.239 diff -u -p -r1.239 pf_ioctl.c --- net/pf_ioctl.c 19 Apr 2011 21:58:03 -0000 1.239 +++ net/pf_ioctl.c 26 Apr 2011 07:35:16 -0000 @@ -2495,6 +2495,8 @@ pf_rule_copyin(struct pf_rule *from, str 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)) Index: net/pf_lb.c =================================================================== RCS file: /home/cvs/src/sys/net/pf_lb.c,v retrieving revision 1.13 diff -u -p -r1.13 pf_lb.c --- net/pf_lb.c 27 Jun 2010 01:39:43 -0000 1.13 +++ net/pf_lb.c 24 Mar 2011 19:59:08 -0000 @@ -103,6 +103,8 @@ void pf_hash(struct pf_addr *, struct 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 **); #define mix(a,b,c) \ do { \ @@ -171,7 +173,7 @@ pf_get_sport(struct pf_pdesc *pd, struct 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); @@ -185,7 +187,7 @@ pf_get_sport(struct pf_pdesc *pd, struct } 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); @@ -249,7 +251,7 @@ pf_get_sport(struct pf_pdesc *pd, struct switch (r->nat.opts & PF_POOL_TYPEMASK) { case PF_POOL_RANDOM: case PF_POOL_ROUNDROBIN: - 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; @@ -259,7 +261,7 @@ pf_get_sport(struct pf_pdesc *pd, struct default: return (1); } - } while (! PF_AEQ(&init_addr, naddr, pd->af) ); + } while (! PF_AEQ(&init_addr, naddr, pd->naf) ); return (1); /* none available */ } @@ -427,6 +429,9 @@ pf_get_transaddr(struct pf_rule *r, stru 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? */ @@ -468,6 +473,135 @@ pf_get_transaddr(struct pf_rule *r, stru PF_ACPY(&pd->ndaddr, &naddr, pd->af); if (nport) pd->ndport = nport; + } + + return (0); +} + +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); Index: net/pfvar.h =================================================================== RCS file: /home/cvs/src/sys/net/pfvar.h,v retrieving revision 1.329 diff -u -p -r1.329 pfvar.h --- net/pfvar.h 23 Apr 2011 10:00:36 -0000 1.329 +++ net/pfvar.h 10 May 2011 21:06:05 -0000 @@ -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 }; @@ -627,6 +627,7 @@ struct pf_rule { #define PF_STATE_SYNPROXY 0x3 u_int8_t keep_state; sa_family_t af; + sa_family_t naf; u_int8_t proto; u_int8_t type; u_int8_t code; @@ -644,7 +645,7 @@ struct pf_rule { #define PF_FLUSH 0x01 #define PF_FLUSH_GLOBAL 0x02 u_int8_t flush; - u_int8_t pad2[3]; + u_int8_t pad2[2]; struct { struct pf_addr addr; @@ -704,6 +705,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; }; @@ -781,6 +783,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 { @@ -868,7 +873,8 @@ struct pfsync_state_key { struct pf_addr addr[2]; u_int16_t port[2]; u_int16_t rdomain; - u_int8_t pad[2]; + sa_family_t af; + u_int8_t pad[1]; }; struct pfsync_state { @@ -1224,8 +1230,10 @@ struct pf_pdesc { u_int16_t flags; #define PFDESC_IP_REAS 0x0002 /* IP frags would've been reassembled */ 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 */ @@ -1764,7 +1772,8 @@ void *pf_pull_hdr(struct mbuf *, int, void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); int pflog_packet(struct pfi_kif *, struct mbuf *, sa_family_t, u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, struct pf_ruleset *, - struct pf_pdesc *); + struct pf_pdesc *); + void pf_send_deferred_syn(struct pf_state *); int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *, struct pf_addr *, sa_family_t); @@ -1805,6 +1814,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(int, struct mbuf **, int, struct pf_pdesc *); void pfr_initialize(void); int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t); Index: netinet/in.c =================================================================== RCS file: /home/cvs/src/sys/netinet/in.c,v retrieving revision 1.65 diff -u -p -r1.65 in.c --- netinet/in.c 28 Apr 2011 09:56:27 -0000 1.65 +++ netinet/in.c 2 May 2011 13:59:16 -0000 @@ -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 *); Index: netinet/in_var.h =================================================================== RCS file: /home/cvs/src/sys/netinet/in_var.h,v retrieving revision 1.16 diff -u -p -r1.16 in_var.h --- netinet/in_var.h 17 Nov 2010 19:25:49 -0000 1.16 +++ netinet/in_var.h 24 Mar 2011 19:59:08 -0000 @@ -212,6 +212,10 @@ struct in_multi *in_addmulti(struct in_a 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 Index: netinet/inet_nat64.c =================================================================== RCS file: netinet/inet_nat64.c diff -N netinet/inet_nat64.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ netinet/inet_nat64.c 24 Mar 2011 19:59:08 -0000 @@ -0,0 +1,218 @@ +/* $OpenBSD$ */ +/* $vantronix: inet_nat64.c,v 1.2 2011/02/28 14:57:58 mike Exp $ */ + +/* + * Copyright (c) 2011 Reyk Floeter + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#ifdef _KERNEL +#include +#include +#else +#include +#include +#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); +} Index: netinet6/in6.h =================================================================== RCS file: /home/cvs/src/sys/netinet6/in6.h,v retrieving revision 1.53 diff -u -p -r1.53 in6.h --- netinet6/in6.h 2 May 2011 13:48:38 -0000 1.53 +++ netinet6/in6.h 4 May 2011 12:12:24 -0000 @@ -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))