[comp.protocols.tcp-ip] 4.3BSD IP Option / Fragmentation Bug Fix

cpw%sneezy@LANL.GOV (C. Philip Wood) (02/24/88)

4.3BSD network bug (#?, ip_output)
Index:	sys/netinet/ip_output.c 4.3BSD FIX

Introduction:

This is not official, if a fix already exists, then excuse me.  I have
shown an IP Option/fragmentation problem to exist by generating IP
Security Option packets large enough to be fragmented.  I was able to
see the corruption via SUN's 'etherfind' on a 4.0beta LAN.  And,
consequently, fixed the problem.  Success being a "correct" response
to an ICMP Echo with IP Security Option, packet size > 1500 bytes,
generating 2 fragments.  I personally would have liked to see the
Security option come back, but what the hey, I got the Echo Reply!.

Description:

When sending IP packets which have to be fragmented, ip_output would
do strange things if any IP Options were included.   The result was that
the packets were discarded by Gateways, or thrown out by receiving
hosts because:

	IP options were essentially copied twice to the outgoing
	fragment due to having incorrectly set the offset
	for copies from the source mbuf.  The IHL was not taken into
	account.  This generated a bogus data portion in the packet.

	If the IP option was one that should only appear in the first
	fragment, then the checksum was calculated wrong on the second
	and succeeding fragments due to the use of the original IHL which
	included the option section length.

	I assume that this is only a problem with packet origination, and
	not with the 'ip forwarding mode'.
Fix:
	Apply the diffs shown below, if you are not a feared of viruses.

Signed:

	Phil Wood, cpw@lanl.gov

*** ip_output.c.orig	Tue Jul 28 10:27:53 1987
--- ip_output.c	Wed Feb 24 00:48:34 1988
***************
*** 5,7 ****
   *
!  *	@(#)ip_output.c	7.1 (Berkeley) 6/5/86
   */
--- 5,7 ----
   *
!  *	@(#)ip_output.c	7.1 (LANL)  2/24/88
   */
***************
*** 44,46 ****
  	register struct ifnet *ifp;
! 	int len, hlen = sizeof (struct ip), off, error = 0;
  	struct route iproute;
--- 44,46 ----
  	register struct ifnet *ifp;
! 	int len, hlen = sizeof (struct ip), off, error = 0, ohlen;
  	struct route iproute;
***************
*** 177,180 ****
  	 */
! 	m->m_len -= sizeof (struct ip);
! 	m->m_off += sizeof (struct ip);
  	for (off = 0; off < ip->ip_len-hlen; off += len) {
--- 177,181 ----
  	 */
! 	m->m_len -= hlen;
! 	m->m_off += hlen;
! 	ohlen = hlen;
  	for (off = 0; off < ip->ip_len-hlen; off += len) {
***************
*** 182,183 ****
--- 183,185 ----
  		struct ip *mhip;
+ 		register olen = 0;
  
***************
*** 191,194 ****
  		if (hlen > sizeof (struct ip)) {
! 			int olen = ip_optcopy(ip, mhip, off);
! 			mh->m_len = sizeof (struct ip) + olen;
  		} else
--- 193,198 ----
  		if (hlen > sizeof (struct ip)) {
! 			olen = ip_optcopy(ip, mhip, off);
! 			ohlen = sizeof (struct ip) + olen;
! 			mh->m_len = ohlen;
! 			mhip->ip_hl = ohlen >> 2;
  		} else
***************
*** 204,206 ****
  		}
! 		mhip->ip_len += sizeof (struct ip);
  		mhip->ip_len = htons((u_short)mhip->ip_len);
--- 208,210 ----
  		}
! 		mhip->ip_len += sizeof (struct ip) + olen;
  		mhip->ip_len = htons((u_short)mhip->ip_len);
***************
*** 214,216 ****
  		mhip->ip_sum = 0;
! 		mhip->ip_sum = in_cksum(mh, hlen);
  		if (error = (*ifp->if_output)(ifp, mh, (struct sockaddr *)dst))
--- 218,220 ----
  		mhip->ip_sum = 0;
! 		mhip->ip_sum = in_cksum(mh, ohlen );
  		if (error = (*ifp->if_output)(ifp, mh, (struct sockaddr *)dst))