第28章 原始套接字
28.5 ping程序
#define _GNU_SOURCE
#include <sys/time.h>
#include <sys/un.h>
#include <string.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/types.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#define BUFSIZE 1500
//#define IPV6
/* globals */
char sendbuf[BUFSIZE];
int datalen; /* # bytes of data following ICMP header */
char *host;
int nsent; /* add 1 for each sendto() */
pid_t pid; /* our PID */
int sockfd;
int verbose;
struct proto {
void (*fproc) (char *, ssize_t, struct msghdr *, struct timeval *);
void (*fsend) (void);
void (*finit) (void);
struct sockaddr *sasend; /* sockaddr{} for send, from getaddrinfo */
struct sockaddr *sarecv; /* sockaddr{} for receiving */
socklen_t salen; /* length of sockaddr{}s */
int icmpproto; /* IPPROTO_xxx value for ICMP */
} *pr;
#define MAXLINE 4096 /* max text line length */
int daemon_proc; /* set nonzero by daemon_init() */
void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
int errno_save, n;
char buf[MAXLINE + 1];
errno_save = errno; /* value caller might want printed */
#ifdef HAVE_VSNPRINTF
vsnprintf(buf, MAXLINE, fmt, ap); /* safe */
#else
vsprintf(buf, fmt, ap); /* not safe */
#endif
n = strlen(buf);
if (errnoflag)
snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
strcat(buf, "\n");
if (daemon_proc) {
syslog(level, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
void err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, LOG_ERR, fmt, ap);
va_end(ap);
exit(1);
}
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, LOG_ERR, fmt, ap);
va_end(ap);
exit(1);
}
char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
static char str[128]; /* Unix domain is largest */
switch (sa->sa_family) {
case AF_INET:{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))
== NULL)
return (NULL);
return (str);
}
#ifdef IPV6
case AF_INET6:{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
if (inet_ntop
(AF_INET6, &sin6->sin6_addr, str,
sizeof(str)) == NULL)
return (NULL);
return (str);
}
#endif
#ifdef AF_UNIX
case AF_UNIX:{
struct sockaddr_un *unp = (struct sockaddr_un *)sa;
/* OK to have no pathname bound to the socket: happens on
every connect() unless client calls bind() first. */
if (unp->sun_path[0] == 0)
strcpy(str, "(no pathname bound)");
else
snprintf(str, sizeof(str), "%s", unp->sun_path);
return (str);
}
#endif
#ifdef HAVE_SOCKADDR_DL_STRUCT
case AF_LINK:{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
if (sdl->sdl_nlen > 0)
snprintf(str, sizeof(str), "%*s",
sdl->sdl_nlen, &sdl->sdl_data[0]);
else
snprintf(str, sizeof(str), "AF_LINK, index=%d",
sdl->sdl_index);
return (str);
}
#endif
default:
snprintf(str, sizeof(str),
"sock_ntop_host: unknown AF_xxx: %d, len %d",
sa->sa_family, salen);
return (str);
}
return (NULL);
}
char *Sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
char *ptr;
if ((ptr = sock_ntop_host(sa, salen)) == NULL)
err_sys("sock_ntop_host error"); /* inet_ntop() sets errno */
return (ptr);
}
void tv_sub(struct timeval *out, struct timeval *in)
{
if ((out->tv_usec -= in->tv_usec) < 0) { /* out -= in */
--out->tv_sec;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
void proc_v4(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)
{
int hlen1, icmplen;
double rtt;
struct ip *ip;
struct icmp *icmp;
struct timeval *tvsend;
ip = (struct ip *)ptr; /* start of IP header */
hlen1 = ip->ip_hl << 2; /* length of IP header */
if (ip->ip_p != IPPROTO_ICMP)
return; /* not ICMP */
icmp = (struct icmp *)(ptr + hlen1); /* start of ICMP header */
if ((icmplen = len - hlen1) < 8)
return; /* malformed packet */
if (icmp->icmp_type == ICMP_ECHOREPLY) {
if (icmp->icmp_id != pid)
return; /* not a response to our ECHO_REQUEST */
if (icmplen < 16)
return; /* not enough data to use */
tvsend = (struct timeval *)icmp->icmp_data;
tv_sub(tvrecv, tvsend);
rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;
printf("%d bytes from %s: seq=%u, ttl=%d, rtt=%.3f ms\n",
icmplen, Sock_ntop_host(pr->sarecv, pr->salen),
icmp->icmp_seq, ip->ip_ttl, rtt);
} else if (verbose) {
printf(" %d bytes from %s: type = %d, code = %d\n",
icmplen, Sock_ntop_host(pr->sarecv, pr->salen),
icmp->icmp_type, icmp->icmp_code);
}
}
void Gettimeofday(struct timeval *tv, void *foo)
{
if (gettimeofday(tv, foo) == -1)
err_sys("gettimeofday error");
return;
}
uint16_t in_cksum(uint16_t * addr, int len)
{
int nleft = len;
uint32_t sum = 0;
uint16_t *w = addr;
uint16_t answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* 4mop up an odd byte, if necessary */
if (nleft == 1) {
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer;
}
/* 4add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
void
Sendto(int fd, const void *ptr, size_t nbytes, int flags,
const struct sockaddr *sa, socklen_t salen)
{
if (sendto(fd, ptr, nbytes, flags, sa, salen) != (ssize_t) nbytes)
err_sys("sendto error");
}
void send_v4(void)
{
int len;
struct icmp *icmp;
icmp = (struct icmp *)sendbuf;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_id = pid;
icmp->icmp_seq = nsent++;
memset(icmp->icmp_data, 0xa5, datalen); /* fill with pattern */
Gettimeofday((struct timeval *)icmp->icmp_data, NULL);
len = 8 + datalen; /* checksum ICMP header and data */
icmp->icmp_cksum = 0;
icmp->icmp_cksum = in_cksum((u_short *) icmp, len);
Sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen);
}
int datalen = 56; /* data that goes with ICMP echo request */
typedef void Sigfunc(int); /* for signal handlers */
Sigfunc *signal(int signo, Sigfunc * func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */
#endif
} else {
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */
#endif
}
if (sigaction(signo, &act, &oact) < 0)
return (SIG_ERR);
return (oact.sa_handler);
}
Sigfunc *Signal(int signo, Sigfunc * func)
{ /* for our signal() function */
Sigfunc *sigfunc;
if ((sigfunc = signal(signo, func)) == SIG_ERR)
err_sys("signal error");
return (sigfunc);
}
struct addrinfo *Host_serv(const char *host, const char *serv, int family,
int socktype)
{
int n;
struct addrinfo hints, *res;
bzero(&hints, sizeof(struct addrinfo));
hints.ai_flags = AI_CANONNAME; /* always return canonical name */
hints.ai_family = family; /* 0, AF_INET, AF_INET6, etc. */
hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
err_quit("host_serv error for %s, %s: %s",
(host == NULL) ? "(no hostname)" : host,
(serv == NULL) ? "(no service name)" : serv,
gai_strerror(n));
return (res); /* return pointer to first on linked list */
}
void *Calloc(size_t n, size_t size)
{
void *ptr;
if ((ptr = calloc(n, size)) == NULL)
err_sys("calloc error");
return (ptr);
}
void sig_alrm(int signo)
{
(*pr->fsend) ();
alarm(1);
return;
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
void readloop(void)
{
int size;
char recvbuf[BUFSIZE];
char controlbuf[BUFSIZE];
struct msghdr msg;
struct iovec iov;
ssize_t n;
struct timeval tval;
sockfd = Socket(pr->sasend->sa_family, SOCK_RAW, pr->icmpproto);
setuid(getuid()); /* don't need special permissions any more */
if (pr->finit)
(*pr->finit) ();
size = 60 * 1024; /* OK if setsockopt fails */
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
sig_alrm(SIGALRM); /* send first packet */
iov.iov_base = recvbuf;
iov.iov_len = sizeof(recvbuf);
msg.msg_name = pr->sarecv;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = controlbuf;
for (;;) {
msg.msg_namelen = pr->salen;
msg.msg_controllen = sizeof(controlbuf);
n = recvmsg(sockfd, &msg, 0);
if (n < 0) {
if (errno == EINTR)
continue;
else
err_sys("recvmsg error");
}
Gettimeofday(&tval, NULL);
(*pr->fproc) (recvbuf, n, &msg, &tval);
}
}
void proc_v6(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)
{
#ifdef IPV6
double rtt;
struct icmp6_hdr *icmp6;
struct timeval *tvsend;
struct cmsghdr *cmsg;
int hlim;
icmp6 = (struct icmp6_hdr *)ptr;
if (len < 8)
return; /* malformed packet */
if (icmp6->icmp6_type == ICMP6_ECHO_REPLY) {
if (icmp6->icmp6_id != pid)
return; /* not a response to our ECHO_REQUEST */
if (len < 16)
return; /* not enough data to use */
tvsend = (struct timeval *)(icmp6 + 1);
tv_sub(tvrecv, tvsend);
rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;
hlim = -1;
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IPV6
&& cmsg->cmsg_type == IPV6_HOPLIMIT) {
hlim = *(u_int32_t *) CMSG_DATA(cmsg);
break;
}
}
printf("%d bytes from %s: seq=%u, hlim=",
len, Sock_ntop_host(pr->sarecv, pr->salen),
icmp6->icmp6_seq);
if (hlim == -1)
printf("???"); /* ancillary data missing */
else
printf("%d", hlim);
printf(", rtt=%.3f ms\n", rtt);
} else if (verbose) {
printf(" %d bytes from %s: type = %d, code = %d\n",
len, Sock_ntop_host(pr->sarecv, pr->salen),
icmp6->icmp6_type, icmp6->icmp6_code);
}
#endif /* IPV6 */
}
void send_v6()
{
#ifdef IPV6
int len;
struct icmp6_hdr *icmp6;
icmp6 = (struct icmp6_hdr *)sendbuf;
icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
icmp6->icmp6_code = 0;
icmp6->icmp6_id = pid;
icmp6->icmp6_seq = nsent++;
memset((icmp6 + 1), 0xa5, datalen); /* fill with pattern */
Gettimeofday((struct timeval *)(icmp6 + 1), NULL);
len = 8 + datalen; /* 8-byte ICMPv6 header */
Sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen);
/* 4kernel calculates and stores checksum for us */
#endif /* IPV6 */
}
void init_v6()
{
#ifdef IPV6
int on = 1;
if (verbose == 0) {
/* install a filter that only passes ICMP6_ECHO_REPLY unless verbose */
struct icmp6_filter myfilt;
ICMP6_FILTER_SETBLOCKALL(&myfilt);
ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &myfilt);
setsockopt(sockfd, IPPROTO_IPV6, ICMP6_FILTER, &myfilt,
sizeof(myfilt));
/* ignore error return; the filter is an optimization */
}
/* ignore error returned below; we just won't receive the hop limit */
#ifdef IPV6_RECVHOPLIMIT
/* RFC 3542 */
setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
#else
/* RFC 2292 */
setsockopt(sockfd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));
#endif
#endif
}
struct proto proto_v4 = { proc_v4, send_v4, NULL, NULL, NULL, 0, IPPROTO_ICMP };
#ifdef IPV6
struct proto proto_v6 =
{ proc_v6, send_v6, init_v6, NULL, NULL, 0, IPPROTO_ICMPV6 };
#endif
int main(int argc, char **argv)
{
int c;
struct addrinfo *ai;
char *h;
opterr = 0; /* don't want getopt() writing to stderr */
while ((c = getopt(argc, argv, "v")) != -1) {
switch (c) {
case 'v':
verbose++;
break;
case '?':
err_quit("unrecognized option: %c", c);
}
}
if (optind != argc - 1)
err_quit("usage: ping [ -v ] <hostname>");
host = argv[optind];
pid = getpid() & 0xffff; /* ICMP ID field is 16 bits */
Signal(SIGALRM, sig_alrm);
ai = Host_serv(host, NULL, 0, 0);
h = Sock_ntop_host(ai->ai_addr, ai->ai_addrlen);
printf("PING %s (%s): %d data bytes\n",
ai->ai_canonname ? ai->ai_canonname : h, h, datalen);
/* 4initialize according to protocol */
if (ai->ai_family == AF_INET) {
pr = &proto_v4;
#ifdef IPV6
} else if (ai->ai_family == AF_INET6) {
pr = &proto_v6;
if (IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6 *)
ai->ai_addr)->sin6_addr)))
err_quit("cannot ping IPv4-mapped IPv6 address");
#endif
} else
err_quit("unknown address family %d", ai->ai_family);
pr->sasend = ai->ai_addr;
pr->sarecv = Calloc(1, ai->ai_addrlen);
pr->salen = ai->ai_addrlen;
readloop();
exit(0);
}
28.6 traceroute程序
#define _GNU_SOURCE
#include <sys/time.h>
#include <sys/un.h>
#include <string.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/types.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/udp.h>
#define BUFSIZE 1500
#define BUFSIZE 1500
struct rec { /* format of outgoing UDP data */
u_short rec_seq; /* sequence number */
u_short rec_ttl; /* TTL packet left with */
struct timeval rec_tv; /* time packet left */
};
/* globals */
char recvbuf[BUFSIZE];
char sendbuf[BUFSIZE];
int datalen; /* # bytes of data following ICMP header */
char *host;
u_short sport, dport;
int nsent; /* add 1 for each sendto() */
pid_t pid; /* our PID */
int probe, nprobes;
int sendfd, recvfd; /* send on UDP sock, read on raw ICMP sock */
int ttl, max_ttl;
int verbose;
/* function prototypes */
struct proto {
const char *(*icmpcode) (int);
int (*recv) (int, struct timeval *);
struct sockaddr *sasend; /* sockaddr{} for send, from getaddrinfo */
struct sockaddr *sarecv; /* sockaddr{} for receiving */
struct sockaddr *salast; /* last sockaddr{} for receiving */
struct sockaddr *sabind; /* sockaddr{} for binding source port */
socklen_t salen; /* length of sockaddr{}s */
int icmpproto; /* IPPROTO_xxx value for ICMP */
int ttllevel; /* setsockopt() level to set TTL */
int ttloptname; /* setsockopt() name to set TTL */
} *pr;
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
//#define IPV6
/* globals */
char sendbuf[BUFSIZE];
int datalen; /* # bytes of data following ICMP header */
char *host;
int nsent; /* add 1 for each sendto() */
pid_t pid; /* our PID */
int sockfd;
int verbose;
#define MAXLINE 4096 /* max text line length */
int daemon_proc; /* set nonzero by daemon_init() */
void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
int errno_save, n;
char buf[MAXLINE + 1];
errno_save = errno; /* value caller might want printed */
#ifdef HAVE_VSNPRINTF
vsnprintf(buf, MAXLINE, fmt, ap); /* safe */
#else
vsprintf(buf, fmt, ap); /* not safe */
#endif
n = strlen(buf);
if (errnoflag)
snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
strcat(buf, "\n");
if (daemon_proc) {
syslog(level, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
void err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, LOG_ERR, fmt, ap);
va_end(ap);
exit(1);
}
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, LOG_ERR, fmt, ap);
va_end(ap);
exit(1);
}
char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
static char str[128]; /* Unix domain is largest */
switch (sa->sa_family) {
case AF_INET:{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))
== NULL)
return (NULL);
return (str);
}
#ifdef IPV6
case AF_INET6:{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
if (inet_ntop
(AF_INET6, &sin6->sin6_addr, str,
sizeof(str)) == NULL)
return (NULL);
return (str);
}
#endif
#ifdef AF_UNIX
case AF_UNIX:{
struct sockaddr_un *unp = (struct sockaddr_un *)sa;
/* OK to have no pathname bound to the socket: happens on
every connect() unless client calls bind() first. */
if (unp->sun_path[0] == 0)
strcpy(str, "(no pathname bound)");
else
snprintf(str, sizeof(str), "%s", unp->sun_path);
return (str);
}
#endif
#ifdef HAVE_SOCKADDR_DL_STRUCT
case AF_LINK:{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
if (sdl->sdl_nlen > 0)
snprintf(str, sizeof(str), "%*s",
sdl->sdl_nlen, &sdl->sdl_data[0]);
else
snprintf(str, sizeof(str), "AF_LINK, index=%d",
sdl->sdl_index);
return (str);
}
#endif
default:
snprintf(str, sizeof(str),
"sock_ntop_host: unknown AF_xxx: %d, len %d",
sa->sa_family, salen);
return (str);
}
return (NULL);
}
char *Sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
char *ptr;
if ((ptr = sock_ntop_host(sa, salen)) == NULL)
err_sys("sock_ntop_host error"); /* inet_ntop() sets errno */
return (ptr);
}
void Gettimeofday(struct timeval *tv, void *foo)
{
if (gettimeofday(tv, foo) == -1)
err_sys("gettimeofday error");
return;
}
uint16_t in_cksum(uint16_t * addr, int len)
{
int nleft = len;
uint32_t sum = 0;
uint16_t *w = addr;
uint16_t answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* 4mop up an odd byte, if necessary */
if (nleft == 1) {
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer;
}
/* 4add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
void
Sendto(int fd, const void *ptr, size_t nbytes, int flags,
const struct sockaddr *sa, socklen_t salen)
{
if (sendto(fd, ptr, nbytes, flags, sa, salen) != (ssize_t) nbytes)
err_sys("sendto error");
}
int datalen = 56; /* data that goes with ICMP echo request */
typedef void Sigfunc(int); /* for signal handlers */
Sigfunc *signal(int signo, Sigfunc * func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */
#endif
} else {
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */
#endif
}
if (sigaction(signo, &act, &oact) < 0)
return (SIG_ERR);
return (oact.sa_handler);
}
Sigfunc *Signal(int signo, Sigfunc * func)
{ /* for our signal() function */
Sigfunc *sigfunc;
if ((sigfunc = signal(signo, func)) == SIG_ERR)
err_sys("signal error");
return (sigfunc);
}
struct addrinfo *Host_serv(const char *host, const char *serv, int family,
int socktype)
{
int n;
struct addrinfo hints, *res;
bzero(&hints, sizeof(struct addrinfo));
hints.ai_flags = AI_CANONNAME; /* always return canonical name */
hints.ai_family = family; /* 0, AF_INET, AF_INET6, etc. */
hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
err_quit("host_serv error for %s, %s: %s",
(host == NULL) ? "(no hostname)" : host,
(serv == NULL) ? "(no service name)" : serv,
gai_strerror(n));
return (res); /* return pointer to first on linked list */
}
void *Calloc(size_t n, size_t size)
{
void *ptr;
if ((ptr = calloc(n, size)) == NULL)
err_sys("calloc error");
return (ptr);
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
const char *icmpcode_v4(int code)
{
static char errbuf[100];
switch (code) {
case 0:
return ("network unreachable");
case 1:
return ("host unreachable");
case 2:
return ("protocol unreachable");
case 3:
return ("port unreachable");
case 4:
return ("fragmentation required but DF bit set");
case 5:
return ("source route failed");
case 6:
return ("destination network unknown");
case 7:
return ("destination host unknown");
case 8:
return ("source host isolated (obsolete)");
case 9:
return ("destination network administratively prohibited");
case 10:
return ("destination host administratively prohibited");
case 11:
return ("network unreachable for TOS");
case 12:
return ("host unreachable for TOS");
case 13:
return
("communication administratively prohibited by filtering");
case 14:
return ("host recedence violation");
case 15:
return ("precedence cutoff in effect");
default:
sprintf(errbuf, "[unknown code %d]", code);
return errbuf;
}
}
int gotalarm;
void sig_alrm(int signo)
{
gotalarm = 1; /* set flag to note that alarm occurred */
return; /* and interrupt the recvfrom() */
}
int recv_v4(int seq, struct timeval *tv)
{
int hlen1, ret;
unsigned hlen2, icmplen;
socklen_t len;
ssize_t n;
struct ip *ip, *hip;
struct icmp *icmp;
struct udphdr *udp;
gotalarm = 0;
alarm(3);
for (;;) {
if (gotalarm)
return (-3); /* alarm expired */
len = pr->salen;
n = recvfrom(recvfd, recvbuf, sizeof(recvbuf), 0, pr->sarecv,
&len);
if (n < 0) {
if (errno == EINTR)
continue;
else
err_sys("recvfrom error");
}
ip = (struct ip *)recvbuf; /* start of IP header */
hlen1 = ip->ip_hl << 2; /* length of IP header */
icmp = (struct icmp *)(recvbuf + hlen1); /* start of ICMP header */
if ((icmplen = n - hlen1) < 8)
continue; /* not enough to look at ICMP header */
if (icmp->icmp_type == ICMP_TIMXCEED &&
icmp->icmp_code == ICMP_TIMXCEED_INTRANS) {
if (icmplen < 8 + sizeof(struct ip))
continue; /* not enough data to look at inner IP */
hip = (struct ip *)(recvbuf + hlen1 + 8);
hlen2 = hip->ip_hl << 2;
if (icmplen < 8 + hlen2 + 4)
continue; /* not enough data to look at UDP ports */
udp = (struct udphdr *)(recvbuf + hlen1 + 8 + hlen2);
if (hip->ip_p == IPPROTO_UDP &&
udp->source == htons(sport) &&
udp->dest == htons(dport + seq)) {
ret = -2; /* we hit an intermediate router */
break;
}
} else if (icmp->icmp_type == ICMP_UNREACH) {
if (icmplen < 8 + sizeof(struct ip))
continue; /* not enough data to look at inner IP */
hip = (struct ip *)(recvbuf + hlen1 + 8);
hlen2 = hip->ip_hl << 2;
if (icmplen < 8 + hlen2 + 4)
continue; /* not enough data to look at UDP ports */
udp = (struct udphdr *)(recvbuf + hlen1 + 8 + hlen2);
if (hip->ip_p == IPPROTO_UDP &&
udp->source == htons(sport) &&
udp->dest == htons(dport + seq)) {
if (icmp->icmp_code == ICMP_UNREACH_PORT)
ret = -1; /* have reached destination */
else
ret = icmp->icmp_code; /* 0, 1, 2, ... */
break;
}
}
if (verbose) {
printf(" (from %s: type = %d, code = %d)\n",
Sock_ntop_host(pr->sarecv, pr->salen),
icmp->icmp_type, icmp->icmp_code);
}
/* Some other ICMP error, recvfrom() again */
}
alarm(0); /* don't leave alarm running */
Gettimeofday(tv, NULL); /* get time of packet arrival */
return (ret);
}
struct proto proto_v4 = { icmpcode_v4, recv_v4, NULL, NULL, NULL, NULL, 0,
IPPROTO_ICMP, IPPROTO_IP, IP_TTL
};
const char *icmpcode_v6(int code)
{
#ifdef IPV6
static char errbuf[100];
switch (code) {
case ICMP6_DST_UNREACH_NOROUTE:
return ("no route to host");
case ICMP6_DST_UNREACH_ADMIN:
return ("administratively prohibited");
case ICMP6_DST_UNREACH_ADDR:
return ("address unreachable");
case ICMP6_DST_UNREACH_NOPORT:
return ("port unreachable");
default:
sprintf(errbuf, "[unknown code %d]", code);
return errbuf;
}
#endif
}
int recv_v6(int seq, struct timeval *tv)
{
#ifdef IPV6
int ret;
unsigned hlen2, icmp6len;
ssize_t n;
socklen_t len;
struct ip6_hdr *hip6;
struct icmp6_hdr *icmp6;
struct udphdr *udp;
gotalarm = 0;
alarm(3);
for (;;) {
if (gotalarm)
return (-3); /* alarm expired */
len = pr->salen;
n = recvfrom(recvfd, recvbuf, sizeof(recvbuf), 0, pr->sarecv,
&len);
if (n < 0) {
if (errno == EINTR)
continue;
else
err_sys("recvfrom error");
}
icmp6 = (struct icmp6_hdr *)recvbuf; /* ICMP header */
if ((icmp6len = n) < 8)
continue; /* not enough to look at ICMP header */
if (icmp6->icmp6_type == ICMP6_TIME_EXCEEDED &&
icmp6->icmp6_code == ICMP6_TIME_EXCEED_TRANSIT) {
if (icmp6len < 8 + sizeof(struct ip6_hdr) + 4)
continue; /* not enough data to look at inner header */
hip6 = (struct ip6_hdr *)(recvbuf + 8);
hlen2 = sizeof(struct ip6_hdr);
udp = (struct udphdr *)(recvbuf + 8 + hlen2);
if (hip6->ip6_nxt == IPPROTO_UDP &&
udp->source == htons(sport) &&
udp->dest == htons(dport + seq))
ret = -2; /* we hit an intermediate router */
break;
} else if (icmp6->icmp6_type == ICMP6_DST_UNREACH) {
if (icmp6len < 8 + sizeof(struct ip6_hdr) + 4)
continue; /* not enough data to look at inner header */
hip6 = (struct ip6_hdr *)(recvbuf + 8);
hlen2 = sizeof(struct ip6_hdr);
udp = (struct udphdr *)(recvbuf + 8 + hlen2);
if (hip6->ip6_nxt == IPPROTO_UDP &&
udp->source == htons(sport) &&
udp->dest == htons(dport + seq)) {
if (icmp6->icmp6_code ==
ICMP6_DST_UNREACH_NOPORT)
ret = -1; /* have reached destination */
else
ret = icmp6->icmp6_code; /* 0, 1, 2, ... */
break;
}
} else if (verbose) {
printf(" (from %s: type = %d, code = %d)\n",
Sock_ntop_host(pr->sarecv, pr->salen),
icmp6->icmp6_type, icmp6->icmp6_code);
}
/* Some other ICMP error, recvfrom() again */
}
alarm(0); /* don't leave alarm running */
Gettimeofday(tv, NULL); /* get time of packet arrival */
return (ret);
#endif
}
#ifdef IPV6
struct proto proto_v6 = { icmpcode_v6, recv_v6, NULL, NULL, NULL, NULL, 0,
IPPROTO_ICMPV6, IPPROTO_IPV6, IPV6_UNICAST_HOPS
};
#endif
int max_ttl = 30;
int nprobes = 3;
u_short dport = 32768 + 666;
void sock_set_port(struct sockaddr *sa, socklen_t salen, int port)
{
switch (sa->sa_family) {
case AF_INET:{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
sin->sin_port = port;
return;
}
#ifdef IPV6
case AF_INET6:{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
sin6->sin6_port = port;
return;
}
#endif
}
return;
}
void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (bind(fd, sa, salen) < 0)
err_sys("bind error");
}
void Setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen)
{
if (setsockopt(fd, level, optname, optval, optlen) < 0)
err_sys("setsockopt error");
}
int sock_cmp_addr(const struct sockaddr *sa1, const struct sockaddr *sa2,
socklen_t salen)
{
if (sa1->sa_family != sa2->sa_family)
return (-1);
switch (sa1->sa_family) {
case AF_INET:{
return (memcmp(&((struct sockaddr_in *)sa1)->sin_addr,
&((struct sockaddr_in *)sa2)->sin_addr,
sizeof(struct in_addr)));
}
#ifdef IPV6
case AF_INET6:{
return (memcmp(&((struct sockaddr_in6 *)sa1)->sin6_addr,
&((struct sockaddr_in6 *)sa2)->sin6_addr,
sizeof(struct in6_addr)));
}
#endif
#ifdef AF_UNIX
case AF_UNIX:{
return (strcmp(((struct sockaddr_un *)sa1)->sun_path,
((struct sockaddr_un *)sa2)->sun_path));
}
#endif
#ifdef HAVE_SOCKADDR_DL_STRUCT
case AF_LINK:{
return (-1); /* no idea what to compare here ? */
}
#endif
}
return (-1);
}
void tv_sub(struct timeval *out, struct timeval *in)
{
if ((out->tv_usec -= in->tv_usec) < 0) { /* out -= in */
--out->tv_sec;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
void traceloop(void)
{
int seq, code, done;
double rtt;
struct rec *rec;
struct timeval tvrecv;
recvfd = Socket(pr->sasend->sa_family, SOCK_RAW, pr->icmpproto);
setuid(getuid()); /* don't need special permissions anymore */
#ifdef IPV6
if (pr->sasend->sa_family == AF_INET6 && verbose == 0) {
struct icmp6_filter myfilt;
ICMP6_FILTER_SETBLOCKALL(&myfilt);
ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &myfilt);
ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &myfilt);
setsockopt(recvfd, IPPROTO_IPV6, ICMP6_FILTER,
&myfilt, sizeof(myfilt));
}
#endif
sendfd = Socket(pr->sasend->sa_family, SOCK_DGRAM, 0);
pr->sabind->sa_family = pr->sasend->sa_family;
sport = (getpid() & 0xffff) | 0x8000; /* our source UDP port # */
sock_set_port(pr->sabind, pr->salen, htons(sport));
Bind(sendfd, pr->sabind, pr->salen);
sig_alrm(SIGALRM);
seq = 0;
done = 0;
for (ttl = 1; ttl <= max_ttl && done == 0; ttl++) {
Setsockopt(sendfd, pr->ttllevel, pr->ttloptname, &ttl,
sizeof(int));
bzero(pr->salast, pr->salen);
printf("%2d ", ttl);
fflush(stdout);
for (probe = 0; probe < nprobes; probe++) {
rec = (struct rec *)sendbuf;
rec->rec_seq = ++seq;
rec->rec_ttl = ttl;
Gettimeofday(&rec->rec_tv, NULL);
sock_set_port(pr->sasend, pr->salen,
htons(dport + seq));
Sendto(sendfd, sendbuf, datalen, 0, pr->sasend,
pr->salen);
if ((code = (*pr->recv) (seq, &tvrecv)) == -3)
printf(" *"); /* timeout, no reply */
else {
char str[NI_MAXHOST];
if (sock_cmp_addr
(pr->sarecv, pr->salast, pr->salen) != 0) {
if (getnameinfo
(pr->sarecv, pr->salen, str,
sizeof(str), NULL, 0, 0) == 0)
printf(" %s (%s)", str,
Sock_ntop_host
(pr->sarecv, pr->salen));
else
printf(" %s",
Sock_ntop_host
(pr->sarecv, pr->salen));
memcpy(pr->salast, pr->sarecv,
pr->salen);
}
tv_sub(&tvrecv, &rec->rec_tv);
rtt =
tvrecv.tv_sec * 1000.0 +
tvrecv.tv_usec / 1000.0;
printf(" %.3f ms", rtt);
if (code == -1) /* port unreachable; at destination */
done++;
else if (code >= 0)
printf(" (ICMP %s)",
(*pr->icmpcode) (code));
}
fflush(stdout);
}
printf("\n");
}
}
int main(int argc, char **argv)
{
int c;
struct addrinfo *ai;
char *h;
opterr = 0; /* don't want getopt() writing to stderr */
while ((c = getopt(argc, argv, "m:v")) != -1) {
switch (c) {
case 'm':
if ((max_ttl = atoi(optarg)) <= 1)
err_quit("invalid -m value");
break;
case 'v':
verbose++;
break;
case '?':
err_quit("unrecognized option: %c", c);
}
}
if (optind != argc - 1)
err_quit("usage: traceroute [ -m <maxttl> -v ] <hostname>");
host = argv[optind];
pid = getpid();
Signal(SIGALRM, sig_alrm);
ai = Host_serv(host, NULL, 0, 0);
h = Sock_ntop_host(ai->ai_addr, ai->ai_addrlen);
printf("traceroute to %s (%s): %d hops max, %d data bytes\n",
ai->ai_canonname ? ai->ai_canonname : h, h, max_ttl, datalen);
/* initialize according to protocol */
if (ai->ai_family == AF_INET) {
pr = &proto_v4;
#ifdef IPV6
} else if (ai->ai_family == AF_INET6) {
pr = &proto_v6;
if (IN6_IS_ADDR_V4MAPPED
(&(((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr)))
err_quit("cannot traceroute IPv4-mapped IPv6 address");
#endif
} else
err_quit("unknown address family %d", ai->ai_family);
pr->sasend = ai->ai_addr; /* contains destination address */
pr->sarecv = Calloc(1, ai->ai_addrlen);
pr->salast = Calloc(1, ai->ai_addrlen);
pr->sabind = Calloc(1, ai->ai_addrlen);
pr->salen = ai->ai_addrlen;
traceloop();
exit(0);
}