bedtime
Posts: 64
Joined: Thu Dec 13, 2018 6:02 pm

Lightweight ping program in C code * requires root *

Sun May 05, 2019 1:56 pm

I found a lightweight ping program that uses Linux sockets to ping a URL or IP. The code spacing left something to be desired, so I cleaned it up a little.

It is only 300 lines of code and, according to 'top' and 'top', uses only less than 2MB of virtual memory. It appears to be working fine; when I shut down the net, it shows no packets received, but I can't say much more

Thought you guys might want to check it out: :)

Courtesy of:
https://github.com/hayderimran7/advance ... ter/ping.c

My cleaned up version is below:

sping.c (i.e., simpleping):

Code: Select all

/* A simple Ping program to ping any address such as google.com in Linux 
   
   You can ping localhost addresses.

   See the RAW socket implementation

   Compile with:
   gcc -Wall sping.c -o sping


   Note: Run under root priveledges!

   ex. usage:

   # ./sping 127.0.0.1 
   # ./sping google.com 

*/

#include <stdio.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <setjmp.h>
#include <errno.h>
#include <string.h> 
#include <stdlib.h>

#define PACKET_SIZE     4096
#define MAX_WAIT_TIME   1
#define MAX_NO_PACKETS  1

char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];

int sockfd, datalen = 56;
int nsend = 0, nreceived = 0;
int printInfo = 0; // My only personal addition to the program. It allows for only a 1 or 0 result.

pid_t pid;

struct sockaddr_in dest_addr;
struct sockaddr_in from;
struct timeval tvrecv;

void statistics(int signo);
unsigned short cal_chksum(unsigned short *addr, int len);
int pack(int pack_no);
void send_packet(void);
void recv_packet(void);
int unpack(char *buf, int len);
void tv_sub(struct timeval *out, struct timeval *in);

void statistics(int signo)
{
    if(printInfo)
    {
        printf("\n------------PING statistics-------------\n");
        printf("%d packets transmitted, %d received , %%%d lost\n", 
                nsend, nreceived, (nsend - nreceived) / nsend * 100);
    }else{
        
        // We only want to know if it was successful

        if((nsend - nreceived) / nsend * 100)
            puts("1");
        else
           puts("0"); 
    }

    close(sockfd);
    exit(1);
} 

unsigned short cal_chksum(unsigned short *addr, int len)
{
    int nleft = len;
    int sum = 0;

    unsigned short *w = addr;
    unsigned short answer = 0;

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

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

        sum += answer;
    }

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

    return answer;
}

int pack(int pack_no)
{
    int packsize;

    struct icmp *icmp;
    //struct timeval *tval;

    icmp = (struct icmp*)sendpacket;
    icmp->icmp_type = ICMP_ECHO;
    icmp->icmp_code = 0;
    icmp->icmp_cksum = 0;
    icmp->icmp_seq = pack_no;
    icmp->icmp_id = pid;

    packsize = 8 + datalen;

    //tval = (struct timeval*)icmp->icmp_data;
    //gettimeofday(tval, NULL); 

    icmp->icmp_cksum = cal_chksum((unsigned short*)icmp, packsize); 

    return packsize;
}

void send_packet()
{
    int packetsize;

    while (nsend < MAX_NO_PACKETS)
    {
        nsend++;
        packetsize = pack(nsend); 

        if (sendto(sockfd, sendpacket, packetsize, 0, (struct sockaddr*)
            &dest_addr, sizeof(dest_addr)) < 0)
        {
            perror("sendto error");

            continue;
        }

        sleep(1); 
    }
}

void recv_packet()
{

    int n, fromlen;
    extern int errno;

    signal(SIGALRM, statistics);

    fromlen = sizeof(from);

    while (nreceived < nsend)
    {
        alarm(MAX_WAIT_TIME);

        if ((n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr*) &from, &fromlen)) < 0)
        {
            if (errno == EINTR)
                continue;

            perror("recvfrom error");

            continue;
        }

        //gettimeofday(&tvrecv, NULL); 

        if (unpack(recvpacket, n) ==  - 1)
            continue;

        nreceived++;
    }
}

int unpack(char *buf, int len)
{
    int iphdrlen;

    struct ip *ip;
    struct icmp *icmp;
    struct timeval *tvsend;

    double rtt;

    ip = (struct ip*)buf;
    iphdrlen = ip->ip_hl << 2; 
    icmp = (struct icmp*)(buf + iphdrlen);
    len -= iphdrlen; 

    if (len < 8)
    {
        printf("ICMP packets\'s length is less than 8\n");

        return  - 1;
    } 

    if ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid))
    {
        tvsend = (struct timeval*)icmp->icmp_data;
        tv_sub(&tvrecv, tvsend); 
        rtt = tvrecv.tv_sec * 1000+tvrecv.tv_usec / 1000;

        if(printInfo)
            printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n", len,

        inet_ntoa(from.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt);
    }else
        return  - 1;

    return 1;
}

int main(int argc, char *argv[])
{
    struct hostent *host;
    struct protoent *protocol;
    unsigned long inaddr = 0l;

//    int waittime = MAX_WAIT_TIME;

    int size = 50 * 1024;

    if (argc < 2)
    {
        printf("usage:%s hostname/IP address\n", argv[0]);

        exit(1);
    }

    if ((protocol = getprotobyname("icmp")) == NULL)
    {
        perror("getprotobyname");

        exit(1);
    }

    if ((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < 0)
    {
        perror("socket error");

        exit(1);
    }

    setuid(getuid());
    setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
    bzero(&dest_addr, sizeof(dest_addr));
    dest_addr.sin_family = AF_INET;

    if ((inaddr = inet_addr(argv[1])) == INADDR_NONE)
    {
        if ((host = gethostbyname(argv[1])) == NULL)
        {
            perror("gethostbyname error");

            exit(1);
        }

        memcpy((char*) &dest_addr.sin_addr, host->h_addr, host->h_length);
    }else
        dest_addr.sin_addr.s_addr = inet_addr(argv[1]);

    pid = getpid();

    if(printInfo)
        printf("PING %s(%s): %d bytes data in ICMP packets.\n", argv[1], inet_ntoa (dest_addr.sin_addr), datalen);

    send_packet(); 
    recv_packet(); 

    statistics(SIGALRM); 

    return 0;
}

void tv_sub(struct timeval *out, struct timeval *in)
{

    if ((out->tv_usec -= in->tv_usec) < 0)
    {
        --out->tv_sec;
        out->tv_usec += 1000000;
    }

    out->tv_sec -= in->tv_sec;
}
What do you guys think? Is it actually working okay?
Last edited by bedtime on Sun May 05, 2019 6:50 pm, edited 2 times in total.

User avatar
AdamStanislav
Posts: 107
Joined: Sun Mar 10, 2019 2:44 am
Location: Wisconsin
Contact: YouTube

Re: Lightweight ping program in C code * requires root *

Sun May 05, 2019 6:12 pm

bedtime wrote:
Sun May 05, 2019 1:56 pm
uses only less than 2m virtual memory.
2m? As in two meters? :?

bedtime
Posts: 64
Joined: Thu Dec 13, 2018 6:02 pm

Re: Lightweight ping program in C code * requires root *

Sun May 05, 2019 6:49 pm

AdamStanislav wrote:
Sun May 05, 2019 6:12 pm
bedtime wrote:
Sun May 05, 2019 1:56 pm
uses only less than 2m virtual memory.
2m? As in two meters? :?
Two megabytes. Just edited it. Thanks.

User avatar
Paeryn
Posts: 2512
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Lightweight ping program in C code * requires root *

Sun May 05, 2019 8:43 pm

Nit-picking (and is from the original code), but the percentage packet loss can only ever show 0% or 100% if more than one packet is used e.g. #define MAX_NO_PACKETS 10

Code: Select all

        printf("%d packets transmitted, %d received , %%%d lost\n",
                nsend, nreceived, (nsend - nreceived) / nsend * 100);
nsend and nreceived are both integers, if say 10 packets were sent but only 8 received you'd have

(10 - 8) / 10 * 100 = 0

Since these are integers, (nsend - nreceived) / nsend will either be 1 if nreceived == nsend, or 0 if nreceived < nsend.
She who travels light — forgot something.

User avatar
scruss
Posts: 2224
Joined: Sat Jun 09, 2012 12:25 pm
Location: Toronto, ON
Contact: Website

Re: Lightweight ping program in C code * requires root *

Sun May 05, 2019 8:50 pm

AdamStanislav wrote:
Sun May 05, 2019 6:12 pm
2m? As in two meters? :?
That would be around 80 bytes on a torsional delay line clocked at 1 MHz :geek:
‘Remember the Golden Rule of Selling: “Do not resort to violence.”’ — McGlashan.

User avatar
AdamStanislav
Posts: 107
Joined: Sun Mar 10, 2019 2:44 am
Location: Wisconsin
Contact: YouTube

Re: Lightweight ping program in C code * requires root *

Sun May 05, 2019 9:18 pm

bedtime wrote:
Sun May 05, 2019 6:49 pm
Two megabytes. Just edited it. Thanks.
You’re welcome. :D

bedtime
Posts: 64
Joined: Thu Dec 13, 2018 6:02 pm

Re: Lightweight ping program in C code * requires root *

Mon May 06, 2019 11:20 am

Paeryn wrote:
Sun May 05, 2019 8:43 pm
Nit-picking (and is from the original code), but the percentage packet loss can only ever show 0% or 100% if more than one packet is used e.g. #define MAX_NO_PACKETS 10

Code: Select all

        printf("%d packets transmitted, %d received , %%%d lost\n",
                nsend, nreceived, (nsend - nreceived) / nsend * 100);
nsend and nreceived are both integers, if say 10 packets were sent but only 8 received you'd have

(10 - 8) / 10 * 100 = 0

Since these are integers, (nsend - nreceived) / nsend will either be 1 if nreceived == nsend, or 0 if nreceived < nsend.
Good catch!

I've revised it:

Code: Select all

int result = 100 - ((nreceived * 200 + nsend) / (nsend * 2));
...
Courtesy of https://stackoverflow.com/questions/212 ... egers-in-c
scruss wrote:
AdamStanislav wrote:
Sun May 05, 2019 6:12 pm
2m? As in two meters? :?
That would be around 80 bytes on a torsional delay line clocked at 1 MHz :geek:
I don't get it. Maybe it's just too early in the morning for me.

Return to “C/C++”