aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2018-11-29 20:59:18 +0000
committerMark Johnston <markj@FreeBSD.org>2018-11-29 20:59:18 +0000
commit952168d0cb3f6e78dbf38b6109eafa4389801ed8 (patch)
tree162c91da31cfd140f203c93021f0b696b75a2b11
parentf2ade22c6c74ad4b65e95577167cba8e2dc11a34 (diff)
MFstable/12 r341259:
Add some additional length checks to the IPv4 fragmentation code. Approved by: re (gjb)
Notes
Notes: svn path=/releng/12.0/; revision=341262
-rw-r--r--sys/netinet/ip_reass.c45
-rw-r--r--sys/netinet/ip_var.h1
2 files changed, 34 insertions, 12 deletions
diff --git a/sys/netinet/ip_reass.c b/sys/netinet/ip_reass.c
index 51633864ea96..bb97b8b07d7d 100644
--- a/sys/netinet/ip_reass.c
+++ b/sys/netinet/ip_reass.c
@@ -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
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index f874628a3fd7..86615a15ad26 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -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 */