feat: implement ICMP send/receive modules

This commit is contained in:
lohhiiccc 2026-01-26 20:12:42 +01:00
parent 10a10f6e0a
commit 3c56ea024b
21 changed files with 422 additions and 0 deletions

28
includes/icmp_types.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef ICMP_TYPES_H
#define ICMP_TYPES_H
/* ICMP Message Types */
#define ICMP_TYPE_ECHO_REPLY 0
#define ICMP_TYPE_DEST_UNREACHABLE 3
#define ICMP_TYPE_SOURCE_QUENCH 4
#define ICMP_TYPE_REDIRECT 5
#define ICMP_TYPE_ECHO_REQUEST 8
#define ICMP_TYPE_ROUTER_ADVERTISEMENT 9
#define ICMP_TYPE_ROUTER_SOLICITATION 10
#define ICMP_TYPE_TIME_EXCEEDED 11
#define ICMP_TYPE_PARAMETER_PROBLEM 12
#define ICMP_TYPE_TIMESTAMP_REQUEST 13
#define ICMP_TYPE_TIMESTAMP_REPLY 14
/* Type 3 (Destination Unreachable) Codes */
#define ICMP_CODE_NET_UNREACHABLE 0
#define ICMP_CODE_HOST_UNREACHABLE 1
#define ICMP_CODE_PROTOCOL_UNREACHABLE 2
#define ICMP_CODE_PORT_UNREACHABLE 3
#define ICMP_CODE_FRAG_NEEDED 4
/* Type 11 (Time Exceeded) Codes */
#define ICMP_CODE_TTL_EXCEEDED 0
#define ICMP_CODE_FRAG_REASM_EXCEEDED 1
#endif

View file

@ -0,0 +1,33 @@
#ifndef ICMP_RECV_H
#define ICMP_RECV_H
#include "icmp.h"
#include "internal/icmp_internal.h"
#include <netinet/in.h>
#include <sys/types.h>
/* Validation helpers */
int recv_validate_params(struct icmp_handle *h, icmp_callback_t cb);
int recv_handle_receive_error(struct icmp_handle *h);
/* Core receive functions */
ssize_t recv_receive_packet(int fd, void *buffer, size_t buffer_len,
struct sockaddr_in *from);
int recv_parse_packet(const void *buffer, size_t buffer_len,
uint8_t *type, uint8_t *code,
uint8_t *ttl, struct in_addr *src_addr,
const void **payload, size_t *payload_len,
size_t *ip_hdr_len);
/* Reply building */
void recv_build_reply(icmp_reply_t *reply, uint8_t type, uint8_t code,
uint8_t ttl, struct in_addr src_addr,
const void *payload, size_t payload_len,
const void *buffer, size_t ip_hdr_len);
/* Process single packet */
void recv_process_single_packet(uint8_t *buffer, ssize_t len,
icmp_callback_t cb, void *userdata);
#endif

View file

@ -0,0 +1,16 @@
#ifndef ICMP_SEND_H
#define ICMP_SEND_H
#include "internal/icmp_internal.h"
#include <netinet/in.h>
void send_prepare_destination(struct sockaddr_in *addr, struct in_addr dest);
int send_set_socket_ttl(struct icmp_handle *h, uint8_t ttl);
int send_packet(struct icmp_handle *h, const void *buffer, size_t len,
struct sockaddr_in *dest);
int send_validate_handle(struct icmp_handle *h);
int send_to_destination(struct icmp_handle *h, const void *packet, size_t len,
struct in_addr dest, uint8_t ttl);
#endif

View file

28
src/recv/api/process.c Normal file
View file

@ -0,0 +1,28 @@
#include "icmp.h"
#include "internal/icmp_internal.h"
#include "internal/icmp_recv.h"
#include <stdint.h>
#define MAX_PACKET_SIZE 1500
int
icmp_process(icmp_handle_t *h, icmp_callback_t cb, void *userdata)
{
uint8_t buffer[MAX_PACKET_SIZE];
struct sockaddr_in from;
ssize_t recv_len;
if (0 == recv_validate_params(h, cb))
return -1;
while (1)
{
recv_len = recv_receive_packet(h->fd, buffer, sizeof(buffer), &from);
if (0 == recv_len)
break;
if (recv_len < 0)
return recv_handle_receive_error(h);
recv_process_single_packet(buffer, recv_len, cb, userdata);
}
icmp_clear_error(h);
return 0;
}

View file

@ -0,0 +1,20 @@
#include "internal/icmp_recv.h"
#include "internal/icmp_packet_internal.h"
int
recv_parse_packet(const void *buffer, size_t buffer_len,
uint8_t *type, uint8_t *code,
uint8_t *ttl, struct in_addr *src_addr,
const void **payload, size_t *payload_len,
size_t *ip_hdr_len)
{
if (icmp_parse_ip_header(buffer, buffer_len, ttl, src_addr,
ip_hdr_len) < 0)
return -1;
if (icmp_parse_icmp_payload(buffer, buffer_len, *ip_hdr_len,
type, code, payload, payload_len) < 0)
return -1;
return 0;
}

View file

@ -0,0 +1,20 @@
#include "internal/icmp_recv.h"
void
recv_process_single_packet(uint8_t *buffer, ssize_t len, icmp_callback_t cb,
void *userdata)
{
icmp_reply_t reply;
uint8_t type, code, ttl;
struct in_addr src_addr;
const void *payload;
size_t payload_len, ip_hdr_len;
if (recv_parse_packet(buffer, len, &type, &code, &ttl, &src_addr,
&payload, &payload_len, &ip_hdr_len) < 0)
return;
recv_build_reply(&reply, type, code, ttl, src_addr, payload, payload_len,
buffer, ip_hdr_len);
reply.ip_payload_len = len - ip_hdr_len;
cb(&reply, userdata);
}

View file

@ -0,0 +1,20 @@
#include "internal/icmp_recv.h"
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
ssize_t
recv_receive_packet(int fd, void *buffer, size_t buffer_len,
struct sockaddr_in *from)
{
socklen_t from_len;
ssize_t n;
from_len = sizeof(*from);
memset(from, 0, sizeof(*from));
n = recvfrom(fd, buffer, buffer_len, 0, (struct sockaddr *)from,
&from_len);
if (n < 0)
return (EAGAIN == errno || EWOULDBLOCK == errno) ? 0 : -1;
return n;
}

View file

@ -0,0 +1,25 @@
#include "internal/icmp_recv.h"
#include "internal/icmp_utils.h"
#include <string.h>
void
recv_build_reply(icmp_reply_t *reply, uint8_t type, uint8_t code,
uint8_t ttl, struct in_addr src_addr,
const void *payload, size_t payload_len,
const void *buffer, size_t ip_hdr_len)
{
memset(reply, 0, sizeof(icmp_reply_t));
reply->type = type;
reply->code = code;
reply->from = src_addr;
reply->ttl = ttl;
reply->payload = (void *)payload;
reply->payload_len = payload_len;
reply->ip_payload = (void *)((const uint8_t *)buffer + ip_hdr_len);
reply->ip_payload_len = 0;
if (icmp_get_time(&reply->timestamp) < 0)
{
reply->timestamp.tv_sec = 0;
reply->timestamp.tv_nsec = 0;
}
}

View file

@ -0,0 +1,15 @@
#include "internal/icmp_internal.h"
#include "internal/icmp_recv.h"
#include <errno.h>
#include <string.h>
int
recv_handle_receive_error(struct icmp_handle *h)
{
int saved_errno;
saved_errno = errno;
icmp_set_error_fmt(h, ICMP_ERR_RECV, "recvfrom() failed: %s",
strerror(saved_errno));
return -1;
}

View file

@ -0,0 +1,24 @@
#include "internal/icmp_internal.h"
#include "internal/icmp_recv.h"
#include <stddef.h>
int
recv_validate_params(struct icmp_handle *h, icmp_callback_t cb)
{
if (NULL == h)
return 0;
if (NULL == cb)
{
icmp_set_error_fmt(h, ICMP_ERR_INVALID, "Callback cannot be NULL");
return 0;
}
if (h->fd < 0)
{
icmp_set_error_fmt(h, ICMP_ERR_INVALID, "Invalid socket FD");
return 0;
}
return 1;
}

View file

28
src/send/api/echo.c Normal file
View file

@ -0,0 +1,28 @@
#include "icmp.h"
#include "internal/icmp_send.h"
#include "icmp_types.h"
#include <stdint.h>
#include <arpa/inet.h>
/* ICMP echo header: type(1) + code(1) + checksum(2) + id(2) + seq(2) = 8 */
#define ECHO_HEADER_SIZE 4
int
icmp_send_echo(icmp_handle_t *h, struct in_addr dest, uint16_t id,
uint16_t seq, uint8_t ttl)
{
if (0 == send_validate_handle(h))
return -1;
uint16_t id_net = htons(id);
uint16_t seq_net = htons(seq);
uint8_t payload[ECHO_HEADER_SIZE];
payload[0] = (id_net >> 8) & 0xFF;
payload[1] = id_net & 0xFF;
payload[2] = (seq_net >> 8) & 0xFF;
payload[3] = seq_net & 0xFF;
return icmp_send_raw(h, ICMP_TYPE_ECHO_REQUEST, 0,
payload, ECHO_HEADER_SIZE, dest, ttl);
}

57
src/send/api/raw.c Normal file
View file

@ -0,0 +1,57 @@
#include "icmp.h"
#include "internal/icmp_internal.h"
#include "internal/icmp_packet_internal.h"
#include "internal/icmp_send.h"
#include <stddef.h>
/* Maximum payload size for simplicity */
#define MAX_PAYLOAD_SIZE 1024
#define MAX_PACKET_SIZE (8 + MAX_PAYLOAD_SIZE)
/* Forward declarations */
static int validate_payload(struct icmp_handle *h, const void *payload,
size_t len);
/* -------------------- */
int
icmp_send_raw(icmp_handle_t *h, uint8_t type, uint8_t code,
const void *payload, size_t len, struct in_addr dest,
uint8_t ttl)
{
if (0 == send_validate_handle(h))
return -1;
if (0 == validate_payload(h, payload, len))
return -1;
uint8_t buffer[MAX_PACKET_SIZE];
int packet_len = icmp_build_packet(buffer, sizeof(buffer),
type, code, 0, 0, payload, len);
if (-1 == packet_len)
{
icmp_set_error_fmt(h, ICMP_ERR_INVALID,
"Failed to build ICMP packet");
return -1;
}
return send_to_destination(h, buffer, packet_len, dest, ttl);
}
static int
validate_payload(struct icmp_handle *h, const void *payload, size_t len)
{
if (len > 0 && NULL == payload)
{
icmp_set_error_fmt(h, ICMP_ERR_INVALID, "Payload is NULL");
return 0;
}
if (len > MAX_PAYLOAD_SIZE)
{
icmp_set_error_fmt(h, ICMP_ERR_INVALID,
"Payload too large (max %d bytes)", MAX_PAYLOAD_SIZE);
return 0;
}
return 1;
}

View file

@ -0,0 +1,39 @@
#include "internal/icmp_send.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
int
send_packet(struct icmp_handle *h, const void *buffer, size_t len,
struct sockaddr_in *dest)
{
ssize_t bytes_sent;
bytes_sent = sendto(h->fd, buffer, len, 0, (struct sockaddr *)dest,
sizeof(struct sockaddr_in));
if (bytes_sent < 0)
{
int saved_errno = errno;
if (EAGAIN == saved_errno || EWOULDBLOCK == saved_errno)
{
icmp_set_error_fmt(h, ICMP_ERR_EAGAIN,
"Send would block (buffer full, retry later)");
return -1;
}
icmp_set_error_fmt(h, ICMP_ERR_SEND, "sendto() failed: %s",
strerror(saved_errno));
return -1;
}
if ((size_t)bytes_sent != len)
{
icmp_set_error_fmt(h, ICMP_ERR_SEND, "Partial send (%zd/%zu bytes)",
bytes_sent, len);
return -1;
}
return 0;
}

View file

@ -0,0 +1,20 @@
#include "internal/icmp_internal.h"
#include "internal/icmp_send.h"
#include <netinet/in.h>
int
send_to_destination(struct icmp_handle *h, const void *packet, size_t len,
struct in_addr dest, uint8_t ttl)
{
if (send_set_socket_ttl(h, ttl) < 0)
return -1;
struct sockaddr_in addr;
send_prepare_destination(&addr, dest);
if (send_packet(h, packet, len, &addr) < 0)
return -1;
icmp_clear_error(h);
return 0;
}

View file

@ -0,0 +1,10 @@
#include "internal/icmp_send.h"
#include <string.h>
void
send_prepare_destination(struct sockaddr_in *addr, struct in_addr dest)
{
memset(addr, 0, sizeof(struct sockaddr_in));
addr->sin_family = AF_INET;
addr->sin_addr = dest;
}

View file

@ -0,0 +1,21 @@
#include "internal/icmp_send.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
int
send_set_socket_ttl(struct icmp_handle *h, uint8_t ttl)
{
int ttl_val = ttl;
if (setsockopt(h->fd, IPPROTO_IP, IP_TTL, &ttl_val, sizeof(ttl_val)) < 0)
{
int saved_errno = errno;
icmp_set_error_fmt(h, ICMP_ERR_SOCKET, "Failed to set TTL: %s",
strerror(saved_errno));
return -1;
}
return 0;
}

View file

@ -0,0 +1,18 @@
#include "internal/icmp_internal.h"
#include "internal/icmp_send.h"
#include <stddef.h>
int
send_validate_handle(struct icmp_handle *h)
{
if (NULL == h)
return 0;
if (h->fd < 0)
{
icmp_set_error_fmt(h, ICMP_ERR_INVALID, "Invalid socket FD");
return 0;
}
return 1;
}

View file

View file