188 lines
4.5 KiB
C
188 lines
4.5 KiB
C
#include "dns.h"
|
|
#include "host.h"
|
|
#include "interval.h"
|
|
#include "packet.h"
|
|
#include "print.h"
|
|
#include "setting.h"
|
|
#include "statistics.h"
|
|
|
|
#include <netinet/ip.h>
|
|
#include <signal.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <stdarg.h>
|
|
#include <netinet/ip_icmp.h>
|
|
#include <arpa/inet.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/time.h>
|
|
#include <stdbool.h>
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <net/ethernet.h>
|
|
|
|
bool loop = true;
|
|
|
|
static void signal_handler(int code)
|
|
{
|
|
(void) code;
|
|
loop = false;
|
|
}
|
|
|
|
static int create_socket(struct setting const *settings)
|
|
{
|
|
int ret;
|
|
(void)settings;
|
|
|
|
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
|
|
|
if (sockfd == -1)
|
|
{
|
|
print_err("open socket failed, maybe try in root (:");
|
|
return -1;
|
|
}
|
|
|
|
ret = setsockopt(sockfd, IPPROTO_IP, IP_TTL, &settings->ttl, sizeof(settings->ttl));
|
|
if (ret != 0)
|
|
{
|
|
close(sockfd);
|
|
print_err("Failed to setsockopt(): ttl");
|
|
return -1;
|
|
}
|
|
struct timeval tv = {2, 0};
|
|
ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
if (ret != 0)
|
|
{
|
|
close(sockfd);
|
|
print_err("Failed to setsockopt(): recv timeout");
|
|
return -1;
|
|
}
|
|
return sockfd;
|
|
}
|
|
|
|
static int preload(struct setting const *settings, int sockfd, char const *packet, size_t packet_size)
|
|
{
|
|
for (size_t i = 0; i < settings->preload; i++)
|
|
{
|
|
if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *) &settings->dest.ip, sizeof(settings->dest.ip)) == -1)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int check_reply(struct sockaddr_in const *me, struct sockaddr_in const *sender, char *sent, char *received, size_t sent_size, size_t recv_size)
|
|
{
|
|
if (sent_size != recv_size)
|
|
return 0;
|
|
if (((struct icmphdr *)sent)->type != 0)
|
|
return 0;
|
|
if (check_packet_conformity(sent, received, sent_size))
|
|
return 1;
|
|
return memcmp(sender, me, sizeof(struct sockaddr_in));
|
|
}
|
|
|
|
int main(int ac, char **av)
|
|
{
|
|
(void) ac;
|
|
struct statistics stats;
|
|
struct setting settings;
|
|
|
|
int ret = get_setting(av + 1, &settings);
|
|
|
|
if (ret == -1)
|
|
return 0;
|
|
if (ret)
|
|
goto error0;
|
|
|
|
size_t packet_size = sizeof(struct icmphdr) + settings.payload_size;
|
|
size_t recv_packet_size = packet_size + sizeof(struct iphdr);
|
|
|
|
signal(SIGINT, signal_handler);
|
|
|
|
if (dns_lookup(&settings.dest))
|
|
goto error0;
|
|
|
|
int sockfd = create_socket(&settings);
|
|
if (sockfd == -1)
|
|
goto error0;
|
|
|
|
char *packet = packet_create(settings.payload_size);
|
|
if (packet == NULL)
|
|
goto error1;
|
|
|
|
char *buffer;
|
|
buffer = malloc((recv_packet_size) * sizeof(char));
|
|
if (buffer == NULL)
|
|
{
|
|
print_err("error: allocation failed.");
|
|
goto error2;
|
|
}
|
|
|
|
struct sockaddr_in sender;
|
|
socklen_t len = sizeof(sender);
|
|
|
|
bzero(&stats, sizeof(struct statistics));
|
|
print_header(&settings);
|
|
|
|
if (preload(&settings, sockfd, packet, packet_size))
|
|
goto error3;
|
|
|
|
while (loop) {
|
|
|
|
struct timeval stop, start;
|
|
|
|
gettimeofday(&start, NULL);
|
|
if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *) &settings.dest.ip, sizeof(settings.dest.ip)) == -1)
|
|
{
|
|
print_err("error: send packet failed.");
|
|
goto error2;
|
|
}
|
|
stats.packets_sent++;
|
|
|
|
ssize_t ret = 0;
|
|
do
|
|
{
|
|
ret = recvfrom(sockfd, buffer, recv_packet_size, 0, (struct sockaddr *) &sender, &len);
|
|
gettimeofday(&stop, NULL);
|
|
}
|
|
while (ret >= 0 && check_reply(&settings.dest.ip, &sender, packet, buffer + sizeof(struct iphdr), packet_size, ret));
|
|
|
|
if (ret >= (ssize_t) (sizeof(struct icmphdr) + sizeof(struct iphdr)))
|
|
{
|
|
if (((struct icmphdr *) (buffer + sizeof(struct iphdr)))->type == 0)
|
|
stats.packets_received++;
|
|
print_recv(&settings, buffer, &start, &stop, &sender);
|
|
}
|
|
|
|
double interval = get_interval(&start, &stop);
|
|
if (interval < 1000)
|
|
usleep((1000 - interval) * 1e3);
|
|
|
|
if (packet_update(packet, settings.payload_size))
|
|
goto error3;
|
|
}
|
|
|
|
free(packet);
|
|
free(buffer);
|
|
close(sockfd);
|
|
|
|
print_statistics(&stats, &settings);
|
|
|
|
return 0;
|
|
|
|
error3:
|
|
free(buffer);
|
|
error2:
|
|
free(packet);
|
|
error1:
|
|
close(sockfd);
|
|
error0:
|
|
return 1;
|
|
} |