************************************************************************
Modifying the linux kernel to get proper SOCK_RAW IPPROTO_RAW behaviour.
************************************************************************


NOTE:  I *believe* the very latest 1.3.x (where x is around 90)
development kernels have been now been fixed.  If for some reason
you wish to use a 1.2 kernel or an old(ish) 1.3 kernel, read on....



This is fairly trivial, AFAIK the only reason that linux doesn't do the
same as BSD 4.4lite in this respect, is because the lack of documentation
describing what *should* actually be done.  Now that I have pointed out
that linux shouldn't overwrite the source address I hope that future
linux kernels should behave properly, however in the meantime, one of
the linux authors kindly told me how I could modify the kernel in order
to get the behaviour I wanted.

The only problem with this mod is that some (very stupid) linux programs,
namely traceroute, will still expect the linux kernel to (incorrectly)
overwrite certain IP fields (such as the total length field and the source
IP address) - In short: traceroute wont work.

The latest kernels at the time of writing are 1.2.13 and 1.3.53, if you
have later kernels than these it is worth checking that the problem hasn't
already been fixed.

OK, on to the mod, this should work fine for 1.3.14, I expect other
1.3.x versions should be similar (I'll describe a mod that works for
1.2.13 later)......

Firstly save a copy of raw.c, just in case you want to put things back
to how they were:

	cd /usr/src/linux/net/ipv4
	cp raw.c raw.c.old

Then edit raw.c.............

The offending function is raw_getrawfrag(), all it really needs to do is
what raw_getfrag() does (i.e. the 'memcpy_fromfs'), although I've filled
in some of the fields like BSD would.

Simply replace the raw_getrawfrag() function with:

 
static void raw_getrawfrag(void *p, int saddr, char *to, unsigned int offset, unsigned int fraglen)
{
	memcpy_fromfs(to, (unsigned char *)p+offset, fraglen);
	if(offset==0)
	{
		struct iphdr *iph=(struct iphdr *)to;

		/* if source address is 0 (or INADDR_ANY), should the source address be filled in???? - Arny */
		if(iph->saddr==0) iph->saddr=saddr;

		/*
	 	 *	Deliberate breach of modularity to keep 
	 	 *	ip_build_xmit clean (well less messy).
		 */
		/* if id is 0 kernel fills in id, this is definately what BSD4.4lite does - Arny */
		if(iph->id==0) iph->id = htons(ip_id_count++);

		/* if checksum is 0 kernel calculates and fills in checksum, I'm not sure but I think this the proper thing to do - Arny */
		if(iph->check==0) iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl);
	}
}


Then recompile the kernel (although make sure that this part *is* actually
recompiled).  Like I say this should work for 1.3.14, I've not tried it
with any other versions yet (perhaps I should, libpcap seems to crash
1.3.14 when I use it).

(if you are only interested in modifying 1.3.x kernels you can stop reading
this file at this point.)

The 1.2.x network code is quite different in this respect.  Rather than
trying to get proper BSD style raw socket behaviour, you can just simply
delete the code that over writes the source address in:

/usr/src/linux/net/inet/raw.c

I.E. you *could* just delete all these lines:


        /*
         *      If we are using IPPROTO_RAW, we need to fill in the source address in
         *      the IP header
         */

        if(sk->protocol==IPPROTO_RAW)
        {
                unsigned char *buff;
                struct iphdr *iph;

                buff = skb->data;
                buff += tmp;

                iph = (struct iphdr *)buff;
                iph->saddr = sk->saddr;
        }


Whilst just deleting the above lines *should* work (I haven't *actually*
tested it) this isn't actually what I did.  In order to let both the
existing (lame) linux traceroute work *and* ypghost, you can simply change
the line:

                iph->saddr = sk->saddr;

to:

                if(iph->saddr==0) iph->saddr = sk->saddr;

Just to reiterate, to modify the 1.2.13 kernel simply so that ypghost
works, just change this one line in /usr/src/linux/net/inet/raw.c and
recompile the kernel.

Many thanks to the aforementioned linux author.

Finally, I'm not responsible for anything this does.


Arny - arny@geek.org.uk

			http://www.unix.geek.org.uk/~arny/  (U.K.)
			http://www.unix.geek.net/~arny/     (U.S.)

