Skip to content

Commit

Permalink
pf: send ICMP destination unreachable fragmentation needed when appro…
Browse files Browse the repository at this point in the history
…priate

Sponsored by:	Rubicon Communications, LLC ("Netgate")
Differential Revision:	https://reviews.freebsd.org/D48805
Targeted-backport-for-stable/14-by: [email protected]
  • Loading branch information
kprovost authored and fichtner committed Feb 5, 2025
1 parent eb2415e commit cc31625
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 0 deletions.
1 change: 1 addition & 0 deletions sys/net/pfvar.h
Original file line number Diff line number Diff line change
Expand Up @@ -2277,6 +2277,7 @@ int pf_normalize_ip6(struct mbuf **, struct pfi_kkif *, u_short *,
void pf_poolmask(struct pf_addr *, struct pf_addr*,
struct pf_addr *, struct pf_addr *, sa_family_t);
void pf_addr_inc(struct pf_addr *, sa_family_t);
int pf_max_frag_size(struct mbuf *);
int pf_refragment6(struct ifnet *, struct mbuf **, struct m_tag *, bool);
#endif /* INET6 */

Expand Down
22 changes: 22 additions & 0 deletions sys/netpfil/pf/pf.c
Original file line number Diff line number Diff line change
Expand Up @@ -8358,6 +8358,15 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0,
h = mtod(m, struct ip *);
off = h->ip_hl << 2;

if (dir == PF_OUT && pflags & PFIL_FWD &&
h->ip_off & htons(IP_DF) && (*m0)->m_pkthdr.len > ifp->if_mtu) {
PF_RULES_RUNLOCK();
icmp_error(*m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
0, ifp->if_mtu);
*m0 = NULL;
return (PF_DROP);
}

if (__predict_false(ip_divert_ptr != NULL) &&
((mtag = m_tag_locate(m, MTAG_PF_DIVERT, 0, NULL)) != NULL)) {
struct pf_divert_mtag *dt = (struct pf_divert_mtag *)(mtag+1);
Expand Down Expand Up @@ -8931,6 +8940,19 @@ pf_test6(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb
h = mtod(m, struct ip6_hdr *);
off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr);

/*
* If we end up changing IP addresses (e.g. binat) the stack may get
* confused and fail to send the icmp6 packet too big error. Just send
* it here, before we do any NAT.
*/
if (dir == PF_OUT && pflags & PFIL_FWD &&
IN6_LINKMTU(ifp) < pf_max_frag_size(*m0)) {
PF_RULES_RUNLOCK();
icmp6_error(*m0, ICMP6_PACKET_TOO_BIG, 0, IN6_LINKMTU(ifp));
*m0 = NULL;
return (PF_DROP);
}

/* We do IP header normalization and packet reassembly here */
if (pf_normalize_ip6(m0, kif, &reason, &pd) != PF_PASS) {
m = *m0;
Expand Down
15 changes: 15 additions & 0 deletions sys/netpfil/pf/pf_norm.c
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,21 @@ pf_reassemble6(struct mbuf **m0, struct ip6_hdr *ip6, struct ip6_frag *fraghdr,
#endif /* INET6 */

#ifdef INET6
int
pf_max_frag_size(struct mbuf *m)
{
struct m_tag *tag;
struct pf_fragment_tag *ftag;

tag = m_tag_find(m, PACKET_TAG_PF_REASSEMBLED, NULL);
if (tag == NULL)
return (m->m_pkthdr.len);

ftag = (struct pf_fragment_tag *)(tag + 1);

return (ftag->ft_maxlen);
}

int
pf_refragment6(struct ifnet *ifp, struct mbuf **m0, struct m_tag *mtag,
bool forward)
Expand Down

0 comments on commit cc31625

Please sign in to comment.