99 lines
2.7 KiB
C
99 lines
2.7 KiB
C
#include "icmp.h"
|
|
#include "icmp_types.h"
|
|
#include "internal/icmp_packet.h"
|
|
#include "internal/icmp_packet_internal.h"
|
|
#include <netinet/in.h>
|
|
#include <stdint.h>
|
|
|
|
#define MIN_ERROR_PAYLOAD_LEN 28
|
|
#define MIN_ICMP_HEADER_LEN 8
|
|
|
|
/* Forward declarations */
|
|
static int is_error_type(uint8_t type);
|
|
static int is_echo_type(uint8_t type);
|
|
static int is_frag_needed(uint8_t type, uint8_t code);
|
|
static int parse_embedded_ip(const icmp_reply_t *reply,
|
|
icmp_offending_packet_t *offending,
|
|
size_t *ip_hdr_len);
|
|
static const struct icmp_header *get_embedded_icmp(const icmp_reply_t *reply,
|
|
size_t ip_hdr_len);
|
|
static void extract_icmp_fields(const struct icmp_header *hdr,
|
|
icmp_offending_packet_t *offending);
|
|
/* -------------------- */
|
|
|
|
int
|
|
icmp_error_extract_offending(const icmp_reply_t *reply,
|
|
icmp_offending_packet_t *offending)
|
|
{
|
|
size_t ip_hdr_len;
|
|
const struct icmp_header *hdr;
|
|
|
|
if ((NULL == reply || NULL == offending) ||
|
|
(!is_error_type(reply->type)) ||
|
|
(reply->payload_len < MIN_ERROR_PAYLOAD_LEN) ||
|
|
(parse_embedded_ip(reply, offending, &ip_hdr_len) < 0) ||
|
|
(reply->payload_len < ip_hdr_len + MIN_ICMP_HEADER_LEN))
|
|
return -1;
|
|
|
|
hdr = get_embedded_icmp(reply, ip_hdr_len);
|
|
extract_icmp_fields(hdr, offending);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
is_error_type(uint8_t type)
|
|
{
|
|
return ICMP_TYPE_DEST_UNREACHABLE == type ||
|
|
ICMP_TYPE_REDIRECT == type ||
|
|
ICMP_TYPE_TIME_EXCEEDED == type ||
|
|
ICMP_TYPE_PARAMETER_PROBLEM == type;
|
|
}
|
|
|
|
static int
|
|
is_echo_type(uint8_t type)
|
|
{
|
|
return ICMP_TYPE_ECHO_REPLY == type ||
|
|
ICMP_TYPE_ECHO_REQUEST == type ||
|
|
ICMP_TYPE_TIMESTAMP_REQUEST == type ||
|
|
ICMP_TYPE_TIMESTAMP_REPLY == type;
|
|
}
|
|
|
|
static int
|
|
is_frag_needed(uint8_t type, uint8_t code)
|
|
{
|
|
return ICMP_TYPE_DEST_UNREACHABLE == type &&
|
|
ICMP_CODE_FRAG_NEEDED == code;
|
|
}
|
|
|
|
static int
|
|
parse_embedded_ip(const icmp_reply_t *reply, icmp_offending_packet_t *offending,
|
|
size_t *ip_hdr_len)
|
|
{
|
|
return icmp_parse_ip_header(reply->payload, reply->payload_len,
|
|
NULL, &offending->src, ip_hdr_len,
|
|
&offending->dst, &offending->protocol);
|
|
}
|
|
|
|
static const struct icmp_header *
|
|
get_embedded_icmp(const icmp_reply_t *reply, size_t ip_hdr_len)
|
|
{
|
|
return (const struct icmp_header *)((const uint8_t *)reply->payload + ip_hdr_len);
|
|
}
|
|
|
|
static void
|
|
extract_icmp_fields(const struct icmp_header *hdr, icmp_offending_packet_t *offending)
|
|
{
|
|
offending->icmp_type = hdr->type;
|
|
offending->icmp_code = hdr->code;
|
|
offending->rest.gateway = hdr->un.gateway;
|
|
|
|
if (is_echo_type(hdr->type))
|
|
{
|
|
offending->rest.echo.id = ntohs(hdr->un.echo.id);
|
|
offending->rest.echo.seq = ntohs(hdr->un.echo.seq);
|
|
}
|
|
else if (is_frag_needed(hdr->type, hdr->code))
|
|
{
|
|
offending->rest.frag.mtu = ntohs(hdr->un.frag.mtu);
|
|
}
|
|
}
|