Trouble passing TCP packets through Raw Socket

Trouble passing TCP packets through Raw Socket

Post by Dave Koogle » Wed, 12 May 1999 04:00:00



I am writing an user-level application for routing IP packets from
an HDLC serial interface through a Linux system. I receive packets
from the serial interface and pass them to the Linux system via a
set of three raw sockets: one for ICMP, another for UDP, and a third
for TCP. Each socket operates with the IP_HDRINCL option, which allows
me to pass the IP header along with the rest of the packet.

ICMP and UDP messages work fine. TCP messages fail, but in a rather
unusual way. It appears that the raw socket passes the IP and TCP
headers, but with the IP total length set as just the size of the
two headers without the data. But the incoming packet does have data.

I have checked all the obvious problems such as wrong packet lengths,
confusion with network and host byte order, and so forth. I just can
not find anything wrong with the code, especially since the TCP
socket code is symetrical vis a vis the UDP and ICMP code.

Has anyone used raw sockets under Linux to pass TCP? Is this feature
working or broken?

Linux version 2.0.36

Thanks for any help you can render,

Dave Koogler

 
 
 

Trouble passing TCP packets through Raw Socket

Post by Dave Koogle » Wed, 12 May 1999 04:00:00



> First this looks hackish. For general purpose packet insertion
> you would probably use a SOCK_PACKET socket though (which operates
> at layer2 only), instead of doing all that special purpose code.
> You would inject it to a dummy interface.

Yes, the raw socket approach is crude. Since I am working from
ignorance, I am leaning heavily on published information such as
Steven's "Unix Network Programming" and Rubini's "Linux Device
Drivers". Steven's discusses raw sockets in great depth, but only for
ICMP and UDP.

SOCK_PACKET seems to be a Linux-ism and I have not found any description
of this socket type (the 'socket' man page just mentions its existance
and says nothing about how it acts). Where is there a description of
this mechanism?

Quote:> Linux 2.2 also has a special purpose device for this application: > ethertap.

Alas, I must stay with the 2.0.xx series. My code is part of a much
larger system and the company has fixed on 2.0.xx as the delivery
vehicle.

Quote:>> ICMP and UDP messages work fine. TCP messages fail, but in a rather
>> unusual way. It appears that the raw socket passes the IP and TCP
>> headers, but with the IP total length set as just the size of the
>> two headers without the data. But the incoming packet does have
>data.

> Does this happen on sending or receiving?

Sending data. According to Steven's, you can not have a raw socket for
reading TCP, only for writing.

Quote:> Could you post a small test program for that? (that would clear the
> ambiguities)

Here is the offending code. 'packet' is the incoming data packet with
a prepended Cisco header. (I have removed my debugging statements so
you can easily see the code).

void
relay_tcp_pkt (unsigned char *packet, int packet_length)
{
        int             on, result;
        struct iphdr    *ip;
        struct tcphdr   *tcp;

          /* Strip off the cisco header. */
        packet = packet + sizeof (struct cisco_hdr);
        packet_length = packet_length - sizeof (struct cisco_hdr);

        ip = (struct iphdr *) packet;
        tcp = (struct tcphdr *) (packet + (ip->ihl * 4));

        sockaddr.sin_family      = AF_INET;
        sockaddr.sin_port        = tcp->dest;
        sockaddr.sin_addr.s_addr = ip->daddr;

        if (TCP_sock == 0) {
                TCP_sock = socket (AF_INET, SOCK_RAW, IPPROTO_TCP);
                if (TCP_sock < 0) {
                        if (errno == EPERM)
                                printf ("Must run as root\n");
                        else
                                perror ("Opening TCP socket");
                        exit (-1);
                }

                on = 1;
                if (setsockopt (TCP_sock, IPPROTO_IP, IP_HDRINCL,
                                &on, sizeof (on)) < 0) {
                        perror ("Setting TCP socket options");
                        exit (-1);
                }
        }

        result = sendto (TCP_sock, packet, packet_length, 0,
                         &sockaddr, sizeof (struct sockaddr));
        if (result < 0) {
                perror ("Error relaying TCP packet");
        }

Quote:}

With a de*, I can see the packet is correctly formed upto the
'sendto' and the packet_length is correct. When I look at the packet
comming back through my device driver, I see the mysteriously shortened
packet with no data, but properly formed IP and TCP headers.

Quote:> Does 2.2 show the same thing?

I will try building a 2.2.xx kernel and see what happens. I also have
access to a Sun system to see how it's raw sockets behave.

Dave Koogler