Skip to content

Commit

Permalink
new module icmp6_echo_time_novalidation that skips validation between…
Browse files Browse the repository at this point in the history
… initial target ip and ip-addr. of response (#12)

Co-authored-by: kochm99 <maynard.k@fu-berlin.de>
  • Loading branch information
mkoch-tud and kochm99 authored Mar 8, 2024
1 parent b2e8392 commit 6c4585b
Show file tree
Hide file tree
Showing 3 changed files with 287 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ set(PROBE_MODULE_SOURCES
probe_modules/module_ipv6_udp.c
probe_modules/module_ipv6_udp_dns.c
probe_modules/module_icmp6_echoscan.c
probe_modules/module_icmp6_echo_time_novalidation.c
)

set(SOURCES
Expand Down Expand Up @@ -277,4 +278,4 @@ install(
ziterate.1
ztee.1
DESTINATION share/man/man1
)
)
281 changes: 281 additions & 0 deletions src/probe_modules/module_icmp6_echo_time_novalidation.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
/*
* ZMapv6 Copyright 2016 Chair of Network Architectures and Services
* Technical University of Munich
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

// probe module for performing ICMP echo request (ping) scans
// without proper answer validation, every reply is accepted
// initial target ip address is encoded in payload and extracted in field initial-ip of response

// Needed for asprintf
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>

#include "../../lib/includes.h"
#include "probe_modules.h"
#include "../fieldset.h"
#include "packet.h"
#include "validate.h"

#define ICMP_SMALLEST_SIZE 5
#define ICMP_TIMXCEED_UNREACH_HEADER_SIZE 8

probe_module_t module_icmp6_echo_time_novalidation;

struct icmp6_payload_for_rtt {
uint32_t sent_tv_sec;
uint32_t sent_tv_usec;
struct in6_addr dst_init;
};

int icmp6_echotime_global_initialize(struct state_conf *conf)
{
// Only look at received packets destined to the specified scanning address (useful for parallel zmap scans)
if (asprintf((char ** restrict) &module_icmp6_echo_time_novalidation.pcap_filter, "%s && ip6 dst host %s", module_icmp6_echo_time_novalidation.pcap_filter, conf->ipv6_source_ip) == -1) {
return 1;
}

return EXIT_SUCCESS;
}

static int icmp6_echotime_init_perthread(void* buf, macaddr_t *src,
macaddr_t *gw, __attribute__((unused)) port_h_t dst_port,
__attribute__((unused)) void **arg_ptr)
{
memset(buf, 0, MAX_PACKET_SIZE);

struct ether_header *eth_header = (struct ether_header *) buf;
make_eth_header_ethertype(eth_header, src, gw, ETHERTYPE_IPV6);

struct ip6_hdr *ip6_header = (struct ip6_hdr *) (&eth_header[1]);
// ICMPv6 header plus time and dst ip payload
uint16_t payload_len = sizeof(struct icmp6_hdr) + sizeof(struct icmp6_payload_for_rtt);
make_ip6_header(ip6_header, IPPROTO_ICMPV6, payload_len);

struct icmp6_hdr *icmp6_header = (struct icmp6_hdr*)(&ip6_header[1]);
make_icmp6_header(icmp6_header);

return EXIT_SUCCESS;
}

static int icmp6_echotime_make_packet(void *buf, size_t *buf_len, UNUSED ipaddr_n_t src_ip, UNUSED ipaddr_n_t dst_ip, uint8_t ttl, uint32_t *validation, UNUSED int probe_num, UNUSED void *arg)
{
struct ether_header *eth_header = (struct ether_header *) buf;
struct ip6_hdr *ip6_header = (struct ip6_hdr *)(&eth_header[1]);
struct icmp6_hdr *icmp6_header = (struct icmp6_hdr*)(&ip6_header[1]);
// add rtt header which includes initial target ip address (dst_init)
struct icmp6_payload_for_rtt *payload =
(struct icmp6_payload_for_rtt *)(((char *)icmp6_header) + 8);
uint16_t icmp_idnum = validation[2] & 0xFFFF;

// Include validation in ICMPv6 payload data
icmp6_header->icmp6_data32[1] = validation[0];
icmp6_header->icmp6_data32[2] = validation[1];

ip6_header->ip6_src = ((struct in6_addr *) arg)[0];
ip6_header->ip6_dst = ((struct in6_addr *) arg)[1];
ip6_header->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl;

icmp6_header->icmp6_id= icmp_idnum;

struct timeval tv;
gettimeofday(&tv, NULL);
payload->sent_tv_sec = tv.tv_sec;
payload->sent_tv_usec = tv.tv_usec;
payload->dst_init = ((struct in6_addr *) arg)[1];

icmp6_header->icmp6_cksum = 0;
icmp6_header->icmp6_cksum= ipv6_payload_checksum(sizeof(struct icmp6_hdr) + sizeof(struct icmp6_payload_for_rtt), &ip6_header->ip6_src, &ip6_header->ip6_dst, (unsigned short *) icmp6_header, IPPROTO_ICMPV6);
// icmp_payload_for_rtt is used as payload, so we add its length here
*buf_len = sizeof(struct ether_header) + sizeof(struct ip6_hdr) + ICMP_MINLEN + sizeof(struct icmp6_payload_for_rtt);

return EXIT_SUCCESS;
}

static void icmp6_echotime_print_packet(FILE *fp, void* packet)
{
struct ether_header *ethh = (struct ether_header *) packet;
struct ip6_hdr *iph = (struct ip6_hdr *) &ethh[1];
struct icmp6_hdr *icmp6_header = (struct icmp6_hdr*) (&iph[1]);

fprintf(fp, "icmp { type: %u | code: %u "
"| checksum: %#04X | id: %u | seq: %u }\n",
icmp6_header->icmp6_type,
icmp6_header->icmp6_code,
ntohs(icmp6_header->icmp6_cksum),
ntohs(icmp6_header->icmp6_id),
ntohs(icmp6_header->icmp6_seq)
);
fprintf_ipv6_header(fp, iph);
fprintf_eth_header(fp, ethh);
fprintf(fp, "------------------------------------------------------\n");
}


static int icmp6_echotime_validate_packet(const struct ip *ip_hdr,
uint32_t len, __attribute__((unused)) uint32_t *src_ip,UNUSED uint32_t *validation)
{
struct ip6_hdr *ip6_hdr = (struct ip6_hdr*) ip_hdr;

if (ip6_hdr->ip6_nxt != IPPROTO_ICMPV6) {
return 0;
}

// IPv6 header is fixed length at 40 bytes + ICMPv6 header + 8 bytes of ICMPv6 data
if ( ( sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + 2 * sizeof(uint32_t)) > len) {
// buffer not large enough to contain expected icmp header
return 0;
}

// offset iphdr by ip header length of 40 bytes to shift pointer to ICMP6 header
struct icmp6_hdr *icmp6_h = (struct icmp6_hdr *) (&ip6_hdr[1]);

// ICMP validation is tricky: for some packet types, we must look inside
// the payload
if (icmp6_h->icmp6_type == ICMP6_TIME_EXCEEDED || icmp6_h->icmp6_type == ICMP6_DST_UNREACH
|| icmp6_h->icmp6_type == ICMP6_PACKET_TOO_BIG || icmp6_h->icmp6_type == ICMP6_PARAM_PROB) {

// IP6 + ICMP6 headers + inner headers + 8 byte payload (validation)
if (2*sizeof(struct ip6_hdr) + 2*sizeof(struct icmp6_hdr) + 2*sizeof(uint32_t) > len) {
return 0;
}

// Use inner headers for validation
ip6_hdr = (struct ip6_hdr *) &icmp6_h[1];
icmp6_h = (struct icmp6_hdr *) &ip6_hdr[1];
// We dont need to validate source and destination IP, so we turn it off
// Send original src and dst IP as data in ICMPv6 payload and regenerate the validation here
//validate_gen_ipv6(&ip6_hdr->ip6_dst, &ip6_hdr->ip6_src,
// (uint8_t *) validation);
}
//we dont validte this part, too
// validate icmp id
/*if (icmp6_h->icmp6_id != (validation[2] & 0xFFFF)) {
return 0;
}
// Validate ICMPv6 data
if (icmp6_h->icmp6_data32[1] != validation[0] || icmp6_h->icmp6_data32[2] != validation[1]) {
return 0;
}*/

return 1;
}

static void icmp6_echotime_process_packet(const u_char *packet,
__attribute__((unused)) uint32_t len, fieldset_t *fs,
__attribute__((unused)) uint32_t *validation,
__attribute__((unused)) struct timespec ts)
{
struct ip6_hdr *ip6_hdr = (struct ip6_hdr *) &packet[sizeof(struct ether_header)];
struct icmp6_hdr *icmp6_hdr = (struct icmp6_hdr *) (&ip6_hdr[1]);
fs_add_uint64(fs, "type", icmp6_hdr->icmp6_type);
fs_add_uint64(fs, "code", icmp6_hdr->icmp6_code);
fs_add_uint64(fs, "icmp-id", ntohs(icmp6_hdr->icmp6_id));
fs_add_uint64(fs, "seq", ntohs(icmp6_hdr->icmp6_seq));
fs_add_string(fs, "outersaddr", make_ipv6_str(&(ip6_hdr->ip6_src)), 1);
struct icmp6_payload_for_rtt *payload =
(struct icmp6_payload_for_rtt *)(((char *)icmp6_hdr) + 8);
fs_add_uint64(fs, "sent_timestamp_ts", (uint64_t)payload->sent_tv_sec);
fs_add_uint64(fs, "sent_timestamp_us", (uint64_t)payload->sent_tv_usec);
fs_add_string(fs, "initial-ip", make_ipv6_str(&(payload->dst_init)), 1);
if (icmp6_hdr->icmp6_type == ICMP6_ECHO_REPLY) {
fs_add_string(fs, "classification", (char*) "echoreply", 0);
fs_add_uint64(fs, "success", 1);
} else {
// Use inner IP header values for unsuccessful ICMP replies
struct ip6_hdr *ip6_inner_hdr = (struct ip6_hdr *) &icmp6_hdr[1];
fs_modify_string(fs, "saddr", make_ipv6_str(&(ip6_inner_hdr->ip6_dst)), 1);
fs_modify_string(fs, "daddr", make_ipv6_str(&(ip6_inner_hdr->ip6_src)), 1);

switch(icmp6_hdr->icmp6_type) {
case ICMP6_DST_UNREACH:
switch(icmp6_hdr->icmp6_code) {
case ICMP6_DST_UNREACH_NOROUTE:
fs_add_string(fs, "classification", (char*) "unreach_noroute", 0);
break;
case ICMP6_DST_UNREACH_ADMIN:
fs_add_string(fs, "classification", (char*) "unreach_admin", 0);
break;
case ICMP6_DST_UNREACH_BEYONDSCOPE:
fs_add_string(fs, "classification", (char*) "unreach_beyondscope", 0);
break;
case ICMP6_DST_UNREACH_ADDR:
fs_add_string(fs, "classification", (char*) "unreach_addr", 0);
break;
case ICMP6_DST_UNREACH_NOPORT:
fs_add_string(fs, "classification", (char*) "unreach_noport", 0);
break;
case 5:
fs_add_string(fs, "classification", (char*) "unreach_policy", 0);
break;
case 6:
fs_add_string(fs, "classification", (char*) "unreach_rejectroute", 0);
break;
case 7:
fs_add_string(fs, "classification", (char*) "unreach_err_src_route", 0);
break;
default:
fs_add_string(fs, "classification", (char*) "unreach", 0);
break;
}
break;
case ICMP6_PACKET_TOO_BIG:
fs_add_string(fs, "classification", (char*) "toobig", 0);
break;
case ICMP6_PARAM_PROB:
fs_add_string(fs, "classification", (char*) "paramprob", 0);
break;
case ICMP6_TIME_EXCEEDED:
fs_add_string(fs, "classification", (char*) "timxceed", 0);
break;
default:
fs_add_string(fs, "classification", (char*) "other", 0);
break;
}
fs_add_uint64(fs, "success", 0);
}
}

static fielddef_t fields[] = {
{.name="type", .type="int", .desc="icmp message type"},
{.name="code", .type="int", .desc="icmp message sub type code"},
{.name="icmp-id", .type="int", .desc="icmp id number"},
{.name="seq", .type="int", .desc="icmp sequence number"},
{.name="outersaddr", .type="string", .desc="outer src address of icmp reply packet"},
{.name = "sent_timestamp_ts", .type = "int", .desc = "timestamp of sent probe in seconds since Epoch"},
{.name = "sent_timestamp_us", .type = "int", .desc = "microsecond part of sent timestamp"},
{.name="initial-ip", .type="string", .desc="initial target ip of the echo request encoded in payload"},
{.name="classification", .type="string", .desc="probe module classification"},
{.name="success", .type="int", .desc="did probe module classify response as success"}
};


probe_module_t module_icmp6_echo_time_novalidation = {
.name = "icmp6_echo_time_novalidation",
.max_packet_length = 70, // 62, // ICMPv4: 64 bit --> Why 62? ICMPv6 also 64 bit --> Leave 64
.pcap_filter = "icmp6 && (ip6[40] == 129 || ip6[40] == 3 || ip6[40] == 1 || ip6[40] == 2 || ip6[40] == 4)", // and icmp6[0]=!8",
.pcap_snaplen = 118, // 14 ethernet header + 40 IPv6 header + 8 ICMPv6 header + 40 inner IPv6 header + 8 inner ICMPv6 header + 8 payload
.port_args = 0,
.global_initialize = &icmp6_echotime_global_initialize,
.thread_initialize = &icmp6_echotime_init_perthread,
.make_packet = &icmp6_echotime_make_packet,
.print_packet = &icmp6_echotime_print_packet,
.process_packet = &icmp6_echotime_process_packet,
.validate_packet = &icmp6_echotime_validate_packet,
.close = NULL,
.fields = fields,
.numfields = 10};
6 changes: 4 additions & 2 deletions src/probe_modules/probe_modules.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ extern probe_module_t module_quic_initial;
extern probe_module_t module_ipv6_quic_initial;

// ADD YOUR MODULE HERE
extern probe_module_t module_icmp6_echo_time_novalidation;

probe_module_t* probe_modules[] = {
&module_tcp_synscan, &module_tcp_synackscan, &module_icmp_echo,
Expand All @@ -50,8 +51,9 @@ probe_module_t* probe_modules[] = {
&module_ipv6_udp_dns,
&module_icmp6_echoscan,
&module_quic_initial,
&module_ipv6_quic_initial
&module_ipv6_quic_initial,
// ADD YOUR MODULE HERE
&module_icmp6_echo_time_novalidation
};

probe_module_t *get_probe_module_by_name(const char *name)
Expand Down Expand Up @@ -159,4 +161,4 @@ fielddef_t sys_fields[] = {
{.name = "timestamp_us",
.type = "int",
.desc =
"microsecond part of timestamp (e.g. microseconds since 'timestamp-ts')"}};
"microsecond part of timestamp (e.g. microseconds since 'timestamp-ts')"}};

0 comments on commit 6c4585b

Please sign in to comment.