Add some additional length checks to the IPv4 fragmentation code.
Specifically, block 0-length fragments, even when the MF bit is clear. Also, ensure that every fragment with the MF bit clear ends at the same offset and that no subsequently-received fragments exceed that offset. Reviewed by: glebius, markj MFC after: 3 days Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D17922
This commit is contained in:
parent
2c054ce924
commit
2157f3c36a
@ -211,19 +211,21 @@ ip_reass(struct mbuf *m)
|
||||
* convert offset of this to bytes.
|
||||
*/
|
||||
ip->ip_len = htons(ntohs(ip->ip_len) - hlen);
|
||||
if (ip->ip_off & htons(IP_MF)) {
|
||||
/*
|
||||
* Make sure that fragments have a data length
|
||||
* that's a non-zero multiple of 8 bytes.
|
||||
*/
|
||||
if (ip->ip_len == htons(0) || (ntohs(ip->ip_len) & 0x7) != 0) {
|
||||
IPSTAT_INC(ips_toosmall); /* XXX */
|
||||
IPSTAT_INC(ips_fragdropped);
|
||||
m_freem(m);
|
||||
return (NULL);
|
||||
}
|
||||
/*
|
||||
* Make sure that fragments have a data length
|
||||
* that's a non-zero multiple of 8 bytes, unless
|
||||
* this is the last fragment.
|
||||
*/
|
||||
if (ip->ip_len == htons(0) ||
|
||||
((ip->ip_off & htons(IP_MF)) && (ntohs(ip->ip_len) & 0x7) != 0)) {
|
||||
IPSTAT_INC(ips_toosmall); /* XXX */
|
||||
IPSTAT_INC(ips_fragdropped);
|
||||
m_freem(m);
|
||||
return (NULL);
|
||||
}
|
||||
if (ip->ip_off & htons(IP_MF))
|
||||
m->m_flags |= M_IP_FRAG;
|
||||
} else
|
||||
else
|
||||
m->m_flags &= ~M_IP_FRAG;
|
||||
ip->ip_off = htons(ntohs(ip->ip_off) << 3);
|
||||
|
||||
@ -301,9 +303,28 @@ ip_reass(struct mbuf *m)
|
||||
fp->ipq_src = ip->ip_src;
|
||||
fp->ipq_dst = ip->ip_dst;
|
||||
fp->ipq_frags = m;
|
||||
if (m->m_flags & M_IP_FRAG)
|
||||
fp->ipq_maxoff = -1;
|
||||
else
|
||||
fp->ipq_maxoff = ntohs(ip->ip_off) + ntohs(ip->ip_len);
|
||||
m->m_nextpkt = NULL;
|
||||
goto done;
|
||||
} else {
|
||||
/*
|
||||
* If we already saw the last fragment, make sure
|
||||
* this fragment's offset looks sane. Otherwise, if
|
||||
* this is the last fragment, record its endpoint.
|
||||
*/
|
||||
if (fp->ipq_maxoff > 0) {
|
||||
i = ntohs(ip->ip_off) + ntohs(ip->ip_len);
|
||||
if (((m->m_flags & M_IP_FRAG) && i >= fp->ipq_maxoff) ||
|
||||
((m->m_flags & M_IP_FRAG) == 0 &&
|
||||
i != fp->ipq_maxoff)) {
|
||||
fp = NULL;
|
||||
goto dropfrag;
|
||||
}
|
||||
} else if ((m->m_flags & M_IP_FRAG) == 0)
|
||||
fp->ipq_maxoff = ntohs(ip->ip_off) + ntohs(ip->ip_len);
|
||||
fp->ipq_nfrags++;
|
||||
atomic_add_int(&nfrags, 1);
|
||||
#ifdef MAC
|
||||
|
@ -61,6 +61,7 @@ struct ipq {
|
||||
u_char ipq_ttl; /* time for reass q to live */
|
||||
u_char ipq_p; /* protocol of this fragment */
|
||||
u_short ipq_id; /* sequence id for reassembly */
|
||||
int ipq_maxoff; /* total length of packet */
|
||||
struct mbuf *ipq_frags; /* to ip headers of fragments */
|
||||
struct in_addr ipq_src,ipq_dst;
|
||||
u_char ipq_nfrags; /* # frags in this packet */
|
||||
|
Loading…
x
Reference in New Issue
Block a user