Subject: IP_HDRINCL send on little-endian machine causes kernel panic
To: None <current-users@NetBSD.ORG, tech-kern@NetBSD.ORG>
From: Jonathan Stone <jonathan@DSG.Stanford.EDU>
List: current-users
Date: 07/22/1996 15:34:44

It seems that NetBSD 1.2 (and I presume -current) have a bug in
sending IP_HDRINCL packets on little-endian machines. Little-endian
kernels will panic when sending a legitimate packet on a raw socket 
with IP_HDRINCL set.

The kernel assumes that outbound IP packets have the ip_len and ip_off
in machine native byteorder.  io_output() converts these fields
to network byte order immediately before checksumming a packet and
sending it to an interface output routine.  Until then, ip_output
interprets these fields in host byteorder.

sys/netinet/raw_ip.c:rip_output() is taking the header it gets from a
socket with IP_HDRINCL set, and passing it into the kernel.  I
_assume_ that the intended user/kernel interface is that packets
written to a raw socket with IP_HDRINCL specify the total length and
offset fields in network byte order.  If so, rip_output should be
byteswapping those fields.  It isn't.

The consequence is that if one uses a raw socket to send an IP packet
with a preformatted header on a little-endian machine, with a
network-order IP length, this length is interpreted in host byteorder
inside ip_output().  (For example, an IP datagram of length
40 bytes is treated as being 102400 bytes long).

When ip_output() examines the byteswapped IP length field, it computes
that a packet that's actually within the interface MTU (e.g., 40
bytes) needs fragmenting, computes how big a fragment it can send, and
calls m_copym() to construct the fragment.  Since the packet isn't
acually that big, m_copym() panics.

I'm guessing that the following an appropriate fix. Is it remotely
possible that the IP_HDRINCL interface is _meant_ to export the
kernel's assumption that, on output, ip_len and ip_off are in native
host byteorder??

*** raw_ip.c	Sun May 26 04:42:43 1996
--- raw_ip.c.dsg	Mon Jul 22 15:25:21 1996
*** 186,191 ****
--- 186,194 ----
  		if (ip->ip_id == 0)
  			ip->ip_id = htons(ip_id++);
  		opts = NULL;
+ 		/* XXX ip_output assumes ip_len and ip_off in host byteorder */
+ 		ip->ip_len = ntohs(ip->ip_len);
+ 		ip->ip_off = ntohs(ip->ip_off);
  		/* XXX prevent ip_output from overwriting header fields */
  		flags |= IP_RAWOUTPUT;