LinuxQuestions.org
Register a domain and help support LQ
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Networking
User Name
Password
Linux - Networking This forum is for any issue related to networks or networking.
Routing, network cards, OSI, etc. Anything is fair game.

Notices

Reply
 
Search this Thread
Old 04-16-2013, 01:22 PM   #1
pdbuchan
LQ Newbie
 
Registered: Oct 2011
Posts: 8

Rep: Reputation: Disabled
Invalid argument for sendmsg() with ancillary data (IPv4)


Hi guys. I posted on comp.os.linux.networking, but it seems to be pretty quiet these days. Please forgive my multiple posting. Here's the question I posed:

I'm looking for strategies for debugging a case where sendmsg() fails with "Invalid argument." The code below is producing that error.

I can't think of how to go about debugging it. I looked at the values of various parameters such as dst, src, data, datalen, outpack, etc., and it all looks correct.

Any tips would be great.

This is running on Ubuntu linux 10.04 LTS. Compiling with

gcc -Wall test.c

Thanks for any advice,
Dave

// Send an IPv4 ICMP echo request packet.
// Changes time-to-live (TTL) using ancillary data method.
// Use setsockopt() to bind socket to interface.
// Includes ICMP data.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // close()
#include <string.h> // strcpy, memset(), and memcpy()

#include <sys/socket.h> // struct msghdr
#include <netinet/in.h> // IPPROTO_IP, IPPROTO_ICMP
#include <netinet/ip.h> // IP_MAXPACKET (which is 65535)
#include <netinet/ip_icmp.h> // struct icmp, ICMP_ECHO
#include <netdb.h> // struct addrinfo
#include <sys/ioctl.h> // macro ioctl is defined
#include <bits/ioctls.h> // defines values for argument "request" of ioctl.
#include <net/if.h> // struct ifreq
#include <errno.h> // errno, perror()

// Define some constants.
#define IP4_HDRLEN 20 // IPv4 header length
#define ICMP_HDRLEN 8 // ICMP header length for echo request, excludes data

// Function prototypes
unsigned short int checksum (unsigned short int *, int);
unsigned short int icmp4_checksum (struct icmp, unsigned char *, int);

int
main (int argc, char **argv)
{
int status, datalen, sd, cmsglen, ttl;
char *interface, *target, *source;
struct icmp *icmphdr;
unsigned char *data, *outpack;
struct addrinfo hints, *res;
struct sockaddr_in src, dst;
struct sockaddr_in *ipv4;
socklen_t srclen;
struct ifreq ifr;
struct msghdr msghdr;
struct cmsghdr *cmsghdr;
struct iovec iov[2];
void *tmp;

// We will add 4 bytes of ICMP data later. Can be anything you choose.
datalen = 4;

// Allocate memory for various arrays.

tmp = (char *) malloc (40 * sizeof (char));
if (tmp != NULL) {
source = tmp;}
else {
fprintf (stderr, "ERROR: Cannot allocate memory for array 'source'.\n");
exit (EXIT_FAILURE);
}
memset (source, 0, 40 * sizeof (char));

tmp = (char *) malloc (40 * sizeof (char));
if (tmp != NULL) {
target = tmp;}
else {
fprintf (stderr, "ERROR: Cannot allocate memory for array 'target'.\n");
exit (EXIT_FAILURE);
}
memset (target, 0, 40 * sizeof (char));

tmp = (char *) malloc (40 * sizeof (char));
if (tmp != NULL) {
interface = tmp;
} else {
fprintf (stderr, "ERROR: Cannot allocate memory for array 'interface'.\n");
exit (EXIT_FAILURE);
}
memset (interface, 0, 40 * sizeof (char));

// Maximum ICMP payload size = 65535 - IPv4 header (20 bytes) - ICMP header (8 bytes) - ICMP payload data
tmp = (unsigned char *) malloc ((IP_MAXPACKET - IP4_HDRLEN - ICMP_HDRLEN - datalen) * sizeof (unsigned char));
if (tmp != NULL) {
data = tmp;
} else {
fprintf (stderr, "ERROR: Cannot allocate memory for array 'data'.\n");
exit (EXIT_FAILURE);
}
memset (data, 0, (IP_MAXPACKET - IP4_HDRLEN - ICMP_HDRLEN - datalen) * sizeof (unsigned char));

tmp = (unsigned char *) malloc ((IP_MAXPACKET - IP4_HDRLEN - ICMP_HDRLEN - datalen) * sizeof (unsigned char));
if (tmp != NULL) {
outpack = tmp;}
else {
fprintf (stderr, "ERROR: Cannot allocate memory for array 'outpack'.\n");
exit (EXIT_FAILURE);
}
memset (outpack, 0, (IP_MAXPACKET - IP4_HDRLEN - ICMP_HDRLEN - datalen) * sizeof (unsigned char));

// Interface to send packet through.
strcpy (interface, "eth0");

// Submit request for a socket descriptor to look up interface.
if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror ("socket() failed to get socket descriptor for using ioctl() ");
exit (EXIT_FAILURE);
}

// Use ioctl() to look up interface index which we will use to
// bind socket descriptor sd to specified interface with setsockopt() since
// none of the other arguments of sendto() specify which interface to use.
memset (&ifr, 0, sizeof (ifr));
snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface);
if (ioctl (sd, SIOCGIFINDEX, &ifr) < 0) {
perror ("ioctl() failed to find interface ");
return (EXIT_FAILURE);
}
close (sd);
printf ("Index for interface %s is %i\n", interface, ifr.ifr_ifindex);

// Source IPv4 address: you need to fill this out
strcpy (source, "192.168.1.132");

// Destination URL or IPv4 address: you need to fill this out
strcpy (target, "www.google.com");

// Fill out hints for getaddrinfo().
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = hints.ai_flags | AI_CANONNAME;

// Resolve source using getaddrinfo().
if ((status = getaddrinfo (source, NULL, &hints, &res)) != 0) {
fprintf (stderr, "getaddrinfo() failed: %s\n", gai_strerror (status));
return (EXIT_FAILURE);
}
memcpy (&src, res->ai_addr, res->ai_addrlen);
srclen = res->ai_addrlen;
freeaddrinfo (res);

// Resolve target using getaddrinfo().
if ((status = getaddrinfo (target, NULL, &hints, &res)) != 0) {
fprintf (stderr, "getaddrinfo() failed: %s\n", gai_strerror (status));
return (EXIT_FAILURE);
}
memcpy (&dst, res->ai_addr, res->ai_addrlen);
freeaddrinfo (res);

// Define first part of buffer outpack to be an IPv4 ICMP struct.
icmphdr = (struct icmp *) outpack;
memset (icmphdr, 0, ICMP_HDRLEN);

// Populate icmphdr portion of buffer outpack.
icmphdr->icmp_type = ICMP_ECHO;
icmphdr->icmp_code = 0;
icmphdr->icmp_id = htons (5);
icmphdr->icmp_seq = htons (300);
icmphdr->icmp_cksum = icmp4_checksum (*icmphdr, data, datalen);

// ICMP data
// datalen set to 4 above.
data[0] = 'T';
data[1] = 'e';
data[2] = 's';
data[3] = 't';

// Append ICMP data.
memcpy (outpack + ICMP_HDRLEN, data, datalen);

// Compose the msghdr structure.
memset (&msghdr, 0, sizeof (msghdr));
msghdr.msg_name = &dst; // pointer to socket address structure
msghdr.msg_namelen = sizeof (dst); // size of socket address structure

memset (&iov, 0, sizeof (iov));
iov[0].iov_base = (unsigned char *) outpack;
iov[0].iov_len = ICMP_HDRLEN + datalen;
msghdr.msg_iov = iov; // scatter/gather array
msghdr.msg_iovlen = 1; // number of elements in scatter/gather array

// Initialize msghdr and control data to total length of the message to be sent.
// Allocate some memory for our cmsghdr data.
cmsglen = CMSG_SPACE (sizeof (int));
tmp = (unsigned char *) malloc (cmsglen * sizeof (unsigned char));
if (tmp != NULL) {
msghdr.msg_control = tmp;
} else {
fprintf (stderr, "ERROR: Cannot allocate memory for array 'msghdr.msg_control'.\n");
exit (EXIT_FAILURE);
}
memset (msghdr.msg_control, 0, cmsglen);
msghdr.msg_controllen = cmsglen;

// Change time-to-live.
ttl = 123;
cmsghdr = CMSG_FIRSTHDR (&msghdr);
cmsghdr->cmsg_level = IPPROTO_IP;
cmsghdr->cmsg_type = IP_TTL; // We want to change time-to-live
cmsghdr->cmsg_len = CMSG_LEN (sizeof (int));
*((int *) CMSG_DATA (cmsghdr)) = ttl;

printf ("Checksum: %x\n", ntohs (icmphdr->icmp_cksum));

// Request a socket descriptor sd.
if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
fprintf (stderr, "Failed to get socket descriptor.\n");
exit (EXIT_FAILURE);
}

// Bind the socket descriptor to the source address.
if (bind (sd, (struct sockaddr *) &src, srclen) != 0) {
fprintf (stderr, "Failed to bind the socket descriptor to the source address.\n");
exit (EXIT_FAILURE);
}

// Bind socket to interface index.
if (setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr)) < 0) {
perror ("setsockopt() failed to bind to interface ");
exit (EXIT_FAILURE);
}

// Send packet.
if (sendmsg (sd, &msghdr, 0) < 0) {
perror ("sendmsg() failed ");
exit (EXIT_FAILURE);
}
close (sd);

// Free allocated memory.
free (target);
free (interface);
free (data);
free (outpack);
free (msghdr.msg_control);

return (EXIT_SUCCESS);
}

// IP checksum function
unsigned short int
checksum (unsigned short int *addr, int len)
{
int nleft = len;
int sum = 0;
unsigned short int *w = addr;
unsigned short int answer = 0;

while (nleft > 1) {
sum += *w++;
nleft -= sizeof (unsigned short int);
}

if (nleft == 1) {
*(unsigned char *) (&answer) = *(unsigned char *) w;
sum += answer;
}

sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}

// Build IPv4 ICMP pseudo-header and call checksum function.
unsigned short int
icmp4_checksum (struct icmp icmphdr, unsigned char *payload, int payloadlen)
{
char buf[IP_MAXPACKET];
char *ptr;
int chksumlen = 0;
int i;

ptr = &buf[0]; // ptr points to beginning of buffer buf

// Copy Message Type to buf (8 bits)
memcpy (ptr, &icmphdr.icmp_type, sizeof (icmphdr.icmp_type));
ptr += sizeof (icmphdr.icmp_type);
chksumlen += sizeof (icmphdr.icmp_type);

// Copy Message Code to buf (8 bits)
memcpy (ptr, &icmphdr.icmp_code, sizeof (icmphdr.icmp_code));
ptr += sizeof (icmphdr.icmp_code);
chksumlen += sizeof (icmphdr.icmp_code);

// Copy ICMP checksum to buf (16 bits)
// Zero, since we don't know it yet
*ptr = 0; ptr++;
*ptr = 0; ptr++;
chksumlen += 2;

// Copy Identifier to buf (16 bits)
memcpy (ptr, &icmphdr.icmp_id, sizeof (icmphdr.icmp_id));
ptr += sizeof (icmphdr.icmp_id);
chksumlen += sizeof (icmphdr.icmp_id);

// Copy Sequence Number to buf (16 bits)
memcpy (ptr, &icmphdr.icmp_seq, sizeof (icmphdr.icmp_seq));
ptr += sizeof (icmphdr.icmp_seq);
chksumlen += sizeof (icmphdr.icmp_seq);

// Copy payload to buf
memcpy (ptr, payload, payloadlen);
ptr += payloadlen;
chksumlen += payloadlen;

// Pad to the next 16-bit boundary
for (i=0; i<payloadlen%2; i++, ptr++) {
*ptr = 0;
ptr++;
chksumlen++;
}

return checksum ((unsigned short int *) buf, chksumlen);
}
 
Old 04-17-2013, 08:59 PM   #2
padeen
Member
 
Registered: Sep 2009
Location: Perth, W.A.
Distribution: Slackware 14, Debian 7, FreeBSD, OpenBSD
Posts: 182

Rep: Reputation: 35
I get an error on (roughly) line 101 where you create the socket: if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0). The socket call returns -1. I haven't tried looking up errno. How did you get past that? [Slackware 14.0]
 
Old 04-18-2013, 09:25 AM   #3
pdbuchan
LQ Newbie
 
Registered: Oct 2011
Posts: 8

Original Poster
Rep: Reputation: Disabled
Did you run it as root? Forgive me, as you likely know this already, but you can't obtain a raw socket descriptor without root priviledges.

If there's a problem with that line, I'm completely stumped.

Dave
 
Old 04-18-2013, 10:36 AM   #4
padeen
Member
 
Registered: Sep 2009
Location: Perth, W.A.
Distribution: Slackware 14, Debian 7, FreeBSD, OpenBSD
Posts: 182

Rep: Reputation: 35
Actually, I didn't know you can't get a raw socket without being root. I'll try that. Also, I notice you don't use *ip4 and it's indentation is different to the other declarations. I assume it's there because you were trying some different things and left it in after you removed those things.
 
Old 04-18-2013, 10:56 AM   #5
pdbuchan
LQ Newbie
 
Registered: Oct 2011
Posts: 8

Original Poster
Rep: Reputation: Disabled
You're right! ip4 is a remnant of old code I need to remove. I missed that.
Let me know if you get anywhere with the "bad argument" issue.
You've got me thinking about Domain, Type, and Protocol values. I'll try a few other combinations when I get home tonight.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Invalid argument runtime error on sendmsg(). JohnHubert Linux - Networking 3 08-30-2010 09:44 AM
invalid argument azza Programming 1 07-01-2009 09:45 PM
invalid argument azza Programming 1 06-29-2009 08:25 AM
message sending failed : Error[22 ] invalid argument .....but each and every argument rakeshranjanjha Linux - Software 2 01-07-2008 11:22 PM
sendmsg : Invalid Argument ksuresh_cse@yahoo.co Programming 0 09-14-2006 07:16 AM


All times are GMT -5. The time now is 11:18 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration