I am trying to learn how the ping command works by implementing it myself. I am facing a problem whilst trying to send the raw datagram which contains both IP header, ICMP header and payload (which I am limiting to 16 bytes + struct timeval size).
When I try to send it, perror logs invalid argument (after a packet that seems to be malformed).
This is the output for my manually made packet:
IP version: 4
IP header length: 5
IP TOS: 0
IP total length: 40
IP ID: 4242
IP flags: 2
IP fragment offset: 0
IP time to live: ff
IP protocol: 1
IP header checksum: 517
IP source address: ac8acd1
IP destination address: d83acc4e // Google IP
ICMP type: 8
ICMP code: 0
ICMP checksum: 3e33
ICMP identifier: 806f
ICMP sequence: 0
ICMP payload seconds: 1693445511
ICMP payload microseconds: 867091
ICMP payload:
5400 4000 4242 0200 ff01 1705 d1ac c80a 4ecc 3ad8 0800 333e 6f80 0000 87ed ef64 0000 0000 133b 0d00 98b6 336b 0100 0000 ad00 9f0e 9419 00b8 e0b6 336b 0100 0000 cc74 ac04 0100 0000 0000 0000 0000 0000 ad00 9f0e 9419 00b8
The structures look like this:
typedef struct s_ip_header {
unsigned char version : 4;
unsigned char ihl : 4;
unsigned char type_of_service : 8;
unsigned short total_length : 16;
unsigned short identification : 16;
unsigned char flags : 3;
unsigned short fragment_offset : 13;
unsigned char time_to_live : 8;
unsigned char protocol : 8;
unsigned short header_checksum : 16;
unsigned int source_address : 32;
unsigned int destination_address : 32;
} __attribute__((__packed__)) t_ip_header;
typedef struct s_ip_header {
unsigned char version : 4;
unsigned char ihl : 4;
unsigned char type_of_service : 8;
unsigned short total_length : 16;
unsigned short identification : 16;
unsigned char flags : 3;
unsigned short fragment_offset : 13;
unsigned char time_to_live : 8;
unsigned char protocol : 8;
unsigned short header_checksum : 16;
unsigned int source_address : 32;
unsigned int destination_address : 32;
} __attribute__((__packed__)) t_ip_header;
And finally the code for the test that I am doing:
#include "../incs/icmp.h"
#include "../incs/utils.h"
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
void my_ping(t_args *args) {
t_icmp_packet packet = build_echo_message_packet(
180923601, 3627732046, (unsigned char *)"payload42 42 42\0");
unsigned short *bytes_packet = (unsigned short *)&packet;
int manual_ip_header = 1;
struct sockaddr_in servaddr;
socklen_t servlen;
for (size_t i = 0; i < sizeof(t_icmp_packet); i++)
*(bytes_packet + i) = htons(*(bytes_packet + i));
int fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (fd < 0)
exit(1);
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &manual_ip_header,
sizeof(manual_ip_header)) == -1)
exit(1);
servaddr.sin_family = AF_INET;
servaddr.sin_port = 0;
servaddr.sin_addr.s_addr = 3627732046;
servlen = sizeof(struct sockaddr_in);
if (sendto(fd, bytes_packet, sizeof(t_icmp_packet), 0,
(struct sockaddr *)&servaddr, servlen) <= 0) {
perror("Error: ");
exit(1);
}
}
I have tried swapping bits without success, and now I am finding myself at a dead-end. If further details are needed let me know and I'll do my best to provide them.
Also, I took a look at https://stackoverflow.com/a/50732329/6800970. It's true that the first 16-bit word is 5400, and if I swap the two fields it becomes 4500. With the next one 4000 it's not the same and should be 0040 instead.
Have a great day, Best regards,