Prevent denial of service using bogus fragmented IPv4 packets.

A attacker sending a lot of bogus fragmented packets to the target
(with different IPv4 identification field - ip_id), may be able
to put the target machine into mbuf starvation state.

By setting a upper limit on the number of reassembly queues we
prevent this situation.

This upper limit is controlled by the new sysctl
net.inet.ip.maxfragpackets which defaults to 200,
as the IPv6 case, this should be sufficient for most
systmes, but you might want to increase it if you have
lots of TCP sessions.
I'm working on making the default value dependent on
nmbclusters.

If you want old behaviour (no upper limit) set this sysctl
to a negative value.

If you don't want to accept any fragments (not recommended)
set the sysctl to 0 (zero).

Obtained from:	NetBSD
MFC after:	1 week
This commit is contained in:
Jesper Skriver 2001-06-03 23:33:23 +00:00
parent 06740f7b41
commit 690a6055ff
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=77665

View File

@ -125,6 +125,12 @@ SYSCTL_INT(_net_inet_ip, IPCTL_KEEPFAITH, keepfaith, CTLFLAG_RW,
&ip_keepfaith, 0,
"Enable packet capture for FAITH IPv4->IPv6 translater daemon");
static int ip_nfragpackets = 0;
static int ip_maxfragpackets = 200;
SYSCTL_INT(_net_inet_ip, OID_AUTO, maxfragpackets, CTLFLAG_RW,
&ip_maxfragpackets, 0,
"Maximum number of IPv4 fragment reassembly queue entries");
/*
* XXX - Setting ip_checkinterface mostly implements the receive side of
* the Strong ES model described in RFC 1122, but since the routing table
@ -865,6 +871,15 @@ ip_reass(m, head, fp)
* If first fragment to arrive, create a reassembly queue.
*/
if (fp == 0) {
/*
* Enforce upper bound on number of fragmented packets
* for which we attempt reassembly;
* If maxfrag is 0, never accept fragments.
* If maxfrag is -1, accept all fragments without limitation.
*/
if ((ip_maxfragpackets >= 0) && (ip_nfragpackets >= ip_maxfragpackets))
goto dropfrag;
ip_nfragpackets++;
if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
goto dropfrag;
fp = mtod(t, struct ipq *);
@ -1013,6 +1028,7 @@ ip_reass(m, head, fp)
TAILQ_REMOVE(head, fp, ipq_list);
nipq--;
(void) m_free(dtom(fp));
ip_nfragpackets--;
m->m_len += (IP_VHL_HL(ip->ip_vhl) << 2);
m->m_data -= (IP_VHL_HL(ip->ip_vhl) << 2);
/* some debugging cruft by sklower, below, will go away soon */
@ -1054,6 +1070,7 @@ ip_freef(fhp, fp)
}
TAILQ_REMOVE(fhp, fp, ipq_list);
(void) m_free(dtom(fp));
ip_nfragpackets--;
nipq--;
}
@ -1081,6 +1098,20 @@ ip_slowtimo()
}
}
}
/*
* If we are over the maximum number of fragments
* (due to the limit being lowered), drain off
* enough to get down to the new limit.
*/
for (i = 0; i < IPREASS_NHASH; i++) {
if (ip_maxfragpackets >= 0) {
while (ip_nfragpackets > ip_maxfragpackets &&
!TAILQ_EMPTY(&ipq[i])) {
ipstat.ips_fragdropped++;
ip_freef(&ipq[i], TAILQ_FIRST(&ipq[i]));
}
}
}
ipflow_slowtimo();
splx(s);
}