Hello Gurus!

I have a question related to RAW socket programming.
At first, I want to describe several simple programs
I have developed (see the source code below).

1) icmp_send
This program sends specified amount of ICMP ECHO requests to
specified host.
You can also specify ID of ICMP ECHO packet.

2) icmp_recv
This program is slightly complex.
It creates RAW socket, sleeps for specified amount for seconds
and then receives ICMP ECHO replies.
Before start of receiving the packets the program calls alarm(2)
system call with specified timeout.
After receiving alarm signal, the program prints statistics and
exits.

3) icmp_cnt
This program simply receives ICMP echo replies and prints the
number of received packets "on fly".

I have also developed two simple shell scripts.
The first one is based on ifconfig utility.
This script periodically prints the number of received and
dropped packets (RX).
The second one is based on netstat raw sockets statistics
(i.e. netstat -sw).
This script periodically prints the number of received ICMP
packets and the number of errors.

And now I want to present the results of my experiments.

The sequence of commands is:
1) I started icmp_cnt
develop ~ # /home/krivenok/Exp/net/raw/icmp_cnt 765
2) I started icmp_recv
develop ~ # /home/krivenok/Exp/net/raw/icmp_recv 765 10 5
3) I started RX statistics.
krivenok@develop 17:41:18 ~/work/misc_scripts $ ./ifstat.sh
4) I started ICMP statistics.
krivenok@develop 17:40:55 ~/work/misc_scripts $ ./icmp_stat.sh
5) I started icmp_send.
develop ~ # /home/krivenok/Exp/net/raw/icmp_send 765 192.168.70.198
5000

Note: 765 is the ID of all ICMP packets.

The results are:

1) The output of icmp_cnt:
develop ~ # /home/krivenok/Exp/net/raw/icmp_cnt 765
my icmp echo = 04446 other = 04446
develop ~ #

2) The output of icmp_recv:

develop ~ # /home/krivenok/Exp/net/raw/icmp_recv 765 10 5
Sleeping for 10 sec.
The program will be killed after 5 sec.
My/Total 44/44
develop ~ #

3) The output of ifstat.sh:

krivenok@develop 17:41:18 ~/work/misc_scripts $ ./ifstat.sh
Packets: 162827730 (+ 0) Dropped: 0 (+ 0)
Packets: 162827759 (+ 29) Dropped: 0 (+ 0)
Packets: 162827767 (+ 8) Dropped: 0 (+ 0)
Packets: 162827801 (+ 34) Dropped: 0 (+ 0)
Packets: 162832838 (+ 5037) Dropped: 0 (+ 0) <---- BOOM!!!
Packets: 162832841 (+ 3) Dropped: 0 (+ 0)
Packets: 162832864 (+ 23) Dropped: 0 (+ 0)
Packets: 162832907 (+ 43) Dropped: 0 (+ 0)
Packets: 162832930 (+ 23) Dropped: 0 (+ 0)
Packets: 162832938 (+ 8) Dropped: 0 (+ 0)
Packets: 162832954 (+ 16) Dropped: 0 (+ 0)
Packets: 162832963 (+ 9) Dropped: 0 (+ 0)
Packets: 162832999 (+ 36) Dropped: 0 (+ 0)
Packets: 162833033 (+ 34) Dropped: 0 (+ 0)
Packets: 162833093 (+ 60) Dropped: 0 (+ 0)
krivenok@develop 17:42:51 ~/work/misc_scripts $

4) The output of icmp_stat.sh:

krivenok@develop 17:40:55 ~/work/misc_scripts $ ./icmp_stat.sh
ICMP Packets: 1532366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1532366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1532366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1532366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1532366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1532366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1532366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1532366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1532366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1537366 (+ 5000) Errors 75076 (+ 0) <---- BOOM!!!
ICMP Packets: 1537366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1537366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1537366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1537366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1537366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1537366 (+ 0) Errors 75076 (+ 0)
ICMP Packets: 1537366 (+ 0) Errors 75076 (+ 0)
krivenok@develop 17:42:48 ~/work/misc_scripts $

Lets consider the results.
As you can see 5000 ICMP echo requests have been sent.
There was no dropped packets as well as ICMP errors.
That is all 5000 (1537366 - 1532366) ICMP echo replies
have reached ICMP module in kernel.

However we lost 554 (5000 - 4446) packets.
I believe it's OK (producer is faster than consumer).

Much more interesting results are obtained with icmp_recv.
The program received ONLY 44 packets!!!
Hence, the socket buffer size is about 44 * 28 = 1232 bytes!
28 = ICMP header size + IP header size = 8 + 20 (note that I
use ICMP packets of minimal allowed size).

My questions are:
1) Are my tests correct?
2) Why the input buffer of RAW socket is limited by 1232 bytes?
How can I change this limit in linux (2.6.22)?
Is it possible at all?

Thank you beforehand!

P.S.

//////////////////////
icmp_send.c ///////////////////////////////////

// socket
#include
#include
// setuid
#include
#include
// memset, strerror
#include
// inet_aton
#include
#include
#include
// printf
#include
// errno
#include
// exit, atoi
#include
// icmp
#include

#define BUFSIZE 1500

// Calculates checksum of ICMP packet.
u_short in_cksum(addr, len)
u_short *addr; int len;
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;

while (nleft > 1) {
sum += *w++;
nleft -= 2;
}

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

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

}

int main(int argc, char** argv)
{
// Parsing of command line options.
if(argc < 4)
{
printf("Usage %s
.\n",argv[0]);
exit(1);
}
unsigned id = atoi(argv[1]);
char* address = argv[2];
int amount = atoi(argv[3]);

// Creating RAW socket.
int s;
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
{
printf("Unable to create raw socket (%s).\n",strerror(errno));
exit(1);
}
setuid(getuid());

// Making sockaddr_in structure.
struct sockaddr_in si_other;
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
if (inet_aton(address, &si_other.sin_addr)==0)
{
printf("inet_aton failed for address '%s' (%s)\n", address,
strerror(errno));
exit(1);
}

// Sending ICMP ECHO requests.
char sendbuf[BUFSIZE];
int i;
for(i=0; i {
// Making ICMP packet header.
struct icmp* icmp = (struct icmp *) sendbuf;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_id = htons(id);
icmp->icmp_seq = htons(i);
int len = 8;
icmp->icmp_cksum = 0;
icmp->icmp_cksum = in_cksum((u_short *) icmp, len);

// Sending the packet.
int rc = sendto(s, sendbuf, len, 0, (const struct
sockaddr*)&si_other, sizeof(si_other));
if(rc < 0)
{
printf("Unable to send ICMP ECHO REQUEST (%s).
\n",strerror(errno));
exit(1);
}
}

return 0;

}

//////////////////////
icmp_recv.c ///////////////////////////////////

// socket
#include
#include
// setuid
#include
#include
// memset, strerror
#include
// inet_aton
#include
#include
#include
// printf
#include
// errno
#include
// exit, atoi
#include
// icmp
#include
// sigaction
#include

#define BUFSIZE 1500

// Total number of ICMP ECHO REPLY packets.
unsigned long total = 0;
// The number of ICMP ECHO REPLY packets with expected ID.
unsigned long total_my = 0;

u_short in_cksum(addr, len)
u_short *addr; int len;
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;

while (nleft > 1) {
sum += *w++;
nleft -= 2;
}

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

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

}

void alarm_handler(int signo)
{
printf("My/Total %lu/%lu\n", total_my, total);
fflush(stdout);
exit(1);

}

int main(int argc, char** argv)
{
// Parsing of command line options.
if(argc < 4)
{
printf("Usage %s .\n",argv[0]);
exit(1);
}

unsigned id = atoi(argv[1]);
unsigned timeout = atoi(argv[2]);
unsigned alarm_to = atoi(argv[3]);

// Creating RAW socket.
int s;
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
{
printf("Unable to create raw socket (%s).\n",strerror(errno));
exit(1);
}
setuid(getuid());

// Registering ALARM signal handler.
static struct sigaction act;
act.sa_handler=alarm_handler;
sigfillset(&(act.sa_mask));
sigaction(SIGALRM, &act, NULL);

// Sleeping for specified amount of seconds.
// Note, that we DO NOT read any packets.
printf("Sleeping for %d sec.\n", timeout);
sleep(timeout);

// The program will be killed by SIGALRM after alarm_to seconds.
printf("The program will be killed after %d sec.\n", alarm_to);
alarm(alarm_to);

// Reading all the packets which are currently in the socket's
buffer.
char recvbuf[BUFSIZE];
while(1)
{
// Reading the IP packet.
struct sockaddr_in si_other;
int slen=sizeof(si_other);

int readed = recvfrom(s, recvbuf, sizeof(recvbuf),0, (struct
sockaddr*)&si_other, (socklen_t*)&slen);
if(readed == -1)
{
printf("Recv error (%s).\n",strerror(errno));
exit(1);
}
total++;

// Calculating the length of IP packet.
struct ip* ip = (struct ip *) recvbuf;
int hlen1 = ip->ip_hl << 2;

// Skipping not ICMP packets.
if (ip->ip_p != IPPROTO_ICMP) continue;

// Skipping not ECHO REPLY packets.
struct icmp* icmp = (struct icmp *) (recvbuf + hlen1);
if (icmp->icmp_type != ICMP_ECHOREPLY) continue;

// Yes, IDs are equal.
if (icmp->icmp_id == htons(id)) total_my++;
}

return 0;

}

////////////////////// icmp_cnt.c ///////////////////////////////////

// socket
#include
#include
// setuid
#include
#include
// memset, strerror
#include
// inet_aton
#include
#include
#include
// printf
#include
// errno
#include
// exit, atoi
#include
// icmp
#include

#define BUFSIZE 1500

// Total number of ICMP ECHO REPLY packets.
unsigned long total = 0;
// The number of ICMP ECHO REPLY packets with expected ID.
unsigned long total_my = 0;

u_short in_cksum(addr, len)
u_short *addr; int len;
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;

while (nleft > 1) {
sum += *w++;
nleft -= 2;
}

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

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

}

void alarm_handler(int signo)
{
printf("My/Total %lu/%lu\n", total_my, total);
fflush(stdout);
exit(1);

}

int main(int argc, char** argv)
{
// Parsing of command line options.
if(argc > 2)
{
printf("Usage %s [].\n",argv[0]);
exit(1);
}
unsigned id = 0;
if(argc == 2) id = atoi(argv[1]);

// Creating RAW socket.
int s;
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
{
printf("Unable to create raw socket (%s).\n",strerror(errno));
exit(1);
}
setuid(getuid());

char recvbuf[BUFSIZE];
while(1)
{
// Reading the IP packet.
struct sockaddr_in si_other;
int slen=sizeof(si_other);

int readed = recvfrom(s, recvbuf, sizeof(recvbuf),0, (struct
sockaddr*)&si_other, (socklen_t*)&slen);
if(readed == -1)
{
printf("Recv error (%s).\n",strerror(errno));
exit(1);
}
total++;

// Calculating the length of IP packet.
struct ip* ip = (struct ip *) recvbuf;
int hlen1 = ip->ip_hl << 2;

// Skipping not ICMP packets.
if (ip->ip_p != IPPROTO_ICMP) continue;

// Skipping not ECHO REPLY packets.
struct icmp* icmp = (struct icmp *) (recvbuf + hlen1);
if (id && icmp->icmp_type != ICMP_ECHOREPLY) continue;

// Yes, IDs are equal.
if (icmp->icmp_id == htons(id)) total_my++;
printf("\r");
printf("my icmp echo = %05lu other = %05lu", total_my, total);
fflush(stdout);
}

return 0;

}

////////////////////// ifstat.sh ///////////////////////////////////

#!/bin/bash

INTERFACE=eth0
TIMEOUT=1

OLD_DROPPED=0
DROPPED_DIFF=0

OLD_PACKETS=0
PACKETS_DIFF=0

while [ "1" ]; do
PACKETS=`ifconfig $INTERFACE | grep "RX p" | awk '{printf "%s:%s",
$2,$4}' | awk -F: '{printf "%s", $2}'`
DROPPED=`ifconfig $INTERFACE | grep "RX p" | awk '{printf "%s:%s",
$2,$4}' | awk -F: '{printf "%s", $4}'`
if [ "$OLD_DROPPED" -ne "0" ]; then
DROPPED_DIFF=`expr $DROPPED - $OLD_DROPPED`
fi
if [ "$OLD_PACKETS" -ne "0" ]; then
PACKETS_DIFF=`expr $PACKETS - $OLD_PACKETS`
fi

printf "Packets: %d (+ %4d) Dropped: %d (+ %4d)\n" $PACKETS
$PACKETS_DIFF $DROPPED $DROPPED_DIFF
OLD_DROPPED=$DROPPED
OLD_PACKETS=$PACKETS
sleep $TIMEOUT
done

//////////////////////
icmp_stat.sh ///////////////////////////////////

#!/bin/bash

TIMEOUT=1

OLD_PACKETS=0
PACKETS_DIFF=0

OLD_ERRORS=0
ERRORS_DIFF=0

while [ "1" ]; do
PACKETS=`/bin/netstat -s | grep "ICMP messages received" | awk
'{print $1}'`
if [ "$OLD_PACKETS" -ne "0" ]; then
PACKETS_DIFF=`expr $PACKETS - $OLD_PACKETS`
fi

ERRORS=`/bin/netstat -s | grep "input ICMP message failed" | awk
'{print $1}'`
if [ "$OLD_ERRORS" -ne "0" ]; then
ERRORS_DIFF=`expr $ERRORS - $OLD_ERRORS`
fi

printf "ICMP Packets: %d (+ %4d) Errors %d (+ %4d)\n" $PACKETS
$PACKETS_DIFF $ERRORS $ERRORS_DIFF
OLD_PACKETS=$PACKETS
OLD_ERRORS=$ERRORS
sleep $TIMEOUT
done