dhilst

Ping/ICMP example

I have tried this on my archlinux i686 and works, on arch x86_64 segfaults and I don't no why, I need to work more on this.
/*
* ping.c
*
* An ping example. I have used some iputils code
* you can get the iputils source here: http://www.skbuff.net/iputils/
*
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <netinet/ip_icmp.h>


#define pexit(s) ({perror(s); exit(EXIT_FAILURE);})

u_short in_cksum(const u_short *addr, register int len, u_short csum);
void print_icmphdr(struct icmphdr *);
void print_iphdr(struct iphdr *);

int main(int argc, char **argv)
{
int sock;
int len;
int bytes;
int count = -1;
u_short cksum;
u_int16_t seq;

struct sockaddr_in dst_addr;
struct sockaddr_in rcv_addr;
struct hostent *dst_host;

#define BUFLEN 1000000
char outpack[BUFLEN];
struct icmphdr *icp;
struct iphdr *ip;

if (argc <= 1) {
printf("Usage: %s HOST [COUNT]");
exit(EXIT_FAILURE);
}



sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sock == -1)
pexit("socket");

dst_host = gethostbyname(argv[1]);
if (!dst_host) {
errno = h_errno;
pexit("gethostbyname");
}

memcpy(&dst_addr.sin_addr.s_addr, dst_host->h_addr_list[0],
sizeof(dst_addr));
dst_addr.sin_family = PF_INET;
dst_addr.sin_port = 0;

if (argc > 2 )
count = atoi(argv[2]);

seq = 1;
while (count--) {
icp = (struct icmphdr *)outpack;
icp->type = ICMP_ECHO;
icp->code = 0;
icp->un.echo.sequence = seq;
icp->un.echo.id = getpid();
icp->checksum = 0;
icp->checksum = in_cksum((u_short *)icp,
sizeof(struct icmphdr), 0);

bytes = sendto(sock, outpack, sizeof(struct icmphdr),
MSG_DONTWAIT, (struct sockaddr *)&dst_addr,
sizeof(dst_addr));
if (bytes < 0)
pexit("sendto");

sleep(1);

len = sizeof(struct sockaddr_in);
bytes = recvfrom(sock, outpack, sizeof(struct iphdr) +
sizeof(struct icmphdr), MSG_DONTWAIT,
(struct sockaddr *)&rcv_addr, &len);
if (bytes < 0) /* I'm ignoring incoming errors */
continue;

ip = (struct iphdr *)outpack;
icp = (struct icmphdr *)&outpack[sizeof(struct iphdr)];

cksum = icp->checksum;
icp->checksum = 0;
icp->checksum = in_cksum((u_short *)icp,
sizeof(struct icmphdr), 0);

if (cksum != icp->checksum) /* and ignoring */
continue; /* corrupted packets */

switch(icp->type) {
case ICMP_ECHOREPLY: /* and repeateds */
if (icp->un.echo.sequence < seq)
continue;
print_iphdr(ip);
print_icmphdr(icp);
putchar('\n');
seq++;
break;
case ICMP_DEST_UNREACH:
printf("Destination unreachable\n");
break;
}

}

return 0;
}


/*
* Taken from iputils/ping.c, at http://www.skbuff.net/iputils/
*/
u_short in_cksum(const u_short *addr, register int len, u_short csum)
{
register int nleft = len;
const u_short *w = addr;
register u_short answer;
register int sum = csum;

/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}

/* mop up an odd byte, if necessary */
if (nleft == 1)
sum += htons(*(u_char *)w << 8);

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

void print_iphdr(struct iphdr *ip)
{
printf("IP tos=%u id=%u ttl=%u saddr=%s daddr=%s ",
ip->tos, ip->id, ip->ttl, inet_ntoa(ip->saddr),
inet_ntoa(ip->daddr));
}
void print_icmphdr(struct icmphdr *icp)
{
printf("ICMP seq=%d ", icp->un.echo.sequence);
}