Index: sys/net/pfvar.h =================================================================== --- sys/net/pfvar.h +++ sys/net/pfvar.h @@ -1549,6 +1549,7 @@ char any[0]; } hdr; + int r_rt; struct pf_krule *nat_rule; /* nat/rdr rule applied to packet */ struct pf_addr *src; /* src address */ struct pf_addr *dst; /* dst address */ @@ -2366,12 +2367,12 @@ const struct pf_addr *, const struct pf_addr *, u_int16_t, u_int16_t, u_int32_t, u_int32_t, u_int8_t, u_int16_t, u_int16_t, u_int8_t, bool, - u_int16_t, u_int16_t, int); + u_int16_t, u_int16_t, int, struct pf_addr *); void pf_send_tcp(const struct pf_krule *, sa_family_t, - const struct pf_addr *, const struct pf_addr *, - u_int16_t, u_int16_t, u_int32_t, u_int32_t, - u_int8_t, u_int16_t, u_int16_t, u_int8_t, bool, - u_int16_t, u_int16_t, int); + const struct pf_addr *, const struct pf_addr *, + u_int16_t, u_int16_t, u_int32_t, u_int32_t, + u_int8_t, u_int16_t, u_int16_t, u_int8_t, bool, + u_int16_t, u_int16_t, int, struct pf_addr *); void pf_syncookies_init(void); void pf_syncookies_cleanup(void); Index: sys/netpfil/pf/pf.c =================================================================== --- sys/netpfil/pf/pf.c +++ sys/netpfil/pf/pf.c @@ -274,9 +274,6 @@ static u_int32_t pf_tcp_iss(struct pf_pdesc *); static int pf_dummynet(struct pf_pdesc *, struct pf_kstate *, struct pf_krule *, struct mbuf **); -static int pf_dummynet_route(struct pf_pdesc *, - struct pf_kstate *, struct pf_krule *, - struct ifnet *, struct sockaddr *, struct mbuf **); static int pf_test_eth_rule(int, struct pfi_kkif *, struct mbuf **); static int pf_test_rule(struct pf_krule **, struct pf_kstate **, @@ -330,19 +327,17 @@ static void pf_purge_unlinked_rules(void); static int pf_mtag_uminit(void *, int, int); static void pf_mtag_free(struct m_tag *); -static void pf_packet_rework_nat(struct mbuf *, struct pf_pdesc *, - int, struct pf_state_key *); #ifdef INET -static void pf_route(struct mbuf **, struct pf_krule *, - struct ifnet *, struct pf_kstate *, - struct pf_pdesc *, struct inpcb *); +static int pf_route(struct mbuf **, struct mbuf **, + struct pf_krule *, struct pf_kstate *, + struct pf_pdesc *); #endif /* INET */ #ifdef INET6 static void pf_change_a6(struct pf_addr *, u_int16_t *, struct pf_addr *, u_int8_t); -static void pf_route6(struct mbuf **, struct pf_krule *, - struct ifnet *, struct pf_kstate *, - struct pf_pdesc *, struct inpcb *); +static int pf_route6(struct mbuf **, struct mbuf **, + struct pf_krule *, struct pf_kstate *, + struct pf_pdesc *); #endif /* INET6 */ static __inline void pf_set_protostate(struct pf_kstate *, int, u_int8_t); @@ -353,16 +348,6 @@ VNET_DEFINE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); -#define PACKET_UNDO_NAT(_m, _pd, _off, _s) \ - do { \ - struct pf_state_key *nk; \ - if ((pd->dir) == PF_OUT) \ - nk = (_s)->key[PF_SK_STACK]; \ - else \ - nk = (_s)->key[PF_SK_WIRE]; \ - pf_packet_rework_nat(_m, _pd, _off, nk); \ - } while (0) - #define PACKET_LOOPED(pd) ((pd)->pf_mtag && \ (pd)->pf_mtag->flags & PF_MTAG_FLAG_PACKET_LOOPED) @@ -492,100 +477,6 @@ return (0); } -static void -pf_packet_rework_nat(struct mbuf *m, struct pf_pdesc *pd, int off, - struct pf_state_key *nk) -{ - - switch (pd->proto) { - case IPPROTO_TCP: { - struct tcphdr *th = &pd->hdr.tcp; - - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af)) - pf_change_ap(m, pd->src, &th->th_sport, pd->ip_sum, - &th->th_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 0, pd->af); - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af)) - pf_change_ap(m, pd->dst, &th->th_dport, pd->ip_sum, - &th->th_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 0, pd->af); - m_copyback(m, off, sizeof(*th), (caddr_t)th); - break; - } - case IPPROTO_UDP: { - struct udphdr *uh = &pd->hdr.udp; - - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af)) - pf_change_ap(m, pd->src, &uh->uh_sport, pd->ip_sum, - &uh->uh_sum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, pd->af); - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af)) - pf_change_ap(m, pd->dst, &uh->uh_dport, pd->ip_sum, - &uh->uh_sum, &nk->addr[pd->didx], - nk->port[pd->didx], 1, pd->af); - m_copyback(m, off, sizeof(*uh), (caddr_t)uh); - break; - } - case IPPROTO_SCTP: { - struct sctphdr *sh = &pd->hdr.sctp; - uint16_t checksum = 0; - - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af)) { - pf_change_ap(m, pd->src, &sh->src_port, pd->ip_sum, - &checksum, &nk->addr[pd->sidx], - nk->port[pd->sidx], 1, pd->af); - } - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af)) { - pf_change_ap(m, pd->dst, &sh->dest_port, pd->ip_sum, - &checksum, &nk->addr[pd->didx], - nk->port[pd->didx], 1, pd->af); - } - - break; - } - case IPPROTO_ICMP: { - struct icmp *ih = &pd->hdr.icmp; - - if (nk->port[pd->sidx] != ih->icmp_id) { - pd->hdr.icmp.icmp_cksum = pf_cksum_fixup( - ih->icmp_cksum, ih->icmp_id, - nk->port[pd->sidx], 0); - ih->icmp_id = nk->port[pd->sidx]; - pd->sport = &ih->icmp_id; - - m_copyback(m, off, ICMP_MINLEN, (caddr_t)ih); - } - /* FALLTHROUGH */ - } - default: - if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af)) { - switch (pd->af) { - case AF_INET: - pf_change_a(&pd->src->v4.s_addr, - pd->ip_sum, nk->addr[pd->sidx].v4.s_addr, - 0); - break; - case AF_INET6: - PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af); - break; - } - } - if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af)) { - switch (pd->af) { - case AF_INET: - pf_change_a(&pd->dst->v4.s_addr, - pd->ip_sum, nk->addr[pd->didx].v4.s_addr, - 0); - break; - case AF_INET6: - PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af); - break; - } - } - break; - } -} - static __inline uint32_t pf_hashkey(struct pf_state_key *sk) { @@ -1720,6 +1611,8 @@ pfse->pfse_m->m_pkthdr.csum_flags |= CSUM_IP_VALID | CSUM_IP_CHECKED; ip_input(pfse->pfse_m); + } else if (pfse->pfse_m->m_flags & M_IP_NEXTHOP) { + ip_forward(pfse->pfse_m, 1); } else { ip_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL); @@ -1736,6 +1629,8 @@ if (pf_isforlocal(pfse->pfse_m, AF_INET6)) { pfse->pfse_m->m_flags |= M_SKIP_FIREWALL; ip6_input(pfse->pfse_m); + } else if (pfse->pfse_m->m_flags & M_IP6_NEXTHOP) { + ip6_forward(pfse->pfse_m, 1); } else { ip6_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL, NULL); @@ -2078,7 +1973,8 @@ s->key[PF_SK_WIRE]->port[1], s->key[PF_SK_WIRE]->port[0], s->src.seqhi, s->src.seqlo + 1, - TH_RST|TH_ACK, 0, 0, 0, true, s->tag, 0, s->act.rtableid); + TH_RST|TH_ACK, 0, 0, 0, true, s->tag, 0, s->act.rtableid, + NULL); } LIST_REMOVE(s, entry); @@ -2828,15 +2724,20 @@ const struct pf_addr *saddr, const struct pf_addr *daddr, u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack, u_int8_t tcp_flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, - bool skip_firewall, u_int16_t mtag_tag, u_int16_t mtag_flags, int rtableid) + bool skip_firewall, u_int16_t mtag_tag, u_int16_t mtag_flags, int rtableid, + struct pf_addr *rt_addr) { struct mbuf *m; + struct m_tag *rt_mtag; int len, tlen; + size_t rt_mtag_len; #ifdef INET struct ip *h = NULL; + struct sockaddr_in dst4; #endif /* INET */ #ifdef INET6 struct ip6_hdr *h6 = NULL; + struct sockaddr_in6 dst6; #endif /* INET6 */ struct tcphdr *th; char *opt; @@ -2884,6 +2785,58 @@ if (rtableid >= 0) M_SETFIB(m, rtableid); + if (rt_addr != NULL) { + + switch (af) { +#ifdef INET + case AF_INET: + rt_mtag_len = sizeof(struct sockaddr_in); + bzero(&dst4, rt_mtag_len); + dst4.sin_family = AF_INET; + dst4.sin_len = rt_mtag_len; + dst4.sin_addr.s_addr = rt_addr->v4.s_addr; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + rt_mtag_len = sizeof(struct sockaddr_in6); + bzero(&dst6, rt_mtag_len); + dst6.sin6_family = AF_INET6; + dst6.sin6_len = rt_mtag_len; + PF_ACPY((struct pf_addr *)&dst6.sin6_addr, + rt_addr, AF_INET6); + break; +#endif /* INET6 */ + default: + panic("%s: unsupported af %d", __func__, af); + } + + rt_mtag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); + if (rt_mtag == NULL) { + rt_mtag = m_tag_get(PACKET_TAG_IPFORWARD, rt_mtag_len, + M_NOWAIT); + if (rt_mtag == NULL) { + m_freem(m); + return (NULL); + } + m_tag_prepend(m, rt_mtag); + } + m->m_flags |= M_IP_NEXTHOP; + switch (af) { +#ifdef INET + case AF_INET: + memcpy(rt_mtag+1, &dst4, rt_mtag_len); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + memcpy(rt_mtag+1, &dst6, rt_mtag_len); + break; +#endif /* INET6 */ + } + + } + #ifdef ALTQ if (r != NULL && r->qid) { pf_mtag->qid = r->qid; @@ -3094,18 +3047,20 @@ const struct pf_addr *saddr, const struct pf_addr *daddr, u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack, u_int8_t tcp_flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, - bool skip_firewall, u_int16_t mtag_tag, u_int16_t mtag_flags, int rtableid) + bool skip_firewall, u_int16_t mtag_tag, u_int16_t mtag_flags, int rtableid, + struct pf_addr *rt_addr) { struct pf_send_entry *pfse; struct mbuf *m; m = pf_build_tcp(r, af, saddr, daddr, sport, dport, seq, ack, tcp_flags, - win, mss, ttl, skip_firewall, mtag_tag, mtag_flags, rtableid); + win, mss, ttl, skip_firewall, mtag_tag, mtag_flags, rtableid, + rt_addr); if (m == NULL) return; /* Allocate outgoing queue entry, mbuf and mbuf tag. */ - pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT); + pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT | M_ZERO); if (pfse == NULL) { m_freem(m); return; @@ -3190,7 +3145,7 @@ pf_send_tcp(r, 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, true, 0, 0, rtableid); + r->return_ttl, true, 0, 0, rtableid, NULL); } } else if (pd->proto == IPPROTO_SCTP && (r->rule_flag & PFRULE_RETURN)) { @@ -4987,7 +4942,7 @@ pf_send_tcp(r, pd->af, pd->dst, pd->src, th->th_dport, th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, TH_SYN|TH_ACK, 0, s->src.mss, 0, true, 0, 0, - pd->act.rtableid); + pd->act.rtableid, NULL); REASON_SET(&reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } @@ -5460,7 +5415,7 @@ th->th_sport, ntohl(th->th_ack), 0, TH_RST, 0, 0, (*state)->rule.ptr->return_ttl, true, 0, 0, - (*state)->act.rtableid); + (*state)->act.rtableid, NULL); src->seqlo = 0; src->seqhi = 1; src->max_win = 1; @@ -5580,8 +5535,13 @@ static int pf_synproxy(struct pf_pdesc *pd, struct pf_kstate **state, u_short *reason) { - struct pf_state_key *sk = (*state)->key[pd->didx]; - struct tcphdr *th = &pd->hdr.tcp; + struct pf_state_key *sk = (*state)->key[pd->didx]; + struct tcphdr *th = &pd->hdr.tcp; + struct pf_addr *rt_addr = NULL; + + if ((*state)->rt) { + rt_addr = &((*state)->rt_addr); + } if ((*state)->src.state == PF_TCPS_PROXY_SRC) { if (pd->dir != (*state)->direction) { @@ -5596,8 +5556,8 @@ pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, pd->src, th->th_dport, th->th_sport, (*state)->src.seqhi, ntohl(th->th_seq) + 1, - TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, true, 0, 0, - (*state)->act.rtableid); + TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, true, 0, + 0, (*state)->act.rtableid, NULL); REASON_SET(reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } else if ((th->th_flags & (TH_ACK|TH_RST|TH_FIN)) != TH_ACK || @@ -5629,7 +5589,7 @@ sk->port[pd->sidx], sk->port[pd->didx], (*state)->dst.seqhi, 0, TH_SYN, 0, (*state)->src.mss, 0, false, (*state)->tag, 0, - (*state)->act.rtableid); + (*state)->act.rtableid, rt_addr); REASON_SET(reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } else if (((th->th_flags & (TH_SYN|TH_ACK)) != @@ -5642,15 +5602,15 @@ (*state)->dst.seqlo = ntohl(th->th_seq); pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, pd->src, th->th_dport, th->th_sport, - ntohl(th->th_ack), ntohl(th->th_seq) + 1, - TH_ACK, (*state)->src.max_win, 0, 0, false, - (*state)->tag, 0, (*state)->act.rtableid); + ntohl(th->th_ack), ntohl(th->th_seq) + 1, TH_ACK, + (*state)->src.max_win, 0, 0, false, (*state)->tag, + 0, (*state)->act.rtableid, rt_addr); pf_send_tcp((*state)->rule.ptr, pd->af, &sk->addr[pd->sidx], &sk->addr[pd->didx], sk->port[pd->sidx], sk->port[pd->didx], (*state)->src.seqhi + 1, (*state)->src.seqlo + 1, TH_ACK, (*state)->dst.max_win, 0, 0, true, 0, 0, - (*state)->act.rtableid); + (*state)->act.rtableid, NULL); (*state)->src.seqdiff = (*state)->dst.seqhi - (*state)->src.seqlo; (*state)->dst.seqdiff = (*state)->src.seqhi - @@ -6693,27 +6653,23 @@ } #ifdef INET -static void -pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp, - struct pf_kstate *s, struct pf_pdesc *pd, struct inpcb *inp) +static int +pf_route(struct mbuf **m, struct mbuf **mdup, struct pf_krule *r, + struct pf_kstate *s, struct pf_pdesc *pd) { - struct mbuf *m0, *m1, *md; + struct mbuf *m0; + struct m_tag *rt_mtag; struct sockaddr_in dst; struct ip *ip; - struct ifnet *ifp = NULL; struct pf_addr naddr; struct pf_ksrc_node *sn = NULL; - int error = 0; - uint16_t ip_len, ip_off; - int r_rt, r_dir; + int r_dir; - KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__)); + KASSERT(m && *m && r, ("%s: invalid parameters", __func__)); if (s) { - r_rt = s->rt; r_dir = s->direction; } else { - r_rt = r->rt; r_dir = r->direction; } @@ -6726,45 +6682,23 @@ pd->pf_mtag->routed++ > 3) { m0 = *m; *m = NULL; - goto bad_locked; + goto bad; } - if (r_rt == PF_DUPTO) { - if ((pd->pf_mtag->flags & PF_MTAG_FLAG_DUPLICATED)) { - if (s == NULL) { - ifp = r->rpool.cur->kif ? - r->rpool.cur->kif->pfik_ifp : NULL; - } else { - ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; - /* If pfsync'd */ - if (ifp == NULL) - ifp = r->rpool.cur->kif ? - r->rpool.cur->kif->pfik_ifp : NULL; - PF_STATE_UNLOCK(s); - } - if (ifp == oifp) { - /* When the 2nd interface is not skipped */ - return; - } else { - m0 = *m; - *m = NULL; - goto bad; - } - } else { - pd->pf_mtag->flags |= PF_MTAG_FLAG_DUPLICATED; - if (((m0 = m_dup(*m, M_NOWAIT)) == NULL)) { - if (s) - PF_STATE_UNLOCK(s); - return; - } + if (pd->r_rt == PF_DUPTO) { + if (((m0 = m_dup(*m, M_NOWAIT)) == NULL)) { + return PF_PASS; } + /* + * Original behaviour of pf was to emit the duplicated + * packet directly. We don't do it anymore in pf so + * set we set the M_SKIP_FIREWALL instead. + */ + m0->m_flags |= M_SKIP_FIREWALL; + pd->pf_mtag->flags |= PF_MTAG_FLAG_DUPLICATED; } else { - if ((r_rt == PF_REPLYTO) == (r_dir == pd->dir)) { - pf_dummynet(pd, s, r, m); - if (s) - PF_STATE_UNLOCK(s); - return; - } + if ((pd->r_rt == PF_REPLYTO) == (r_dir == pd->dir)) + return PF_PASS; m0 = *m; } @@ -6778,153 +6712,62 @@ bzero(&naddr, sizeof(naddr)); if (s == NULL) { - if (TAILQ_EMPTY(&r->rpool.list)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); - goto bad_locked; - } - pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src, - &naddr, NULL, &sn); - if (!PF_AZERO(&naddr, AF_INET)) - dst.sin_addr.s_addr = naddr.v4.s_addr; - ifp = r->rpool.cur->kif ? - r->rpool.cur->kif->pfik_ifp : NULL; + if (pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src, + &naddr, NULL, &sn) != 0) + goto bad; + dst.sin_addr.s_addr = naddr.v4.s_addr; } else { - if (!PF_AZERO(&s->rt_addr, AF_INET)) - dst.sin_addr.s_addr = - s->rt_addr.v4.s_addr; - ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; - PF_STATE_UNLOCK(s); - } - /* If pfsync'd */ - if (ifp == NULL) - ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; - if (ifp == NULL) - goto bad; - - if (pd->dir == PF_IN) { - if (pf_test(PF_OUT, 0, ifp, &m0, inp, &pd->act) != PF_PASS) - goto bad; - else if (m0 == NULL) - goto done; - if (m0->m_len < sizeof(struct ip)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("%s: m0->m_len < sizeof(struct ip)\n", __func__)); - goto bad; - } - ip = mtod(m0, struct ip *); - } - - if (ifp->if_flags & IFF_LOOPBACK) - m0->m_flags |= M_SKIP_FIREWALL; - - ip_len = ntohs(ip->ip_len); - ip_off = ntohs(ip->ip_off); - - /* Copied from FreeBSD 10.0-CURRENT ip_output. */ - m0->m_pkthdr.csum_flags |= CSUM_IP; - if (m0->m_pkthdr.csum_flags & CSUM_DELAY_DATA & ~ifp->if_hwassist) { - in_delayed_cksum(m0); - m0->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; - } - if (m0->m_pkthdr.csum_flags & CSUM_SCTP & ~ifp->if_hwassist) { - pf_sctp_checksum(m0, (uint32_t)(ip->ip_hl << 2)); - m0->m_pkthdr.csum_flags &= ~CSUM_SCTP; + dst.sin_addr.s_addr = s->rt_addr.v4.s_addr; } /* - * If small enough for interface, or the interface will take - * care of the fragmentation for us, we can just send directly. + * XXX Interface is ignored! + * That is sadly not yet reflected in structures, ioctls and userspace. */ - if (ip_len <= ifp->if_mtu || - (m0->m_pkthdr.csum_flags & ifp->if_hwassist & CSUM_TSO) != 0) { - ip->ip_sum = 0; - if (m0->m_pkthdr.csum_flags & CSUM_IP & ~ifp->if_hwassist) { - ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); - m0->m_pkthdr.csum_flags &= ~CSUM_IP; - } - m_clrprotoflags(m0); /* Avoid confusing lower layers. */ - md = m0; - error = pf_dummynet_route(pd, s, r, ifp, sintosa(&dst), &md); - if (md != NULL) - error = (*ifp->if_output)(ifp, md, sintosa(&dst), NULL); - goto done; - } - - /* Balk when DF bit is set or the interface didn't support TSO. */ - if ((ip_off & IP_DF) || (m0->m_pkthdr.csum_flags & CSUM_TSO)) { - error = EMSGSIZE; - KMOD_IPSTAT_INC(ips_cantfrag); - if (r_rt != PF_DUPTO) { - if (s && pd->nat_rule != NULL) - PACKET_UNDO_NAT(m0, pd, - (ip->ip_hl << 2) + (ip_off & IP_OFFMASK), - s); - - icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, - ifp->if_mtu); - goto done; - } else + rt_mtag = m_tag_find(m0, PACKET_TAG_IPFORWARD, NULL); + if (rt_mtag == NULL) { + rt_mtag = m_tag_get(PACKET_TAG_IPFORWARD, + sizeof(struct sockaddr_in6), M_NOWAIT); + if (rt_mtag == NULL) { goto bad; + } + m_tag_prepend(m0, rt_mtag); + } + m0->m_flags |= M_IP_NEXTHOP; + memcpy(rt_mtag+1, &dst, sizeof(struct sockaddr_in)); + + if (pd->r_rt == PF_DUPTO) { + *mdup = m0; } - error = ip_fragment(ip, &m0, ifp->if_mtu, ifp->if_hwassist); - if (error) - goto bad; + return PF_PASS; - for (; m0; m0 = m1) { - m1 = m0->m_nextpkt; - m0->m_nextpkt = NULL; - if (error == 0) { - m_clrprotoflags(m0); - md = m0; - error = pf_dummynet_route(pd, s, r, ifp, - sintosa(&dst), &md); - if (md != NULL) - error = (*ifp->if_output)(ifp, md, - sintosa(&dst), NULL); - } else - m_freem(m0); - } - - if (error == 0) - KMOD_IPSTAT_INC(ips_fragmented); - -done: - if (r_rt != PF_DUPTO) - *m = NULL; - return; - -bad_locked: - if (s) - PF_STATE_UNLOCK(s); bad: m_freem(m0); - goto done; + *m = NULL; + return PF_DROP; } #endif /* INET */ #ifdef INET6 -static void -pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp, - struct pf_kstate *s, struct pf_pdesc *pd, struct inpcb *inp) +static int +pf_route6(struct mbuf **m, struct mbuf **mdup, struct pf_krule *r, + struct pf_kstate *s, struct pf_pdesc *pd) { - struct mbuf *m0, *md; + struct mbuf *m0; + struct m_tag *rt_mtag; struct sockaddr_in6 dst; struct ip6_hdr *ip6; - struct ifnet *ifp = NULL; struct pf_addr naddr; struct pf_ksrc_node *sn = NULL; - int r_rt, r_dir; + int r_dir; - KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__)); + KASSERT(m && *m && r, ("%s: invalid parameters", __func__)); if (s) { - r_rt = s->rt; r_dir = s->direction; } else { - r_rt = r->rt; r_dir = r->direction; } @@ -6937,45 +6780,23 @@ pd->pf_mtag->routed++ > 3) { m0 = *m; *m = NULL; - goto bad_locked; + goto bad; } - if (r_rt == PF_DUPTO) { - if ((pd->pf_mtag->flags & PF_MTAG_FLAG_DUPLICATED)) { - if (s == NULL) { - ifp = r->rpool.cur->kif ? - r->rpool.cur->kif->pfik_ifp : NULL; - } else { - ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; - /* If pfsync'd */ - if (ifp == NULL) - ifp = r->rpool.cur->kif ? - r->rpool.cur->kif->pfik_ifp : NULL; - PF_STATE_UNLOCK(s); - } - if (ifp == oifp) { - /* When the 2nd interface is not skipped */ - return; - } else { - m0 = *m; - *m = NULL; - goto bad; - } - } else { - pd->pf_mtag->flags |= PF_MTAG_FLAG_DUPLICATED; - if (((m0 = m_dup(*m, M_NOWAIT)) == NULL)) { - if (s) - PF_STATE_UNLOCK(s); - return; - } + if (pd->r_rt == PF_DUPTO) { + if (((m0 = m_dup(*m, M_NOWAIT)) == NULL)) { + return PF_PASS; } + /* + * Original behaviour of pf was to emit the duplicated + * packet directly. We don't do it anymore in pf so + * set we set the M_SKIP_FIREWALL instead. + */ + m0->m_flags |= M_SKIP_FIREWALL; + pd->pf_mtag->flags |= PF_MTAG_FLAG_DUPLICATED; } else { - if ((r_rt == PF_REPLYTO) == (r_dir == pd->dir)) { - pf_dummynet(pd, s, r, m); - if (s) - PF_STATE_UNLOCK(s); - return; - } + if ((pd->r_rt == PF_REPLYTO) == (r_dir == pd->dir)) + return PF_PASS; m0 = *m; } @@ -6985,97 +6806,46 @@ dst.sin6_family = AF_INET6; dst.sin6_len = sizeof(dst); dst.sin6_addr = ip6->ip6_dst; + dst.sin6_scope_id = in6_get_unicast_scopeid(&ip6->ip6_dst, (*m)->m_pkthdr.rcvif); bzero(&naddr, sizeof(naddr)); if (s == NULL) { - if (TAILQ_EMPTY(&r->rpool.list)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__)); - goto bad_locked; - } - pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src, - &naddr, NULL, &sn); - if (!PF_AZERO(&naddr, AF_INET6)) - PF_ACPY((struct pf_addr *)&dst.sin6_addr, - &naddr, AF_INET6); - ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; + if (pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src, + &naddr, NULL, &sn) != 0) + goto bad; + PF_ACPY((struct pf_addr *)&dst.sin6_addr, &naddr, AF_INET6); } else { - if (!PF_AZERO(&s->rt_addr, AF_INET6)) - PF_ACPY((struct pf_addr *)&dst.sin6_addr, - &s->rt_addr, AF_INET6); - ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; - } - - if (s) - PF_STATE_UNLOCK(s); - - /* If pfsync'd */ - if (ifp == NULL) - ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; - if (ifp == NULL) - goto bad; - - if (pd->dir == PF_IN) { - if (pf_test6(PF_OUT, 0, ifp, &m0, inp, &pd->act) != PF_PASS) - goto bad; - else if (m0 == NULL) - goto done; - if (m0->m_len < sizeof(struct ip6_hdr)) { - DPFPRINTF(PF_DEBUG_URGENT, - ("%s: m0->m_len < sizeof(struct ip6_hdr)\n", - __func__)); - goto bad; - } - ip6 = mtod(m0, struct ip6_hdr *); - } - - if (ifp->if_flags & IFF_LOOPBACK) - m0->m_flags |= M_SKIP_FIREWALL; - - if (m0->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6 & - ~ifp->if_hwassist) { - uint32_t plen = m0->m_pkthdr.len - sizeof(*ip6); - in6_delayed_cksum(m0, plen, sizeof(struct ip6_hdr)); - m0->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6; + PF_ACPY((struct pf_addr *)&dst.sin6_addr, &s->rt_addr, AF_INET6); } /* - * If the packet is too large for the outgoing interface, - * send back an icmp6 error. + * XXX Interface is ignored! + * That is sadly not yet reflected in structures, ioctls and userspace. */ - if (IN6_IS_SCOPE_EMBED(&dst.sin6_addr)) - dst.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) { - md = m0; - pf_dummynet_route(pd, s, r, ifp, sintosa(&dst), &md); - if (md != NULL) - nd6_output_ifp(ifp, ifp, md, &dst, NULL); - } - else { - in6_ifstat_inc(ifp, ifs6_in_toobig); - if (r_rt != PF_DUPTO) { - if (s && pd->nat_rule != NULL) - PACKET_UNDO_NAT(m0, pd, - ((caddr_t)ip6 - m0->m_data) + - sizeof(struct ip6_hdr), s); - icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); - } else + rt_mtag = m_tag_find(m0, PACKET_TAG_IPFORWARD, NULL); + if (rt_mtag == NULL) { + rt_mtag = m_tag_get(PACKET_TAG_IPFORWARD, + sizeof(struct sockaddr_in), M_NOWAIT); + if (rt_mtag == NULL) { goto bad; + } + m_tag_prepend(m0, rt_mtag); + } + m0->m_flags |= M_IP6_NEXTHOP; + memcpy(rt_mtag+1, &dst, sizeof(struct sockaddr_in6)); + + if (pd->r_rt == PF_DUPTO) { + *mdup = m0; } -done: - if (r_rt != PF_DUPTO) - *m = NULL; - return; + return PF_PASS; -bad_locked: - if (s) - PF_STATE_UNLOCK(s); bad: m_freem(m0); - goto done; + *m = NULL; + return PF_DROP; } #endif /* INET6 */ @@ -7308,14 +7078,6 @@ static int pf_dummynet(struct pf_pdesc *pd, struct pf_kstate *s, struct pf_krule *r, struct mbuf **m0) -{ - return (pf_dummynet_route(pd, s, r, NULL, NULL, m0)); -} - -static int -pf_dummynet_route(struct pf_pdesc *pd, struct pf_kstate *s, - struct pf_krule *r, struct ifnet *ifp, struct sockaddr *sa, - struct mbuf **m0) { NET_EPOCH_ASSERT(); @@ -7334,27 +7096,10 @@ return (ENOMEM); } - if (ifp != NULL) { - pd->pf_mtag->flags |= PF_MTAG_FLAG_ROUTE_TO; - - pd->pf_mtag->if_index = ifp->if_index; - pd->pf_mtag->if_idxgen = ifp->if_idxgen; - - MPASS(sa != NULL); - - if (pd->af == AF_INET) - memcpy(&pd->pf_mtag->dst, sa, - sizeof(struct sockaddr_in)); - else - memcpy(&pd->pf_mtag->dst, sa, - sizeof(struct sockaddr_in6)); - } - if (pf_pdesc_to_dnflow(pd, r, s, &dnflow)) { pd->pf_mtag->flags |= PF_MTAG_FLAG_DUMMYNET; ip_dn_io_ptr(m0, &dnflow); if (*m0 != NULL) { - pd->pf_mtag->flags &= ~PF_MTAG_FLAG_ROUTE_TO; pd->pf_mtag->flags &= ~PF_MTAG_FLAG_DUMMYNET; } } @@ -7370,7 +7115,7 @@ { struct pfi_kkif *kif; u_short action, reason = 0; - struct mbuf *m = *m0; + struct mbuf *m = *m0, *mdup = NULL; struct ip *h = NULL; struct m_tag *ipfwtag; struct pf_krule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; @@ -7379,7 +7124,6 @@ struct pf_pdesc pd; int off, dirndx, use_2nd_queue = 0; uint16_t tag; - uint8_t rt; PF_RULES_RLOCK_TRACKER; KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: bad direction %d\n", __func__, dir)); @@ -7413,23 +7157,6 @@ memcpy(&pd.act, default_actions, sizeof(pd.act)); pd.pf_mtag = pf_find_mtag(m); - if (pd.pf_mtag != NULL && (pd.pf_mtag->flags & PF_MTAG_FLAG_ROUTE_TO)) { - pd.pf_mtag->flags &= ~PF_MTAG_FLAG_ROUTE_TO; - - ifp = ifnet_byindexgen(pd.pf_mtag->if_index, - pd.pf_mtag->if_idxgen); - if (ifp == NULL || ifp->if_flags & IFF_DYING) { - PF_RULES_RUNLOCK(); - m_freem(*m0); - *m0 = NULL; - return (PF_PASS); - } - PF_RULES_RUNLOCK(); - (ifp->if_output)(ifp, m, sintosa(&pd.pf_mtag->dst), NULL); - *m0 = NULL; - return (PF_PASS); - } - if (pd.pf_mtag && pd.pf_mtag->dnpipe) { pd.act.dnpipe = pd.pf_mtag->dnpipe; pd.act.flags = pd.pf_mtag->dnflags; @@ -7689,10 +7416,10 @@ if (s) { memcpy(&pd.act, &s->act, sizeof(struct pf_rule_actions)); tag = s->tag; - rt = s->rt; + pd.r_rt = s->rt; } else { tag = r->tag; - rt = r->rt; + pd.r_rt = r->rt; } if (tag > 0 && pf_tag_packet(m, &pd, tag)) { @@ -7890,14 +7617,14 @@ *m0 = NULL; break; default: - /* pf_route() returns unlocked. */ - if (rt) { - pf_route(m0, r, kif->pfik_ifp, s, &pd, inp); - return (action); + if (pd.r_rt) { + action = pf_route(m0, &mdup, r, s, &pd); } - if (pf_dummynet(&pd, s, r, m0) != 0) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); + if (*m0 != NULL) { + if (pf_dummynet(&pd, s, r, m0) != 0) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } } break; } @@ -7907,6 +7634,10 @@ if (s) PF_STATE_UNLOCK(s); + if (mdup != NULL) { + ip_output(mdup, NULL, NULL, 0, NULL, NULL); + } + return (action); } #endif /* INET */ @@ -7918,7 +7649,7 @@ { struct pfi_kkif *kif; u_short action, reason = 0; - struct mbuf *m = *m0, *n = NULL; + struct mbuf *m = *m0, *n = NULL, *mdup = NULL; struct m_tag *mtag; struct ip6_hdr *h = NULL; struct pf_krule *a = NULL, *r = &V_pf_default_rule, *tr, *nr; @@ -7927,7 +7658,6 @@ struct pf_pdesc pd; int off, terminal = 0, dirndx, rh_cnt = 0, use_2nd_queue = 0; uint16_t tag; - uint8_t rt; PF_RULES_RLOCK_TRACKER; KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: bad direction %d\n", __func__, dir)); @@ -7960,24 +7690,6 @@ memcpy(&pd.act, default_actions, sizeof(pd.act)); pd.pf_mtag = pf_find_mtag(m); - if (pd.pf_mtag != NULL && (pd.pf_mtag->flags & PF_MTAG_FLAG_ROUTE_TO)) { - pd.pf_mtag->flags &= ~PF_MTAG_FLAG_ROUTE_TO; - - ifp = ifnet_byindexgen(pd.pf_mtag->if_index, - pd.pf_mtag->if_idxgen); - if (ifp == NULL || ifp->if_flags & IFF_DYING) { - PF_RULES_RUNLOCK(); - m_freem(*m0); - *m0 = NULL; - return (PF_PASS); - } - PF_RULES_RUNLOCK(); - nd6_output_ifp(ifp, ifp, m, - (struct sockaddr_in6 *)&pd.pf_mtag->dst, NULL); - *m0 = NULL; - return (PF_PASS); - } - if (pd.pf_mtag && pd.pf_mtag->dnpipe) { pd.act.dnpipe = pd.pf_mtag->dnpipe; pd.act.flags = pd.pf_mtag->dnflags; @@ -8157,7 +7869,6 @@ s->src.seqhi = ntohl(pd.hdr.tcp.th_ack) - 1; s->src.seqlo = ntohl(pd.hdr.tcp.th_seq) - 1; pf_set_protostate(s, PF_PEER_SRC, PF_TCPS_PROXY_DST); - action = pf_synproxy(&pd, &s, &reason); break; } else { @@ -8286,10 +7997,10 @@ if (s) { memcpy(&pd.act, &s->act, sizeof(struct pf_rule_actions)); tag = s->tag; - rt = s->rt; + pd.r_rt = s->rt; } else { tag = r->tag; - rt = r->rt; + pd.r_rt = r->rt; } if (tag > 0 && pf_tag_packet(m, &pd, tag)) { @@ -8435,14 +8146,14 @@ *m0 = NULL; break; default: - /* pf_route6() returns unlocked. */ - if (rt) { - pf_route6(m0, r, kif->pfik_ifp, s, &pd, inp); - return (action); + if (pd.r_rt) { + action = pf_route6(m0, &mdup, r, s, &pd); } - if (pf_dummynet(&pd, s, r, m0) != 0) { - action = PF_DROP; - REASON_SET(&reason, PFRES_MEMORY); + if (*m0 != NULL) { + if (pf_dummynet(&pd, s, r, m0) != 0) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } } break; } @@ -8450,6 +8161,10 @@ if (s) PF_STATE_UNLOCK(s); + if (mdup != NULL) { + ip6_output(mdup, NULL, NULL, 0, NULL, NULL, NULL); + } + /* If reassembled packet passed, create new fragments. */ if (action == PF_PASS && *m0 && dir == PF_OUT && (mtag = m_tag_find(m, PACKET_TAG_PF_REASSEMBLED, NULL)) != NULL) Index: sys/netpfil/pf/pf_mtag.h =================================================================== --- sys/netpfil/pf/pf_mtag.h +++ sys/netpfil/pf/pf_mtag.h @@ -36,7 +36,7 @@ #ifdef _KERNEL /* pf_mtag -> flags */ -#define PF_MTAG_FLAG_ROUTE_TO 0x01 +/* 0x01 unused */ #define PF_MTAG_FLAG_DUMMYNET 0x02 #define PF_MTAG_FLAG_TRANSLATE_LOCALHOST 0x04 #define PF_MTAG_FLAG_PACKET_LOOPED 0x08 Index: sys/netpfil/pf/pf_syncookies.c =================================================================== --- sys/netpfil/pf/pf_syncookies.c +++ sys/netpfil/pf/pf_syncookies.c @@ -300,7 +300,7 @@ iss = pf_syncookie_generate(m, off, pd, mss); pf_send_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport, iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss, - 0, true, 0, 0, pd->act.rtableid); + 0, true, 0, 0, pd->act.rtableid, NULL); counter_u64_add(V_pf_status.lcounters[KLCNT_SYNCOOKIES_SENT], 1); /* XXX Maybe only in adaptive mode? */ atomic_add_64(&V_pf_status.syncookies_inflight[V_pf_syncookie_status.oddeven], @@ -519,5 +519,5 @@ return (pf_build_tcp(NULL, pd->af, pd->src, pd->dst, *pd->sport, *pd->dport, seq, 0, TH_SYN, wscale, mss, ttl, false, 0, - PF_MTAG_FLAG_SYNCOOKIE_RECREATED, pd->act.rtableid)); + PF_MTAG_FLAG_SYNCOOKIE_RECREATED, pd->act.rtableid, NULL)); }