2016-11-25 17:18:40 +01:00
|
|
|
/*
|
|
|
|
* This file is part of the UCB release of Plan 9. It is subject to the license
|
|
|
|
* terms in the LICENSE file found in the top-level directory of this
|
|
|
|
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
|
|
* part of the UCB release of Plan 9, including this file, may be copied,
|
|
|
|
* modified, propagated, or distributed except according to the terms contained
|
|
|
|
* in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <u.h>
|
2017-04-19 23:33:14 +02:00
|
|
|
#include <lib9.h>
|
2016-11-25 17:18:40 +01:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <bio.h>
|
|
|
|
#include <ndb.h>
|
|
|
|
#include <ip.h>
|
|
|
|
#include "icmp.h"
|
|
|
|
|
|
|
|
enum{
|
|
|
|
Maxstring= 128,
|
|
|
|
Maxpath= 256,
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct DS DS;
|
|
|
|
struct DS {
|
|
|
|
/* dial string */
|
|
|
|
char buf[Maxstring];
|
|
|
|
char *netdir;
|
|
|
|
char *proto;
|
|
|
|
char *rem;
|
|
|
|
};
|
|
|
|
|
|
|
|
char *argv0;
|
|
|
|
int debug;
|
|
|
|
|
|
|
|
void histogram(int32_t *t, int n, int buckets, int32_t lo,
|
|
|
|
int32_t hi);
|
|
|
|
|
|
|
|
void
|
|
|
|
usage(void)
|
|
|
|
{
|
|
|
|
fprint(2,
|
|
|
|
"usage: %s [-n][-a tries][-h buckets][-t ttl][-x net] [protocol!]destination\n",
|
|
|
|
argv0);
|
|
|
|
exits("usage");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
csquery(DS *ds, char *clone, char *dest)
|
|
|
|
{
|
|
|
|
int n, fd;
|
|
|
|
char *p, buf[Maxstring];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* open connection server
|
|
|
|
*/
|
|
|
|
snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
|
|
|
|
fd = open(buf, ORDWR);
|
|
|
|
if(fd < 0){
|
|
|
|
if(!isdigit(*dest)){
|
|
|
|
werrstr("can't translate");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no connection server, don't translate */
|
|
|
|
snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
|
|
|
|
strcpy(dest, ds->rem);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ask connection server to translate
|
|
|
|
*/
|
|
|
|
sprint(buf, "%s!%s", ds->proto, ds->rem);
|
|
|
|
if(write(fd, buf, strlen(buf)) < 0){
|
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get an address.
|
|
|
|
*/
|
|
|
|
seek(fd, 0, 0);
|
|
|
|
n = read(fd, buf, sizeof(buf) - 1);
|
|
|
|
close(fd);
|
|
|
|
if(n <= 0){
|
|
|
|
werrstr("problem with cs");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[n] = 0;
|
|
|
|
p = strchr(buf, ' ');
|
|
|
|
if(p == 0){
|
|
|
|
werrstr("problem with cs");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*p++ = 0;
|
|
|
|
strcpy(clone, buf);
|
|
|
|
strcpy(dest, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call the dns process and have it try to resolve the mx request
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dodnsquery(DS *ds, char *ip, char *dom)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
Ndbtuple *t, *nt;
|
|
|
|
|
|
|
|
p = strchr(ip, '!');
|
|
|
|
if(p)
|
|
|
|
*p = 0;
|
|
|
|
|
|
|
|
t = dnsquery(ds->netdir, ip, "ptr");
|
|
|
|
for(nt = t; nt != nil; nt = nt->entry)
|
|
|
|
if(strcmp(nt->attr, "dom") == 0){
|
|
|
|
strcpy(dom, nt->val);
|
|
|
|
ndbfree(t);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ndbfree(t);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for connection oriented protocols (il, tcp) we just need
|
|
|
|
* to try dialing. resending is up to it.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
tcpilprobe(int cfd, int dfd, char *dest, int interval)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
char msg[Maxstring];
|
|
|
|
|
|
|
|
USED(dfd);
|
|
|
|
|
|
|
|
n = snprint(msg, sizeof msg, "connect %s", dest);
|
|
|
|
alarm(interval);
|
|
|
|
n = write(cfd, msg, n);
|
|
|
|
alarm(0);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* for udp, we keep sending to an improbable port
|
|
|
|
* till we timeout or someone complains
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
udpprobe(int cfd, int dfd, char *dest, int interval)
|
|
|
|
{
|
|
|
|
int n, i, rv;
|
|
|
|
char msg[Maxstring];
|
|
|
|
char err[Maxstring];
|
|
|
|
|
|
|
|
seek(cfd, 0, 0);
|
|
|
|
n = snprint(msg, sizeof msg, "connect %s", dest);
|
|
|
|
if(write(cfd, msg, n)< 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
rv = -1;
|
|
|
|
for(i = 0; i < 3; i++){
|
|
|
|
alarm(interval/3);
|
|
|
|
if(write(dfd, "boo hoo ", 8) < 0)
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* a hangup due to an error looks like 3 eofs followed
|
|
|
|
* by a real error. this is a qio.c qbread() strangeness
|
|
|
|
* done for pipes.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
n = read(dfd, msg, sizeof(msg)-1);
|
|
|
|
} while(n == 0);
|
|
|
|
alarm(0);
|
|
|
|
if(n > 0){
|
|
|
|
rv = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
errstr(err, sizeof err);
|
|
|
|
if(strstr(err, "alarm") == 0){
|
|
|
|
werrstr(err);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
werrstr(err);
|
|
|
|
}
|
|
|
|
alarm(0);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MSG "traceroute probe"
|
|
|
|
#define MAGIC (0xdead)&0xFF
|
|
|
|
|
|
|
|
/* ICMPv4 only */
|
|
|
|
static int
|
|
|
|
icmpprobe(int cfd, int dfd, char *dest, int interval)
|
|
|
|
{
|
|
|
|
int x, i, n, len, rv;
|
|
|
|
char buf[512], err[Maxstring], msg[Maxstring];
|
|
|
|
Icmphdr *ip;
|
|
|
|
|
|
|
|
seek(cfd, 0, 0);
|
|
|
|
n = snprint(msg, sizeof msg, "connect %s", dest);
|
|
|
|
if(write(cfd, msg, n)< 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
rv = -1;
|
|
|
|
ip = (Icmphdr *)(buf + IPV4HDR_LEN);
|
|
|
|
for(i = 0; i < 3; i++){
|
|
|
|
alarm(interval/3);
|
|
|
|
ip->type = EchoRequest;
|
|
|
|
ip->code = 0;
|
|
|
|
strcpy((char*)ip->data, MSG);
|
|
|
|
ip->seq[0] = MAGIC;
|
|
|
|
ip->seq[1] = MAGIC>>8;
|
|
|
|
len = IPV4HDR_LEN + ICMP_HDRSIZE + sizeof(MSG);
|
|
|
|
|
|
|
|
/* send a request */
|
|
|
|
if(write(dfd, buf, len) < len)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* wait for reply */
|
|
|
|
n = read(dfd, buf, sizeof(buf));
|
|
|
|
alarm(0);
|
|
|
|
if(n < 0){
|
|
|
|
errstr(err, sizeof err);
|
|
|
|
if(strstr(err, "alarm") == 0){
|
|
|
|
werrstr(err);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
werrstr(err);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
x = (ip->seq[1]<<8) | ip->seq[0];
|
|
|
|
if(n >= len && ip->type == EchoReply && x == MAGIC &&
|
|
|
|
strcmp((char*)ip->data, MSG) == 0){
|
|
|
|
rv = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
alarm(0);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
catch(void *a, char *msg)
|
|
|
|
{
|
|
|
|
USED(a);
|
|
|
|
if(strstr(msg, "alarm"))
|
|
|
|
noted(NCONT);
|
|
|
|
else
|
|
|
|
noted(NDFLT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
call(DS *ds, char *clone, char *dest, int ttl, int32_t *interval)
|
|
|
|
{
|
|
|
|
int cfd, dfd, rv, n;
|
|
|
|
char msg[Maxstring];
|
|
|
|
char file[Maxstring];
|
|
|
|
int64_t start;
|
|
|
|
|
|
|
|
notify(catch);
|
|
|
|
|
|
|
|
/* start timing */
|
|
|
|
start = nsec()/1000;
|
|
|
|
rv = -1;
|
|
|
|
|
|
|
|
cfd = open(clone, ORDWR);
|
|
|
|
if(cfd < 0){
|
|
|
|
werrstr("%s: %r", clone);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
dfd = -1;
|
|
|
|
|
|
|
|
/* get conversation number */
|
|
|
|
n = read(cfd, msg, sizeof(msg)-1);
|
|
|
|
if(n <= 0)
|
|
|
|
goto out;
|
|
|
|
msg[n] = 0;
|
|
|
|
|
|
|
|
/* open data file */
|
|
|
|
sprint(file, "%s/%s/%s/data", ds->netdir, ds->proto, msg);
|
|
|
|
dfd = open(file, ORDWR);
|
|
|
|
if(dfd < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* set ttl */
|
|
|
|
if(ttl)
|
|
|
|
fprint(cfd, "ttl %d", ttl);
|
|
|
|
|
|
|
|
/* probe */
|
|
|
|
if(strcmp(ds->proto, "udp") == 0)
|
|
|
|
rv = udpprobe(cfd, dfd, dest, 3000);
|
|
|
|
else if(strcmp(ds->proto, "icmp") == 0)
|
|
|
|
rv = icmpprobe(cfd, dfd, dest, 3000);
|
|
|
|
else /* il and tcp */
|
|
|
|
rv = tcpilprobe(cfd, dfd, dest, 3000);
|
|
|
|
out:
|
|
|
|
/* turn off alarms */
|
|
|
|
alarm(0);
|
|
|
|
*interval = nsec()/1000 - start;
|
|
|
|
close(cfd);
|
|
|
|
close(dfd);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* parse a dial string. default netdir is /net.
|
|
|
|
* default proto is tcp.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
dial_string_parse(char *str, DS *ds)
|
|
|
|
{
|
|
|
|
char *p, *p2;
|
|
|
|
|
|
|
|
strncpy(ds->buf, str, Maxstring);
|
|
|
|
ds->buf[Maxstring-3] = 0;
|
|
|
|
|
|
|
|
p = strchr(ds->buf, '!');
|
|
|
|
if(p == 0) {
|
|
|
|
ds->netdir = 0;
|
|
|
|
ds->proto = "tcp";
|
|
|
|
ds->rem = ds->buf;
|
|
|
|
} else {
|
|
|
|
if(*ds->buf != '/'){
|
|
|
|
ds->netdir = 0;
|
|
|
|
ds->proto = ds->buf;
|
|
|
|
} else {
|
|
|
|
for(p2 = p; *p2 != '/'; p2--)
|
|
|
|
;
|
|
|
|
*p2++ = 0;
|
|
|
|
ds->netdir = ds->buf;
|
|
|
|
ds->proto = p2;
|
|
|
|
}
|
|
|
|
*p = 0;
|
|
|
|
ds->rem = p + 1;
|
|
|
|
}
|
|
|
|
if(strchr(ds->rem, '!') == 0)
|
|
|
|
strcat(ds->rem, "!32767");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int buckets, ttl, j, done, tries, notranslate;
|
|
|
|
uintptr_t lo, hi, sum, x;
|
|
|
|
int32_t *t;
|
|
|
|
char *net, *p;
|
|
|
|
char clone[Maxpath], dest[Maxstring], hop[Maxstring], dom[Maxstring];
|
|
|
|
char err[Maxstring];
|
|
|
|
DS ds;
|
|
|
|
|
|
|
|
buckets = 0;
|
|
|
|
tries = 3;
|
|
|
|
notranslate = 0;
|
|
|
|
net = "/net";
|
|
|
|
ttl = 1;
|
|
|
|
ARGBEGIN{
|
|
|
|
case 'a':
|
|
|
|
tries = atoi(EARGF(usage()));
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
debug++;
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
buckets = atoi(EARGF(usage()));
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
notranslate++;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
ttl = atoi(EARGF(usage()));
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
net = EARGF(usage());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
}ARGEND;
|
|
|
|
|
|
|
|
if(argc < 1)
|
|
|
|
usage();
|
|
|
|
|
|
|
|
t = malloc(tries*sizeof(uintptr_t));
|
|
|
|
|
|
|
|
dial_string_parse(argv[0], &ds);
|
|
|
|
|
|
|
|
if(ds.netdir == 0)
|
|
|
|
ds.netdir = net;
|
|
|
|
if(csquery(&ds, clone, dest) < 0){
|
|
|
|
fprint(2, "%s: %s: %r\n", argv0, argv[0]);
|
|
|
|
exits(0);
|
|
|
|
}
|
|
|
|
print("trying %s/%s!%s\n\n", ds.netdir, ds.proto, dest);
|
|
|
|
print(" round trip times in µs\n");
|
|
|
|
print(" low avg high\n");
|
|
|
|
print(" --------------------------\n");
|
|
|
|
|
|
|
|
done = 0;
|
|
|
|
for(; ttl < 32; ttl++){
|
|
|
|
for(j = 0; j < tries; j++){
|
|
|
|
if(call(&ds, clone, dest, ttl, &t[j]) >= 0){
|
|
|
|
if(debug)
|
|
|
|
print("%ld %s\n", t[j], dest);
|
|
|
|
strcpy(hop, dest);
|
|
|
|
done = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
errstr(err, sizeof err);
|
|
|
|
if(strstr(err, "refused")){
|
|
|
|
strcpy(hop, dest);
|
|
|
|
p = strchr(hop, '!');
|
|
|
|
if(p)
|
|
|
|
*p = 0;
|
|
|
|
done = 1;
|
|
|
|
} else if(strstr(err, "unreachable")){
|
|
|
|
snprint(hop, sizeof(hop), "%s", err);
|
|
|
|
p = strchr(hop, '!');
|
|
|
|
if(p)
|
|
|
|
*p = 0;
|
|
|
|
done = 1;
|
|
|
|
} else if(strncmp(err, "ttl exceeded at ", 16) == 0)
|
|
|
|
strcpy(hop, err+16);
|
|
|
|
else {
|
|
|
|
strcpy(hop, "*");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(debug)
|
|
|
|
print("%ld %s\n", t[j], hop);
|
|
|
|
}
|
|
|
|
if(strcmp(hop, "*") == 0){
|
|
|
|
print("*\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
lo = 10000000;
|
|
|
|
hi = 0;
|
|
|
|
sum = 0;
|
|
|
|
for(j = 0; j < tries; j++){
|
|
|
|
x = t[j];
|
|
|
|
sum += x;
|
|
|
|
if(x < lo)
|
|
|
|
lo = x;
|
|
|
|
if(x > hi)
|
|
|
|
hi = x;
|
|
|
|
}
|
|
|
|
if(notranslate == 1 || dodnsquery(&ds, hop, dom) < 0)
|
|
|
|
dom[0] = 0;
|
|
|
|
/* don't truncate: ipv6 addresses can be quite long */
|
|
|
|
print("%-18s %8ld %8ld %8ld %s\n", hop, lo, sum/tries, hi, dom);
|
|
|
|
if(buckets)
|
|
|
|
histogram(t, tries, buckets, lo, hi);
|
|
|
|
if(done)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
exits(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *order = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
|
|
|
|
void
|
|
|
|
histogram(int32_t *t, int n, int buckets, int32_t lo, int32_t hi)
|
|
|
|
{
|
|
|
|
int i, j, empty;
|
|
|
|
int32_t span;
|
|
|
|
static char *bar;
|
|
|
|
char *p;
|
|
|
|
char x[64];
|
|
|
|
|
|
|
|
if(bar == nil)
|
|
|
|
bar = malloc(n+1);
|
|
|
|
|
|
|
|
print("+++++++++++++++++++++++\n");
|
|
|
|
span = (hi-lo)/buckets;
|
|
|
|
span++;
|
|
|
|
empty = 0;
|
|
|
|
for(i = 0; i < buckets; i++){
|
|
|
|
p = bar;
|
|
|
|
for(j = 0; j < n; j++)
|
|
|
|
if(t[j] >= lo+i*span && t[j] <= lo+(i+1)*span)
|
|
|
|
*p++ = order[j];
|
|
|
|
*p = 0;
|
|
|
|
if(p != bar){
|
|
|
|
snprint(x, sizeof x, "[%ld-%ld]", lo+i*span, lo+(i+1)*span);
|
|
|
|
print("%-16s %s\n", x, bar);
|
|
|
|
empty = 0;
|
|
|
|
} else if(!empty){
|
|
|
|
print("...\n");
|
|
|
|
empty = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
print("+++++++++++++++++++++++\n");
|
|
|
|
}
|