2014年12月10日 星期三

Setting up shared memory with robust pthread_mutex and pthread_cond


I've tried to experiment with shared memory.
Eventually, I figured out that you can use  pthread_mutex and pthread_contion to synchronize the access of a shared memory.

During the process, I found out that it is very important to make the pthread_mutex "robust".

This so called robust mutex is important because this mutex can be shared by multiple processes, and therefore if one process crashes without releasing the mutex, all the other process could be stuck forever.

A robust mutex will release it's possession to the next process, if the current mutex possessing process crashes.
The pthread_mutex_lock() of the new mutex possessing process, will return with "EOWNERDEAD".

On this event, the new process should be aware that the share memory might have experienced a exception; therefore, it should be checked.

Once the shared memory and mutex is recovered, you should use "pthread_mutex_consistent()" to return the mutex to a "consistent" state.



the soure code needs to be built with "-lrt"  and "-lpthread".

source code:


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h> //shm_open
#include <stdio.h>  //printf
#include <stdlib.h> //exit
#include <unistd.h> //close
#include <string.h> //strerror
#include <pthread.h>//pthread_cond* pthread_mutex*

/* This will be created under /dev/shm/ */
#define STATE_FILE "/program.shared" 

/* Define a struct we wish to share. Notice that we will allocate 
 *  * only sizeof SHARED_VAR, so all sizes are constant              
 *   */
typedef struct
{
 int flags;
 pthread_mutex_t mutex;
 pthread_cond_t cond;
}  SHARED_VAR;

int shared_mutex_init(SHARED_VAR *conf)
{
 pthread_mutexattr_t mutexAttr;
 pthread_mutexattr_init(&mutexAttr);

 do{

  if(pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED) != 0 ){
   printf("failed to set PTHREAD_PROCESS_SHARED\n");
   break;
  }

  if(pthread_mutexattr_setrobust(&mutexAttr, PTHREAD_MUTEX_ROBUST) != 0){
   printf("failed to set PTHREAD_MUTEX_ROBUST\n");
   break;
  }

  if(pthread_mutex_init(&(conf->mutex), &mutexAttr) != 0){
   printf("failed to init mutex\n");
   break;
  }

  printf("%s success\n", __FUNCTION__);

  return 0;

 }while(0);

 pthread_mutexattr_destroy(&mutexAttr);

 return -1;
}


int shared_cond_init(SHARED_VAR *conf)
{
 pthread_condattr_t condAttr;
 pthread_condattr_init(&condAttr);

 do{

  if(pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED) != 0 ){
   printf("failed to set PTHREAD_PROCESS_SHARED\n");
   break;
  }


  if(pthread_cond_init(&(conf->cond), &condAttr) != 0){
   printf("failed to init mutex\n");
   break;
  }

  printf("%s success\n", __FUNCTION__);

  return 0;

 }while(0);

 pthread_condattr_destroy(&condAttr);

 return -1;
}

int main (void)
{
 int first = 0;
 int i;
 int shm_fd;
 static SHARED_VAR *conf;
 int process_id;
 struct timespec abstime;

 /* Try to open the shm instance with  O_EXCL,
  *    * this tests if the shm is already opened by someone else 
  *       */
 if((shm_fd = shm_open(STATE_FILE, (O_CREAT | O_EXCL | O_RDWR), 
     (S_IREAD | S_IWRITE))) > 0 ) {
  first = 1; /* We are the first instance */
 }
 else if((shm_fd = shm_open(STATE_FILE, (O_CREAT | O_RDWR), 
     (S_IREAD | S_IWRITE))) < 0) {
  /* Try to open the shm instance normally and share it with 
   *     * existing clients 
   *         */
  printf("Could not create shm object. %s\n", strerror(errno));
  return errno;
 } 

 /* Set the size of the SHM to be the size of the struct. */
 ftruncate(shm_fd, sizeof(SHARED_VAR));

 /* Connect the conf pointer to set to the shared memory area,
  *    * with desired permissions 
  *       */
 if((conf =  mmap(0, sizeof(SHARED_VAR), (PROT_READ | PROT_WRITE), 
     MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {

  return errno;

 }
 if(first) {
  /* Run a set up for the first time, fill some args */
  printf("First creation of the shm. Setting up default values\n");
  conf->flags = 0;
 }
 else
 {
  printf("Value of flags = %d\n", conf->flags);
 }
 
 if(shared_mutex_init(conf) != 0){
  printf("failed to create shared_mutex\n");
  return 0;
 }

 if(shared_cond_init(conf) != 0){
  printf("failed to create shared_cond\n");
  return 0;
 }

 
 int lockReturn;
 int consistent;
 int condReturn;

 do{

  lockReturn  = pthread_mutex_lock(&(conf->mutex));

  if(lockReturn ==  EOWNERDEAD){   

   /*Previous process died before releasing the mutex do some cleanup */

   consistent = pthread_mutex_consistent(&(conf->mutex));/*address that the cleanup is done*/
  }else if(lockReturn ! = 0){

   /*didn't get lock*/
   continue;
  }

  if(/*need to wait*/){
   clock_gettime(CLOCK_REALTIME, &abstime);
   abstime.tv_sec += 5;
   condReturn = pthread_cond_timedwait(&(conf->cond), &(conf->mutex), &abstime);
  }

  if(/*need to signal*/){
   pthread_cond_signal(&(conf->cond));
  }

  /*do something*/

  pthread_mutex_unlock(&(conf->mutex));

 }while( /*running()*/);

 close(shm_fd);
 exit(0);
}

2014年12月9日 星期二

How you receive an ethernet packet: from the ethernet driver to the BSD socket.


I've worked with lots of Ethernet driver developers and network applicaton engineers in the past few years.
It surprises me how one person might have dedicated years of programming into writing Ethernet MAC drivers and BSD sockets, but never bothered to know how the data is passed from the hw driver through the IP stack to the BSD socket API.

I wanted to share this because I think some times it is better to know the whole picture.

p.s.
I've referenced the kernel source code, which I was currently working on; therefore, the function & variable names might differ in various kernels, however the idea is the same.

OVERVIEW:

[HW driver] --> lookup packet type(ipv4? ipv6? ...etc) --> lookup destination (do route, drop, or input ...) [if(input)]--> lookup protocol(tcp, udp, icmp ... etc) -->lookup socket --> copy to user_space

PATH:

net device driver calls "netif_receive_sk()"

netif_receive_sk(){

    //1. check and return struct packet_type *
    //2. exec packet_type->function
    //
    // example:
    //    returned : struct packet_type ipv6_packet_type{
    //            .func = ipv6_rcv(struct sk_buff)
    //            }
    //
    //    struct sk_buff{
    //            struct dst_entry ._skb_dst = ipv6_input()
    //    }
   
    packet_type->func();
}

                                                                                                                                                   
ipv6_rcv()
{
    //1. look up the destination(returns a "struct dst_entry *")
    //2. calls ip6_rcv_finish()
    NF_INET_PRE_ROUTING(ip6_rcv_finish())
}

ip6_rcv_finish(){
    // 1. exec dst_entry->input() by calling ip6_route_input()
    //
    //example:
    //dstinsation is a ipv6 destinaition
    //
    //struct dst_entry
    //{
    //    .intput = ip6_input
    //}
    //
    ip6_route_input() //ip6_route_input() looks up all possible routes if(input){est_entry->input()}
   
}



ip6_input()
{
    //calls ip6_input_finish()
    NF_HOOK(ip6_input_finish());
}

ip6_input_finish()
{
    //1. lookup protocol by checking with the ip header(TCP or ICMP or UDP...etc)
    //2. returns a "struct inet_protocol"
    //3. exec inet_protocol.handler()
    //
    //example:
    //returns struct inet6_protocol tcpv6_protocol{
    //    .handler = tcp_v6_rcv()
    //    }
    //
   
    protocol.handler()  // according to example, calls tcp_v6_rcv()
}

tcp_v6_rcv()
{
    __inet6_lookup_skb() // lookup, match or create socket
        tcp_v6_do_rcv();
}

tcp_v6_do_rcv()
{
    tcp_rcv_established() -->skb_copy_datagram_iovec()-->memcpy_toiovec()-->copy_to_user()
}








others:

addrconf_dst_alloc() //create dst_entry

2012年9月17日 星期一

Interesting things with openSSL programing.

    It's not hard to find a OpenSSL programming tutorial or some example code on the internet, but there are some interesting detail that is not mentioned in most of the articles online.
    A while ago, I was involved in a new project that gave me the chance to play with libSSL for a while, I did read some tutorials and studied some examples on the Internet; but when I was testing the actual source code, I found out that It was quite unstable; the software that I wrote acts unexpectedly as if It has its own mind and wound crash due to segmentation faults or memory violations unpredictably.
    After tracing the actual source code of the OpenSSL library, I figured out a few interesting things that I would like to share.
  1.  OpenSSL library could be build without multi-thread support; since most commercial software products are designed with multi-thread, please be sure to check if the library that you're using is build with multi-thread enabled.
  2. Even if you have enabled the multi-thread support, OpenSSL library is not thread safe right outside of the box, you have to implement two operations:
    1. first is a function that will return the current thread ID.
    2. the second is a function that will handle the mutex lock and unlock actuion.
  3. the two operation functions mentioned in the previous section should be introduced  to the OpenSSL library by using some provided APIs like:
    • CRYPTO_set_id_callback
    • CRYPTO_set_locking_callback
if you are looking for a example code you can download the openssl source code and checkout the demo code (crypto/threads/mttest.c), which comes with the  source.



2012年9月12日 星期三

Speeding up TCP connection lost detection in a TCP client application

When we're writing a TCP server, it is very easy to detect connection lost of a remote TCP peer by enabling "TCP keep alive";with this feature enabled, when no data is actually transmitting on the present TCP connection, a "tcp-keep-alive" is sent and a "ack" return from the remote peer is required.
If there is no "ack" after several retries, the connection is  determined as "lost" by the OS.
When connection lost is detected, the system(Linux) will signal you by returning "read data ready" on the "select()" function, but returning a zero length data on "read()".
But, when we're writing a TCP client, the tricky part is: as a client, 90% of the time we always have data to send, so when the connection is lost, the TCP stack automatically retransmits the data; as a result, before "tcp-retransmit" actually timeouts, the "tcp-kee-alive" mechanism will not kick in.
Thus, if you are looking for a way to speed up "TCP connection lost" detection efficiency  in a client application, this "keep alive" mechanism  will do you no good.
A possible approach is to use "ioctl(socket , SIOCOUTQ, &BufferUsedSize)" to return the actual data size that is still waiting in the send queue, if no data has been successfully sent for a certain period of time, than the connection is possibly lost.

reference:
man 7 tcp

2011年7月17日 星期日

getting both IPv4 and IPv6 addresses of all local network interfaces via "getifaddrs"

In the old days, we often use ioctl to get the IPv4 address  of our local interfaces via :
ioctl(socketfd, SIOCGIFCONF, (struct ifconf)&buffer);
if we are interested in the IPv6 local addresses, we can always check them out via "/proc", for example, this is what we might do:

[sauron@icomserver ~]$ cat /proc/net/if_inet6
fe8000000000000002d0c9fffebb030b      03 40 20 80       eth1
00000000000000000000000000000001 01 80 10 80       lo
  1. IPv6 address displayed in 32 hexadecimal chars without colons as separator
  2. Netlink device number (interface index) in hexadecimal (see “ip addr” , too)
  3. Prefix length in hexadecimal
  4. Scope value (see kernel source “ include/net/ipv6.h” and “net/ipv6/addrconf.c” for more)
  5. Interface flags (see “include/linux/rtnetlink.h” and “net/ipv6/addrconf.c” for more)
  6. Device name
Quite easy, right? But what should we do to get both IPv4 and IPv6 addresses via the same API?
if you are seeking for a IPv4 and IPv6 total solution, then "getifaddrs"  is the answer that satisfies all your needs.

reference:
man 3 getifaddrs, man  3 getaddrinfo, man 3 getnameinfo




#include <arpa/inet.h>
#include <sys/socket.h>

#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int

main(int argc, char *argv[])
{
   struct ifaddrs *ifaddr, *ifa;

   int family, s;
   char host[NI_MAXHOST];

   if (getifaddrs(&ifaddr) == -1) {
       perror("getifaddrs");

       exit(EXIT_FAILURE);
   }

/* Walk through linked list, maintaining head pointer so we
*               can free list later */

   for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {

       family = ifa->ifa_addr->sa_family;

/*   Display interface name and family (including symbolic
*               form of the latter for the common families) */

       printf("%s  address family: %d%s\n",

        ifa->ifa_name, family,
        (family == AF_PACKET) ? " (AF_PACKET)" :
        (family == AF_INET) ?   " (AF_INET)" :
        (family == AF_INET6) ?  " (AF_INET6)" : "");

       /* For an AF_INET* interface address, display the address */

       if (family == AF_INET || family == AF_INET6) {

    s = getnameinfo(ifa->ifa_addr,
     (family == AF_INET) ? sizeof(struct sockaddr_in) :

      sizeof(struct sockaddr_in6),
     host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);

    if (s != 0) {
        printf("getnameinfo() failed: %s\n", gai_strerror(s));

        exit(EXIT_FAILURE);
    }
    printf("\taddress: <%s>\n", host);
       }
   }

   freeifaddrs(ifaddr);
   exit(EXIT_SUCCESS);
}

2011年7月6日 星期三

how to get default gateway via rtnetlink (c code example)

this is a example code showing you how to get the ip of the default gateway via netlink socket. I've also attached some useful information if you want to study netlink
further reading:

Sample Code To Learn Netlink Infrastructure

 understanding and programming with netlink sockets(PDF)

reference:
man 3 rtnetlink, man 7 rtnetlink, man 3 netlink, man 7 netlink


example code:
#include <asm/types.h>
#include <netinet/ether.h>

#include <netinet/in.h>
#include <net/if.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>

#include <linux/rtnetlink.h>
#include <sys/types.h>

#define BUFSIZE 8192

 char gateway[255];

 struct route_info{
  u_int dstAddr;
  u_int srcAddr;
  u_int gateWay;

  char ifName[IF_NAMESIZE];
 };

 int readNlSock(int sockFd, char *bufPtr, int seqNum, int pId){

  struct nlmsghdr *nlHdr;
  int readLen = 0, msgLen = 0;

  do{
   /* Recieve response from the kernel */
   if((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0){

    perror("SOCK READ: ");
    return -1;
   }

   nlHdr = (struct nlmsghdr *)bufPtr;

   /* Check if the header is valid */
   if((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR))
   {

    perror("Error in recieved packet");
    return -1;
   }

   /* Check if the its the last message */

   if(nlHdr->nlmsg_type == NLMSG_DONE) {
   break;
   }
   else{

   /* Else move the pointer to buffer appropriately */
   bufPtr += readLen;
   msgLen += readLen;
   }

   /* Check if its a multi part message */
   if((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) {

   /* return if its not */
   break;
   }
  } while((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId));

  return msgLen;
 }

 /* For printing the routes. */
 void printRoute(struct route_info *rtInfo)
 {

  char tempBuf[512];

  /* Print Destination address */
  if(rtInfo->dstAddr != 0)

   strcpy(tempBuf, (char *)inet_ntoa(rtInfo->dstAddr));
  else

   sprintf(tempBuf,"*.*.*.*\t");
  fprintf(stdout,"%s\t", tempBuf);

  /* Print Gateway address */
  if(rtInfo->gateWay != 0)
   strcpy(tempBuf, (char *)inet_ntoa(rtInfo->gateWay));

  else
   sprintf(tempBuf,"*.*.*.*\t");
  fprintf(stdout,"%s\t", tempBuf);

  /* Print Interface Name*/
  fprintf(stdout,"%s\t", rtInfo->ifName);

  /* Print Source address */
  if(rtInfo->srcAddr != 0)
   strcpy(tempBuf, (char *)inet_ntoa(rtInfo->srcAddr));

  else
   sprintf(tempBuf,"*.*.*.*\t");
  fprintf(stdout,"%s\n", tempBuf);
 }

 void printGateway()
 {
  printf("%s\n", gateway);
 }

 /* For parsing the route info returned */
 void parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo)
 {

  struct rtmsg *rtMsg;
  struct rtattr *rtAttr;

  int rtLen;
  char *tempBuf = NULL;

  tempBuf = (char *)malloc(100);
  rtMsg = (struct rtmsg *)NLMSG_DATA(nlHdr);

  /* If the route is not for AF_INET or does not belong to main routing table
  then return. */
  if((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN))

   return;

  /* get the rtattr field */
  rtAttr = (struct rtattr *)RTM_RTA(rtMsg);

  rtLen = RTM_PAYLOAD(nlHdr);
  for(;RTA_OK(rtAttr,rtLen);rtAttr = RTA_NEXT(rtAttr,rtLen)){

   switch(rtAttr->rta_type) {
   case RTA_OIF:
     if_indextoname(*(int *)RTA_DATA(rtAttr), rtInfo->ifName);

     break;
   case RTA_GATEWAY:
     rtInfo->gateWay.s_addr = *(u_int *)RTA_DATA(rtAttr);

     break;
   case RTA_PREFSRC:
     rtInfo->srcAddr.s_addr = *(u_int *)RTA_DATA(rtAttr);

     break;
   case RTA_DST:
     rtInfo->dstAddr.s_addr = *(u_int *)RTA_DATA(rtAttr);

     break;
   }
  }
  //printf("%s\n", (char *)inet_ntoa(rtInfo->dstAddr));
  if (strstr((char *)inet_ntoa(rtInfo->dstAddr), "0.0.0.0"))

   sprintf(gateway, (char *)inet_ntoa(rtInfo->gateWay));
  //printRoute(rtInfo);

  free(tempBuf);
  return;
 }

 int main()
 {

  struct nlmsghdr *nlMsg;
  struct rtmsg *rtMsg;

  struct route_info *rtInfo;
  char msgBuf[BUFSIZE];

  int sock, len, msgSeq = 0;

  char buff[1024];

  /* Create Socket */
  if((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)

   perror("Socket Creation: ");

  /* Initialize the buffer */
  memset(msgBuf, 0, BUFSIZE);

  /* point the header and the msg structure pointers into the buffer */
  nlMsg = (struct nlmsghdr *)msgBuf;
  rtMsg = (struct rtmsg *)NLMSG_DATA(nlMsg);

  /* Fill in the nlmsg header*/
  nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); // Length of message.

  nlMsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .

  nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.

  nlMsg->nlmsg_seq = msgSeq++; // Sequence of the message packet.
  nlMsg->nlmsg_pid = getpid(); // PID of process sending the request.


  /* Send the request */
  if(send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0){

   printf("Write To Socket Failed...\n");
   return -1;
  }

  /* Read the response */

  if((len = readNlSock(sock, msgBuf, msgSeq, getpid())) < 0) {

   printf("Read From Socket Failed...\n");
   return -1;
  }
  /* Parse and print the response */
  rtInfo = (struct route_info *)malloc(sizeof(struct route_info));

  /* THIS IS THE NETTSTAT -RL code I commented out the printing here and in parse routes */
  //fprintf(stdout, "Destination\tGateway\tInterface\tSource\n");
  for(;NLMSG_OK(nlMsg,len);nlMsg = NLMSG_NEXT(nlMsg,len)){

   memset(rtInfo, 0, sizeof(struct route_info));
   parseRoutes(nlMsg, rtInfo);
  }

  free(rtInfo);
  close(sock);

  printGateway();

  return 0;
 }
   

2010年9月14日 星期二

Using socket RAW to send an ICMP protocal

this is a simple program that i found on the internet showing how to send ICMP using RAW socket.
example code:
/*
 *    pinger.c 
 *    This is a ping imitation program 
 *    It will send an ICMP ECHO packet to the server of 
 *    your choice and listen for an ICMP REPLY packet
 *    Have fun!
 */
/*
 *    pinger.c 
 *    This is a ping imitation program 
 *    It will send an ICMP ECHO packet to the server of 
 *    your choice and listen for an ICMP REPLY packet
 *    Have fun!
 */
#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <linux/ip.h>

#include <linux/icmp.h>
#include <string.h>
#include <unistd.h>


char dst_addr[15];

char src_addr[15];

unsigned short in_cksum(unsigned short *, int);

void parse_argvs(char**, char*, char* );

void usage();
char* getip();

int main(int argc, char* argv[])

{
    struct iphdr* ip;
    struct iphdr* ip_reply;

    struct icmphdr* icmp;
    struct sockaddr_in connection;

    char* packet;
    char* buffer;
    int sockfd;

    int optval;
    int addrlen;
    
    if (getuid() != 0)

    {
 fprintf(stderr, "%s: root privelidges needed\n", *(argv + 0));

 exit(EXIT_FAILURE);
    }

    parse_argvs(argv, dst_addr, src_addr);

    printf("Source address: %s\n", src_addr);
    printf("Destination address: %s\n", dst_addr);

    
    /*
     * allocate all necessary memory
    */
    ip = malloc(sizeof(struct iphdr));

    ip_reply = malloc(sizeof(struct iphdr));
    icmp = malloc(sizeof(struct icmphdr));

    packet = malloc(sizeof(struct iphdr) + sizeof(struct icmphdr));

    buffer = malloc(sizeof(struct iphdr) + sizeof(struct icmphdr));

    /****************************************************************/
    
    ip = (struct iphdr*) packet;

    icmp = (struct icmphdr*) (packet + sizeof(struct iphdr));

    
    /*  
     * here the ip packet is set up except checksum
     */
    ip->ihl   = 5;
    ip->version   = 4;

    ip->tos   = 0;
    ip->tot_len   = sizeof(struct iphdr) + sizeof(struct icmphdr);

    ip->id   = htons(random());
    ip->ttl   = 255;

    ip->protocol  = IPPROTO_ICMP;
    ip->saddr   = inet_addr(src_addr);

    ip->daddr   = inet_addr(dst_addr);

    
    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)

    {
 perror("socket");
 exit(EXIT_FAILURE);

    }
    
    /* 
     * IP_HDRINCL must be set on the socket so that
     * the kernel does not attempt to automatically add
     * a default ip header to the packet
     */
    
    setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(int));

    
    /*
     * here the icmp packet is created
     * also the ip checksum is generated
     */
    icmp->type   = ICMP_ECHO;
    icmp->code   = 0;

    icmp->un.echo.id  = 0;
    icmp->un.echo.sequence = 0;

    icmp->checksum   = 0;
    icmp-> checksum  = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr));

    
    ip->check   = in_cksum((unsigned short *)ip, sizeof(struct iphdr));

    
    connection.sin_family = AF_INET;
    connection.sin_addr.s_addr = inet_addr(dst_addr);

    
    /*
     * now the packet is sent
     */
    
    sendto(sockfd, packet, ip->tot_len, 0, (struct sockaddr *)&connection, sizeof(struct sockaddr));

    printf("Sent %d byte packet to %s\n", sizeof(packet), dst_addr);
    
    /*
     * now we listen for responses
     */

    addrlen = sizeof(connection);
    if (recvfrom(sockfd, buffer, sizeof(struct iphdr) + sizeof(struct icmphdr), 0, (struct sockaddr *)&connection, &addrlen) == -1)

    {
 perror("recv");
    }
    else
    {

 printf("Received %d byte reply from %s:\n", sizeof(buffer), dst_addr);
        ip_reply = (struct iphdr*) buffer;

 printf("ID: %d\n", ntohs(ip_reply->id));
 printf("TTL: %d\n", ip_reply->ttl);

    }
    close(sockfd);
    return 0;
}

void parse_argvs(char** argv, char* dst, char* src)

{
    int i;
    if(!(*(argv + 1))) 
    {

 /* there are no options on the command line */
 usage();
 exit(EXIT_FAILURE); 
    }

    if (*(argv + 1) && (!(*(argv + 2)))) 
    {

 /* 
  *   only one argument provided
  *   assume it is the destination server
  *   source address is local host
  */
 strncpy(dst, *(argv + 1), 15);

 strncpy(src, getip(), 15);
 return;

    }
    else if ((*(argv + 1) && (*(argv + 2))))

    {
 /* 
  *    both the destination and source address are defined
  *    for now only implemented is a source address and 
  *    destination address
  */
 strncpy(dst, *(argv + 1), 15);

 i = 2;
 while(*(argv + i + 1))

 {
     if (strncmp(*(argv + i), "-s", 2) == 0)

     {
  strncpy(src, *(argv + i + 1), 15);

  break;
     }
     i++;
 }

    }
}

void usage()
{
    fprintf(stderr, "\nUsage: pinger [destination] <-s [source]>\n");

    fprintf(stderr, "Destination must be provided\n");
    fprintf(stderr, "Source is optional\n\n");

}

char* getip()
{
    char buffer[256];

    struct hostent* h;
    
    gethostname(buffer, 256);

    h = gethostbyname(buffer);
    
    return inet_ntoa(*(struct in_addr *)h->h_addr);

    
}
/*
 * in_cksum --
 * Checksum routine for Internet Protocol
 * family headers (C Version)
 */
unsigned short in_cksum(unsigned short *addr, int len)

{
    register int sum = 0;
    u_short answer = 0;

    register u_short *w = addr;
    register int nleft = len;

    /*
     * 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)

    {
   *(u_char *) (&answer) = *(u_char *) w;

   sum += answer;
    }
    /* 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);
}