< RAW Socket, ICMP & Ping | RAW Socket Main | Traceroute Program Example >
What do we have in this chapter 11 part 2?
|
IPv4 and IPv6 Ping Program Example
The following program example illustrates how to create a socket capable of sending and receiving ICMP packets, as well as how to use the IP_OPTIONS socket option to implement the record route option (supported for IPv4 only).
Add the following source code.
// Sample: IPv4 and IPv6 Ping Sample // // Files: // iphdrv2.h - IPv4 and IPv6 packet header definitions // ipv4ipv6pingexamplesrc.cpp - this file // resolve.cpp - Common name resolution routine // resolve.h - Header file for common name resolution routines |
//
// Description:
// This sample illustrates how to use raw sockets to send ICMP
// echo requests and receive their response. This sample performs
// both IPv4 and IPv6 ICMP echo requests. When using raw sockets,
// the protocol value supplied to the socket API is used as the
// protocol field (or next header field) of the IP packet. Then
// as a part of the data submitted to sendto, we include both
// the ICMP request and data.
//
// For IPv4 the IP record route option is supported via the
// IP_OPTIONS socket option.
//
// Command Line Options/Parameters:
// ipv4ipv6pingexample [-a 4|6] [-i ttl] [-l datasize] [-r] [host]
//
// -a Address family (IPv4 or IPv6)
// -i ttl TTL value to set on socket
// -l size Amount of data to send as part of the ICMP request
// -r Use IPv4 record route
// host Hostname or literal address
//
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
// Link to ws2_32.lib
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include "resolve.h"
#include "iphdrv2.h"
#define DEFAULT_DATA_SIZE 32 // default data size
#define DEFAULT_SEND_COUNT 4 // number of ICMP requests to send
#define DEFAULT_RECV_TIMEOUT 6000 // six second
#define DEFAULT_TTL 128
int gAddressFamily=AF_UNSPEC, // Address family to use
gProtocol=IPPROTO_ICMP, // Protocol value
gTtl=DEFAULT_TTL; // Default TTL value
int gDataSize=DEFAULT_DATA_SIZE; // Amount of data to send
BOOL bRecordRoute=FALSE; // Use IPv4 record route?
char *gDestination=NULL; // Destination
// Function: usage
// Description: Print usage information.
int usage(char *progname)
{
printf("Usage: %s -r <host> [data size]\n", progname);
printf(" -a 4|6 Address family\n");
printf(" -i ttl Time to live\n");
printf(" -l bytes Amount of data to send\n");
printf(" -r Record route (IPv4 only)\n");
printf(" host Remote machine to ping\n");
printf("Example: ipv4ipv6pingexample -a 4 -l 64 -r www.google.com\n");
printf("\n");
return 0;
}
// Function: InitIcmpHeader
// Description: Helper function to fill in various stuff in our ICMP request.
void InitIcmpHeader(char *buf, int datasize)
{
ICMP_HDR *icmp_hdr=NULL;
char *datapart=NULL;
icmp_hdr = (ICMP_HDR *)buf;
icmp_hdr->icmp_type = ICMPV4_ECHO_REQUEST_TYPE; // request an ICMP echo
icmp_hdr->icmp_code = ICMPV4_ECHO_REQUEST_CODE;
icmp_hdr->icmp_id = (USHORT)GetCurrentProcessId();
icmp_hdr->icmp_checksum = 0;
icmp_hdr->icmp_sequence = 0;
icmp_hdr->icmp_timestamp= GetTickCount();
datapart = buf + sizeof(ICMP_HDR);
// Place some junk in the buffer.
memset(datapart, 'E', datasize);
}
// Function: InitIcmp6Header
// Description: Initialize the ICMP6 header as well as the echo request header.
int InitIcmp6Header(char *buf, int datasize)
{
ICMPV6_HDR *icmp6_hdr=NULL;
ICMPV6_ECHO_REQUEST *icmp6_req=NULL;
char *datapart=NULL;
// Initialize the ICMP6 header fields
icmp6_hdr = (ICMPV6_HDR *)buf;
icmp6_hdr->icmp6_type = ICMPV6_ECHO_REQUEST_TYPE;
icmp6_hdr->icmp6_code = ICMPV6_ECHO_REQUEST_CODE;
icmp6_hdr->icmp6_checksum = 0;
// Initialize the echo request fields
icmp6_req = (ICMPV6_ECHO_REQUEST *)(buf + sizeof(ICMPV6_HDR));
icmp6_req->icmp6_echo_id = (USHORT)GetCurrentProcessId();
icmp6_req->icmp6_echo_sequence = 0;
datapart = (char *)buf + sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST);
memset(datapart, '$', datasize);
return (sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST));
}
// Function: checksum
// Description: This function calculates the 16-bit one's complement sum of the supplied buffer (ICMP) header.
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while (size > 1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}
if (size)
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
// Function: ValidateArgs
// Description: Parse the command line arguments.
void ValidateArgs(int argc, char **argv)
{
int i;
for(i=1; i < argc ;i++)
{
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
{
switch (tolower(argv[i][1]))
{
case 'a': // address family
if (i+1 >= argc)
usage(argv[0]);
if (argv[i+1][0] == '4')
gAddressFamily = AF_INET;
else if (argv[i+1][0] == '6')
gAddressFamily = AF_INET6;
else
usage(argv[0]);
i++;
break;
case 'i': // Set TTL value
if (i+1 >= argc)
usage(argv[0]);
gTtl = atoi(argv[++i]);
break;
case 'l': // buffer size tos end
if (i+1 >= argc)
usage(argv[0]);
gDataSize = atoi(argv[++i]);
break;
case 'r': // record route option
bRecordRoute = TRUE;
break;
default:
usage(argv[0]);
break;
}
}
else
{
gDestination = argv[i];
}
}
return;
}
// Function: SetIcmpSequence
// Description: This routine sets the sequence number of the ICMP request packet.
void SetIcmpSequence(char *buf)
{
ULONG sequence=0;
sequence = GetTickCount();
if (gAddressFamily == AF_INET)
{
ICMP_HDR *icmpv4=NULL;
icmpv4 = (ICMP_HDR *)buf;
icmpv4->icmp_sequence = (USHORT)sequence;
}
else if (gAddressFamily == AF_INET6)
{
ICMPV6_HDR *icmpv6=NULL;
ICMPV6_ECHO_REQUEST *req6=NULL;
icmpv6 = (ICMPV6_HDR *)buf;
req6 = (ICMPV6_ECHO_REQUEST *)(buf + sizeof(ICMPV6_HDR));
req6->icmp6_echo_sequence = (USHORT)sequence;
}
}
// Function: ComputeIcmp6PseudoHeaderChecksum
// Description:
// This routine computes the ICMP6 checksum which includes the pseudo
// header of the IPv6 header (see RFC2460 and RFC2463). The one difficulty
// here is we have to know the source and destination IPv6 addresses which
// will be contained in the IPv6 header in order to compute the checksum.
// To do this we call the SIO_ROUTING_INTERFACE_QUERY ioctl to find which
// local interface for the outgoing packet.
USHORT ComputeIcmp6PseudoHeaderChecksum(SOCKET s, char *icmppacket, int icmplen, struct addrinfo *dest)
{
SOCKADDR_STORAGE localif;
DWORD bytes;
char tmp[65535], *ptr=NULL, proto=0, zero=0;
int rc, total, length, i;
// Find out which local interface for the destination
rc = WSAIoctl(s, SIO_ROUTING_INTERFACE_QUERY, dest->ai_addr,dest->ai_addrlen,
(SOCKADDR *)&localif, sizeof(localif), &bytes, NULL, NULL);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "WSAIoctl() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("WSAIoctl() is OK!\n");
// We use a temporary buffer to calculate the pseudo header.
ptr = tmp;
total = 0;
// Copy source address
memcpy(ptr, &((SOCKADDR_IN6 *)&localif)->sin6_addr, sizeof(struct in6_addr));
ptr += sizeof(struct in6_addr);
total += sizeof(struct in6_addr);
// Copy destination address
memcpy(ptr, &((SOCKADDR_IN6 *)dest->ai_addr)->sin6_addr, sizeof(struct in6_addr));
ptr += sizeof(struct in6_addr);
total += sizeof(struct in6_addr);
// Copy ICMP packet length
length = htonl(icmplen);
memcpy(ptr, &length, sizeof(length));
ptr += sizeof(length);
total += sizeof(length);
// Zero the 3 bytes
memset(ptr, 0, 3);
ptr += 3;
total += 3;
// Copy next hop header
proto = IPPROTO_ICMP6;
memcpy(ptr, &proto, sizeof(proto));
ptr += sizeof(proto);
total += sizeof(proto);
// Copy the ICMP header and payload
memcpy(ptr, icmppacket, icmplen);
ptr += icmplen;
total += icmplen;
for(i=0; i < icmplen%2 ;i++)
{
*ptr = 0;
ptr++;
total++;
}
return checksum((USHORT *)tmp, total);
}
// Function: ComputeIcmpChecksum
// Description:
// This routine computes the checksum for the ICMP request. For IPv4 its
// easy, just compute the checksum for the ICMP packet and data. For IPv6,
// its more complicated. The pseudo checksum has to be computed for IPv6
// which includes the ICMP6 packet and data plus portions of the IPv6
// header which is difficult since we aren't building our own IPv6 header
void ComputeIcmpChecksum(SOCKET s, char *buf, int packetlen, struct addrinfo *dest)
{
if (gAddressFamily == AF_INET)
{
ICMP_HDR *icmpv4=NULL;
icmpv4 = (ICMP_HDR *)buf;
icmpv4->icmp_checksum = 0;
icmpv4->icmp_checksum = checksum((USHORT *)buf, packetlen);
}
else if (gAddressFamily == AF_INET6)
{
ICMPV6_HDR *icmpv6=NULL;
icmpv6 = (ICMPV6_HDR *)buf;
icmpv6->icmp6_checksum = 0;
icmpv6->icmp6_checksum = ComputeIcmp6PseudoHeaderChecksum(s, buf, packetlen, dest);
}
}
// Function: PostRecvfrom
// Description: This routine posts an overlapped WSARecvFrom on the raw socket.
int PostRecvfrom(SOCKET s, char *buf, int buflen, SOCKADDR *from, int *fromlen, WSAOVERLAPPED *ol)
{
WSABUF wbuf;
DWORD flags, bytes;
int rc;
wbuf.buf = buf;
wbuf.len = buflen;
flags = 0;
rc = WSARecvFrom(s, &wbuf, 1, &bytes, &flags, from, fromlen, ol, NULL);
if (rc == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
fprintf(stderr, "WSARecvFrom() failed with error code %d\n", WSAGetLastError());
return SOCKET_ERROR;
}
}
return NO_ERROR;
}
// Function: PrintPayload
// Description:
// This routine is for IPv4 only. It determines if there are any IP options
// present (by seeing if the IP header length is greater than 20 bytes) and
// if so it prints the IP record route options.
void PrintPayload(char *buf, int bytes)
{
int hdrlen=0, routes=0, i;
if (gAddressFamily == AF_INET)
{
SOCKADDR_IN hop;
IPV4_OPTION_HDR *v4opt=NULL;
IPV4_HDR *v4hdr=NULL;
hop.sin_family = (USHORT)gAddressFamily;
hop.sin_port = 0;
v4hdr = (IPV4_HDR *)buf;
hdrlen = (v4hdr->ip_verlen & 0x0F) * 4;
// If the header length is greater than the size of the basic IPv4
// header then there are options present. Find them and print them.
if (hdrlen > sizeof(IPV4_HDR))
{
v4opt = (IPV4_OPTION_HDR *)(buf + sizeof(IPV4_HDR));
routes = (v4opt->opt_ptr / sizeof(ULONG)) - 1;
for(i=0; i < routes ;i++)
{
hop.sin_addr.s_addr = v4opt->opt_addr[i];
// Print the route
if (i == 0)
printf(" Route: ");
else
printf(" ");
PrintAddress((SOCKADDR *)&hop, sizeof(hop));
if (i < routes-1)
printf(" ->\n");
else
printf("\n");
}
}
}
return;
}
// Function: SetTtl
// Description: Sets the TTL on the socket.
int SetTtl(SOCKET s, int ttl)
{
int optlevel, option, rc;
rc = NO_ERROR;
if (gAddressFamily == AF_INET)
{
optlevel = IPPROTO_IP;
option = IP_TTL;
}
else if (gAddressFamily == AF_INET6)
{
optlevel = IPPROTO_IPV6;
option = IPV6_UNICAST_HOPS;
}
else
{
rc = SOCKET_ERROR;
}
if (rc == NO_ERROR)
{
rc = setsockopt(s, optlevel, option, (char *)&ttl, sizeof(ttl));
}
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "SetTtl: setsockopt() failed: %d\n", WSAGetLastError());
}
return rc;
}
// Function: main
// Description:
// Setup the ICMP raw socket and create the ICMP header. Add
// the appropriate IP option header and start sending ICMP
// echo requests to the endpoint. For each send and receive we
// set a timeout value so that we don't wait forever for a
// response in case the endpoint is not responding. When we receive a packet decode it.
int main(int argc, char **argv)
{
WSADATA wsd;
WSAOVERLAPPED recvol;
SOCKET s=INVALID_SOCKET;
char *icmpbuf=NULL, recvbuf[0xFFFF];
struct addrinfo *dest=NULL, *local=NULL;
IPV4_OPTION_HDR ipopt;
SOCKADDR_STORAGE from;
DWORD bytes, flags;
int packetlen=0, recvbuflen=0xFFFF, fromlen, time=0, rc, i;
if(argc < 2)
{
usage(argv[0]);
exit(1);
}
// Load Winsock
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
printf("WSAStartup() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("WSAStartup() should be fine!\n");
// Parse the command line
ValidateArgs(argc, argv);
// Resolve the destination address
dest = ResolveAddress(gDestination, "0", gAddressFamily, 0, 0);
if (dest == NULL)
{
printf("Bad name %s\n", gDestination);
return -1;
}
else
printf("ResolveAddress() is pretty damn OK!\n");
gAddressFamily = dest->ai_family;
if (gAddressFamily == AF_INET)
gProtocol = IPPROTO_ICMP;
else if (gAddressFamily == AF_INET6)
gProtocol = IPPROTO_ICMP6;
// Get the bind address
local = ResolveAddress(NULL, "0", gAddressFamily, 0, 0);
if (local == NULL)
{
printf("Unable to obtain the bind address!\n");
return -1;
}
else
printf("ResolveAddress() is pretty damn OK!\n");
// Create the raw socket
s = socket(gAddressFamily, SOCK_RAW, gProtocol);
if (s == INVALID_SOCKET)
{
printf("socket() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("socket() is OK!\n");
SetTtl(s, gTtl);
// Figure out the size of the ICMP header and payload
if (gAddressFamily == AF_INET)
packetlen += sizeof(ICMP_HDR);
else if (gAddressFamily == AF_INET6)
packetlen += sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST);
// Add in the data size
packetlen += gDataSize;
// Allocate the buffer that will contain the ICMP request
icmpbuf = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, packetlen);
if (icmpbuf == NULL)
{
fprintf(stderr, "HeapAlloc() for ICMP buffer failed with error code %d\n", GetLastError());
return -1;
}
else
printf("HeapAlloc() for ICMP buffer is OK!\n");
// Initialize the ICMP headers
if (gAddressFamily == AF_INET)
{
if (bRecordRoute)
{
// Setup the IP option header to go out on every ICMP packet
ZeroMemory(&ipopt, sizeof(ipopt));
ipopt.opt_code = IP_RECORD_ROUTE; // record route option
ipopt.opt_ptr = 4; // point to the first addr offset
ipopt.opt_len = 39; // length of option header
rc = setsockopt(s, IPPROTO_IP, IP_OPTIONS, (char *)&ipopt, sizeof(ipopt));
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "setsockopt(IP_OPTIONS) failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("setsockopt(IP_OPTIONS) is OK!\n");
}
InitIcmpHeader(icmpbuf, gDataSize);
}
else if (gAddressFamily == AF_INET6)
{
InitIcmp6Header(icmpbuf, gDataSize);
}
// Bind the socket -- need to do this since we post a receive first
rc = bind(s, local->ai_addr, local->ai_addrlen);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "bind() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("bind() looks fine!\n");
// Setup the receive operation
memset(&recvol, 0, sizeof(recvol));
recvol.hEvent = WSACreateEvent();
// Post the first overlapped receive
fromlen = sizeof(from);
PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);
printf("\nPinging --> ");
PrintAddress(dest->ai_addr, dest->ai_addrlen);
printf(" with %d bytes of data\n\n", gDataSize);
// Start sending the ICMP requests
for(i=0; i < DEFAULT_SEND_COUNT ;i++)
{
// Set the sequence number and compute the checksum
SetIcmpSequence(icmpbuf);
ComputeIcmpChecksum(s, icmpbuf, packetlen, dest);
time = GetTickCount();
rc = sendto(s, icmpbuf, packetlen, 0, dest->ai_addr, dest->ai_addrlen);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "sendto() failed with error code %d\n", WSAGetLastError());
return -1;
}
else
printf("sendto() is OK!\n");
// Waite for a response
rc = WaitForSingleObject((HANDLE)recvol.hEvent, DEFAULT_RECV_TIMEOUT);
if (rc == WAIT_FAILED)
{
fprintf(stderr, "WaitForSingleObject() failed with error code %d\n", GetLastError());
return -1;
}
else if (rc == WAIT_TIMEOUT)
{
printf("Request timed out.\n");
}
else
{
printf("WaitForSingleObject() looks OK!\n");
rc = WSAGetOverlappedResult(s, &recvol, &bytes, FALSE, &flags);
if (rc == FALSE)
{
fprintf(stderr, "WSAGetOverlappedResult() failed with error code %d\n", WSAGetLastError());
}
else
printf("WSAGetOverlappedResult() is OK!\n");
time = time - GetTickCount();
WSAResetEvent(recvol.hEvent);
printf("Reply from ");
PrintAddress((SOCKADDR *)&from, fromlen);
if (time == 0)
printf(": bytes=%d time<1ms TTL=%d\n", gDataSize, gTtl);
else
printf(": bytes=%d time=%dms TTL=%d\n", gDataSize, time, gTtl);
PrintPayload(recvbuf, bytes);
if (i < DEFAULT_SEND_COUNT)
{
fromlen = sizeof(from);
PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);
}
}
Sleep(1000);
}
// Cleanup
printf("Clean up steps....\n");
freeaddrinfo(dest);
freeaddrinfo(local);
if (s != INVALID_SOCKET)
closesocket(s);
HeapFree(GetProcessHeap(), 0, icmpbuf);
WSACleanup();
return 0;
}
Add the iphdrv2.h header file
-------------------------------------------------------------
Add the source code.
// Sample: Protocol header definitions used by ping (raw sockets)
//
// Files:
// iphdrv2.h - this file
//
// Description:
// This file contains various protocol header definitions used by
// the raw socket ping sample.
//
// Align on a 1-byte boundary
#include <pshpack1.h>
// IPv4 header
typedef struct ip_hdr
{
unsigned char ip_verlen; // 4-bit IPv4 version 4-bit header length (in 32-bit words)
unsigned char ip_tos; // IP type of service
unsigned short ip_totallength; // Total length
unsigned short ip_id; // Unique identifier
unsigned short ip_offset; // Fragment offset field
unsigned char ip_ttl; // Time to live
unsigned char ip_protocol; // Protocol(TCP,UDP etc)
unsigned short ip_checksum; // IP checksum
unsigned int ip_srcaddr; // Source address
unsigned int ip_destaddr; // Source address
} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;
// IPv4 option header
typedef struct ipv4_option_hdr
{
unsigned char opt_code; // option type
unsigned char opt_len; // length of the option header
unsigned char opt_ptr; // offset into options
unsigned long opt_addr[9]; // list of IPv4 addresses
} IPV4_OPTION_HDR, *PIPV4_OPTION_HDR, FAR *LPIPV4_OPTION_HDR;
// ICMP header
typedef struct icmp_hdr
{
unsigned char icmp_type;
unsigned char icmp_code;
unsigned short icmp_checksum;
unsigned short icmp_id;
unsigned short icmp_sequence;
unsigned long icmp_timestamp;
} ICMP_HDR, *PICMP_HDR, FAR *LPICMP_HDR;
// IPv6 protocol header
typedef struct ipv6_hdr
{
unsigned long ipv6_vertcflow; // 4-bit IPv6 version, 8-bit traffic class, 20-bit flow label
unsigned short ipv6_payloadlen; // payload length
unsigned char ipv6_nexthdr; // next header protocol value
unsigned char ipv6_hoplimit; // TTL
struct in6_addr ipv6_srcaddr; // Source address
struct in6_addr ipv6_destaddr; // Destination address
} IPV6_HDR, *PIPV6_HDR, FAR * LPIPV6_HDR;
// IPv6 fragment header
typedef struct ipv6_fragment_hdr
{
unsigned char ipv6_frag_nexthdr;
unsigned char ipv6_frag_reserved;
unsigned short ipv6_frag_offset;
unsigned long ipv6_frag_id;
} IPV6_FRAGMENT_HDR, *PIPV6_FRAGMENT_HDR, FAR * LPIPV6_FRAGMENT_HDR;
// ICMPv6 header
typedef struct icmpv6_hdr {
unsigned char icmp6_type;
unsigned char icmp6_code;
unsigned short icmp6_checksum;
} ICMPV6_HDR;
// ICMPv6 echo request body
typedef struct icmpv6_echo_request
{
unsigned short icmp6_echo_id;
unsigned short icmp6_echo_sequence;
} ICMPV6_ECHO_REQUEST;
// Define the UDP header
typedef struct udp_hdr
{
unsigned short src_portno; // Source port no.
unsigned short dst_portno; // Dest. port no.
unsigned short udp_length; // Udp packet length
unsigned short udp_checksum; // Udp checksum (optional)
} UDP_HDR, *PUDP_HDR;
// IPv4 option for record route
#define IP_RECORD_ROUTE 0x7
// ICMP6 protocol value (used in the socket call and IPv6 header)
#define IPPROTO_ICMP6 58
// ICMP types and codes
#define ICMPV4_ECHO_REQUEST_TYPE 8
#define ICMPV4_ECHO_REQUEST_CODE 0
#define ICMPV4_ECHO_REPLY_TYPE 0
#define ICMPV4_ECHO_REPLY_CODE 0
#define ICMPV4_MINIMUM_HEADER 8
// ICPM6 types and codes
#define ICMPV6_ECHO_REQUEST_TYPE 128
#define ICMPV6_ECHO_REQUEST_CODE 0
#define ICMPV6_ECHO_REPLY_TYPE 129
#define ICMPV6_ECHO_REPLY_CODE 0
// Restore byte alignment back to default
#include <poppack.h>
Add the resolve.h and its definition files into this project which were created in the previous project.
Build and run the project.
ipv4ipv6pingexample -a 4 -l 64 -r www.google.com
ipv4ipv6pingexample -a 6 www.ipv6.org
The following screenshots shows the Windows ping6 utility program with its’ options/switches.
One noticeable feature of the Ping example is its use of the IP_OPTIONS socket option. We use the record route IPv4 option so that when the ICMP packet hits a router, its IPv4 address is added into the IPv4 option header at the location indicated by the offset field in the IPv4 option header. This offset is also incremented by four each time a router adds its address. The increment value is based on the fact that an IPv4 address is 4 bytes long. Once you receive the echo reply, decode the option header and print the IP addresses and host names of the routers visited.
< RAW Socket, ICMP & Ping | RAW Socket Main | Traceroute Program Example >