Prevent overflow with fragmented packets

Reviewed by: wollman
This commit is contained in:
Andrey A. Chernov 1997-09-15 23:07:01 +00:00
parent 211fed7e35
commit 194a213eff
1 changed files with 67 additions and 26 deletions

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)ip_input.c 8.2 (Berkeley) 1/4/94
* $Id: ip_input.c,v 1.63 1997/06/02 05:02:36 julian Exp $
* $Id: ip_input.c,v 1.64 1997/07/25 03:58:21 brian Exp $
* $ANA: ip_input.c,v 1.5 1996/09/18 14:34:59 wollman Exp $
*/
@ -110,7 +110,17 @@ SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD,
&ipintrq.ifq_drops, 0, "");
struct ipstat ipstat;
static struct ipq ipq;
/* Packet reassembly stuff */
#define IPREASS_NHASH_LOG2 6
#define IPREASS_NHASH (1 << IPREASS_NHASH_LOG2)
#define IPREASS_HMASK (IPREASS_NHASH - 1)
#define IPREASS_HASH(x,y) \
((((x) & 0xF | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
static struct ipq ipq[IPREASS_NHASH];
static int nipq = 0; /* total # of reass queues */
static int maxnipq;
#ifdef IPCTL_DEFMTU
SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW,
@ -171,7 +181,7 @@ static void ip_enq __P((struct ipasfrag *, struct ipasfrag *));
static void ip_forward __P((struct mbuf *, int));
static void ip_freef __P((struct ipq *));
static struct ip *
ip_reass __P((struct ipasfrag *, struct ipq *));
ip_reass __P((struct ipasfrag *, struct ipq *, struct ipq *));
static struct in_ifaddr *
ip_rtaddr __P((struct in_addr));
static void ipintr __P((void));
@ -196,7 +206,12 @@ ip_init()
if (pr->pr_domain->dom_family == PF_INET &&
pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW)
ip_protox[pr->pr_protocol] = pr - inetsw;
ipq.next = ipq.prev = &ipq;
for (i = 0; i < IPREASS_NHASH; i++)
ipq[i].next = ipq[i].prev = &ipq[i];
maxnipq = nmbclusters/4;
ip_id = time.tv_sec & 0xffff;
ipintrq.ifq_maxlen = ipqmaxlen;
#ifdef IPFIREWALL
@ -221,7 +236,7 @@ ip_input(struct mbuf *m)
struct ip *ip;
struct ipq *fp;
struct in_ifaddr *ia;
int hlen;
int i, hlen;
u_short sum;
#ifdef DIAGNOSTIC
@ -476,19 +491,37 @@ ours:
}
ip = mtod(m, struct ip *);
}
sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
/*
* Look for queue of fragments
* of this datagram.
*/
for (fp = ipq.next; fp != &ipq; fp = fp->next)
for (fp = ipq[sum].next; fp != &ipq[sum]; fp = fp->next)
if (ip->ip_id == fp->ipq_id &&
ip->ip_src.s_addr == fp->ipq_src.s_addr &&
ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
ip->ip_p == fp->ipq_p)
goto found;
fp = 0;
found:
fp = 0;
/* check if there's a place for the new queue */
if (nipq > maxnipq) {
/*
* drop something from the tail of the current queue
* before proceeding further
*/
if (ipq[sum].prev == &ipq[sum]) { /* gak */
for (i = 0; i < IPREASS_NHASH; i++) {
if (ipq[i].prev != &ipq[i]) {
ip_freef(ipq[i].prev);
break;
}
}
} else
ip_freef(ipq[sum].prev);
}
found:
/*
* Adjust ip_len to not reflect header,
* set ip_mff if more fragments are expected,
@ -507,7 +540,7 @@ found:
*/
if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
ipstat.ips_fragments++;
ip = ip_reass((struct ipasfrag *)ip, fp);
ip = ip_reass((struct ipasfrag *)ip, fp, &ipq[sum]);
if (ip == 0)
return;
ipstat.ips_reassembled++;
@ -583,9 +616,10 @@ NETISR_SET(NETISR_IP, ipintr);
* is given as fp; otherwise have to make a chain.
*/
static struct ip *
ip_reass(ip, fp)
ip_reass(ip, fp, where)
register struct ipasfrag *ip;
register struct ipq *fp;
struct ipq *where;
{
register struct mbuf *m = dtom(ip);
register struct ipasfrag *q;
@ -607,7 +641,8 @@ ip_reass(ip, fp)
if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL)
goto dropfrag;
fp = mtod(t, struct ipq *);
insque(fp, &ipq);
insque(fp, where);
nipq++;
fp->ipq_ttl = IPFRAGTTL;
fp->ipq_p = ip->ip_p;
fp->ipq_id = ip->ip_id;
@ -733,6 +768,7 @@ insert:
((struct ip *)ip)->ip_src = fp->ipq_src;
((struct ip *)ip)->ip_dst = fp->ipq_dst;
remque(fp);
nipq--;
(void) m_free(dtom(fp));
m = dtom(ip);
m->m_len += (ip->ip_hl << 2);
@ -769,6 +805,7 @@ ip_freef(fp)
}
remque(fp);
(void) m_free(dtom(fp));
nipq--;
}
/*
@ -808,18 +845,19 @@ ip_slowtimo()
{
register struct ipq *fp;
int s = splnet();
int i;
fp = ipq.next;
if (fp == 0) {
splx(s);
return;
}
while (fp != &ipq) {
--fp->ipq_ttl;
fp = fp->next;
if (fp->prev->ipq_ttl == 0) {
ipstat.ips_fragtimeout++;
ip_freef(fp->prev);
for (i = 0; i < IPREASS_NHASH; i++) {
fp = ipq[i].next;
if (fp == 0)
continue;
while (fp != &ipq[i]) {
--fp->ipq_ttl;
fp = fp->next;
if (fp->prev->ipq_ttl == 0) {
ipstat.ips_fragtimeout++;
ip_freef(fp->prev);
}
}
}
splx(s);
@ -831,11 +869,14 @@ ip_slowtimo()
void
ip_drain()
{
while (ipq.next != &ipq) {
ipstat.ips_fragdropped++;
ip_freef(ipq.next);
}
int i;
for (i = 0; i < IPREASS_NHASH; i++) {
while (ipq[i].next != &ipq[i]) {
ipstat.ips_fragdropped++;
ip_freef(ipq[i].next);
}
}
in_rtqdrain();
}