summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>2000-02-04 18:11:39 +0000
committerJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>2000-02-04 18:11:39 +0000
commit2de30dd27dde925c1c0d525c081d8cb7b6f57ee2 (patch)
tree144277500972c3104180b8bfaeb1e3a9a657f7a0
parent1f1d0e81c7d897db84659ba09ccf38f0172d97da (diff)
make IPv6 reass work on alpha. NetBSD PR 9340.
-rw-r--r--sys/netinet6/frag6.c130
-rw-r--r--sys/netinet6/ip6_var.h53
2 files changed, 107 insertions, 76 deletions
diff --git a/sys/netinet6/frag6.c b/sys/netinet6/frag6.c
index e33f260741d..2f84c07d804 100644
--- a/sys/netinet6/frag6.c
+++ b/sys/netinet6/frag6.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: frag6.c,v 1.4 2000/01/08 05:28:08 deraadt Exp $ */
+/* $OpenBSD: frag6.c,v 1.5 2000/02/04 18:11:38 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -71,17 +71,50 @@ int frag6_doing_reass;
u_int frag6_nfragpackets;
struct ip6q ip6q; /* ip6 reassemble queue */
+#ifndef offsetof /* XXX */
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+#endif
+
/*
* Initialise reassembly queue and fragment identifier.
*/
void
frag6_init()
{
- ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q;
ip6_id = arc4random();
+ ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q;
}
/*
+ * In RFC2460, fragment and reassembly rule do not agree with each other,
+ * in terms of next header field handling in fragment header.
+ * While the sender will use the same value for all of the fragmented packets,
+ * receiver is suggested not to check the consistency.
+ *
+ * fragment rule (p20):
+ * (2) A Fragment header containing:
+ * The Next Header value that identifies the first header of
+ * the Fragmentable Part of the original packet.
+ * -> next header field is same for all fragments
+ *
+ * reassembly rule (p21):
+ * The Next Header field of the last header of the Unfragmentable
+ * Part is obtained from the Next Header field of the first
+ * fragment's Fragment header.
+ * -> should grab it from the first fragment only
+ *
+ * The following note also contradicts with fragment rule - noone is going to
+ * send different fragment with different next header field.
+ *
+ * additional note (p22):
+ * The Next Header values in the Fragment headers of different
+ * fragments of the same original packet may differ. Only the value
+ * from the Offset zero fragment packet is used for reassembly.
+ * -> should grab it from the first fragment only
+ *
+ * There is no explicit reason given in the RFC. Historical reason maybe?
+ */
+/*
* Fragment input
*/
int
@@ -93,10 +126,10 @@ frag6_input(mp, offp, proto)
struct ip6_hdr *ip6;
struct ip6_frag *ip6f;
struct ip6q *q6;
- struct ip6asfrag *af6, *ip6af;
+ struct ip6asfrag *af6, *ip6af, *af6dwn;
int offset = *offp, nxt, i, next;
int first_frag = 0;
- u_short fragoff, frgpartlen;
+ int fragoff, frgpartlen; /* must be larger than u_int16_t */
struct ifnet *dstifp;
#ifdef IN6_IFSTAT_STRICT
static struct route_in6 ro;
@@ -157,7 +190,7 @@ frag6_input(mp, offp, proto)
(((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) {
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
- (caddr_t)&ip6->ip6_plen - (caddr_t)ip6);
+ offsetof(struct ip6_hdr, ip6_plen));
in6_ifstat_inc(dstifp, ifs6_reass_fail);
return IPPROTO_DONE;
}
@@ -165,16 +198,8 @@ frag6_input(mp, offp, proto)
ip6stat.ip6s_fragments++;
in6_ifstat_inc(dstifp, ifs6_reass_reqd);
- /*
- * Presence of header sizes in mbufs
- * would confuse code below.
- */
-#ifdef PULLDOWN_TEST
- /* XXX too strong mbuf requirement in m_pulldown() world */
-#endif
+ /* offset now points to data portion */
offset += sizeof(struct ip6_frag);
- m->m_data += offset;
- m->m_len -= offset;
for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next)
if (ip6f->ip6f_ident == q6->ip6q_ident &&
@@ -204,17 +229,12 @@ frag6_input(mp, offp, proto)
M_DONTWAIT);
if (q6 == NULL)
goto dropfrag;
+ bzero(q6, sizeof(*q6));
frag6_insque(q6, &ip6q);
+ /* ip6q_nxt will be filled afterwards, from 1st fragment */
q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6;
-#if 0
- /*
- * It is not necessarily the first segment; fragment offset
- * might be non-0.
- */
- q6->ip6q_nxt = ip6f->ip6f_nxt;
-#endif
#ifdef notyet
q6->ip6q_nxtp = (u_char *)nxtp;
#endif
@@ -242,22 +262,20 @@ frag6_input(mp, offp, proto)
* in size.
* If it would exceed, discard the fragment and return an ICMP error.
*/
- frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset;
+ frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset;
if (q6->ip6q_unfrglen >= 0) {
/* The 1st fragment has already arrived. */
if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) {
- m->m_data -= offset;
- m->m_len += offset;
icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
- offset - sizeof(struct ip6_frag) + 2);
+ offset - sizeof(struct ip6_frag) +
+ offsetof(struct ip6_frag, ip6f_offlg));
return(IPPROTO_DONE);
}
}
else if (fragoff + frgpartlen > IPV6_MAXPACKET) {
- m->m_data -= offset;
- m->m_len += offset;
icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
- offset - sizeof(struct ip6_frag) + 2);
+ offset - sizeof(struct ip6_frag) +
+ offsetof(struct ip6_frag, ip6f_offlg));
return(IPPROTO_DONE);
}
/*
@@ -265,8 +283,6 @@ frag6_input(mp, offp, proto)
* fragment already stored in the reassembly queue.
*/
if (fragoff == 0) {
- struct ip6asfrag *af6dwn;
-
for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
af6 = af6dwn) {
af6dwn = af6->ip6af_down;
@@ -279,10 +295,9 @@ frag6_input(mp, offp, proto)
/* dequeue the fragment. */
frag6_deq(af6);
+ free(af6, M_FTABLE);
/* adjust pointer. */
- merr->m_data -= af6->ip6af_offset;
- merr->m_len += af6->ip6af_offset;
ip6err = mtod(merr, struct ip6_hdr *);
/*
@@ -294,13 +309,21 @@ frag6_input(mp, offp, proto)
icmp6_error(merr, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_HEADER,
- erroff - sizeof(struct ip6_frag) + 2);
+ erroff - sizeof(struct ip6_frag) +
+ offsetof(struct ip6_frag, ip6f_offlg));
}
}
}
- /* Override the IPv6 header */
- ip6af = (struct ip6asfrag *)ip6;
+ ip6af = (struct ip6asfrag *)malloc(sizeof(struct ip6asfrag), M_FTABLE,
+ M_DONTWAIT);
+ if (ip6af == NULL)
+ goto dropfrag;
+ bzero(ip6af, sizeof(*ip6af));
+ ip6af->ip6af_head = ip6->ip6_flow;
+ ip6af->ip6af_len = ip6->ip6_plen;
+ ip6af->ip6af_nxt = ip6->ip6_nxt;
+ ip6af->ip6af_hlim = ip6->ip6_hlim;
ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG;
ip6af->ip6af_off = fragoff;
ip6af->ip6af_frglen = frgpartlen;
@@ -414,20 +437,25 @@ insert:
/*
* Reassembly is complete; concatenate fragments.
*/
-
ip6af = q6->ip6q_down;
t = m = IP6_REASS_MBUF(ip6af);
af6 = ip6af->ip6af_down;
+ frag6_deq(ip6af);
while (af6 != (struct ip6asfrag *)q6) {
+ af6dwn = af6->ip6af_down;
+ frag6_deq(af6);
while (t->m_next)
t = t->m_next;
t->m_next = IP6_REASS_MBUF(af6);
- af6 = af6->ip6af_down;
+ m_adj(t->m_next, af6->ip6af_offset);
+ free(af6, M_FTABLE);
+ af6 = af6dwn;
}
/* adjust offset to point where the original next header starts */
offset = ip6af->ip6af_offset - sizeof(struct ip6_frag);
- ip6 = (struct ip6_hdr *)ip6af;
+ free(ip6af, M_FTABLE);
+ ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons((u_short)next + offset - sizeof(struct ip6_hdr));
ip6->ip6_src = q6->ip6q_src;
ip6->ip6_dst = q6->ip6q_dst;
@@ -439,16 +467,22 @@ insert:
/*
* Delete frag6 header with as a few cost as possible.
*/
-
- if (offset < m->m_len)
+ if (offset < m->m_len) {
ovbcopy((caddr_t)ip6, (caddr_t)ip6 + sizeof(struct ip6_frag),
offset);
- else {
- ovbcopy(mtod(m, caddr_t), (caddr_t)ip6 + offset, m->m_len);
- m->m_data -= sizeof(struct ip6_frag);
+ m->m_data += sizeof(struct ip6_frag);
+ m->m_len -= sizeof(struct ip6_frag);
+ } else {
+ /* this comes with no copy if the boundary is on cluster */
+ if ((t = m_split(m, offset, M_DONTWAIT)) == NULL) {
+ frag6_remque(q6);
+ free(q6, M_FTABLE);
+ frag6_nfragpackets--;
+ goto dropfrag;
+ }
+ m_adj(t, sizeof(struct ip6_frag));
+ m_cat(m, t);
}
- m->m_data -= offset;
- m->m_len += offset;
/*
* Store NXT to the original.
@@ -514,8 +548,6 @@ frag6_freef(q6)
struct ip6_hdr *ip6;
/* adjust pointer */
- m->m_data -= af6->ip6af_offset;
- m->m_len += af6->ip6af_offset;
ip6 = mtod(m, struct ip6_hdr *);
/* restoure source and destination addresses */
@@ -524,9 +556,9 @@ frag6_freef(q6)
icmp6_error(m, ICMP6_TIME_EXCEEDED,
ICMP6_TIME_EXCEED_REASSEMBLY, 0);
- }
- else
+ } else
m_freem(m);
+ free(af6, M_FTABLE);
}
frag6_remque(q6);
free(q6, M_FTABLE);
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index 1d4f4b3f023..214fe3088f8 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_var.h,v 1.3 2000/01/08 13:54:36 itojun Exp $ */
+/* $OpenBSD: ip6_var.h,v 1.4 2000/02/04 18:11:38 itojun Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -72,38 +72,37 @@
* being reassembled is attached to one of these structures.
*/
struct ip6q {
- u_long ip6q_head;
- u_short ip6q_len;
- u_char ip6q_nxt;
- u_char ip6q_hlim;
- struct ip6asfrag *ip6q_down;
- struct ip6asfrag *ip6q_up;
- u_long ip6q_ident;
- u_char ip6q_arrive;
- u_char ip6q_ttl;
- struct in6_addr ip6q_src, ip6q_dst;
- struct ip6q *ip6q_next;
- struct ip6q *ip6q_prev;
- int ip6q_unfrglen;
+ u_int32_t ip6q_head;
+ u_int16_t ip6q_len;
+ u_int8_t ip6q_nxt; /* ip6f_nxt in first fragment */
+ u_int8_t ip6q_hlim;
+ struct ip6asfrag *ip6q_down;
+ struct ip6asfrag *ip6q_up;
+ u_int32_t ip6q_ident;
+ u_int8_t ip6q_arrive;
+ u_int8_t ip6q_ttl;
+ struct in6_addr ip6q_src, ip6q_dst;
+ struct ip6q *ip6q_next;
+ struct ip6q *ip6q_prev;
+ int ip6q_unfrglen; /* len of unfragmentable part */
#ifdef notyet
- u_char *ip6q_nxtp;
+ u_char *ip6q_nxtp;
#endif
};
struct ip6asfrag {
- u_long ip6af_head;
- u_short ip6af_len;
- u_char ip6af_nxt;
- u_char ip6af_hlim;
+ u_int32_t ip6af_head;
+ u_int16_t ip6af_len;
+ u_int8_t ip6af_nxt;
+ u_int8_t ip6af_hlim;
/* must not override the above members during reassembling */
- struct ip6asfrag *ip6af_down;
- struct ip6asfrag *ip6af_up;
- u_short ip6af_mff;
- u_short ip6af_off;
- struct mbuf *ip6af_m;
- u_long ip6af_offset; /* offset where next header starts */
- u_short ip6af_frglen; /* fragmentable part length */
- u_char ip6af_x1[10];
+ struct ip6asfrag *ip6af_down;
+ struct ip6asfrag *ip6af_up;
+ struct mbuf *ip6af_m;
+ int ip6af_offset; /* offset in ip6af_m to next header */
+ int ip6af_frglen; /* fragmentable part length */
+ int ip6af_off; /* fragment offset */
+ u_int16_t ip6af_mff; /* more fragment bit in frag off */
};
#define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m))