setsockopt HDRINCL 'error code 22, Invalid arg' OpenBSD 3.3 using gcc 2.95.3

Hi all. Im having problems allowing the kernel to let me include my own ip header.

I am writing a network/firewall and protocol analysis tool for my work.

Running on x86/OpenBSD 3.3 platform using gcc 2.95.3 with no special opts apart from debugging.

The src compiles ok, but when run; 'perror' recieves error code '22' 'invalid arg' from setsockopt ( i presume ) and I cannot understand why???

I have tested the prog bypassing the setsockopt function and it works ok, alas with the kernel ip header included ;(

Please forgive me if its a glaringly obvious error on my part as I am returning to c & *nix after a few years in the wilderness.

N.B Apologies, but my code seems to have lost all its tabs when i posted it



Many thanks for any help offered

Chris

--------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <unistd.h>
#include "decs.h"  

int main( void )
{
    unsigned int tport;    
    unsigned short uno, s;
    struct iphead *iphead;
    struct tcphead *tcphead;
    struct sockaddr_in dst;
    char dgram[BUFFER];
    tport = 1500;
    uno = 1;

    if( getuid() ) {
        printf("ALERT: You need to be r00t\n");
        exit( 0 );
    }

    if ( !( s = socket( AF_INET, SOCK_RAW, IPPROTO_TCP ) ) )
        perror("socket error");

    // cast dgram to struct and point *iphead at element
    iphead = ( struct iphead *) dgram;
    // as above but with size of ip_header offset
    tcphead = ( struct tcphead *) dgram + sizeof( struct iphead );

    dst.sin_family = AF_INET;
    dst.sin_port = htons( tport );
    dst.sin_addr.s_addr = inet_addr( "192.168.0.7" );
    memset( dgram, 0, BUFFER );

    // custom values
    iphead->ip_hl = 5;
    iphead->ip_v = 4;
    iphead->ip_tos = 0;
    iphead->ip_len = BUFFER;
    iphead->ip_id = htonl( 1234 );
    iphead->ip_off = 0;
    iphead->ip_ttl = 255;
    iphead->ip_p = 6; // tcp
    iphead->ip_sum = 0; // set to 0 by default
    iphead->ip_src.s_addr = inet_addr( "192.168.0.1" );
    iphead->ip_dst.s_addr = dst.sin_addr.s_addr;
    tcphead->th_sport = htons( 1234 );
    tcphead->th_dport = htons( tport );
    tcphead->th_seq = random();
    tcphead->th_ack = 0;
    tcphead->th_x2 = 0;
    tcphead->th_off = 0;
    tcphead->th_flags = TH_SYN; // init new connection
    tcphead->th_win = htonl( MAX_WINDOW );
    tcphead->th_sum = 0;
    tcphead->th_urp = 0; // no urgency flags

    // generate chksum
    iphead->ip_sum = chksum( ( unsigned short *)dgram, iphead->ip_len >> 1 );

    // header check
    if ( setsockopt( s,
    IPPROTO_IP,
    IP_HDRINCL,
    &uno,
    sizeof( uno ) ) < 0 ) {
    perror( "Alert: setsockopt cannot set HDRINCL" );
    exit( 1 );
    }

    // kill with control-c
    while( 1 ) {
        if( sendto( s,
        dgram,
        sizeof( dgram ),
        0,
        ( struct sockaddr * ) &dst,
        sizeof( dst ) ) < 0 ) {
            perror( "Alert: sendto failed" );
            exit( 1 );
        } else {
            printf( "." );
        }
    }
exit( 0 );
}


--------------------------------------------------

// Decs.h

#ifndef H_DECS
#define H_DECS
#define BUFFER sizeof( struct iphead ) + sizeof( struct tcphead )
#define MAX_WINDOW 65535

__BEGIN_DECLS
unsigned short chksum( unsigned short *, int );
void chkhdr( unsigned short );
__END_DECLS

/* initial command line opts */
typedef struct opts {
unsigned char *cfg; // absolute path
unsigned char *host; // dest host
unsigned short int port; // dest port
unsigned short int pkts; // number of packets to send
}cli_opts;

struct iphead {
unsigned char ip_hl, ip_v; // 4 bits respectively
unsigned char ip_tos; // limo or taxi???
unsigned short int ip_len; // total len of header and data
unsigned short int ip_id; // help assemble fragments
unsigned short ip_off; // 3bit control flags, 13bit offset. Do i frag?
unsigned char ip_ttl; // --ttl each hop. if 0 then destroy
unsigned char ip_p; // next level protocol
unsigned short int ip_sum; // header checksum
    struct in_addr ip_src, ip_dst; // source and dest
};

struct tcphead {
unsigned short int th_sport; // source port
unsigned short int th_dport; // destination port
unsigned int th_seq; // seq number of first data octet in segment
unsigned int th_ack; // next seq num expected to recieve
unsigned char th_x2, th_off; /* x2 reserved for future use ( must be 0 )
* data_off indicates where data begins */
unsigned char th_flags; /* TH_URG: urgent, TH_ACK: yup, TH_PSH, do not
                                    * buffer segment push straight thro stack
                                    * TH_RST: tell peer to terminate
                                    * TH_SYN: init new connect
                                    * TH_FIN: close connection
                                    * control bits */
unsigned short int th_win; /* amount of data octets to be accepted
                                        * including original seq_num octet */
unsigned short int th_sum; // initial value = 0
unsigned short int th_urp; // only used if TH_URG is set in tcp_flags
};


/* standard BSD checksum */
unsigned short chksum ( unsigned short *addr, int len ) {
    register int sum = 0;
    u_short answer = 0;
    register u_short *w = addr;
    register int nleft = len;

    /* 32 bit accumulator 'sum', add sequential 16 bit words to it '*w'.
    * At the end fold back all the carry bits from top 16 bits into
    * lower 16 bits */
    while ( nleft > 1 ) {
    sum += *w++;
    nleft -= 2;
    }


    // mop up odd byte, if necessary
    if ( nleft == 1 ) {
    *( u_char * ) ( &answer ) = *( u_char * )w ;
     sum += answer;
    }

    // add back carry outs from top 16 bits to lower 16 bits
    sum = ( sum >> 16 ) + ( sum + 0xffff ); // add hi 16 to low 16
    sum += ( sum >> 16 ); // add carry
    answer = ~sum; // truncate to 16bits
    return ( answer );
    }

    // check kernel hasnt inserted header
    void chkhdr( unsigned short sockd ) {

    }

#endif
--------------------------------------------------
crizzaAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

sunnycoderCommented:
I hevent worked on BSD but here are my 2 cents

>> if ( !( s = socket( AF_INET, SOCK_RAW, IPPROTO_TCP ) ) )
>>       perror("socket error");

socket returns -1 and not 0 if error occurs.... and in C everything except 0 is true ...

thus in case of error your statement would look like

if ( ! -1 )                       ==   if ( 0 )
        perror();

It may be possible that you are encountering some error in socket() and the invalid socket descriptor is being used in setsockopt() and hence the error
crizzaAuthor Commented:
Hi Sunnycoder.

Thanks for your 2 cents!!! you spotted a rather stupid error on my part.
I have thus changed the code to this ==>

if ( ( s = socket( AF_INET, SOCK_RAW, IPPROTO_TCP ) ) < 0 )
              perror( "socket error" );

changed s to a regular unsigned integer to make the comparison meaningful and hopefully now I will get an error if the socket descriptor cannot be assigned.

Unfortunately I am still encountering the same error as before with the setsockopt .

I had a look through the code for the ping prog because ping uses raw sockets and the setsockopt is the same there using the same values as I have ( eg uno = 1 , the ping code uses a var hold = 1 and they pass the address of hold to the function aswell )

Even if we cannot come across a full solution for this problem together sunny I would still like to give you some points for pointing out the error on my return value comparison for the socket function. How does 150 sound??? Unfortunately I do not know how to allocate a portion of my points to you individually. Any ideas???
sunnycoderCommented:
sounds great ...
to divide points, use point split feature ... you will find it right on top of the box in which you type your replies
split points when you have decided to close the question ...

Incase you dont get a satisfactory reply to the question, you can post in community support to adjust the point value

alternatively you can request mods to delete the question and post another question with 150 points

meanwhile i will try to find out why is setsockopt failing ...

linux man page lists following errors

ERRORS
       EBADF  The argument s is not a valid descriptor.

       ENOTSOCK
              The argument s is a file, not a socket.

       ENOPROTOOPT
              The option is unknown at the level indicated.

       EFAULT The  address pointed to by optval is not in a valid
              part of the process address space.  For getsockopt,
              this error may also be returned if optlen is not in
              a valid part of the process address space.

is it EBADF or ENOTSOCK which you are encountering
Learn SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

crizzaAuthor Commented:
Apparently I have to allocate all points over a spread of answers at one time. If for some reason I forget then dont hesitate to remind me pls.  Unless of course you get all the points !

Back to business!

I have the same/similar manpage with same errors, but the only error I can deduce Is the one that perror is sent on the failure of the call.

I dont know about Linux but 'perror' translates the global error code ( extern int errno that resides in header file errno.h ) that is set whenever ( most ) functions fail.

The global  errno translated by perror is code 22 EINVAL - Invalid arg ( see 'man 2 errno' ) 'Ive printed it via printf ( eg without perror ) to check'

IP_HDRINCL is defined in hdr file in.h like so:
#define IP_HDRINCL               2  // int; header is included with data

and also ( slightly differently )  in hdr file in_pcb.h like so

#define INP_HDRINCL            0x008 // user supplies entire IP header

I have tried both of these and their header files in different combos with no luck.

I am at my wits end sunny, I have been trying to solve this for days now without even a clue or hint as to were im going wrong ;( And ill put money on it that its something really simple. these things always are, like a needle in a haystack, shoot i dont even like hay  lol.

Oh well, get there in the end I think, just a matter of how long!

Chris
sunnycoderCommented:
>>And ill put money on it that its something really simple. these things always are
lol ... thats somehow always true

Ok heres some hit and trial thing

1. are you executing this code as root ? If no, then try executing this as root

2.
if ( !( s = socket( AF_INET, SOCK_RAW, IPPROTO_TCP ) ) )
       perror("socket error"); >>>>>>>>>>>> try using IPPROTO_RAW and execute as root ... does not make sense I know :)

3.
if ( setsockopt( s,       >>>>>>>>>>> if socket is successful then this is a valid argument
   IPPROTO_IP,           >>>>>>>>>>> valid constant
   IP_HDRINCL,          >>>>>>>>>>> valid constant
   &uno,                      >>>>>>>>>>> clearly valid arg
   sizeof( uno ) ) < 0 ) {   >>>>>>>>> another valid arg
   perror( "Alert: setsockopt cannot set HDRINCL" );
   exit( 1 );
   }
so even I am foxed, why the error ??? are you sure of the error message being printed by perror ... try clearing errno before making any calls and then check the error message

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
crizzaAuthor Commented:
Hi Sunnycoder, thanks for you post

1. are you executing this code as root ? If no, then try executing this as root

 Program wont run unless uid == 0;

2.
if ( !( s = socket( AF_INET, SOCK_RAW, IPPROTO_TCP ) ) )
      perror("socket error"); >>>>>>>>>>>> try using IPPROTO_RAW and execute as root ... does not make sense I know :)

      global int errno == BAD FILE DESCRIPTOR ( after perror translation )  which as you say, makes sense


same result with gobal int errno set to zero before call to socket

I am thinking that it is todo with openbsd security policy, its so goddam locked down that it makes sense im not allowed to modify kernel things such as the ip header, also am not allowed to bring down secure level past 1, it would seem that 1 is the default setting which also would make sense.

Im gonna re-write this on FreeBSD because Im not making any progress and time is dwindling away. You can have all the points sunny cause you put loads of effort into helping me ;)

Thanks Again

Chris  
sunnycoderCommented:
>>global int errno == BAD FILE DESCRIPTOR
now it does make some sense

if socket was successful and setsockopt failed with this error, then it most likely is security policy

if it turns out to be something else, let me know too :-) cuz I am feeling lost too

thanks for the points ...
crizzaAuthor Commented:
Hi Sunny.

I have found the solution to my problem.

To write and read raw TCP/UDP packets on FreeBSD and OpenBSD you have to use the bpf ( berkely packet filter ) which provides an interface to data link layers and has a builtin Filter to descriminate between packets.

If your not running *BSD then you can check out the manpage here:
http://www.openbsd.org/cgi-bin/man.cgi?query=bpf&apropos=0&sektion=0&manpath=OpenBSD+Current&arch=i386&format=html

Not quite the simple solution lol especially the Filter machine syntax :o

Cheers

Chris
sunnycoderCommented:
Thanks Chris... thats something new ... havent read the man page yet ( thanks for the link too ) but this definitely was not so simple ( for someone who does not know it )

Alas!
>>>>And ill put money on it that its something really simple. these things always are
>>lol ... thats somehow always true
no longer holds ;-)
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Unix OS

From novice to tech pro — start learning today.