summaryrefslogtreecommitdiffstats
path: root/sys/netinet/ip_input.c
diff options
context:
space:
mode:
authorandre <andre@FreeBSD.org>2004-08-03 12:31:38 +0000
committerandre <andre@FreeBSD.org>2004-08-03 12:31:38 +0000
commitfd961632464d6804f3fe478dba325cdfc4cc2e30 (patch)
tree8b4ba610fc0294632ab0b3b0f74762ddb2e4d817 /sys/netinet/ip_input.c
parent8d14ac3ae01961e2e0819c370a584442520fe050 (diff)
downloadFreeBSD-src-fd961632464d6804f3fe478dba325cdfc4cc2e30.zip
FreeBSD-src-fd961632464d6804f3fe478dba325cdfc4cc2e30.tar.gz
o Move all parts of the IP reassembly process into the function ip_reass() to
make it fully self-contained. o ip_reass() now returns a new mbuf with the reassembled packet and ip->ip_len including the IP header. o Computation of the delayed checksum is moved into divert_packet(). Reviewed by: silby
Diffstat (limited to 'sys/netinet/ip_input.c')
-rw-r--r--sys/netinet/ip_input.c239
1 files changed, 123 insertions, 116 deletions
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 048f613..da67941 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -240,7 +240,7 @@ static int ip_dooptions(struct mbuf *m, int,
static void ip_forward(struct mbuf *m, int srcrt,
struct sockaddr_in *next_hop);
static void ip_freef(struct ipqhead *, struct ipq *);
-static struct mbuf *ip_reass(struct mbuf *, struct ipqhead *, struct ipq *);
+static struct mbuf *ip_reass(struct mbuf *);
/*
* IP initialization: fill in IP protocol switch table.
@@ -296,7 +296,6 @@ void
ip_input(struct mbuf *m)
{
struct ip *ip = NULL;
- struct ipq *fp;
struct in_ifaddr *ia = NULL;
struct ifaddr *ifa;
int i, checkif, hlen = 0;
@@ -726,103 +725,19 @@ ours:
}
/*
- * If offset or IP_MF are set, must reassemble.
- * Otherwise, nothing need be done.
- * (We could look in the reassembly queue to see
- * if the packet was previously fragmented,
- * but it's not worth the time; just let them time out.)
+ * Attempt reassembly; if it succeeds, proceed.
+ * ip_reass() will return a different mbuf.
*/
if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
-
- /* If maxnipq is 0, never accept fragments. */
- if (maxnipq == 0) {
- ipstat.ips_fragments++;
- ipstat.ips_fragdropped++;
- goto bad;
- }
-
- sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
- IPQ_LOCK();
- /*
- * Look for queue of fragments
- * of this datagram.
- */
- TAILQ_FOREACH(fp, &ipq[sum], ipq_list)
- 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 &&
-#ifdef MAC
- mac_fragment_match(m, fp) &&
-#endif
- ip->ip_p == fp->ipq_p)
- goto found;
-
- fp = NULL;
-
- /*
- * Enforce upper bound on number of fragmented packets
- * for which we attempt reassembly;
- * If maxnipq is -1, accept all fragments without limitation.
- */
- if ((nipq > maxnipq) && (maxnipq > 0)) {
- /*
- * drop something from the tail of the current queue
- * before proceeding further
- */
- struct ipq *q = TAILQ_LAST(&ipq[sum], ipqhead);
- if (q == NULL) { /* gak */
- for (i = 0; i < IPREASS_NHASH; i++) {
- struct ipq *r = TAILQ_LAST(&ipq[i], ipqhead);
- if (r) {
- ipstat.ips_fragtimeout += r->ipq_nfrags;
- ip_freef(&ipq[i], r);
- break;
- }
- }
- } else {
- ipstat.ips_fragtimeout += q->ipq_nfrags;
- ip_freef(&ipq[sum], q);
- }
- }
-found:
- /*
- * Adjust ip_len to not reflect header,
- * convert offset of this to bytes.
- */
- ip->ip_len -= hlen;
- if (ip->ip_off & IP_MF) {
- /*
- * Make sure that fragments have a data length
- * that's a non-zero multiple of 8 bytes.
- */
- if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) {
- IPQ_UNLOCK();
- ipstat.ips_toosmall++; /* XXX */
- goto bad;
- }
- m->m_flags |= M_FRAG;
- } else
- m->m_flags &= ~M_FRAG;
- ip->ip_off <<= 3;
-
- /*
- * Attempt reassembly; if it succeeds, proceed.
- * ip_reass() will return a different mbuf.
- */
- ipstat.ips_fragments++;
- m->m_pkthdr.header = ip;
- m = ip_reass(m, &ipq[sum], fp);
- IPQ_UNLOCK();
- if (m == 0)
+ m = ip_reass(m);
+ if (m == NULL)
return;
- ipstat.ips_reassembled++;
ip = mtod(m, struct ip *);
/* Get the header length of the reassembled packet */
hlen = ip->ip_hl << 2;
#ifdef IPDIVERT
/* Restore original checksum before diverting packet */
if (divert_find_info(m) != 0) {
- ip->ip_len += hlen;
ip->ip_len = htons(ip->ip_len);
ip->ip_off = htons(ip->ip_off);
ip->ip_sum = 0;
@@ -832,11 +747,15 @@ found:
ip->ip_sum = in_cksum(m, hlen);
ip->ip_off = ntohs(ip->ip_off);
ip->ip_len = ntohs(ip->ip_len);
- ip->ip_len -= hlen;
}
#endif
- } else
- ip->ip_len -= hlen;
+ }
+
+ /*
+ * Further protocols expect the packet length to be w/o the
+ * IP header.
+ */
+ ip->ip_len -= hlen;
#ifdef IPDIVERT
/*
@@ -951,27 +870,112 @@ bad:
/*
* Take incoming datagram fragment and try to reassemble it into
- * whole datagram. If a chain for reassembly of this datagram already
- * exists, then it is given as fp; otherwise have to make a chain.
- *
- * When IPDIVERT enabled, keep additional state with each packet that
- * tells us if we need to divert or tee the packet we're building.
- * In particular, *divinfo includes the port and TEE flag,
- * *divert_rule is the number of the matching rule.
+ * whole datagram. If the argument is the first fragment or one
+ * in between the function will return NULL and store the mbuf
+ * in the fragment chain. If the argument is the last fragment
+ * the packet will be reassembled and the pointer to the new
+ * mbuf returned for further processing. Only m_tags attached
+ * to the first packet/fragment are preserved.
+ * The IP header is *NOT* adjusted out of iplen.
*/
-static struct mbuf *
-ip_reass(struct mbuf *m, struct ipqhead *head, struct ipq *fp)
+struct mbuf *
+ip_reass(struct mbuf *m)
{
- struct ip *ip = mtod(m, struct ip *);
- register struct mbuf *p, *q, *nq;
- struct mbuf *t;
- int hlen = ip->ip_hl << 2;
- int i, next;
+ struct ip *ip;
+ struct mbuf *p, *q, *nq, *t;
+ struct ipq *fp = NULL;
+ struct ipqhead *head;
+ int i, hlen, next;
u_int8_t ecn, ecn0;
+ u_short hash;
- IPQ_LOCK_ASSERT();
+ /* If maxnipq is 0, never accept fragments. */
+ if (maxnipq == 0) {
+ ipstat.ips_fragments++;
+ ipstat.ips_fragdropped++;
+ goto dropfrag;
+ }
+
+ ip = mtod(m, struct ip *);
+ hlen = ip->ip_hl << 2;
+
+ hash = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
+ head = &ipq[hash];
+ IPQ_LOCK();
+
+ /*
+ * Look for queue of fragments
+ * of this datagram.
+ */
+ TAILQ_FOREACH(fp, head, ipq_list)
+ 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 &&
+#ifdef MAC
+ mac_fragment_match(m, fp) &&
+#endif
+ ip->ip_p == fp->ipq_p)
+ goto found;
+
+ fp = NULL;
+
+ /*
+ * Enforce upper bound on number of fragmented packets
+ * for which we attempt reassembly;
+ * If maxnipq is -1, accept all fragments without limitation.
+ */
+ if ((nipq > maxnipq) && (maxnipq > 0)) {
+ /*
+ * drop something from the tail of the current queue
+ * before proceeding further
+ */
+ struct ipq *q = TAILQ_LAST(head, ipqhead);
+ if (q == NULL) { /* gak */
+ for (i = 0; i < IPREASS_NHASH; i++) {
+ struct ipq *r = TAILQ_LAST(&ipq[i], ipqhead);
+ if (r) {
+ ipstat.ips_fragtimeout += r->ipq_nfrags;
+ ip_freef(&ipq[i], r);
+ break;
+ }
+ }
+ } else {
+ ipstat.ips_fragtimeout += q->ipq_nfrags;
+ ip_freef(head, q);
+ }
+ }
+
+found:
+ /*
+ * Adjust ip_len to not reflect header,
+ * convert offset of this to bytes.
+ */
+ ip->ip_len -= hlen;
+ if (ip->ip_off & IP_MF) {
+ /*
+ * Make sure that fragments have a data length
+ * that's a non-zero multiple of 8 bytes.
+ */
+ if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) {
+ IPQ_UNLOCK();
+ ipstat.ips_toosmall++; /* XXX */
+ goto dropfrag;
+ }
+ m->m_flags |= M_FRAG;
+ } else
+ m->m_flags &= ~M_FRAG;
+ ip->ip_off <<= 3;
+
+
+ /*
+ * Attempt reassembly; if it succeeds, proceed.
+ * ip_reass() will return a different mbuf.
+ */
+ ipstat.ips_fragments++;
+ m->m_pkthdr.header = ip;
+ /* Previous ip_reass() started here. */
/*
* Presence of header sizes in mbufs
* would confuse code below.
@@ -1114,7 +1118,7 @@ inserted:
ipstat.ips_fragdropped += fp->ipq_nfrags;
ip_freef(head, fp);
}
- return (0);
+ goto done;
}
next += GETIP(q)->ip_len;
}
@@ -1124,7 +1128,7 @@ inserted:
ipstat.ips_fragdropped += fp->ipq_nfrags;
ip_freef(head, fp);
}
- return (0);
+ goto done;
}
/*
@@ -1136,7 +1140,7 @@ inserted:
ipstat.ips_toolong++;
ipstat.ips_fragdropped += fp->ipq_nfrags;
ip_freef(head, fp);
- return (0);
+ goto done;
}
/*
@@ -1161,12 +1165,11 @@ inserted:
#endif
/*
- * Create header for new ip packet by
- * modifying header of first packet;
- * dequeue and discard fragment reassembly header.
+ * Create header for new ip packet by modifying header of first
+ * packet; dequeue and discard fragment reassembly header.
* Make header visible.
*/
- ip->ip_len = next;
+ ip->ip_len = (ip->ip_hl << 2) + next;
ip->ip_src = fp->ipq_src;
ip->ip_dst = fp->ipq_dst;
TAILQ_REMOVE(head, fp, ipq_list);
@@ -1177,6 +1180,8 @@ inserted:
/* some debugging cruft by sklower, below, will go away soon */
if (m->m_flags & M_PKTHDR) /* XXX this should be done elsewhere */
m_fixhdr(m);
+ ipstat.ips_reassembled++;
+ IPQ_UNLOCK();
return (m);
dropfrag:
@@ -1184,7 +1189,9 @@ dropfrag:
if (fp != NULL)
fp->ipq_nfrags--;
m_freem(m);
- return (0);
+done:
+ IPQ_UNLOCK();
+ return (NULL);
#undef GETIP
}
OpenPOWER on IntegriCloud