[net.bugs.4bsd] 4.2BSD as Gateway bug

ron@brl-tgr.UUCP (05/15/84)

Subject: 4.2BSD as Gateway bug
Newsgroups: net.bugs.4bsd

Subject: Fragmentation bug on packet forwarding
Index:	sys/netinet/ip_output.c 4.2BSD

Description:
	When the packets are being forwarded through a 4.2 BSD system
	a problem sometimes shows up that results in lost packets and
	transmission of illegally formatted internet packets.  The
	problem occurs when a 4.2 BSD system is gatewaying from nets
	that have unequal Maximum Transfer Units (MTU).  When a fragmented
	packet arriving from an outside source must be fragmented again
	the MORE FRAGMENTS bit is never set on the last fragment even
	though it may be required.  When other than the last fragment of
	the original packet is fragmented, the MORE FRAGMENTS bit must be
	set to indicate that the subsequent fragments of the original packet
	still follow (got that?).  This problem does not happen when you
	are not playing gateway because ip_output does not get passed
	already fragmented packets in that situation.  Example:  Host
	X sends ip packet A to BSD-HOST in two fragments, A1 and A2.
	BSD-HOST must then fragment A1 into A1a and A1b.  A1b should have
	MF set because fragment A2 still follows.

Repeat-By:
	Set the MTU on you host to some small number, and then try to
	forward already fragmented packets through it.

Fix:
	Set the bit on the last fragment to what it was on the packet
	before fragmenting began.  Below is the fragmentation code from
	ip_ouptut():
	/*
	 * Discard IP header from logical mbuf for m_copy's sake.
	 * Loop through length of segment, make a copy of each
	 * part and output.
	 */
	m->m_len -= sizeof (struct ip);
	m->m_off += sizeof (struct ip);
	for (off = 0; off < ip->ip_len-hlen; off += len) {
		struct mbuf *mh = m_get(M_DONTWAIT, MT_HEADER);
		struct ip *mhip;

		if (mh == 0) {
			error = ENOBUFS;
			goto bad;
		}
		mh->m_off = MMAXOFF - hlen;
		mhip = mtod(mh, struct ip *);
		*mhip = *ip;
		if (hlen > sizeof (struct ip)) {
			int olen = ip_optcopy(ip, mhip, off);
			mh->m_len = sizeof (struct ip) + olen;
		} else
			mh->m_len = sizeof (struct ip);
		mhip->ip_off = off >> 3;

		/*******************************************************\
		|*						       *|
		|*		A D D  T H I S  L I N E		       *|
		|*						       *|
		|*  If the packet we're fragmenting has fragments from *|
		|*  other systems, propagate the MORE_FRAGMENTS flag.  *|
		\*******************************************************/
		if(ip->ip_off & IP_MF) mhip->ip_off |= IP_MF;

		if (off + len >= ip->ip_len-hlen)
			len = mhip->ip_len = ip->ip_len - hlen - off;
		else {
			mhip->ip_len = len;
			mhip->ip_off |= IP_MF;
		}