Basic example icmp server and client

  • In this program, you are going to learn

  • How to create a Socket ?

  • How to send a data ?

  • How to recv a data ?

Let us answer few basic questions in this socket

What does socket(AF_INET, SOCK_RAW, IPPROTO_ICMP) do?

Why use AF_INET as the address family?

What is the purpose of SOCK_RAW in the socket type?

Why specify IPPROTO_ICMP as the protocol?

Can this socket send ICMP packets as well?

How does this socket differ from a standard UDP or TCP socket?

Is error checking needed after creating the socket?

Can this socket be used for other protocols besides ICMP?

Why close the socket after processing?

How is the source IP address extracted from the received packet?

Can this socket receive ICMP messages from any source?

Why cast to struct iphdr and struct icmphdr in packet processing?

https://www.plantuml.com/plantuml/svg/LOwn3e8m54NtViLpAqa7PrmWH1CY0a6COrFAQoI29Ns5mQyNxUHoavkJUt8JyihvPHhPpZzmGg1PQHnLntw0nMJr4prFJp8lZws0QvMTPPFU1UHrtLHj9VFiKaS75ZlKgt5sueHkHIV3Ka2t68DE00rlj8Q76WdO2oXlHH7oA-2h7Vf8ZvRmtx51ydPc2RxwxUe7
  • There are many functions used in socket. We can classify those functions based on functionalities.

    • Create Socket

    • Recv data_packet

    • Close socket

  • socket() is used to create a new socket. For example,

server_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  • recvfrom is used in network programming to receive data from a connected socket. For example,

ret = recvfrom(server_socket, buffer, sizeof(buffer), 0, NULL, NULL);
  • close is used to close the socket To free up system resources associated with the socket. For example,

(void)close(server_socket);
  • See the full program below,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <signal.h>

#define BUFFER_SIZE 1024

int server_socket = -1;

static void sigint_handler(int signo)
{
  (void)close(server_socket);
  sleep(2);
  (void)printf("Caught sigINT!\n");
  exit(EXIT_SUCCESS);
}

void register_signal_handler(
int signum,
void (*handler)(int))
{
  if (signal(signum, handler) ==
      SIG_ERR) {
    printf("Cannot handle signal\n");
    exit(EXIT_FAILURE);
  }
}

int main() 
{
  int ret;
  char buffer[BUFFER_SIZE];
  char *original_data;
  struct iphdr *ip_header;
  struct icmphdr *icmp_header;

  register_signal_handler(SIGINT,
  sigint_handler);

  server_socket = socket(AF_INET, 
                  SOCK_RAW, 
                  IPPROTO_ICMP);

  if (server_socket < 0) {
    perror("Socket failed");
    return -1; 
  }

  ret = recvfrom(server_socket, 
  buffer, sizeof(buffer), 0, NULL, NULL);
  
  if (ret < 0) {
    perror("recv");
    (void)close(server_socket);
    return -2;
  }

  ip_header = (struct iphdr *)buffer;
  icmp_header = (struct icmphdr *)
  (buffer + sizeof(struct iphdr));

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

  printf("Received ICMP message\n");
  printf("Source IP: %s\n", 
  inet_ntoa(*(struct in_addr *)&
  (ip_header->saddr)));
  printf("Type: %d\n", 
  icmp_header->type);
  printf("Code: %d\n", 
  icmp_header->code);
  printf("Original Data: %s\n", 
  original_data);

  printf("added ip header\n");
  printf("recvied icmp hex dump\n");
  for (int i = 0; i < ret; i++) {
     printf("%02X ",
     (unsigned char)buffer[i]);
 }

  (void)close(server_socket);

  return 0;
}

$ gcc -o server server.c

$ sudo ./server

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!
added ip header
recvied icmp hex dump
45 00 00 2E F1 2A 40 00 40 01 4B A2 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21
https://www.plantuml.com/plantuml/svg/LO_12i8m44Jl_OhLWoIQWsUzAA9OH5jKy1Xgii5YsfHawy6lDxLOyBRCx3ncLe4AJsrban4zi488JQ7G5lVaQoIBuDGJYQrtCZrjhm8ksUOWp-kRW3JFpzara-dccFDb_CHQasFAbbYJx7C2xgqnw0M4yetEi42-LGIbgfg7zXnc41MBDHllqLe7nhiAUiHep0NC1Vmo7R7Gsa_vH6CWsTq3Tr1uLuMzNAcviYxWVvze1d9DiefTu-WF
  • There are many functions used in socket. We can classify those functions based on functionalities.

    • Create Socket

    • Sendto data_packet

    • Close socket

  • socket is used to create a new socket. For example,

client_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  • sendto is used to send the encoded message to the specified server address and port using a socket. For example,

ret = sendto(client_socket, buffer, sizeof(struct icmphdr) + strlen("Hello from client!"), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
  • close is used to close the socket To free up system resources associated with the socket. For example,

(void)close(client_socket);
  • See the full program below,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <signal.h>

#define BUFFER_SIZE 1024

int client_socket = -1;

static void sigint_handler(
int signo)
{
  (void)close(client_socket);
  sleep(2);
  (void)printf("Caught sigINT!\n");
  exit(EXIT_SUCCESS);
}

void register_signal_handler(
int signum,
void (*handler)(int))
{
  if (signal(signum, handler) ==
      SIG_ERR) {
    printf("Cannot handle signal\n");
    exit(EXIT_FAILURE);
  }
}

void validate_convert_addr(
char *ip_str,
struct sockaddr_in *sock_addr)
{
  if (ip_str == NULL) {
   perror("Invalid ip_str\n");
   exit(EXIT_FAILURE);
 }

 if (sock_addr == NULL) {
   perror("Invalid sock_addr\n");
   exit(EXIT_FAILURE);
 }

 printf("IP Address: %s\n", ip_str);

 if (inet_pton(AF_INET, ip_str,
 &(sock_addr->sin_addr)) <= 0) {
    perror("Invalid address\n");
    exit(EXIT_FAILURE);
  }
}

int main(int argc, char *argv[])
{
  int ret;
  struct sockaddr_in 
  dest_addr;
  char buffer[BUFFER_SIZE];
  struct icmphdr *icmp_header;

  register_signal_handler(SIGINT,
  sigint_handler);

  if (argc != 2) {
    printf("%s<ip-addr>\n",
    argv[0]);
    exit(EXIT_FAILURE);
  }

  client_socket = socket(AF_INET, 
                  SOCK_RAW, 
                  IPPROTO_ICMP);

  if (client_socket < 0) {
    perror("Socket failed");
    return -1;
  }

  memset(&dest_addr, 0, 
  sizeof(dest_addr));
  dest_addr.sin_family = AF_INET;
  validate_convert_addr(argv[1],
  &dest_addr);

  snprintf(buffer + 
  sizeof(struct icmphdr), 
  sizeof(buffer) - 
  sizeof(struct icmphdr), 
  "Hello from client!");

  icmp_header = 
  (struct icmphdr *)buffer;
  icmp_header->type = ICMP_ECHO;
  icmp_header->code = 0;
  icmp_header->checksum = 0;
  icmp_header->un.echo.id = 0;
  icmp_header->un.echo.sequence = 0;

  ret = sendto(client_socket, buffer, 
  sizeof(struct icmphdr) + 
  strlen("Hello from client!"), 0,
  (struct sockaddr *)&dest_addr, 
  sizeof(dest_addr));
  
 if (ret < 0) {
    perror("sendto");
    (void)close(client_socket);
    return -2;
  }

 printf("sending icmp hex dump\n");
 for (int i = 0; i < ret; i++) {
     printf("%02X ", 
     (unsigned char)buffer[i]);
 } 

 (void)close(client_socket);

  return 0;
}

$ gcc -o client client.c

$ sudo ./client 127.0.0.1

IP Address: 127.0.0.1
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21
https://www.plantuml.com/plantuml/svg/LOz12u9048NlyokcsoxjeRCM964XPKhMUJ4RQKdRsDcC-lLPTkaoZyTxl8-Pio-TlxSDZFoPMaA-bHKrvH6-0GHigmjv4Irriett2ejiiTQxA5MOvFakssSwMQIv324mECD928-pQGZ5JmnESNj8THeNHRIA2odHC8ediOJ0KTNLphQ2oNNazC1IUBpNDJc5R5vaQp5OgN2Y-gtDPhWVv7XiGg3hwGk3G7JMd6JLMAR_oRxBtjvWtdVxNzy=
  • There are many functions used in socket. We can classify those functions based on functionalities.

    • Create Socket

    • Recv data_packet

    • Close socket

  • socket() is used to create a new socket. For example,

server_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  • recvfrom is used in network programming to receive data from a connected socket. For example,

ret = recvfrom(server_socket, buffer, sizeof(buffer), 0, NULL, NULL);
  • close is used to close the socket To free up system resources associated with the socket. For example,

(void)close(server_socket);
  • See the full program below,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <signal.h>

#define BUFFER_SIZE 1024
#define NUM_MESSAGES 10

int server_socket = -1;

static void sigint_handler(
int signo)
{
  (void)close(server_socket);
  sleep(2);
  (void)printf("Caught sigINT!\n");
  exit(EXIT_SUCCESS);
}

void register_signal_handler(
int signum,
void (*handler)(int))
{
  if (signal(signum, handler) ==
      SIG_ERR) {
    printf("Cannot handle signal\n");
    exit(EXIT_FAILURE);
  }
}

int main() 
{
  int ret, i;
  char buffer[BUFFER_SIZE];
  char *original_data;
  struct iphdr *ip_header;
  struct icmphdr *icmp_header;

  register_signal_handler(SIGINT,
  sigint_handler);

  server_socket = socket(AF_INET, 
                  SOCK_RAW, 
                  IPPROTO_ICMP);

  if (server_socket < 0) {
    perror("Socket failed");
    return -1; 
  }

  i = 0;
  while (i < NUM_MESSAGES) {
   ret = recvfrom(server_socket, 
   buffer, sizeof(buffer), 0, NULL, NULL);
  
   if (ret < 0) {
    perror("recv");
    (void)close(server_socket);
    return -2;
   }

   ip_header = 
   (struct iphdr *)buffer;
   icmp_header = 
   (struct icmphdr *)
   (buffer + 
   sizeof(struct iphdr));

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

   printf("Received ICMP message\n");
   printf("Source IP: %s\n", 
   inet_ntoa(*(struct in_addr *)&
   (ip_header->saddr)));
   printf("Type: %d\n", 
   icmp_header->type);
   printf("Code: %d\n", 
   icmp_header->code);
   printf("Original Data: %s\n",
   original_data);

   printf("\n\n Recevied %d time\n",
		   i);
   printf("added ip header\n");
   printf("recvied icmp hex dump\n");
   for (int i = 0; i < ret; i++) {
       printf("%02X ",
       (unsigned char)buffer[i]);
   }
   ++i;	
  }
  
   (void)close(server_socket);

   return 0;
}

$ gcc -o server server.c

$ sudo ./server

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!


Recevied 0 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 36 40 00 40 01 C4 96 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!

Recevied 1 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 37 40 00 40 01 C4 95 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!


Recevied 2 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 38 40 00 40 01 C4 94 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!

Recevied 3 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 39 40 00 40 01 C4 93 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!

Recevied 4 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 3A 40 00 40 01 C4 92 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!

Recevied 5 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 3B 40 00 40 01 C4 91 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!

Recevied 6 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 3C 40 00 40 01 C4 90 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!

Recevied 7 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 3D 40 00 40 01 C4 8F 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!

Recevied 8 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 3E 40 00 40 01 C4 8E 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Received ICMP message
Source IP: 127.0.0.1
Type: 8
Code: 0
Original Data: Hello from client!

Recevied 9 time
added ip header
recvied icmp hex dump
45 00 00 2E 78 3F 40 00 40 01 C4 8D 7F 00 00 01 7F 00 00 01 08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21
https://www.plantuml.com/plantuml/svg/LP11JyCm38Nl-HNU3YYXEUpSWRHgAb2XhjKwpb6Nk6f4sbH92e9VJxh194wsdf-VFtddG-l2F1Yo2ZqE27vg9PhsJ2u3adehtp7Gx4aKX_p4eQdsh-AObHoAkZvMfqeK-x9cMv9gU8JDbdpssY3GtwBX0GvlfIZpficUyuOnq1xe5teMit5KmL9fD8v1N2znECzTXuw3rzzeE-g3csK0BOUfLuv10b4nEDBr2nfZeNDsW6l4QiquR3ZyxIo9hLBkdjqfz44i_Ittfh05FadqbaIS2pP9wOVLYabZFVx7YruVx4HsqHk_zWC=
  • There are many functions used in socket. We can classify those functions based on functionalities.

    • Create Socket

    • Sendto data_packet

    • Close socket

  • socket is used to create a new socket. For example,

client_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  • sendto is used to send the encoded message to the specified server address and port using a socket. For example,

ret = sendto(client_socket, buffer, sizeof(struct icmphdr) + strlen("Hello from client!"), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
  • close is used to close the socket To free up system resources associated with the socket. For example,

(void)close(client_socket);
  • See the full program below,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <signal.h>

#define BUFFER_SIZE 1024
#define NUM_MESSAGES 10

int client_socket = -1;

static void sigint_handler(int signo)
{
  (void)close(client_socket);
  sleep(2);
  (void)printf("Caught sigINT!\n");
  exit(EXIT_SUCCESS);
}

void register_signal_handler(
int signum,
void (*handler)(int))
{
  if (signal(signum, handler) ==
      SIG_ERR) {
    printf("Cannot handle signal\n");
    exit(EXIT_FAILURE);
  }
}

void validate_convert_addr(
char *ip_str,
struct sockaddr_in *sock_addr)
{
  if (ip_str == NULL) {
   perror("Invalid ip_str\n");
   exit(EXIT_FAILURE);
 }

 if (sock_addr == NULL) {
   perror("Invalid sock_addr\n");
   exit(EXIT_FAILURE);
 }

 printf("IP Address: %s\n", ip_str);

 if (inet_pton(AF_INET, ip_str,
 &(sock_addr->sin_addr)) <= 0) {
    perror("Invalid address\n");
    exit(EXIT_FAILURE);
  }
}

int main(int argc, char *argv[])
{
  int ret, i;
  struct sockaddr_in 
  dest_addr;
  char buffer[BUFFER_SIZE];
  struct icmphdr *icmp_header;

  register_signal_handler(SIGINT,
  sigint_handler);

  if (argc != 2) {
    printf("%s<ip-addr>\n",
    argv[0]);
    exit(EXIT_FAILURE);
  }

  client_socket = socket(AF_INET, 
                  SOCK_RAW, 
                  IPPROTO_ICMP);

  if (client_socket < 0) {
    perror("Socket failed");
    return -1;
  }

  memset(&dest_addr, 0, 
  sizeof(dest_addr));
  dest_addr.sin_family = AF_INET;
  validate_convert_addr(argv[1],
  &dest_addr);

  snprintf(buffer + 
  sizeof(struct icmphdr), 
  sizeof(buffer) - sizeof(struct icmphdr), 
  "Hello from client!");

  icmp_header = 
  (struct icmphdr *)buffer;
  icmp_header->type = ICMP_ECHO;
  icmp_header->code = 0;
  icmp_header->checksum = 0;
  icmp_header->un.echo.id = 0;
  icmp_header->un.echo.sequence = 0;

  i = 0;
  while (i < NUM_MESSAGES) {
   ret = sendto(client_socket, buffer, 
   sizeof(struct icmphdr) + 
   strlen("Hello from client!"), 0,
   (struct sockaddr *)&dest_addr, 
   sizeof(dest_addr));
  
  if (ret < 0) {
     perror("sendto");
     (void)close(client_socket);
     return -2;
   }

  printf("sending icmp hex dump\n");
  for (int i = 0; i < ret; i++) {
      printf("%02X ", 
      (unsigned char)buffer[i]);
  } 

  printf("\n\n Sent %d time\n", 
  i);
  ++i;
  }

  (void)close(client_socket);

  return 0;
}

$ gcc -o client client.c

$ sudo ./client 8080 127.0.0.1

IP Address: 127.0.0.1
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 0 time
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 1 time
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 2 time
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 3 time
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 4 time
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 5 time
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 6 time
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 7 time
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 8 time
sending icmp hex dump
08 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 66 72 6F 6D 20 63 6C 69 65 6E 74 21

Sent 9 time

Default Domain:

By default, the socket is configured to work in the AF_INET domain, handling all types of network data.

Additional Domain Support:

We expand the socket’s capabilities to also function in the PF_INET domain, allowing it to operate similarly to AF_INET.

Socket Creation:

We set up a network connection point known as a socket using socket(PF_INET, SOCK_RAW, IPPROTO_ICMP).

Working Scenario:

Despite the change in domain to PF_INET, the socket continues to operate the same way, handling general network data.

Socket API

Learning

socket

Create a new socket

recvfrom

It provides information about the source (sender) of the data, including the sender’s IP address and port number.

sendto

Send the encoded message to the specified server address and port using a socket.