feat: implement ICMP send/receive modules
This commit is contained in:
parent
10a10f6e0a
commit
3c56ea024b
21 changed files with 422 additions and 0 deletions
28
includes/icmp_types.h
Normal file
28
includes/icmp_types.h
Normal 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
|
||||||
33
includes/internal/icmp_recv.h
Normal file
33
includes/internal/icmp_recv.h
Normal 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
|
||||||
16
includes/internal/icmp_send.h
Normal file
16
includes/internal/icmp_send.h
Normal 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
|
||||||
28
src/recv/api/process.c
Normal file
28
src/recv/api/process.c
Normal 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;
|
||||||
|
}
|
||||||
20
src/recv/core/parse_packet.c
Normal file
20
src/recv/core/parse_packet.c
Normal 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;
|
||||||
|
}
|
||||||
20
src/recv/core/process_single_packet.c
Normal file
20
src/recv/core/process_single_packet.c
Normal 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);
|
||||||
|
}
|
||||||
20
src/recv/core/receive_packet.c
Normal file
20
src/recv/core/receive_packet.c
Normal 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;
|
||||||
|
}
|
||||||
25
src/recv/helpers/build_reply.c
Normal file
25
src/recv/helpers/build_reply.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/recv/helpers/handle_receive_error.c
Normal file
15
src/recv/helpers/handle_receive_error.c
Normal 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;
|
||||||
|
}
|
||||||
24
src/recv/helpers/validate_params.c
Normal file
24
src/recv/helpers/validate_params.c
Normal 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;
|
||||||
|
}
|
||||||
28
src/send/api/echo.c
Normal file
28
src/send/api/echo.c
Normal 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
57
src/send/api/raw.c
Normal 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;
|
||||||
|
}
|
||||||
39
src/send/core/send_packet.c
Normal file
39
src/send/core/send_packet.c
Normal 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;
|
||||||
|
}
|
||||||
20
src/send/core/send_to_destination.c
Normal file
20
src/send/core/send_to_destination.c
Normal 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;
|
||||||
|
}
|
||||||
10
src/send/helpers/prepare_destination.c
Normal file
10
src/send/helpers/prepare_destination.c
Normal 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;
|
||||||
|
}
|
||||||
21
src/send/helpers/set_socket_ttl.c
Normal file
21
src/send/helpers/set_socket_ttl.c
Normal 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;
|
||||||
|
}
|
||||||
18
src/send/helpers/validate_handle.c
Normal file
18
src/send/helpers/validate_handle.c
Normal 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;
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue