mirror of
https://git.savannah.gnu.org/git/inetutils.git
synced 2026-01-12 00:19:39 +08:00
848 lines
19 KiB
C
848 lines
19 KiB
C
/*
|
|
Copyright (C) 2007-2025 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Inetutils.
|
|
|
|
GNU Inetutils is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or (at
|
|
your option) any later version.
|
|
|
|
GNU Inetutils is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see `http://www.gnu.org/licenses/'. */
|
|
|
|
/* Written by Elian Gidoni. */
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/file.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
/* #include <netinet/ip_icmp.h> -- Deliberately not including this
|
|
since the definitions in use are being pulled in by libicmp. */
|
|
#ifdef HAVE_NETINET_IP_VAR_H
|
|
# include <netinet/ip_var.h>
|
|
#endif
|
|
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <error.h>
|
|
#include <progname.h>
|
|
#include <limits.h>
|
|
#include <assert.h>
|
|
#include <argp.h>
|
|
#include <attribute.h>
|
|
#include <timespec.h>
|
|
#include <icmp.h>
|
|
#ifdef HAVE_LOCALE_H
|
|
# include <locale.h>
|
|
#endif
|
|
#if defined HAVE_IDN2_H && defined HAVE_IDN2
|
|
# include <idn2.h>
|
|
#elif defined HAVE_IDNA_H
|
|
# include <idna.h>
|
|
#endif
|
|
|
|
#include "xalloc.h"
|
|
#include "libinetutils.h"
|
|
|
|
#define TRACE_UDP_PORT 33434
|
|
#define TRACE_TTL 1
|
|
|
|
enum trace_type
|
|
{
|
|
TRACE_UDP, /* UDP datagrams. */
|
|
TRACE_ICMP, /* ICMP echo requests. */
|
|
TRACE_1393 /* RFC 1393 requests. */
|
|
};
|
|
|
|
typedef struct trace
|
|
{
|
|
int icmpfd, udpfd;
|
|
enum trace_type type;
|
|
int no_ident;
|
|
struct sockaddr_in to, from;
|
|
int ttl;
|
|
struct timespec tsent;
|
|
} trace_t;
|
|
|
|
void trace_init (trace_t * t, const struct sockaddr_in to,
|
|
const enum trace_type type);
|
|
void trace_ip_opts (struct sockaddr_in *to);
|
|
void trace_inc_ttl (trace_t * t);
|
|
void trace_inc_port (trace_t * t);
|
|
void trace_port (trace_t * t, const unsigned short port);
|
|
int trace_read (trace_t * t, int *type, int *code);
|
|
int trace_write (trace_t * t);
|
|
int trace_udp_sock (trace_t * t);
|
|
int trace_icmp_sock (trace_t * t);
|
|
|
|
#define TIME_INTERVAL 3
|
|
|
|
void do_try (trace_t * trace, const int hop,
|
|
const int max_hops, const int max_tries);
|
|
|
|
char *get_hostname (struct in_addr *addr);
|
|
|
|
int stop = 0;
|
|
int pid;
|
|
int seqno; /* Most recent sequence number. */
|
|
static char *hostname = NULL;
|
|
char addrstr[INET6_ADDRSTRLEN];
|
|
struct sockaddr_in dest;
|
|
|
|
#ifdef IP_OPTIONS
|
|
size_t len_ip_opts = 0;
|
|
char ip_opts[MAX_IPOPTLEN];
|
|
#endif /* IP_OPTIONS */
|
|
|
|
/* Cause for destination unreachable reply,
|
|
* encoded as a single character.
|
|
*/
|
|
const char unreach_sign[NR_ICMP_UNREACH + 2] = "NHPPFS**U**TTXXX";
|
|
|
|
static enum trace_type opt_type = TRACE_UDP;
|
|
int opt_port = TRACE_UDP_PORT;
|
|
int opt_max_hops = 64;
|
|
static int opt_max_tries = 3;
|
|
int opt_resolve_hostnames = 0;
|
|
int opt_tos = -1; /* Triggers with non-negative values. */
|
|
int opt_ttl = TRACE_TTL;
|
|
int opt_wait = TIME_INTERVAL;
|
|
#ifdef IP_OPTIONS
|
|
char *opt_gateways = NULL;
|
|
#endif
|
|
|
|
const char args_doc[] = "HOST";
|
|
const char doc[] = "Print the route packets trace to network host.";
|
|
const char *program_authors[] = {
|
|
"Elian Gidoni",
|
|
NULL
|
|
};
|
|
|
|
/* Define keys for long options that do not have short counterparts. */
|
|
enum
|
|
{
|
|
OPT_RESOLVE = 256
|
|
};
|
|
|
|
static struct argp_option argp_options[] = {
|
|
#define GRP 0
|
|
{"first-hop", 'f', "NUM", 0, "set initial hop distance, i.e., time-to-live",
|
|
GRP + 1},
|
|
#ifdef IP_OPTIONS
|
|
{"gateways", 'g', "GATES", 0, "list of gateways for loose source routing",
|
|
GRP + 1},
|
|
#endif
|
|
{"icmp", 'I', NULL, 0, "use ICMP ECHO as probe", GRP + 1},
|
|
{"max-hop", 'm', "NUM", 0, "set maximal hop count (default: 64)", GRP + 1},
|
|
{"port", 'p', "PORT", 0, "use destination PORT port (default: 33434)",
|
|
GRP + 1},
|
|
{"resolve-hostnames", OPT_RESOLVE, NULL, 0, "resolve hostnames", GRP + 1},
|
|
{"tos", 't', "NUM", 0, "set type of service (TOS) to NUM", GRP + 1},
|
|
{"tries", 'q', "NUM", 0, "send NUM probe packets per hop (default: 3)",
|
|
GRP + 1},
|
|
{"type", 'M', "METHOD", 0, "use METHOD (`icmp' or `udp') for traceroute "
|
|
"operations, defaulting to `udp'", GRP + 1},
|
|
{"wait", 'w', "NUM", 0, "wait NUM seconds for response (default: 3)",
|
|
GRP + 1},
|
|
#undef GRP
|
|
{NULL, 0, NULL, 0, NULL, 0}
|
|
};
|
|
|
|
static error_t
|
|
parse_opt (int key, char *arg, struct argp_state *state)
|
|
{
|
|
char *p;
|
|
static bool host_is_given = false;
|
|
|
|
switch (key)
|
|
{
|
|
case 'f':
|
|
opt_ttl = strtol (arg, &p, 0);
|
|
if (*p || opt_ttl <= 0 || opt_ttl > 255)
|
|
error (EXIT_FAILURE, 0, "impossible distance `%s'", arg);
|
|
break;
|
|
|
|
#ifdef IP_OPTIONS
|
|
case 'g':
|
|
if (opt_gateways)
|
|
{
|
|
size_t len = 0;
|
|
|
|
len = strlen (opt_gateways);
|
|
opt_gateways = xrealloc (opt_gateways, len + strlen (arg) + 3);
|
|
|
|
/* Append new gateways to old list,
|
|
* separating the two parts by a comma.
|
|
*/
|
|
opt_gateways[len] = ',';
|
|
opt_gateways[len + 1] = '\0';
|
|
strcat (opt_gateways, arg);
|
|
}
|
|
else
|
|
opt_gateways = xstrdup (arg);
|
|
break;
|
|
#endif /* IP_OPTIONS */
|
|
|
|
case 'I':
|
|
opt_type = TRACE_ICMP;
|
|
break;
|
|
|
|
case 'm':
|
|
opt_max_hops = strtol (arg, &p, 0);
|
|
if (*p || opt_max_hops <= 0 || opt_max_hops > 255)
|
|
error (EXIT_FAILURE, 0, "invalid hops value `%s'", arg);
|
|
break;
|
|
|
|
case 'p':
|
|
opt_port = strtol (arg, &p, 0);
|
|
if (*p || opt_port <= 0 || opt_port > 65536)
|
|
error (EXIT_FAILURE, 0, "invalid port number `%s'", arg);
|
|
break;
|
|
|
|
case 'q':
|
|
opt_max_tries = (int) strtol (arg, &p, 10);
|
|
if (*p)
|
|
argp_error (state, "invalid value (`%s' near `%s')", arg, p);
|
|
if (opt_max_tries < 1 || opt_max_tries > 10)
|
|
error (EXIT_FAILURE, 0, "number of tries should be between 1 and 10");
|
|
break;
|
|
|
|
case 't':
|
|
opt_tos = strtol (arg, &p, 0);
|
|
if (*p || opt_tos < 0 || opt_tos > 255)
|
|
error (EXIT_FAILURE, 0, "invalid TOS value `%s'", arg);
|
|
break;
|
|
|
|
case 'M':
|
|
if (strcmp (arg, "icmp") == 0)
|
|
opt_type = TRACE_ICMP;
|
|
else if (strcmp (arg, "udp") == 0)
|
|
opt_type = TRACE_UDP;
|
|
else
|
|
argp_error (state, "invalid method");
|
|
break;
|
|
|
|
case 'w':
|
|
opt_wait = strtol (arg, &p, 0);
|
|
if (*p || opt_wait < 0 || opt_wait > 60)
|
|
error (EXIT_FAILURE, 0, "ridiculous waiting time `%s'", arg);
|
|
break;
|
|
|
|
case OPT_RESOLVE:
|
|
opt_resolve_hostnames = 1;
|
|
break;
|
|
|
|
case ARGP_KEY_ARG:
|
|
host_is_given = true;
|
|
hostname = xstrdup (arg);
|
|
break;
|
|
|
|
case ARGP_KEY_SUCCESS:
|
|
if (!host_is_given)
|
|
argp_error (state, "missing host operand");
|
|
break;
|
|
|
|
default:
|
|
return ARGP_ERR_UNKNOWN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct argp argp =
|
|
{ argp_options, parse_opt, args_doc, doc, NULL, NULL, NULL };
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
int hop, rc;
|
|
char *rhost;
|
|
struct addrinfo hints, *res;
|
|
trace_t trace;
|
|
|
|
set_program_name (argv[0]);
|
|
|
|
#ifdef HAVE_SETLOCALE
|
|
setlocale (LC_ALL, "");
|
|
#endif
|
|
|
|
pid = getpid () & 0xffff;
|
|
|
|
/* Parse command line */
|
|
iu_argp_init ("traceroute", program_authors);
|
|
argp_parse (&argp, argc, argv, 0, NULL, NULL);
|
|
|
|
if ((hostname == NULL) || (*hostname == '\0'))
|
|
error (EXIT_FAILURE, 0, "unknown host");
|
|
|
|
/* Hostname lookup first for better information */
|
|
memset (&hints, 0, sizeof (hints));
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_flags = AI_CANONNAME;
|
|
#ifdef AI_IDN
|
|
hints.ai_flags |= AI_IDN;
|
|
# ifdef AI_CANONIDN
|
|
hints.ai_flags |= AI_CANONIDN;
|
|
# endif
|
|
#endif
|
|
|
|
#if defined HAVE_IDN || defined HAVE_IDN2
|
|
rc = idna_to_ascii_lz (hostname, &rhost, 0);
|
|
free (hostname);
|
|
|
|
if (rc)
|
|
error (EXIT_FAILURE, 0, "unknown host");
|
|
#else /* !HAVE_IDN && !HAVE_IDN2 */
|
|
rhost = hostname;
|
|
#endif
|
|
|
|
rc = getaddrinfo (rhost, NULL, &hints, &res);
|
|
|
|
if (rc)
|
|
error (EXIT_FAILURE, 0, "unknown host");
|
|
|
|
memcpy (&dest, res->ai_addr, res->ai_addrlen);
|
|
dest.sin_port = htons (opt_port);
|
|
|
|
getnameinfo (res->ai_addr, res->ai_addrlen, addrstr, sizeof (addrstr),
|
|
NULL, 0, NI_NUMERICHOST);
|
|
|
|
printf ("traceroute to %s (%s), %d hops max\n",
|
|
res->ai_canonname ? res->ai_canonname : rhost,
|
|
addrstr, opt_max_hops);
|
|
|
|
free (rhost);
|
|
freeaddrinfo (res);
|
|
|
|
trace_ip_opts (&dest);
|
|
|
|
trace_init (&trace, dest, opt_type);
|
|
|
|
hop = 1;
|
|
seqno = -1; /* One less than first usable packet number 0. */
|
|
|
|
while (!stop)
|
|
{
|
|
if (hop > opt_max_hops)
|
|
exit (EXIT_FAILURE);
|
|
do_try (&trace, hop, opt_max_hops, opt_max_tries);
|
|
trace_inc_ttl (&trace);
|
|
trace_inc_port (&trace);
|
|
hop++;
|
|
}
|
|
|
|
exit (EXIT_SUCCESS);
|
|
}
|
|
|
|
void
|
|
do_try (trace_t *trace, const int hop,
|
|
const int max_hops MAYBE_UNUSED, const int max_tries)
|
|
{
|
|
fd_set readset;
|
|
int ret, tries, readonly = 0;
|
|
struct timespec timeout, diff;
|
|
double triptime = 0.0;
|
|
uint32_t prev_addr = 0;
|
|
|
|
printf (" %2d ", hop);
|
|
|
|
for (tries = 0; tries < max_tries; tries++)
|
|
{
|
|
int save_errno;
|
|
int fd = trace_icmp_sock (trace);
|
|
|
|
FD_ZERO (&readset);
|
|
FD_SET (fd, &readset);
|
|
|
|
/* *INDENT-OFF* */
|
|
timeout = (struct timespec) { .tv_sec = opt_wait, .tv_nsec = 0 };
|
|
/* *INDENT-ON* */
|
|
|
|
if (!readonly)
|
|
trace_write (trace);
|
|
|
|
errno = 0;
|
|
ret = pselect (fd + 1, &readset, NULL, NULL, &timeout, NULL);
|
|
save_errno = errno;
|
|
|
|
diff = timespec_sub (current_timespec (), trace->tsent);
|
|
|
|
if (ret < 0)
|
|
{
|
|
switch (save_errno)
|
|
{
|
|
case EINTR:
|
|
/* was interrupted */
|
|
break;
|
|
default:
|
|
error (EXIT_FAILURE, errno, "select failed");
|
|
break;
|
|
}
|
|
}
|
|
else if (ret == 0)
|
|
{
|
|
/* time expired */
|
|
printf (" * ");
|
|
fflush (stdout);
|
|
}
|
|
else
|
|
{
|
|
if (FD_ISSET (fd, &readset))
|
|
{
|
|
int rc, type, code;
|
|
|
|
triptime = timespectod (diff) * 1000.0;
|
|
|
|
rc = trace_read (trace, &type, &code);
|
|
|
|
if (rc < 0)
|
|
{
|
|
/* FIXME: printf ("Some error occurred\n"); */
|
|
tries--;
|
|
readonly = 1;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (tries == 0 || prev_addr != trace->from.sin_addr.s_addr)
|
|
{
|
|
printf (" %s ", inet_ntoa (trace->from.sin_addr));
|
|
if (opt_resolve_hostnames)
|
|
printf ("(%s) ",
|
|
get_hostname (&trace->from.sin_addr));
|
|
}
|
|
printf (" %.3fms ", triptime);
|
|
|
|
/* Additional messages. */
|
|
if (rc > 0 && type == ICMP_DEST_UNREACH)
|
|
printf ("!%c ", unreach_sign[code & 0x0f]);
|
|
|
|
}
|
|
prev_addr = trace->from.sin_addr.s_addr;
|
|
}
|
|
}
|
|
readonly = 0;
|
|
fflush (stdout);
|
|
}
|
|
printf ("\n");
|
|
}
|
|
|
|
char *
|
|
get_hostname (struct in_addr *addr)
|
|
{
|
|
struct hostent *info =
|
|
gethostbyaddr ((char *) addr, sizeof (*addr), AF_INET);
|
|
if (info != NULL)
|
|
return info->h_name;
|
|
|
|
return inet_ntoa (*addr);
|
|
}
|
|
|
|
void
|
|
trace_init (trace_t *t, const struct sockaddr_in to,
|
|
const enum trace_type type)
|
|
{
|
|
int fd;
|
|
const int *ttlp;
|
|
|
|
assert (t);
|
|
ttlp = &t->ttl;
|
|
|
|
t->type = type;
|
|
t->to = to;
|
|
t->ttl = opt_ttl;
|
|
t->no_ident = 0;
|
|
|
|
if (t->type == TRACE_UDP)
|
|
{
|
|
t->udpfd = socket (PF_INET, SOCK_DGRAM, 0);
|
|
if (t->udpfd < 0)
|
|
error (EXIT_FAILURE, errno, "socket");
|
|
|
|
if (setsockopt (t->udpfd, IPPROTO_IP, IP_TTL, ttlp, sizeof (*ttlp)) < 0)
|
|
error (EXIT_FAILURE, errno, "setsockopt");
|
|
}
|
|
|
|
if (t->type == TRACE_ICMP || t->type == TRACE_UDP)
|
|
{
|
|
struct protoent *protocol = getprotobyname ("icmp");
|
|
if (protocol)
|
|
{
|
|
t->icmpfd = socket (PF_INET, SOCK_RAW, protocol->p_proto);
|
|
if (t->icmpfd < 0 && (errno == EPERM || errno == EACCES))
|
|
{
|
|
/* A subprivileged user on GNU/Linux might be allowed
|
|
* to create ICMP packets from a datagram socket.
|
|
* Such packets are always severely crippled.
|
|
*/
|
|
errno = 0;
|
|
t->icmpfd = socket (PF_INET, SOCK_DGRAM, protocol->p_proto);
|
|
t->no_ident++;
|
|
|
|
/* Recover error message for non-Linux systems. */
|
|
if (errno == EPROTONOSUPPORT)
|
|
errno = EPERM;
|
|
}
|
|
|
|
if (t->icmpfd < 0)
|
|
error (EXIT_FAILURE, errno, "socket");
|
|
|
|
if (setsockopt (t->icmpfd, IPPROTO_IP, IP_TTL,
|
|
ttlp, sizeof (*ttlp)) < 0)
|
|
error (EXIT_FAILURE, errno, "setsockopt");
|
|
}
|
|
else
|
|
{
|
|
/* FIXME: Should we error out? */
|
|
error (EXIT_FAILURE, 0, "can't find supplied protocol 'icmp'");
|
|
}
|
|
|
|
/* free (protocol); ??? */
|
|
/* FIXME: ... */
|
|
}
|
|
else
|
|
{
|
|
/* FIXME: type according to RFC 1393 */
|
|
}
|
|
|
|
fd = (t->type == TRACE_UDP ? t->udpfd : t->icmpfd);
|
|
|
|
if (opt_tos >= 0)
|
|
if (setsockopt (fd, IPPROTO_IP, IP_TOS, &opt_tos, sizeof (opt_tos)) < 0)
|
|
error (0, errno, "setsockopt(IP_TOS)");
|
|
|
|
#ifdef IP_OPTIONS
|
|
if (len_ip_opts)
|
|
if (setsockopt (fd, IPPROTO_IP, IP_OPTIONS, &ip_opts, len_ip_opts) < 0)
|
|
error (0, errno, "setsockopt(IPOPT_LSRR)");
|
|
#endif /* IP_OPTIONS */
|
|
}
|
|
|
|
void
|
|
trace_port (trace_t *t, const unsigned short int port)
|
|
{
|
|
assert (t);
|
|
if (port < IPPORT_RESERVED)
|
|
t->to.sin_port = TRACE_UDP_PORT;
|
|
else
|
|
t->to.sin_port = port;
|
|
}
|
|
|
|
/* Returned packet may contain, according to specifications:
|
|
*
|
|
* IP-header + IP-options (new IP-header)
|
|
* + ICMP-header + old-IP-header (ICMP message)
|
|
* + old-IP-options + old-ICMP-header
|
|
*/
|
|
|
|
#define CAPTURE_LEN (MAXIPLEN + MAXICMPLEN)
|
|
|
|
int
|
|
trace_read (trace_t *t, int *type, int *code)
|
|
{
|
|
int len, rc = 0;
|
|
unsigned char data[CAPTURE_LEN];
|
|
struct ip *ip;
|
|
icmphdr_t *ic;
|
|
socklen_t siz;
|
|
|
|
assert (t);
|
|
|
|
siz = sizeof (t->from);
|
|
|
|
len = recvfrom (t->icmpfd, (char *) data, sizeof (data), 0,
|
|
(struct sockaddr *) &t->from, &siz);
|
|
if (len < 0)
|
|
error (EXIT_FAILURE, errno, "recvfrom");
|
|
|
|
icmp_generic_decode (data, sizeof (data), &ip, &ic);
|
|
|
|
/* Pass type and code of incoming packet. */
|
|
*type = ic->icmp_type;
|
|
*code = ic->icmp_code;
|
|
|
|
switch (t->type)
|
|
{
|
|
case TRACE_UDP:
|
|
{
|
|
unsigned short *port;
|
|
if ((ic->icmp_type != ICMP_TIME_EXCEEDED
|
|
&& ic->icmp_type != ICMP_DEST_UNREACH))
|
|
return -1;
|
|
|
|
/* check whether it's for us */
|
|
port = (unsigned short *) ((void *) &ic->icmp_ip +
|
|
(ic->icmp_ip.ip_hl << 2) +
|
|
sizeof (in_port_t));
|
|
if (*port != t->to.sin_port) /* Network byte order! */
|
|
return -1;
|
|
|
|
if (ic->icmp_type == ICMP_DEST_UNREACH)
|
|
/* FIXME: Ugly hack. */
|
|
stop = 1;
|
|
|
|
/* Only ICMP_PORT_UNREACH is an expected reply,
|
|
* all other denials produce additional information.
|
|
*/
|
|
if (ic->icmp_type == ICMP_DEST_UNREACH
|
|
&& ic->icmp_code != ICMP_PORT_UNREACH)
|
|
rc = 1;
|
|
}
|
|
break;
|
|
|
|
case TRACE_ICMP:
|
|
if (!(ic->icmp_type == ICMP_TIME_EXCEEDED
|
|
|| ic->icmp_type == ICMP_ECHOREPLY
|
|
|| ic->icmp_type == ICMP_DEST_UNREACH))
|
|
return -1;
|
|
|
|
if (ic->icmp_type == ICMP_ECHOREPLY
|
|
&& (ntohs (ic->icmp_seq) != seqno
|
|
|| (ntohs (ic->icmp_id) != pid && t->no_ident == 0)))
|
|
return -1;
|
|
|
|
if (ic->icmp_type == ICMP_TIME_EXCEEDED
|
|
|| ic->icmp_type == ICMP_DEST_UNREACH)
|
|
{
|
|
unsigned short seq, ident;
|
|
struct ip *old_ip;
|
|
icmphdr_t *old_icmp;
|
|
|
|
old_ip = (struct ip *) &ic->icmp_ip;
|
|
old_icmp = (icmphdr_t *) ((void *) old_ip + (old_ip->ip_hl << 2));
|
|
seq = ntohs (old_icmp->icmp_seq);
|
|
ident = ntohs (old_icmp->icmp_id);
|
|
|
|
/* An expired packet tests identity and sequence number,
|
|
* whereas an undeliverable packet only checks identity.
|
|
*/
|
|
if (ident != pid
|
|
|| (ic->icmp_type == ICMP_TIME_EXCEEDED && seq != seqno))
|
|
return -1;
|
|
}
|
|
|
|
if (ip->ip_src.s_addr == dest.sin_addr.s_addr
|
|
|| ic->icmp_type == ICMP_DEST_UNREACH)
|
|
/* FIXME: Ugly hack. */
|
|
stop = 1;
|
|
|
|
if (ic->icmp_type == ICMP_DEST_UNREACH)
|
|
rc = 1;
|
|
break;
|
|
|
|
/* FIXME: Type according to RFC 1393. */
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
trace_write (trace_t *t)
|
|
{
|
|
int len;
|
|
|
|
assert (t);
|
|
|
|
switch (t->type)
|
|
{
|
|
case TRACE_UDP:
|
|
{
|
|
char data[] = "SUPERMAN";
|
|
|
|
len = sendto (t->udpfd, (char *) data, sizeof (data),
|
|
0, (struct sockaddr *) &t->to, sizeof (t->to));
|
|
if (len < 0)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case ECONNRESET:
|
|
break;
|
|
default:
|
|
error (EXIT_FAILURE, errno, "sendto");
|
|
}
|
|
}
|
|
|
|
t->tsent = current_timespec ();
|
|
}
|
|
break;
|
|
|
|
case TRACE_ICMP:
|
|
{
|
|
icmphdr_t hdr;
|
|
unsigned int i;
|
|
|
|
/* Deposit deterministic values throughout the header! */
|
|
for (i = 0; i < sizeof (hdr); ++i)
|
|
*((char *) &hdr + i) = i;
|
|
|
|
/* The subprivileged use case of ICMP sent over datagram
|
|
* sockets needs extra help with identification of target.
|
|
*/
|
|
if (t->no_ident)
|
|
*((int *) &hdr + 12 / sizeof (int)) = dest.sin_addr.s_addr;
|
|
|
|
/* The sequence number is updated to a valid value! */
|
|
if (icmp_echo_encode ((unsigned char *) &hdr, sizeof (hdr),
|
|
pid, ++seqno))
|
|
return -1;
|
|
|
|
len = sendto (t->icmpfd, (char *) &hdr, sizeof (hdr),
|
|
0, (struct sockaddr *) &t->to, sizeof (t->to));
|
|
if (len < 0)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case ECONNRESET:
|
|
break;
|
|
default:
|
|
error (EXIT_FAILURE, errno, "sendto");
|
|
}
|
|
}
|
|
|
|
t->tsent = current_timespec ();
|
|
}
|
|
break;
|
|
|
|
/* FIXME: type according to RFC 1393 */
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
trace_udp_sock (trace_t *t)
|
|
{
|
|
return (t != NULL ? t->udpfd : -1);
|
|
}
|
|
|
|
int
|
|
trace_icmp_sock (trace_t *t)
|
|
{
|
|
return (t != NULL ? t->icmpfd : -1);
|
|
}
|
|
|
|
void
|
|
trace_inc_ttl (trace_t *t)
|
|
{
|
|
int fd;
|
|
const int *ttlp;
|
|
|
|
assert (t);
|
|
|
|
ttlp = &t->ttl;
|
|
t->ttl++;
|
|
fd = (t->type == TRACE_UDP ? t->udpfd : t->icmpfd);
|
|
if (setsockopt (fd, IPPROTO_IP, IP_TTL, ttlp, sizeof (*ttlp)) < 0)
|
|
error (EXIT_FAILURE, errno, "setsockopt");
|
|
}
|
|
|
|
void
|
|
trace_inc_port (trace_t *t)
|
|
{
|
|
assert (t);
|
|
if (t->type == TRACE_UDP)
|
|
t->to.sin_port = htons (ntohs (t->to.sin_port) + 1);
|
|
}
|
|
|
|
void
|
|
trace_ip_opts (struct sockaddr_in *to)
|
|
{
|
|
#ifdef IP_OPTIONS
|
|
if (opt_gateways && *opt_gateways)
|
|
{
|
|
char *gateway, *optbase;
|
|
struct addrinfo hints, *res;
|
|
|
|
memset (&hints, 0, sizeof (hints));
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
memset (&ip_opts, 0, sizeof (ip_opts));
|
|
optbase = ip_opts;
|
|
|
|
/* Set up any desired options. Keep
|
|
* `optbase' updated, pointing to the
|
|
* part presently under construction.
|
|
*/
|
|
|
|
/* 1. Loose source routing. */
|
|
gateway = opt_gateways;
|
|
optbase[IPOPT_OPTVAL] = IPOPT_LSRR;
|
|
optbase[IPOPT_OLEN] = IPOPT_MINOFF - 1; /* No payload yet. */
|
|
optbase[IPOPT_OFFSET] = IPOPT_MINOFF; /* Empty payload. */
|
|
|
|
/* Traverse the gateway list, inserting
|
|
* addresses in the stated order. Take
|
|
* care not to overflow available space.
|
|
*/
|
|
while (gateway && *gateway
|
|
&& optbase[IPOPT_OFFSET]
|
|
< (int) (MAX_IPOPTLEN - sizeof (struct in_addr)))
|
|
{
|
|
int rc;
|
|
char *p;
|
|
|
|
p = strpbrk (gateway, " ,;:");
|
|
if (p)
|
|
*p++ = '\0';
|
|
|
|
rc = getaddrinfo (gateway, NULL, &hints, &res);
|
|
if (rc)
|
|
error (EXIT_FAILURE, errno, "gateway `%s' %s",
|
|
gateway, gai_strerror (rc));
|
|
|
|
/* Put target into next unused slot. */
|
|
memcpy (optbase + optbase[IPOPT_OLEN],
|
|
&((struct sockaddr_in *) res->ai_addr)->sin_addr,
|
|
sizeof (struct in_addr));
|
|
|
|
freeaddrinfo (res);
|
|
|
|
/* Option gained in length. */
|
|
optbase[IPOPT_OLEN] += sizeof (struct in_addr);
|
|
|
|
gateway = p;
|
|
}
|
|
|
|
if (gateway && *gateway)
|
|
error (EXIT_FAILURE, 0, "too many gateways specified");
|
|
|
|
/* Append the final destination. */
|
|
memcpy (optbase + optbase[IPOPT_OLEN],
|
|
&to->sin_addr, sizeof (to->sin_addr));
|
|
optbase[IPOPT_OLEN] += sizeof (to->sin_addr);
|
|
|
|
/* 2. There is an implicit IPOPT_EOL after
|
|
* IPOPT_LSRR, ensured by the call to memset().
|
|
* Use it!
|
|
*/
|
|
len_ip_opts = optbase[IPOPT_OLEN] + 1;
|
|
}
|
|
#endif /* IP_OPTIONS */
|
|
}
|