Reputation: 543
I want to send ICMP message and receive the echo. I take a week studying raw socket and search reference. And then I write the below code that I guarantee sending ICMP message success,Because the return of the 'socket' and 'send' is ok (not minus 1). Unfortunately, the 'recvfrom' receive nothing, below is my succinct code on windows:
#define _WIN32_WINNT 0x501
#include <stdio.h>
#include <ws2tcpip.h>
#include <winsock2.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#pragma comment(lib,"Ws2_32.lib")
WSADATA wsaData;
int main(int args, char *argv[]){
WSAStartup(MAKEWORD(2,2),&wsaData);
//**************icmp book******************/
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;
ICMP_HDR *icmp = NULL;
char buffer[sizeof(ICMP_HDR) + 32];
icmp = (ICMP_HDR *)buffer; //
icmp->icmp_type = 8;
icmp->icmp_code = 0;
icmp->icmp_id = 1314;
icmp->icmp_checksum = sizeof(ICMP_HDR) + 32;
icmp->icmp_sequence = 0;
icmp->icmp_timestamp = GetTickCount();
memset(&buffer[sizeof(ICMP_HDR)],'@',32);
int my_socket = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
struct in_addr my_addr;
my_addr.s_addr = inet_addr("127.0.0.1");
// inet_aton(argv[1],&my_addr);
struct sockaddr_in sa_t;
sa_t.sin_family = AF_INET;
sa_t.sin_port = htons(0);
sa_t.sin_addr = my_addr;
int p = sendto(my_socket,buffer,sizeof(ICMP_HDR)+32,0,(struct sockaddr *)&sa_t, sizeof(sa_t));
printf("return : %d\n ------s: %d\n",p,my_socket);
int tmp_add = sizeof(sa_t);
char buf[1024];
recvfrom(my_socket,buf,1024,0,(struct sockaddr *)&sa_t,&tmp_add);
printf("receive is: %s\n",buf);
return 1;
}
when I compile and run this code, my expectation is the 'recvfrom' function will receive something whatever it is.
But nothing from the 'recvfrom', It take me two days, thanks in here.
Upvotes: 0
Views: 475
Reputation: 6659
First you need to insure that the compiler doesn't pad your struct trying to align everything on 4, 8 or 16 byte address boundaries (#pragma pack
), then you have to properly align the struct and correctly calculate the checksum. On the response side, you can't treat the buffer as a string, it could have non-ASCII data in it. Also, network byte order is not always the same as the byte order on either of the host architectures, so you should always convert before sending/receiving.
This works. I'll leave it to you to work out what the extra 20 or so bytes in the response are all about.
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <ws2tcpip.h>
#include <winsock2.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#pragma comment(lib,"Ws2_32.lib")
#define STRING_LENGTH 32
//**************icmp book******************/
#pragma pack(push, 1)
typedef struct icmp_msg {
uint8_t type;
uint8_t code;
uint16_t checksum;
uint16_t id;
uint16_t sequence;
uint32_t timestamp;
uint8_t tail[STRING_LENGTH + 2];
} ICMP_MSG;
#pragma pack(pop)
// malloc() is gauranteed to provide suitable alignement.
ICMP_MSG *ping = NULL;
void PrintMsg(ICMP_MSG *msg)
{
printf("Ping:\n\tId: %u16\n\tSequence: %u16\n\tTimestamp: %lu\n\tString: ", ntohs(msg->id), ntohs(msg->sequence), ntohl(msg->timestamp));
char *it = (char*)&(msg->tail[0]);
char *it_end = (char*)msg + sizeof(ICMP_MSG);
while (it < it_end)
{
char *hexString[65] = { 0 };
_itoa_s(*it, (char*)hexString, 32, 16);
printf("0x%s,", (char*)hexString);
it++;
}
printf("\n\n");
}
// Ripped from https://github.com/pocoproject/poco/blob/develop/Net/src/ICMPPacketImpl.cpp#L98 and cleaned-up.
uint16_t Checksum(uint16_t *addr, size_t len)
{
size_t nleft = len;
uint16_t* w = addr;
uint16_t answer;
int32_t sum = 0;
while (nleft > 1)
{
sum += *w++;
nleft -= sizeof(uint16_t);
}
if (nleft == 1)
{
uint16_t u = 0;
*(uint8_t*)(&u) = *(uint8_t*)w;
sum += u;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum & 0xffff;
return answer;
}
int main(void) {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
ping = malloc(sizeof(ICMP_MSG));
ping->type = 8;
ping->code = 0;
ping->id = htons(1314);
ping->checksum = 0; // Must be zero prior to checksum calculation.
ping->sequence = 0;
ping->timestamp = htonl(GetTickCount());
// 0x40 == '@' in ASCII
memset(ping->tail, 0x40, STRING_LENGTH);
// NUL pad the last two bytes fo the string.
ping->tail[STRING_LENGTH] = '\0';
ping->tail[STRING_LENGTH + 1] = '\0';
ping->checksum = Checksum((uint16_t*)ping, sizeof(ICMP_MSG));
int res;
int my_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
struct in_addr my_addr;
my_addr.s_addr = inet_addr("127.0.0.1");
//res = inet_pton(AF_INET, "127.0.0.1", &my_addr.s_addr);
struct sockaddr_in sa_t;
sa_t.sin_family = AF_INET;
sa_t.sin_port = htons(0);
sa_t.sin_addr = my_addr;
PrintMsg(ping);
res = sendto(my_socket, (const char*)ping, sizeof(ICMP_MSG), 0, (struct sockaddr *)&sa_t, sizeof(sa_t));
printf("Socket: %d\nsendto returned: %d\n", my_socket, res);
int tmp_add = sizeof(sa_t);
ICMP_MSG *resp = calloc(1, 2048 /*sizeof(ICMP_MSG)*/);
res = recvfrom(my_socket, (char*)resp, 2048, 0, (struct sockaddr *)&sa_t, &tmp_add);
printf("recvfrom returned: %d\n", res);
if (SOCKET_ERROR == res)
{
res = WSAGetLastError();
printf("WSAGetLastError() returned: %d (", res);
switch (res)
{
case WSAEMSGSIZE:
printf("WSAMSGSIZE)\n");
break;
default:
printf("Unexpected)\n");
break;
}
}
PrintMsg(resp);
system("pause");
free(ping);
free(resp);
return 1;
}
Upvotes: 1