diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index b443dd0fc433..2e8d7f7aa0c6 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -1,4 +1,4 @@ -/* $NetBSD: ip_input.c,v 1.289 2010/07/19 14:09:45 rmind Exp $ */ +/* $NetBSD: ip_input.c,v 1.290 2010/11/05 00:21:51 rmind Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -91,7 +91,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.289 2010/07/19 14:09:45 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.290 2010/11/05 00:21:51 rmind Exp $"); #include "opt_inet.h" #include "opt_compat_netbsd.h" @@ -806,58 +806,21 @@ ours: * If offset or IP_MF are set, must reassemble. */ if (ip->ip_off & ~htons(IP_DF|IP_RF)) { - struct mbuf *m_final; - u_int off, flen; - bool mff; - - /* - * Prevent TCP blind data attacks by not allowing non-initial - * fragments to start at less than 68 bytes (minimal fragment - * size) and making sure the first fragment is at least 68 - * bytes. - */ - off = (ntohs(ip->ip_off) & IP_OFFMASK) << 3; - if ((off > 0 ? off + hlen : len) < IP_MINFRAGSIZE - 1) { - IP_STATINC(IP_STAT_BADFRAGS); - goto bad; - } - - /* Fragment length and MF flag. */ - flen = ntohs(ip->ip_len) - hlen; - mff = (ip->ip_off & htons(IP_MF)) != 0; - if (mff) { - /* - * Make sure that fragments have a data length - * which is non-zero and multiple of 8 bytes. - */ - if (flen == 0 || (flen & 0x7) != 0) { - IP_STATINC(IP_STAT_BADFRAGS); - goto bad; - } - } - - /* - * Adjust total IP length to not reflect header and convert - * offset of this to bytes. XXX: clobbers struct ip. - */ - ip->ip_len = htons(flen); - ip->ip_off = htons(off); - /* * Pass to IP reassembly mechanism. */ - if (ip_reass_packet(m, ip, mff, &m_final) != 0) { + if (ip_reass_packet(&m, ip) != 0) { /* Failed; invalid fragment(s) or packet. */ goto bad; } - if (m_final == NULL) { + if (m == NULL) { /* More fragments should come; silently return. */ return; } - /* Reassembly is done, we have the final packet. */ - m = m_final; - - /* Updated local variable(s). */ + /* + * Reassembly is done, we have the final packet. + * Updated cached data in local variable(s). + */ ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; } diff --git a/sys/netinet/ip_reass.c b/sys/netinet/ip_reass.c index db3b5299530c..3ec214554afa 100644 --- a/sys/netinet/ip_reass.c +++ b/sys/netinet/ip_reass.c @@ -1,4 +1,4 @@ -/* $NetBSD: ip_reass.c,v 1.6 2010/10/07 03:15:49 yamt Exp $ */ +/* $NetBSD: ip_reass.c,v 1.7 2010/11/05 00:21:51 rmind Exp $ */ /* * Copyright (c) 1982, 1986, 1988, 1993 @@ -46,7 +46,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ip_reass.c,v 1.6 2010/10/07 03:15:49 yamt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ip_reass.c,v 1.7 2010/11/05 00:21:51 rmind Exp $"); #include #include @@ -86,9 +86,11 @@ typedef struct ipfr_qent { bool ipqe_mff; } ipfr_qent_t; +TAILQ_HEAD(ipfr_qent_head, ipfr_qent); + typedef struct ipfr_queue { LIST_ENTRY(ipfr_queue) ipq_q; /* to other reass headers */ - TAILQ_HEAD(, ipfr_qent) ipq_fragq; /* queue of fragment entries */ + struct ipfr_qent_head ipq_fragq; /* queue of fragment entries */ uint8_t ipq_ttl; /* time for reass q to live */ uint8_t ipq_p; /* protocol of this fragment */ uint16_t ipq_id; /* sequence id for reassembly */ @@ -221,10 +223,10 @@ ip_nmbclusters_changed(void) struct mbuf * ip_reass(ipfr_qent_t *ipqe, ipfr_queue_t *fp, const u_int hash) { - const int hlen = ipqe->ipqe_ip->ip_hl << 2; + struct ip *ip = ipqe->ipqe_ip, *qip; + const int hlen = ip->ip_hl << 2; struct mbuf *m = ipqe->ipqe_m, *t; ipfr_qent_t *nq, *p, *q; - struct ip *ip; int i, next; KASSERT(mutex_owned(&ipfr_lock)); @@ -270,15 +272,15 @@ ip_reass(ipfr_qent_t *ipqe, ipfr_queue_t *fp, const u_int hash) if (fp == NULL) { goto dropfrag; } - LIST_INSERT_HEAD(&ip_frags[hash], fp, ipq_q); + TAILQ_INIT(&fp->ipq_fragq); fp->ipq_nfrags = 1; fp->ipq_ttl = IPFRAGTTL; - fp->ipq_p = ipqe->ipqe_ip->ip_p; - fp->ipq_id = ipqe->ipqe_ip->ip_id; - fp->ipq_tos = ipqe->ipqe_ip->ip_tos; - TAILQ_INIT(&fp->ipq_fragq); - fp->ipq_src = ipqe->ipqe_ip->ip_src; - fp->ipq_dst = ipqe->ipqe_ip->ip_dst; + fp->ipq_p = ip->ip_p; + fp->ipq_id = ip->ip_id; + fp->ipq_tos = ip->ip_tos; + fp->ipq_src = ip->ip_src; + fp->ipq_dst = ip->ip_dst; + LIST_INSERT_HEAD(&ip_frags[hash], fp, ipq_q); p = NULL; goto insert; } else { @@ -288,10 +290,15 @@ ip_reass(ipfr_qent_t *ipqe, ipfr_queue_t *fp, const u_int hash) /* * Find a segment which begins after this one does. */ - for (p = NULL, q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; - p = q, q = TAILQ_NEXT(q, ipqe_q)) - if (ntohs(q->ipqe_ip->ip_off) > ntohs(ipqe->ipqe_ip->ip_off)) + TAILQ_FOREACH(q, &fp->ipq_fragq, ipqe_q) { + if (ntohs(q->ipqe_ip->ip_off) > ntohs(ip->ip_off)) break; + } + if (q != NULL) { + p = TAILQ_PREV(q, ipfr_qent_head, ipqe_q); + } else { + p = TAILQ_LAST(&fp->ipq_fragq, ipfr_qent_head); + } /* * If there is a preceding segment, it may provide some of our @@ -300,16 +307,14 @@ ip_reass(ipfr_qent_t *ipqe, ipfr_queue_t *fp, const u_int hash) */ if (p != NULL) { i = ntohs(p->ipqe_ip->ip_off) + ntohs(p->ipqe_ip->ip_len) - - ntohs(ipqe->ipqe_ip->ip_off); + ntohs(ip->ip_off); if (i > 0) { - if (i >= ntohs(ipqe->ipqe_ip->ip_len)) { + if (i >= ntohs(ip->ip_len)) { goto dropfrag; } m_adj(ipqe->ipqe_m, i); - ipqe->ipqe_ip->ip_off = - htons(ntohs(ipqe->ipqe_ip->ip_off) + i); - ipqe->ipqe_ip->ip_len = - htons(ntohs(ipqe->ipqe_ip->ip_len) - i); + ip->ip_off = htons(ntohs(ip->ip_off) + i); + ip->ip_len = htons(ntohs(ip->ip_len) - i); } } @@ -317,16 +322,18 @@ ip_reass(ipfr_qent_t *ipqe, ipfr_queue_t *fp, const u_int hash) * While we overlap succeeding segments trim them or, if they are * completely covered, dequeue them. */ - for (; q != NULL && - ntohs(ipqe->ipqe_ip->ip_off) + ntohs(ipqe->ipqe_ip->ip_len) > - ntohs(q->ipqe_ip->ip_off); q = nq) { - i = (ntohs(ipqe->ipqe_ip->ip_off) + - ntohs(ipqe->ipqe_ip->ip_len)) - ntohs(q->ipqe_ip->ip_off); - if (i < ntohs(q->ipqe_ip->ip_len)) { - q->ipqe_ip->ip_len = - htons(ntohs(q->ipqe_ip->ip_len) - i); - q->ipqe_ip->ip_off = - htons(ntohs(q->ipqe_ip->ip_off) + i); + while (q != NULL) { + size_t end; + + qip = q->ipqe_ip; + end = ntohs(ip->ip_off) + ntohs(ip->ip_len); + if (end <= ntohs(qip->ip_off)) { + break; + } + i = end - ntohs(qip->ip_off); + if (i < ntohs(qip->ip_len)) { + qip->ip_len = htons(ntohs(qip->ip_len) - i); + qip->ip_off = htons(ntohs(qip->ip_off) + i); m_adj(q->ipqe_m, i); break; } @@ -336,6 +343,7 @@ ip_reass(ipfr_qent_t *ipqe, ipfr_queue_t *fp, const u_int hash) pool_cache_put(ipfren_cache, q); fp->ipq_nfrags--; ip_nfrags--; + q = nq; } insert: @@ -348,18 +356,20 @@ insert: TAILQ_INSERT_AFTER(&fp->ipq_fragq, p, ipqe, ipqe_q); } next = 0; - for (p = NULL, q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; - p = q, q = TAILQ_NEXT(q, ipqe_q)) { - if (ntohs(q->ipqe_ip->ip_off) != next) { + TAILQ_FOREACH(q, &fp->ipq_fragq, ipqe_q) { + qip = q->ipqe_ip; + if (ntohs(qip->ip_off) != next) { mutex_exit(&ipfr_lock); return NULL; } - next += ntohs(q->ipqe_ip->ip_len); + next += ntohs(qip->ip_len); } + p = TAILQ_LAST(&fp->ipq_fragq, ipfr_qent_head); if (p->ipqe_mff) { mutex_exit(&ipfr_lock); return NULL; } + /* * Reassembly is complete. Check for a bogus message size. */ @@ -602,15 +612,49 @@ ip_reass_slowtimo(void) * => Passed fragment should have IP_MF flag and/or offset set. * => Fragment should not have other than IP_MF flags set. * - * => Returns 0 on success or error otherwise. When reassembly is complete, - * m_final representing a constructed final packet is set. + * => Returns 0 on success or error otherwise. + * => On complete, m0 represents a constructed final packet. */ int -ip_reass_packet(struct mbuf *m, struct ip *ip, bool mff, struct mbuf **m_final) +ip_reass_packet(struct mbuf **m0, struct ip *ip) { + const int hlen = ip->ip_hl << 2; + const int len = ntohs(ip->ip_len); + struct mbuf *m = *m0; ipfr_queue_t *fp; ipfr_qent_t *ipqe; - u_int hash; + u_int hash, off, flen; + bool mff; + + /* + * Prevent TCP blind data attacks by not allowing non-initial + * fragments to start at less than 68 bytes (minimal fragment + * size) and making sure the first fragment is at least 68 + * bytes. + */ + off = (ntohs(ip->ip_off) & IP_OFFMASK) << 3; + if ((off > 0 ? off + hlen : len) < IP_MINFRAGSIZE - 1) { + IP_STATINC(IP_STAT_BADFRAGS); + return EINVAL; + } + + /* + * Fragment length and MF flag. Make sure that fragments have + * a data length which is non-zero and multiple of 8 bytes. + */ + flen = ntohs(ip->ip_len) - hlen; + mff = (ip->ip_off & htons(IP_MF)) != 0; + if (mff && (flen == 0 || (flen & 0x7) != 0)) { + IP_STATINC(IP_STAT_BADFRAGS); + return EINVAL; + } + + /* + * Adjust total IP length to not reflect header and convert + * offset of this to bytes. XXX: clobbers struct ip. + */ + ip->ip_len = htons(flen); + ip->ip_off = htons(off); /* Look for queue of fragments of this datagram. */ mutex_enter(&ipfr_lock); @@ -648,9 +692,9 @@ ip_reass_packet(struct mbuf *m, struct ip *ip, bool mff, struct mbuf **m_final) ipqe->ipqe_m = m; ipqe->ipqe_ip = ip; - *m_final = ip_reass(ipqe, fp, hash); - if (*m_final) { - /* Note if finally reassembled. */ + *m0 = ip_reass(ipqe, fp, hash); + if (*m0) { + /* Note that finally reassembled. */ IP_STATINC(IP_STAT_REASSEMBLED); } return 0; diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index 0329fdb17743..8f1b2f2958a9 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -1,4 +1,4 @@ -/* $NetBSD: ip_var.h,v 1.95 2010/08/25 00:05:14 rmind Exp $ */ +/* $NetBSD: ip_var.h,v 1.96 2010/11/05 00:21:51 rmind Exp $ */ /* * Copyright (c) 1982, 1986, 1993 @@ -196,7 +196,7 @@ int ip_fragment(struct mbuf *, struct ifnet *, u_long); int ip_pcbopts(struct mbuf **, const struct sockopt *); void ip_reass_init(void); -int ip_reass_packet(struct mbuf *, struct ip *, bool, struct mbuf **); +int ip_reass_packet(struct mbuf **, struct ip *); void ip_reass_slowtimo(void); void ip_reass_drain(void);