From 698b29d4d20c67c2a72cc2e8d7c5993a824bf6b7 Mon Sep 17 00:00:00 2001 From: Philippe Houdoin Date: Tue, 27 Aug 2002 07:25:01 +0000 Subject: [PATCH] Moving arp, ifconfig, ping, route and traceroute dirs from /current/src/apps/. to /current/src/apps/bin/., where they belongs. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@893 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/bin/arp/Jamfile | 2 + src/apps/bin/arp/arp.c | 636 +++++++++++ src/apps/bin/ifconfig/Jamfile | 2 + src/apps/bin/ifconfig/ifconfig.c | 627 +++++++++++ src/apps/bin/ping/Jamfile | 2 + src/apps/bin/ping/ping.c | 1298 ++++++++++++++++++++++ src/apps/bin/route/Jamfile | 2 + src/apps/bin/route/keywords.c | 58 + src/apps/bin/route/keywords.h | 57 + src/apps/bin/route/route.c | 1498 ++++++++++++++++++++++++++ src/apps/bin/route/show.c | 377 +++++++ src/apps/bin/traceroute/Jamfile | 2 + src/apps/bin/traceroute/traceroute.c | 1030 ++++++++++++++++++ 13 files changed, 5591 insertions(+) create mode 100644 src/apps/bin/arp/Jamfile create mode 100644 src/apps/bin/arp/arp.c create mode 100644 src/apps/bin/ifconfig/Jamfile create mode 100644 src/apps/bin/ifconfig/ifconfig.c create mode 100644 src/apps/bin/ping/Jamfile create mode 100644 src/apps/bin/ping/ping.c create mode 100644 src/apps/bin/route/Jamfile create mode 100644 src/apps/bin/route/keywords.c create mode 100644 src/apps/bin/route/keywords.h create mode 100644 src/apps/bin/route/route.c create mode 100644 src/apps/bin/route/show.c create mode 100644 src/apps/bin/traceroute/Jamfile create mode 100644 src/apps/bin/traceroute/traceroute.c diff --git a/src/apps/bin/arp/Jamfile b/src/apps/bin/arp/Jamfile new file mode 100644 index 0000000000..d2f3206a02 --- /dev/null +++ b/src/apps/bin/arp/Jamfile @@ -0,0 +1,2 @@ +SubDir OBOS_TOP src apps bin arp ; + diff --git a/src/apps/bin/arp/arp.c b/src/apps/bin/arp/arp.c new file mode 100644 index 0000000000..47f3f6b1aa --- /dev/null +++ b/src/apps/bin/arp/arp.c @@ -0,0 +1,636 @@ +/* + * Copyright (c) 1984, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Sun Microsystems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * arp - display, set, and delete arp table entries + */ + +#include +#include +#include +//#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +//#include +//#include +#include +#include +#include +//#include +#include +#include + +int delete (const char *, const char *); +void search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl, + struct sockaddr_inarp *sin, struct rt_msghdr *rtm)); +void print_entry(struct sockaddr_dl *sdl, + struct sockaddr_inarp *sin, struct rt_msghdr *rtm); +void nuke_entry(struct sockaddr_dl *sdl, + struct sockaddr_inarp *sin, struct rt_msghdr *rtm); +void ether_print __P((const u_char *)); +int file (char *); +int get (const char *); +int getinetaddr (const char *, struct in_addr *); +void getsocket (void); +int rtmsg (int); +int set (int, char **); +void usage (void); + +static int pid; +static int nflag; /* no reverse dns lookups */ +static int aflag; /* do it for all entries */ +static int s = -1; + +/* which function we're supposed to do */ +#define F_GET 1 +#define F_SET 2 +#define F_FILESET 3 +#define F_DELETE 4 + +void err(int error, char *msg) +{ + printf("Error: %s\n", msg); + printf("Code: %d\n", error); + printf("Desc: %s\n", strerror(error)); + exit(-1); +} + +int +main(argc, argv) + int argc; + char **argv; +{ + int ch, func, rtn; + + pid = getpid(); + opterr = 0; + func = 0; + + while ((ch = getopt(argc, argv, "andsf")) != -1) { + switch ((char)ch) { + case 'a': + aflag = 1; + break; + case 'n': + nflag = 1; + break; + case 'd': + if (func) + usage(); + func = F_DELETE; + break; + case 's': + if (func) + usage(); + func = F_SET; + break; + case 'f': + if (func) + usage(); + func = F_FILESET; + break; + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (!func) + func = F_GET; + rtn = 0; + + switch (func) { + case F_GET: + if (aflag && argc == 0) + search(0, print_entry); + else if (!aflag && argc == 1) + rtn = get(argv[0]); + else + usage(); + break; + case F_SET: + if (argc < 2 || argc > 5) + usage(); + rtn = set(argc, argv) ? 1 : 0; + break; + case F_DELETE: + if (aflag && argc == 0) + search(0, nuke_entry); + else if (!aflag && argc == 1) + rtn = delete(argv[0], argv[1]); + else + usage(); + break; + case F_FILESET: + if (argc != 1) + usage(); + rtn = file(argv[0]); + break; + } + return (rtn); +} + +/* + * Process a file to set standard arp entries + */ +int +file(name) + char *name; +{ + char line[100], arg[5][50], *args[5]; + int i, retval; + FILE *fp; + + if ((fp = fopen(name, "r")) == NULL) { + printf("cannot open %s", name); + exit(1); + } + args[0] = &arg[0][0]; + args[1] = &arg[1][0]; + args[2] = &arg[2][0]; + args[3] = &arg[3][0]; + args[4] = &arg[4][0]; + retval = 0; + while (fgets(line, 100, fp) != NULL) { + i = sscanf(line, "%49s %49s %49s %49s %49s", arg[0], arg[1], arg[2], + arg[3], arg[4]); + if (i < 2) { + printf("bad line: %s", line); + retval = 1; + continue; + } + if (set(i, args)) + retval = 1; + } + fclose(fp); + return (retval); +} + +void +getsocket() +{ + if (s >= 0) + return; + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + err(1, "socket"); +} + +struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}}; +struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m; +struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m; +int expire_time, flags, export_only, doing_proxy, found_entry; +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +/* + * Set an individual arp entry + */ +int +set(argc, argv) + int argc; + char **argv; +{ + register struct sockaddr_inarp *sin; + register struct sockaddr_dl *sdl; + register struct rt_msghdr *rtm; + u_char *eaddr; + struct ether_addr *ea; + char *host = argv[0]; + + sin = &sin_m; + rtm = &(m_rtmsg.m_rtm); + eaddr = argv[1]; + + getsocket(); + argc -= 2; + argv += 2; + sdl_m = blank_sdl; /* struct copy */ + sin_m = blank_sin; /* struct copy */ + if (getinetaddr(host, &sin->sin_addr) == -1) + return (1); + ea = ether_aton(eaddr); + if (ea == NULL) { + printf("invalid ethernet address: %s\n", eaddr); + exit(-1); + } + memcpy(LLADDR(&sdl_m), ea, sizeof(*ea)); + sdl_m.sdl_alen = 6; + doing_proxy = flags = export_only = expire_time = 0; + while (argc-- > 0) { + if (strncmp(argv[0], "temp", 4) == 0) { + struct timeval time; + (void)gettimeofday(&time, 0); + expire_time = time.tv_sec + 20 * 60; + if (flags & RTF_PERMANENT_ARP) { + /* temp or permanent, not both */ + usage(); + return (0); + } + } + else if (strncmp(argv[0], "pub", 3) == 0) { + flags |= RTF_ANNOUNCE; + doing_proxy = SIN_PROXY; + } + else if (strncmp(argv[0], "permanent", 9) == 0) { + flags |= RTF_PERMANENT_ARP; + if (expire_time != 0) { + /* temp or permanent, not both */ + usage(); + return (0); + } + } else if (strncmp(argv[0], "trail", 5) == 0) { + (void)printf( + "%s: Sending trailers is no longer supported\n", + host); + } + argv++; + } +tryagain: + if (rtmsg(RTM_GET) < 0) { + printf("%s\n", host); + return (1); + } + sin = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); + if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { + if (sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { + case IFT_ETHER: +/* + case IFT_FDDI: + case IFT_ISO88023: + case IFT_ISO88024: + case IFT_ISO88025: +*/ + goto overwrite; + } + if (doing_proxy == 0) { + (void)printf("set: can only proxy for %s\n", host); + return (1); + } + if (sin_m.sin_other & SIN_PROXY) { + (void)printf( + "set: proxy entry exists for non 802 device\n"); + return (1); + } + sin_m.sin_other = SIN_PROXY; + export_only = 1; + goto tryagain; + } +overwrite: + if (sdl->sdl_family != AF_LINK) { + (void)printf("cannot intuit interface index and type for %s\n", + host); + return (1); + } + sdl_m.sdl_type = sdl->sdl_type; + sdl_m.sdl_index = sdl->sdl_index; + return (rtmsg(RTM_ADD)); +} + +/* + * Display an individual arp entry + */ +int +get(host) + const char *host; +{ + struct sockaddr_inarp *sin; + + sin = &sin_m; + sin_m = blank_sin; /* struct copy */ + if (getinetaddr(host, &sin->sin_addr) == -1) + exit(1); + search(sin->sin_addr.s_addr, print_entry); + if (found_entry == 0) { + (void)printf("%s (%s) -- no entry\n", host, + inet_ntoa(sin->sin_addr)); + return (1); + } + return (0); +} + +/* + * Delete an arp entry + */ +int +delete(host, info) + const char *host; + const char *info; +{ + register struct sockaddr_inarp *sin; + register struct rt_msghdr *rtm; + struct sockaddr_dl *sdl; + + sin = &sin_m; + rtm = &m_rtmsg.m_rtm; + + if (info && strncmp(info, "pro", 3) ) + export_only = 1; + getsocket(); + sin_m = blank_sin; /* struct copy */ + if (getinetaddr(host, &sin->sin_addr) == -1) + return (1); +tryagain: + if (rtmsg(RTM_GET) < 0) { + printf("%s\n", host); + return (1); + } + sin = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); + if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { + if (sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { + case IFT_ETHER: +/* + case IFT_FDDI: + case IFT_ISO88023: + case IFT_ISO88024: + case IFT_ISO88025: +*/ + goto delete; + } + } + if (sin_m.sin_other & SIN_PROXY) { + printf("delete: can't locate %s\n", host); + return (1); + } else { + sin_m.sin_other = SIN_PROXY; + goto tryagain; + } +delete: + if (sdl->sdl_family != AF_LINK) { + (void)printf("cannot locate %s\n", host); + return (1); + } + if (rtmsg(RTM_DELETE)) + return (1); + (void)printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr)); + return (0); +} + +/* + * Search the entire arp table, and do some action on matching entries. + */ +void +search(addr, action) + in_addr_t addr; + void (*action)(struct sockaddr_dl *sdl, + struct sockaddr_inarp *sin, + struct rt_msghdr *rtm); +{ + int mib[6]; + size_t needed; + char *lim, *buf, *next; + struct rt_msghdr *rtm; + struct sockaddr_inarp *sin; + struct sockaddr_dl *sdl; + extern int h_errno; + + //mib[0] = CTL_NET; + mib[0] = PF_ROUTE; + mib[1] = 0; + mib[2] = AF_INET; + mib[3] = NET_RT_FLAGS; + mib[4] = RTF_LLINFO; + if (sysctl(mib, 5, NULL, &needed, NULL, 0) < 0) + err(1, "route-sysctl-estimate"); + if (needed == 0) + return; + if ((buf = malloc(needed)) == NULL) + err(1, "malloc"); + if (sysctl(mib, 5, buf, &needed, NULL, 0) < 0) + err(1, "actual retrieval of routing table"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + sin = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(sin + 1); + if (addr) { + if (addr != sin->sin_addr.s_addr) + continue; + found_entry = 1; + } + (*action)(sdl, sin, rtm); + } +} + +/* + * Display an arp entry + */ +void +print_entry(sdl, sin, rtm) + struct sockaddr_dl *sdl; + struct sockaddr_inarp *sin; + struct rt_msghdr *rtm; +{ + char *host; + extern int h_errno; + struct hostent *hp; + + if (nflag == 0) + hp = gethostbyaddr((caddr_t)&(sin->sin_addr), + sizeof(sin->sin_addr), AF_INET); + else + hp = 0; + if (hp) + host = hp->h_name; + else { + host = "?"; + if (h_errno == TRY_AGAIN) + nflag = 1; + } + (void)printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr)); + if (sdl->sdl_alen) + ether_print(LLADDR(sdl)); + else + (void)printf("(incomplete)"); + if (rtm->rtm_flags & RTF_PERMANENT_ARP) + (void)printf(" permanent"); + if (rtm->rtm_rmx.rmx_expire == 0) + (void)printf(" static"); + if (sin->sin_other & SIN_PROXY) + (void)printf(" published (proxy only)"); + if (rtm->rtm_addrs & RTA_NETMASK) { + sin = (struct sockaddr_inarp *) + (sdl->sdl_len + (char *)sdl); + if (sin->sin_addr.s_addr == 0xffffffff) + (void)printf(" published"); + if (sin->sin_len != 8) + (void)printf("(weird)"); + } + printf("\n"); +} + +/* + * Nuke an arp entry + */ +void +nuke_entry(sdl, sin, rtm) + struct sockaddr_dl *sdl; + struct sockaddr_inarp *sin; + struct rt_msghdr *rtm; +{ + char ip[20]; + + strncpy(ip, inet_ntoa(sin->sin_addr), sizeof(ip)); + delete(ip, NULL); +} + +void +ether_print(cp) + const u_char *cp; +{ + (void)printf("%02x:%02x:%02x:%02x:%02x:%02x", + cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: arp [-n] hostname\n"); + (void)fprintf(stderr, "usage: arp [-n] -a\n"); + (void)fprintf(stderr, "usage: arp -d hostname\n"); + (void)fprintf(stderr, "usage: arp -d -a\n"); + (void)fprintf(stderr, + "usage: arp -s hostname ether_addr [temp | permanent] [pub]\n"); + (void)fprintf(stderr, "usage: arp -f filename\n"); + exit(1); +} + +int +rtmsg(cmd) + int cmd; +{ + static int seq; + int rlen; + register struct rt_msghdr *rtm; + register char *cp; + register int l; + + rtm = &m_rtmsg.m_rtm; + cp = m_rtmsg.m_space; + errno = 0; + + if (cmd == RTM_DELETE) + goto doit; + (void)memset(&m_rtmsg, 0, sizeof(m_rtmsg)); + rtm->rtm_flags = flags; + rtm->rtm_version = RTM_VERSION; + + switch (cmd) { + default: + printf("internal wrong cmd\n"); + exit(-1); + /*NOTREACHED*/ + case RTM_ADD: + rtm->rtm_addrs |= RTA_GATEWAY; + rtm->rtm_rmx.rmx_expire = expire_time; + rtm->rtm_inits = RTV_EXPIRE; + rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); + sin_m.sin_other = 0; + if (doing_proxy) { + if (export_only) + sin_m.sin_other = SIN_PROXY; + else { + rtm->rtm_addrs |= RTA_NETMASK; + rtm->rtm_flags &= ~RTF_HOST; + } + } + /* FALLTHROUGH */ + case RTM_GET: + rtm->rtm_addrs |= RTA_DST; + } +#define NEXTADDR(w, s) \ + if (rtm->rtm_addrs & (w)) { \ + (void)memcpy(cp, &s, sizeof(s)); cp += sizeof(s);} + + NEXTADDR(RTA_DST, sin_m); + NEXTADDR(RTA_GATEWAY, sdl_m); + NEXTADDR(RTA_NETMASK, so_mask); + + rtm->rtm_msglen = cp - (char *)&m_rtmsg; +doit: + l = rtm->rtm_msglen; + rtm->rtm_seq = ++seq; + rtm->rtm_type = cmd; + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + if (errno != ESRCH || cmd != RTM_DELETE) { + printf("writing to routing socket\n"); + return (-1); + } + } + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm->rtm_seq != seq)); /* || rtm->rtm_pid != pid)); */ + if (l < 0) + printf("read from routing socket\n"); + return (0); +} + +int +getinetaddr(host, inap) + const char *host; + struct in_addr *inap; +{ + struct hostent *hp; + + if (inet_aton(host, inap) == 1) + return (0); + if ((hp = gethostbyname(host)) == NULL) { + /* should be hstrerror and h_errno */ + printf("%s: %s\n", host, strerror(errno)); + return (-1); + } + (void)memcpy(inap, hp->h_addr, sizeof(*inap)); + return (0); +} diff --git a/src/apps/bin/ifconfig/Jamfile b/src/apps/bin/ifconfig/Jamfile new file mode 100644 index 0000000000..8d8e241a06 --- /dev/null +++ b/src/apps/bin/ifconfig/Jamfile @@ -0,0 +1,2 @@ +SubDir OBOS_TOP src apps bin ifconfig ; + diff --git a/src/apps/bin/ifconfig/ifconfig.c b/src/apps/bin/ifconfig/ifconfig.c new file mode 100644 index 0000000000..fe911a4121 --- /dev/null +++ b/src/apps/bin/ifconfig/ifconfig.c @@ -0,0 +1,627 @@ +/* ifconfig.c */ + +#include +#include +#include +#include /* for exit() */ +#include +#include + +#include "netinet/in.h" +#include "sys/socket.h" +#include "sys/sockio.h" +#include "arpa/inet.h" +#include "net/if.h" +#include "net/if_dl.h" + +#define version "0.2 pre-alpha" +#define NEXTARG 0xffffff + +/* globals */ +int sock; /* the socket we're using */ +int flags = 0; /* interface flags */ +int setaddr; +struct ifreq ifr; /* our interface information */ +struct ifreq ridreq; /* ?? */ +struct ifaliasreq addreq; /* aliases details */ +struct sockaddr_in netmask; /* the netmask */ +int mflag; +int newaddr = 0; /* do we have a new address? */ +int doalias; /* do we process alias information */ +int clearaddr; /* are we clearing the address? */ +char name[IFNAMSIZ]; /* the name of the interface we're using */ + +/* we only deal with a single interface at once, so these globals + * refer to their respective information for that interface + */ +int af = AF_INET; /* address family we're dealing with */ +int metric; /* the metric */ +int mtu; /* mtu vlaue */ + +/* forward declarations... */ +int getinfo(struct ifreq *ifr); +void setifaddr(char *addr, int param); +void setifflags(char *vname, int value); +void setifnetmask(char *addr); +void setifmetric(char *val); +void setifmtu(char *val, int d); +void in_status(int force); +void in_getaddr(char *s, int which); +void in_getprefix(char *plen, int which); +void status(int link); +void printif(struct ifreq *ifrm, int ifaliases); + +/* The commands we recognise... */ +struct _cmd_ { + char *c_name; /* what the command line will say */ + int c_parameter; /* the parameter, NEXTARG = next argv */ + void (*c_func)(); /* the function to call */ +} cmds [] = { + {"up", IFF_UP, setifflags}, + {"down", -IFF_UP, setifflags}, + {"netmask", NEXTARG, setifnetmask}, + {"metric", NEXTARG, setifmetric}, + {"mtu", NEXTARG, setifmtu}, + {NULL, /*src*/0, setifaddr }, +// {NULL, /*dst*/0, setifdstaddr }, + {NULL, 0, NULL} +}; + +/* Known address families - thus far ONLY AF_INET */ +/* XXX - it'd be very cool if we could have a way of doing this + * by modules/add-ons :) + */ +const struct afswtch { + char *af_name; + short af_af; + void (*af_status)(); + void (*af_getaddr)(); + void (*af_getprefix)(); + u_long af_difaddr; + u_long af_aifaddr; + caddr_t af_ridreq; + caddr_t af_addreq; +} afs[] = { +#define C(x) ((caddr_t) &x) + { "inet", AF_INET, in_status, in_getaddr, in_getprefix, + SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) }, + { 0, 0, 0, 0 } +}; + +const struct afswtch *afp; /*the address family being set or asked about*/ + +void usage() +{ + fprintf(stderr, "OpenBeOS Network Team: ifconfig: version %s\n", version); + fprintf(stderr, "usage: ifconfig [ -m ] [ -a ] [ -A ] [ interface ]\n" + "\t[ [af] [ address [ dest_addr ] ] [ up ] [ down ] " + "[ netmask mask ] ]\n" + "\t[ metric n ]\n" + "\t[ mtu n ]\n" + " ifconfig [-a | -A | -am | -Am] [ af ]\n" + " ifconfig -m interface [af]\n"); + exit(1); +} + +static void err(int exitval, char *where) +{ + printf("error: %s: error %d [%s]\n", where, errno, strerror(errno)); + exit(exitval); +} + +static void errx(int exitval, char *fmt_string, char *value) +{ + printf("error: "); + printf(fmt_string, value); + exit(exitval); +} + +static void warn(char *what) +{ + printf("warning: %s: error %d [%s]\n", what, errno, strerror(errno)); +} + +int main(int argc, char **argv) +{ + int aflag = 0, ifaliases = 0; + const struct afswtch *rafp = NULL; + int i; + + mflag = 0; + + if (argc < 2) + usage(); + argc--, argv++; /* skip app name */ + + /* handle common optins and get the interface name */ + if (!strcmp(*argv, "-a")) + aflag = 1; + else if (!strcmp(*argv, "-A")) { + aflag = 1; + ifaliases = 1; + } else if (!strcmp(*argv, "-ma") || !strcmp(*argv, "-am")) { + aflag = 1; + mflag = 1; + } + else if (!strcmp(*argv, "-mA") || !strcmp(*argv, "-Am")) { + aflag = 1; + ifaliases = 1; + mflag = 1; + } + else if (!strcmp(*argv, "-m")) { + mflag = 1; + argc--, argv++; + if (argc < 1) + usage(); + (void) strcpy(name, *argv); + } else + (void) strcpy(name, *argv); + argc--, argv++; + + /* Try to establish the address family we're talking about + * and set the appropriate structure of commands into the + * pointer afp; + */ + if (argc > 0) { + for (afp = rafp = afs; rafp->af_name; rafp++) + if (strcmp(rafp->af_name, *argv) == 0) { + afp = rafp; argc--; argv++; + break; + } + rafp = afp; + af = ifr.ifr_addr.sa_family = rafp->af_af; + } + + if (argc > 0) { + for (afp = rafp = afs; rafp->af_name; rafp++) + if (strcmp(rafp->af_name, *argv) == 0) { + printf("afp: %s\n", *argv); + afp = rafp; argc--; argv++; + break; + } + rafp = afp; + af = ifr.ifr_addr.sa_family = rafp->af_af; + } + + if (aflag) { + if (argc > 0) + usage(); + printif(NULL, ifaliases); + exit(0); + } + + /* put the name in the ifr structure */ + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + /* if there's no more arguments then print information + * on the named interface */ + if (argc == 0) { + printif(&ifr, 1); + exit(0); + } + + /* we've probably got something to do, so get the information */ + if (getinfo(&ifr) < 0) + exit(1); + + while (argc > 0) { + const struct _cmd_ *p; + + for (p = cmds; p->c_name; p++) + if (strcmp(*argv, p->c_name) == 0) + break; + /* we've found a command! */ + if (p->c_name == NULL && setaddr) { + printf("pc->name = NULL!\n"); + for (i = setaddr; i > 0; i--) { + p++; + if (p->c_func == NULL) + err(1, "extra address not accepted"); + } + } + if (p->c_func) { + if (p->c_parameter == NEXTARG) { + if (argv[1] == NULL) + errx(1, "'%s' requires argument", + p->c_name); + (*p->c_func)(argv[1]); + argc--, argv++; + } else + (*p->c_func)(*argv, p->c_parameter); + } + + argc--, argv++; + } + if (clearaddr) { + int ret; + strncpy(rafp->af_ridreq, name, sizeof(ifr.ifr_name)); + if ((ret = ioctl(sock, rafp->af_difaddr, rafp->af_ridreq)) < 0) { + if (errno == EADDRNOTAVAIL && (doalias >= 0)) { + /* means no previous address for interface */ + } else + warn("SIOCDIFADDR"); + } + } + if (newaddr) { + strncpy(rafp->af_addreq, name, sizeof(ifr.ifr_name)); + if (ioctl(sock, rafp->af_aifaddr, rafp->af_addreq) < 0) + warn("SIOCAIFADDR"); + } + exit(0); +} + +static void getsock(int naf) +{ + static int oaf = -1; + + if (oaf == naf) + return; + if (oaf != -1) + close(sock); + sock = socket(naf, SOCK_DGRAM, 0); + if (sock < 0) + oaf = -1; + else + oaf = naf; +} + +int getinfo(struct ifreq *ifr) +{ + getsock(af); + if (sock < 0) + err(1, "socket"); + if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)ifr) < 0) { + warn("SIOCGIFFLAGS"); + return (-1); + } + flags = ifr->ifr_flags; + if (ioctl(sock, SIOCGIFMETRIC, (caddr_t)ifr) < 0) { + warn("SIOCGIFMETRIC"); + metric = 0; + } else + metric = ifr->ifr_metric; + if (ioctl(sock, SIOCGIFMTU, (caddr_t)ifr) < 0) + mtu = 0; + else + mtu = ifr->ifr_mtu; + return (0); +} + +void printif(struct ifreq *ifrm, int ifaliases) +{ + char *inbuf = NULL; + struct ifconf ifc; + struct ifreq ifreq, *ifrp; + int i, siz, len = 8192; + int count = 0, noinet = 1; + char ifrbuf[8192]; + + getsock(af); + if (sock < 0) + err(1, "socket"); + while (1) { + ifc.ifc_len = len; + ifc.ifc_buf = inbuf = realloc(inbuf, len); + if (inbuf == NULL) + err(1, "malloc"); + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) + err(1, "SIOCGIFCONF"); + if (ifc.ifc_len + sizeof(ifreq) < len) + break; + len *= 2; + } + ifrp = ifc.ifc_req; + ifreq.ifr_name[0] = '\0'; + for (i = 0; i < ifc.ifc_len; ) { + ifrp = (struct ifreq *)((caddr_t)ifc.ifc_req + i); + memcpy(ifrbuf, ifrp, sizeof(*ifrp)); + siz = ((struct ifreq *)ifrbuf)->ifr_addr.sa_len; + if (siz < sizeof(ifrp->ifr_addr)) + siz = sizeof(ifrp->ifr_addr); + siz += sizeof(ifrp->ifr_name); + i += siz; + /* avoid alignment issue */ + if (sizeof(ifrbuf) < siz) + err(1, "ifr too big"); + memcpy(ifrbuf, ifrp, siz); + ifrp = (struct ifreq *)ifrbuf; + + if (ifrm && strncmp(ifrm->ifr_name, ifrp->ifr_name, + sizeof(ifrp->ifr_name))) + continue; + strncpy(name, ifrp->ifr_name, sizeof(ifrp->ifr_name)); + if (ifrp->ifr_addr.sa_family == AF_LINK) { + ifreq = ifr = *ifrp; + if (getinfo(&ifreq) < 0) + continue; + status(1); + count++; + noinet = 1; + continue; + } + if (!strncmp(ifreq.ifr_name, ifrp->ifr_name, + sizeof(ifrp->ifr_name))) { + const struct afswtch *p; + + if (ifrp->ifr_addr.sa_family == AF_INET && + ifaliases == 0 && noinet == 0) + continue; + ifr = *ifrp; + if ((p = afp) != NULL) { + if (ifr.ifr_addr.sa_family == p->af_af) + (*p->af_status)(1); + } else { + for (p = afs; p->af_name; p++) { + if (ifr.ifr_addr.sa_family == p->af_af) + (*p->af_status)(0); + } + } + count++; + if (ifrp->ifr_addr.sa_family == AF_INET) + noinet = 0; + continue; + } + } + free(inbuf); + if (count == 0) { + fprintf(stderr, "%s: no such interface\n", name); + exit(1); + } +} + +#define RIDADDR 0 +#define ADDR 1 +#define MASK 2 +#define DSTADDR 3 + +/*ARGSUSED*/ +void setifaddr(char *addr, int param) +{ + /* + * Delay the ioctl to set the interface addr until flags are all set. + * The address interpretation may depend on the flags, + * and the flags may change when the address is set. + */ + setaddr++; + newaddr = 1; + if (doalias == 0) + clearaddr = 1; + (*afp->af_getaddr)(addr, (doalias >= 0 ? ADDR : RIDADDR)); +} + +void setifnetmask(char *addr) +{ + (*afp->af_getaddr)(addr, MASK); +} + +/* + * Note: doing an SIOCIGIFFLAGS scribbles on the union portion + * of the ifreq structure, which may confuse other parts of ifconfig. + * Make a private copy so we can avoid that. + */ +void setifflags(char *vname, int value) +{ + struct ifreq my_ifr; + + memcpy((char *)&my_ifr, (char *)&ifr, sizeof(struct ifreq)); + + if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) + err(1, "SIOCGIFFLAGS"); + strncpy(my_ifr.ifr_name, name, sizeof(my_ifr.ifr_name)); + flags = my_ifr.ifr_flags; + + if (value < 0) { + value = -value; + flags &= ~value; + } else + flags |= value; + my_ifr.ifr_flags = flags; + if (ioctl(sock, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) + err(1, "SIOCSIFFLAGS"); +} + +void setifmetric(char *val) +{ + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_metric = atoi(val); + if (ioctl(sock, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) + warn("SIOCSIFMETRIC"); +} + +void setifmtu(char *val, int d) +{ + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_mtu = atoi(val); + if (ioctl(sock, SIOCSIFMTU, (caddr_t)&ifr) < 0) + warn("SIOCSIFMTU"); +} + + +#define SIN(x) ((struct sockaddr_in *) &(x)) +struct sockaddr_in *sintab[] = { +SIN(ridreq.ifr_addr), SIN(addreq.ifra_addr), +SIN(addreq.ifra_mask), SIN(addreq.ifra_broadaddr)}; + +void in_getaddr(char *s, int which) +{ + struct sockaddr_in *sin = sintab[which]; +/* + struct hostent *hp; + struct netent *np; +*/ + sin->sin_len = sizeof(*sin); + if (which != MASK) + sin->sin_family = AF_INET; + + if (inet_aton(s, &sin->sin_addr) == 0) { +/* +XXX - we don't have these yet! + + if ((hp = gethostbyname(s))) + memcpy(&sin->sin_addr, hp->h_addr, hp->h_length); + else if ((np = getnetbyname(s))) + sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); + else +*/ + errx(1, "%s: bad value", s); + } +} + +void in_status(int force) +{ + struct sockaddr_in *sin, sin2; + + getsock(AF_INET); + if (sock < 0) { + if (errno == EPROTONOSUPPORT) + return; + err(1, "socket"); + } + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + sin = (struct sockaddr_in *)&ifr.ifr_addr; + + /* + * We keep the interface address and reset it before each + * ioctl() so we can get ifaliases information (as opposed + * to the primary interface netmask/dstaddr/broadaddr, if + * the ifr_addr field is zero). + */ + memcpy(&sin2, &ifr.ifr_addr, sizeof(sin2)); + + printf("\tinet %s ", inet_ntoa(sin->sin_addr)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (ioctl(sock, SIOCGIFNETMASK, (caddr_t)&ifr) < 0) { + if (errno != EADDRNOTAVAIL) + warn("SIOCGIFNETMASK"); + memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr)); + } else + netmask.sin_addr = + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; + if (flags & IFF_POINTOPOINT) { + memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2)); + if (ioctl(sock, SIOCGIFDSTADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr)); + else + warn("SIOCGIFDSTADDR"); + } + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + sin = (struct sockaddr_in *)&ifr.ifr_dstaddr; + printf("--> %s ", inet_ntoa(sin->sin_addr)); + } + printf("netmask 0x%08lx ", ntohl(netmask.sin_addr.s_addr)); + if (flags & IFF_BROADCAST) { + memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2)); + if (ioctl(sock, SIOCGIFBRDADDR, (caddr_t)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) + memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr)); + else + warn("SIOCGIFBRDADDR"); + } + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + sin = (struct sockaddr_in *)&ifr.ifr_addr; + if (sin->sin_addr.s_addr != 0) + printf("broadcast %s", inet_ntoa(sin->sin_addr)); + } + putchar('\n'); +} +/* + 1 IFF_UP + 2 IFF_BROADCAST + 3 IFF_PROMISC + 4 IFF_RUNNING = 0x0008, + 5 IFF_MULTICAST = 0x0010, + 6 IFF_BROADCAST = 0x0020, + 7 IFF_POINTOPOINT = 0x0040, + 8 IFF_NOARP = 0x0080, + 9 IFF_LOOPBACK = 0x0100, + 10 IFF_DEBUG = 0x0200, + 11 IFF_LINK0 = 0x0400, + 12 IFF_LINK1 = 0x0800, + 13 IFF_LINK2 = 0x1000 + +*/ +#define IFFBITS \ +"\017\1UP\2BROADCAST\3PROMISC\4RUNNING\5MULTICAST\6BROADCAST\7POINTOPOINT\10NOARP\ +\11LOOPBACK\12DEBUG\13SIMPLEX\15LINK0\16LINK1\17LINK2" + + +/* + * Print a value a la the %b format of the kernel's printf + */ +static void printb(char *s, uint16 v, char *bits) +{ + int i, any = 0; + char c; + + if (bits && *bits == 8) + printf("%s=%o", s, v); + else + printf("%s=%x", s, v); + bits++; + if (bits) { + putchar('<'); + while ((i = *bits++)) { + if (v & (1 << (i-1))) { + if (any) + putchar(','); + any = 1; + for (; (c = *bits) > 32; bits++) + putchar(c); + } else + for (; *bits > 32; bits++) + ; + } + putchar('>'); + } +} + +void in_getprefix(char *plen, int which) +{ + struct sockaddr_in *sin = sintab[which]; + u_char *cp; + int len = strtol(plen, (char **)NULL, 10); + + if ((len < 0) || (len > 32)) + errx(1, "%s: bad value", plen); + sin->sin_len = sizeof(*sin); + if (which != MASK) + sin->sin_family = AF_INET; + if ((len == 0) || (len == 32)) { + memset(&sin->sin_addr, 0xff, sizeof(struct in_addr)); + return; + } + memset((void *)&sin->sin_addr, 0x00, sizeof(sin->sin_addr)); + for (cp = (u_char *)&sin->sin_addr; len > 7; len -= 8) + *cp++ = 0xff; + if (len) + *cp = 0xff << (8 - len); +} + +/* + * Print the status of the interface. If an address family was + * specified, show it and it only; otherwise, show them all. + */ +void status(int link) +{ + const struct afswtch *p = afp; + + printf("%s: ", name); + printb("flags", flags, IFFBITS); + if (metric) + printf(" metric %d", metric); + if (mtu) + printf(" mtu %d", mtu); + putchar('\n'); + + if (link == 0) { + if ((p = afp) != NULL) { + (*p->af_status)(1); + } else for (p = afs; p->af_name; p++) { + ifr.ifr_addr.sa_family = p->af_af; + (*p->af_status)(0); + } + } + +// phys_status(0); +} + diff --git a/src/apps/bin/ping/Jamfile b/src/apps/bin/ping/Jamfile new file mode 100644 index 0000000000..c85d65873d --- /dev/null +++ b/src/apps/bin/ping/Jamfile @@ -0,0 +1,2 @@ +SubDir OBOS_TOP src apps bin ping ; + diff --git a/src/apps/bin/ping/ping.c b/src/apps/bin/ping/ping.c new file mode 100644 index 0000000000..698efd3d55 --- /dev/null +++ b/src/apps/bin/ping/ping.c @@ -0,0 +1,1298 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * This program has to run SUID to ROOT to access the ICMP socket. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "netinet/ip.h" +#include "netinet/ip_icmp.h" +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include "sys/select.h" + +#define DEFDATALEN (64 - 8) /* default data length */ +#define MAXIPLEN 60 +#define MAXICMPLEN 76 +#define MAXPAYLOAD (IP_MAXPACKET - MAXIPLEN - 8) /* max ICMP payload size */ +#define MAXWAIT_DEFAULT 10 /* secs to wait for response */ +#define NROUTES 9 /* number of record route slots */ + +#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ +#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ +#define SET(bit) (A(bit) |= B(bit)) +#define CLR(bit) (A(bit) &= (~B(bit))) +#define TST(bit) (A(bit) & B(bit)) + +/* various options */ +int options; +#define F_FLOOD 0x001 +#define F_INTERVAL 0x002 +#define F_NUMERIC 0x004 +#define F_PINGFILLED 0x008 +#define F_QUIET 0x010 +#define F_RROUTE 0x020 +#define F_SO_DEBUG 0x040 +#define F_SO_DONTROUTE 0x080 +#define F_VERBOSE 0x100 +#define F_SADDR 0x200 +#define F_HDRINCL 0x400 +#define F_TTL 0x800 + +/* multicast options */ +int moptions; +#define MULTICAST_NOLOOP 0x001 +#define MULTICAST_TTL 0x002 +#define MULTICAST_IF 0x004 + +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. Change 128 + * to 8192 for complete accuracy... + */ +#define MAX_DUP_CHK (8 * 128) +int mx_dup_ck = MAX_DUP_CHK; +char rcvd_tbl[MAX_DUP_CHK / 8]; + +struct sockaddr whereto; /* who to ping */ +struct sockaddr_in whence; /* Which interface we come from */ +int datalen = DEFDATALEN; +int s; /* socket file descriptor */ +u_char outpackhdr[IP_MAXPACKET]; /* Max packet size = 65535 */ +u_char *outpack = outpackhdr+sizeof(struct ip); +char BSPACE = '\b'; /* characters written for flood */ +char DOT = '.'; +char *hostname; +int ident; /* process id to identify our packets */ + +/* counters */ +long npackets; /* max packets to transmit */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +double interval = 1; /* interval between packets */ + +/* timing */ +int timing; /* flag to do timing */ +int maxwait = MAXWAIT_DEFAULT; /* max seconds to wait for response */ +uint64 tmin = 999999999; /* minimum round trip time in millisec */ +uint64 tmax = 0; /* maximum round trip time in millisec */ +uint64 tsum = 0; /* sum of all times in millisec, for doing average */ +uint64 tsumsq = 0; /* sum of all times squared, for std. dev. */ + +#ifdef SIGINFO +int reset_kerninfo; +#endif + +#define DEFAULT_BUFSPACE 60*1024 /* default read buffer size */ +int bufspace = DEFAULT_BUFSPACE; + +void fill (char *, char *); +void catcher(), prtsig(), finish(), summary(int); +int in_cksum (u_short *, int); +void pinger(); +char *pr_addr (in_addr_t); +int check_icmph (struct ip *); +void pr_icmph (struct icmp *); +void pr_pack (char *, int, struct sockaddr_in *); +void pr_retip (struct ip *); +uint64 qsqrt (uint64); +void usage(); + + +static void err(int exitval, char *where) +{ + printf("error: %s: error %d [%s]\n", where, errno, strerror(errno)); + exit(exitval); +} + +static void errx(int exitval, char *fmt_string, char *value) +{ + printf("error: "); + printf(fmt_string, value); + exit(exitval); +} + +int main(int argc, char **argv) +{ + struct timeval timeout; + struct hostent *hp; + struct sockaddr_in *to; + struct protoent *proto; + struct in_addr saddr; + int i; + int ch, hold = 1, packlen, preload; + int maxsize, fdmasks; + size_t maxsizelen; + u_char *datap, *packet; + char *target, hnamebuf[MAXHOSTNAMELEN]; + u_char ttl = MAXTTL, loop = 1, df = 0; + int tos = 0; +#ifdef IP_OPTIONS + char rspace[3 + 4 * NROUTES + 1]; /* record route space */ +#endif + fd_set *fdmaskp; + + if (!(proto = getprotobyname("icmp"))) + err(1, "unknown protocol icmp"); + if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) + err(1, "socket"); + + /* revoke privs */ +// seteuid(getuid()); +// setuid(getuid()); + + preload = 0; + datap = &outpack[8 + sizeof(uint64)]; + while ((ch = getopt(argc, argv, "DI:LRS:c:dfh:i:l:np:qrs:T:t:vw:")) != -1) + switch(ch) { + case 'c': + npackets = strtol(optarg, NULL, 0); + if (npackets <= 0) + errx(1, "bad number of packets to transmit: %s", + optarg); + break; + case 'D': + options |= F_HDRINCL; + df = -1; + break; + case 'd': + options |= F_SO_DEBUG; + break; + case 'f': + if (getuid()) + errx(1, "%s", strerror(EPERM)); + options |= F_FLOOD; + setbuf(stdout, (char *)NULL); + break; + case 'I': + case 'S': /* deprecated */ + if (inet_aton(optarg, &saddr) == 0) { + if ((hp = gethostbyname(optarg)) == NULL) + errx(1, "bad interface address: %s", + optarg); + memcpy(&saddr, hp->h_addr, sizeof(saddr)); + } + options |= F_SADDR; + break; + case 'i': /* wait between sending packets */ + interval = strtod(optarg, NULL); + + if (interval <= 0 || interval >= INT_MAX) + errx(1, "bad timing interval: %s", optarg); + + if (interval < 1) + if (getuid()) + errx(1, "%s: only root may use interval < 1s", strerror(EPERM)); + + if (interval < 0.01) + interval = 0.01; + + options |= F_INTERVAL; + break; + case 'L': + moptions |= MULTICAST_NOLOOP; + loop = 0; + break; + case 'l': + if (getuid()) + errx(1, "%s", strerror(EPERM)); + preload = strtol(optarg, NULL, 0); + if (preload < 0) + errx(1, "bad preload value: %s", optarg); + break; + case 'n': + options |= F_NUMERIC; + break; + case 'p': /* fill buffer with user pattern */ + options |= F_PINGFILLED; + fill((char *)datap, optarg); + break; + case 'q': + options |= F_QUIET; + break; + case 'R': + options |= F_RROUTE; + break; + case 'r': + options |= F_SO_DONTROUTE; + break; + case 's': /* size of packet to send */ + datalen = strtol(optarg, NULL, 0); + if (datalen <= 0) + errx(1, "bad packet size: %s", optarg); + if (datalen > MAXPAYLOAD) + errx(1, "packet size too large: %s", optarg); + break; + case 'T': + options |= F_HDRINCL; + tos = strtoul(optarg, NULL, 0); + if (tos > 0xFF) + errx(1, "bad tos value: %s", optarg); + break; + case 't': + options |= F_TTL; + ttl = strtol(optarg, NULL, 0); + if (ttl <= 0) + errx(1, "bad ttl value: %s", optarg); + if (ttl > 255) + errx(1, "ttl value too large: %s", optarg); + break; + case 'v': + options |= F_VERBOSE; + break; + case 'w': + maxwait = strtol(optarg, NULL, 0); + if (maxwait <= 0) + errx(1, "bad maxwait value: %s", optarg); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + target = *argv; + + memset(&whereto, 0, sizeof(struct sockaddr)); + to = (struct sockaddr_in *)&whereto; + to->sin_len = sizeof(struct sockaddr_in); + to->sin_family = AF_INET; + if (inet_aton(target, &to->sin_addr) != 0) + hostname = target; + else { + hp = gethostbyname(target); + if (!hp) + errx(1, "unknown host: %s", target); + to->sin_family = hp->h_addrtype; + memcpy(&to->sin_addr, hp->h_addr, hp->h_length); + (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf)); + hostname = hnamebuf; + } + + if (options & F_FLOOD && options & F_INTERVAL) + err(1, "-f and -i options are incompatible"); + + if (datalen >= sizeof(uint64)) /* can we time transfer */ + timing = 1; + packlen = datalen + MAXIPLEN + MAXICMPLEN; + if (!(packet = (u_char *)malloc((u_int)packlen))) + err(1, "malloc"); + if (!(options & F_PINGFILLED)) + for (i = sizeof(bigtime_t); i < datalen; ++i) + *datap++ = i; + + ident = getpid() & 0xFFFF; + + if (options & F_SADDR) { + if (IN_MULTICAST(ntohl(to->sin_addr.s_addr))) + moptions |= MULTICAST_IF; + else { + memset(&whence, 0, sizeof(whence)); + whence.sin_len = sizeof(whence); + whence.sin_family = AF_INET; + memcpy(&whence.sin_addr.s_addr, &saddr, sizeof(saddr)); + if (bind(s, (struct sockaddr*)&whence, + sizeof(whence)) < 0) + err(1, "bind"); + } + } + + if (options & F_SO_DEBUG) + (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, + sizeof(hold)); + if (options & F_SO_DONTROUTE) + (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, + sizeof(hold)); + + if (options & F_TTL) { + if (IN_MULTICAST(ntohl(to->sin_addr.s_addr))) + moptions |= MULTICAST_TTL; + else + options |= F_HDRINCL; + } + + if (options & F_RROUTE && options & F_HDRINCL) + err(1, "-R option and -D or -T, or -t to unicast destinations" + " are incompatible"); + + if (options & F_HDRINCL) { + struct ip *ip = (struct ip*)outpackhdr; + + setsockopt(s, IPPROTO_IP, IP_HDRINCL, &hold, sizeof(hold)); + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(struct ip) >> 2; + ip->ip_tos = tos; + ip->ip_id = 0; + ip->ip_off = htons(df?IP_DF:0); + ip->ip_ttl = ttl; + ip->ip_p = proto->p_proto; + ip->ip_src.s_addr = INADDR_ANY; + ip->ip_dst = to->sin_addr; + } + + /* record route option */ + if (options & F_RROUTE) { + if (IN_MULTICAST(ntohl(to->sin_addr.s_addr))) + err(1, "record route not valid to multicast destinations"); +#ifdef IP_OPTIONS + rspace[IPOPT_OPTVAL] = IPOPT_RR; + rspace[IPOPT_OLEN] = sizeof(rspace)-1; + rspace[IPOPT_OFFSET] = IPOPT_MINOFF; + if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, + sizeof(rspace)) < 0) { + perror("ping: record route"); + exit(1); + } +#else + err(1, "record route not available in this implementation"); +#endif /* IP_OPTIONS */ + } + + if ((moptions & MULTICAST_NOLOOP) && + setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, + sizeof(loop)) < 0) + err(1, "setsockopt IP_MULTICAST_LOOP"); + if ((moptions & MULTICAST_TTL) && + setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)) < 0) + err(1, "setsockopt IP_MULTICAST_TTL"); + if ((moptions & MULTICAST_IF) && + setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &saddr, + sizeof(saddr)) < 0) + err(1, "setsockopt IP_MULTICAST_IF"); + + /* + * When trying to send large packets, you must increase the + * size of both the send and receive buffers... + */ + maxsizelen = sizeof maxsize; + if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &maxsize, &maxsizelen) < 0) + err(1, "getsockopt"); + if (maxsize < packlen && + setsockopt(s, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(maxsize)) < 0) + err(1, "setsockopt"); + /* + * When pinging the broadcast address, you can get a lot of answers. + * Doing something so evil is useful if you are trying to stress the + * ethernet, or just want to fill the arp cache to get some stuff for + * /etc/ethers. + */ + while (setsockopt(s, SOL_SOCKET, SO_RCVBUF, + (void*)&bufspace, sizeof(bufspace)) < 0) { + if ((bufspace -= 1024) <= 0) + err(1, "Cannot set the receive buffer size"); + } + if (bufspace < DEFAULT_BUFSPACE) + printf("Could only allocate a receive buffer of %i bytes (default %i)\n", + bufspace, DEFAULT_BUFSPACE); + + if (to->sin_family == AF_INET) + (void)printf("PING %s (%s): %d data bytes\n", hostname, + inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), + datalen); + else + (void)printf("PING %s: %d data bytes\n", hostname, datalen); + + (void)signal(SIGINT, finish); + (void)signal(SIGALRM, catcher); +#ifdef SIGINFO + (void)signal(SIGINFO, prtsig); +#endif + + while (preload--) /* fire off them quickies */ + pinger(); + + if ((options & F_FLOOD) == 0) + catcher(); /* start things going */ + + fdmasks = howmany(s+1, NFDBITS) * sizeof(fd_mask); + if ((fdmaskp = (fd_set *)malloc(fdmasks)) == NULL) + err(1, "malloc"); + + for (;;) { + struct sockaddr_in from; + int cc; + size_t fromlen; + sigset_t omask, nmask; + + if (options & F_FLOOD) { + pinger(); + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + memset(fdmaskp, 0, fdmasks); + FD_SET(s, fdmaskp); + if (select(s + 1, (fd_set *)fdmaskp, (fd_set *)NULL, + (fd_set *)NULL, &timeout) < 1) + continue; + } + fromlen = sizeof(from); + if ((cc = recvfrom(s, (char *)packet, packlen, 0, + (struct sockaddr *)&from, &fromlen)) < 0) { + if (errno == EINTR) + continue; + perror("ping: recvfrom"); + continue; + } + sigemptyset(&nmask); + sigaddset(&nmask, SIGALRM); + sigprocmask(SIG_BLOCK, &nmask, &omask); + pr_pack((char *)packet, cc, &from); + sigprocmask(SIG_SETMASK, &omask, NULL); + if (npackets && nreceived >= npackets) + break; + } + free(fdmaskp); + finish(); + /* NOTREACHED */ + exit(0); /* Make the compiler happy */ +} + +/* + * catcher -- + * This routine causes another PING to be transmitted, and then + * schedules another SIGALRM for 1 second from now. + * + * bug -- + * Our sense of time will slowly skew (i.e., packets will not be + * launched exactly at 1-second intervals). This does not affect the + * quality of the delay and loss statistics. + */ +void catcher() +{ + int waittime; + int save_errno = errno; + + pinger(); + (void)signal(SIGALRM, catcher); + + if (!npackets || ntransmitted < npackets) + set_alarm(interval * 1000000, B_ONE_SHOT_RELATIVE_ALARM); + else { + if (nreceived) { + waittime = 2 * tmax / 1000000; + if (!waittime) + waittime = 1; + } else + waittime = maxwait; + (void)signal(SIGALRM, finish); + (void)alarm((uint)waittime); + } + errno = save_errno; +} + +/* + * Print statistics when SIGINFO is received. + * XXX not race safe + */ +void prtsig() +{ + int save_errno = errno; + + summary(0); + errno = save_errno; +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first 8 bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +void pinger() +{ + struct icmp *icp; + int cc; + int i; + char *packet = outpack; + + icp = (struct icmp *)outpack; + icp->icmp_type = ICMP_ECHO; + icp->icmp_code = 0; + icp->icmp_cksum = 0; + icp->icmp_seq = htons(ntransmitted++); + icp->icmp_id = ident; /* ID */ + + CLR(ntohs(icp->icmp_seq) % mx_dup_ck); + + if (timing) { + bigtime_t tm = real_time_clock_usecs(); + memcpy(&outpack[8], &tm, sizeof(tm)); + } + + cc = datalen + 8; /* skips ICMP portion */ + + /* compute ICMP checksum here */ + icp->icmp_cksum = in_cksum((u_short *)icp, cc); + + if (options & F_HDRINCL) { + struct ip *ip = (struct ip*)outpackhdr; + + packet = (char*)ip; + cc += sizeof(struct ip); + ip->ip_len = htons(cc); + ip->ip_sum = in_cksum((u_short *)outpackhdr, cc); + } + + i = sendto(s, (char *)packet, cc, 0, &whereto, + sizeof(struct sockaddr)); + + if (i < 0 || i != cc) { + if (i < 0) + perror("ping: sendto"); + (void)printf("ping: wrote %s %d chars, ret=%d\n", + hostname, cc, i); + } + if (!(options & F_QUIET) && options & F_FLOOD) + (void)write(STDOUT_FILENO, &DOT, 1); +} + +/* + * pr_pack -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +void pr_pack(char *buf, int cc, struct sockaddr_in *from) +{ + struct icmp *icp; + in_addr_t l; + u_int i, j; + u_char *cp, *dp; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; + struct ip *ip, *ip2; +// struct timeval tv, tp; + char *pkttime; + uint64 triptime = 0; + int hlen, hlen2, dupflag; + +// (void)gettimeofday(&tv, (struct timezone *)NULL); + + /* Check the IP header */ + ip = (struct ip *)buf; + hlen = ip->ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) { + if (options & F_VERBOSE) + printf("packet too short (%d bytes) from %s\n", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); + return; + } + + /* Now the ICMP part */ + cc -= hlen; + icp = (struct icmp *)(buf + hlen); + if (icp->icmp_type == ICMP_ECHOREPLY) { + if (icp->icmp_id != ident) + return; /* 'Twas not our ECHO */ + ++nreceived; + if (timing) { + bigtime_t tvi; + +#ifndef icmp_data + pkttime = (char *)&icp->icmp_ip; +#else + pkttime = (char *)icp->icmp_data; +#endif + memcpy(&tvi, pkttime, sizeof tvi); + triptime = real_time_clock_usecs() - tvi; + tsum += triptime; + tsumsq += triptime * triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + } + + if (TST(ntohs(icp->icmp_seq) % mx_dup_ck)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(ntohs(icp->icmp_seq) % mx_dup_ck); + dupflag = 0; + } + + if (options & F_QUIET) + return; + + if (options & F_FLOOD) + (void)write(STDOUT_FILENO, &BSPACE, 1); + else { + (void)printf("%d bytes from %s: icmp_seq=%u", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), + ntohs(icp->icmp_seq)); + (void)printf(" ttl=%d", ip->ip_ttl); + if (timing) + (void)printf(" time=%d.%03d ms", + (int)(triptime / 1000), + (int)(triptime % 1000)); + if (dupflag) + (void)printf(" (DUP!)"); + /* check the data */ + cp = (u_char*)&icp->icmp_data[sizeof(bigtime_t)]; + dp = &outpack[8 + sizeof(bigtime_t)]; + for (i = 8 + sizeof(bigtime_t); i < datalen; + ++i, ++cp, ++dp) { + if (*cp != *dp) { + (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", + i, *dp, *cp); + cp = (u_char*)&icp->icmp_data[0]; + for (i = 8; i < datalen; ++i, ++cp) { + if ((i % 32) == 8) + (void)printf("\n\t"); + (void)printf("%x ", *cp); + } + break; + } + } + } + } else { + /* We've got something other than an ECHOREPLY */ + if (!(options & F_VERBOSE)) + return; + ip2 = (struct ip *) (buf + hlen + sizeof (struct icmp)); + hlen2 = ip2->ip_hl << 2; + if (cc >= hlen2 + 8 && check_icmph((struct ip *)(icp + + sizeof (struct icmp))) != 1) + return; + (void)printf("%d bytes from %s: ", cc, + pr_addr(from->sin_addr.s_addr)); + pr_icmph(icp); + } + + /* Display any IP options */ + cp = (u_char *)buf + sizeof(struct ip); + + for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) + switch (*cp) { + case IPOPT_EOL: + hlen = 0; + break; + case IPOPT_LSRR: + (void)printf("\nLSRR: "); + hlen -= 2; + j = *++cp; + ++cp; + i = 0; + if (j > IPOPT_MINOFF) { + for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", + pr_addr(ntohl(l))); + hlen -= 4; + j -= 4; + i += 4; + if (j <= IPOPT_MINOFF) + break; + if (i >= MAX_IPOPTLEN) { + (void)printf("\t(truncated route)"); + break; + } + (void)putchar('\n'); + } + } + break; + case IPOPT_RR: + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + hlen -= 2; + if (i > j) + i = j; + i -= IPOPT_MINOFF; + if (i <= 0) + continue; + if (i == old_rrlen + && cp == (u_char *)buf + sizeof(struct ip) + 2 + && !memcmp(cp, old_rr, i) + && !(options & F_FLOOD)) { + (void)printf("\t(same route)"); + i = ((i + 3) / 4) * 4; + hlen -= i; + cp += i; + break; + } + if (i < MAX_IPOPTLEN) { + old_rrlen = i; + memcpy(old_rr, cp, i); + } else + old_rrlen = 0; + + (void)printf("\nRR: "); + j = 0; + for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", pr_addr(ntohl(l))); + hlen -= 4; + i -= 4; + j += 4; + if (i <= 0) + break; + if (j >= MAX_IPOPTLEN) { + (void)printf("\t(truncated route)"); + break; + } + (void)putchar('\n'); + } + break; + case IPOPT_NOP: + (void)printf("\nNOP"); + break; + default: + (void)printf("\nunknown option %x", *cp); + hlen = hlen + cp[1] - 1; + cp = cp + cp[1] - 1; + break; + } + if (!(options & F_FLOOD)) { + (void)putchar('\n'); + (void)fflush(stdout); + } +} + +/* + * in_cksum -- + * Checksum routine for Internet Protocol family headers (C Version) + */ +int +in_cksum(addr, len) + u_short *addr; + int len; +{ + int nleft = len; + u_short *w = addr; + int sum = 0; + u_short 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; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) { + *(u_char *)(&answer) = *(u_char *)w ; + sum += answer; + } + + /* add 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 summary(int header) +{ + (void)putchar('\r'); + (void)fflush(stdout); + + if (header) + (void)printf("--- %s ping statistics ---\n", hostname); + + (void)printf("%ld packets transmitted, ", ntransmitted); + (void)printf("%ld packets received, ", nreceived); + if (nrepeats) + (void)printf("%ld duplicates, ", nrepeats); + if (ntransmitted) { + if (nreceived > ntransmitted) + (void)printf("-- somebody's printing up packets!"); + else + (void)printf("%d%% packet loss", + (int) (((ntransmitted - nreceived) * 100) / + ntransmitted)); + } + (void)putchar('\n'); + if (nreceived && timing) { + uint64 num = nreceived + nrepeats; + uint64 avg = tsum / num; + uint64 dev = qsqrt(tsumsq / num - avg * avg); + (void)printf("round-trip min/avg/max/std-dev = " + "%d.%03d/%d.%03d/%d.%03d/%d.%03d ms\n", + (int)(tmin / 1000), (int)(tmin % 1000), + (int)(avg / 1000), (int)(avg % 1000), + (int)(tmax / 1000), (int)(tmax % 1000), + (int)(dev / 1000), (int)(dev % 1000)); + } +} + +uint64 qsqrt(uint64 qdev) +{ + uint64 y, x = 1; + + if (!qdev) + return(0); + + do { /* newton was a stinker */ + y = x; + x = qdev / x; + x += y; + x /= 2; + } while ((x - y) > 1 || (x - y) > -1); + + return(x); +} + +/* + * finish -- + * Print out statistics, and give up. + */ +void finish() +{ + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGALRM, SIG_IGN); + + summary(1); + exit(nreceived ? 0 : 1); +} + +#ifdef notdef +static char *ttab[] = { + "Echo Reply", /* ip + seq + udata */ + "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ + "Source Quench", /* IP */ + "Redirect", /* redirect type, gateway, + IP */ + "Echo", + "Time Exceeded", /* transit, frag reassem + IP */ + "Parameter Problem", /* pointer + IP */ + "Timestamp", /* id + seq + three timestamps */ + "Timestamp Reply", /* " */ + "Info Request", /* id + sq */ + "Info Reply" /* " */ +}; +#endif + +/* + * pr_icmph -- + * Print a descriptive string about an ICMP header. + */ +void +pr_icmph(icp) + struct icmp *icp; +{ + switch(icp->icmp_type) { + case ICMP_ECHOREPLY: + (void)printf("Echo Reply\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_UNREACH: + switch(icp->icmp_code) { + case ICMP_UNREACH_NET: + (void)printf("Destination Net Unreachable\n"); + break; + case ICMP_UNREACH_HOST: + (void)printf("Destination Host Unreachable\n"); + break; + case ICMP_UNREACH_PROTOCOL: + (void)printf("Destination Protocol Unreachable\n"); + break; + case ICMP_UNREACH_PORT: + (void)printf("Destination Port Unreachable\n"); + break; + case ICMP_UNREACH_NEEDFRAG: + if (icp->icmp_nextmtu != 0) + (void)printf("frag needed and DF set (MTU %d)\n", + ntohs(icp->icmp_nextmtu)); + else + (void)printf("frag needed and DF set\n"); + break; + case ICMP_UNREACH_SRCFAIL: + (void)printf("Source Route Failed\n"); + break; + case ICMP_UNREACH_NET_UNKNOWN: + (void)printf("Network Unknown\n"); + break; + case ICMP_UNREACH_HOST_UNKNOWN: + (void)printf("Host Unknown\n"); + break; + case ICMP_UNREACH_ISOLATED: + (void)printf("Source Isolated\n"); + break; + case ICMP_UNREACH_NET_PROHIB: + (void)printf("Dest. Net Administratively Prohibited\n"); + break; + case ICMP_UNREACH_HOST_PROHIB: + (void)printf("Dest. Host Administratively Prohibited\n"); + break; + case ICMP_UNREACH_TOSNET: + (void)printf("Destination Net Unreachable for TOS\n"); + break; + case ICMP_UNREACH_TOSHOST: + (void)printf("Desination Host Unreachable for TOS\n"); + break; + case ICMP_UNREACH_FILTER_PROHIB: + (void)printf("Route administratively prohibited\n"); + break; + case ICMP_UNREACH_HOST_PRECEDENCE: + (void)printf("Host Precedence Violation\n"); + break; + case ICMP_UNREACH_PRECEDENCE_CUTOFF: + (void)printf("Precedence Cutoff\n"); + break; + default: + (void)printf("Dest Unreachable, Unknown Code: %d\n", + icp->icmp_code); + break; + } + /* Print returned IP header information */ +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_SOURCEQUENCH: + (void)printf("Source Quench\n"); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_REDIRECT: + switch(icp->icmp_code) { + case ICMP_REDIRECT_NET: + (void)printf("Redirect Network"); + break; + case ICMP_REDIRECT_HOST: + (void)printf("Redirect Host"); + break; + case ICMP_REDIRECT_TOSNET: + (void)printf("Redirect Type of Service and Network"); + break; + case ICMP_REDIRECT_TOSHOST: + (void)printf("Redirect Type of Service and Host"); + break; + default: + (void)printf("Redirect, Unknown Code: %d", icp->icmp_code); + break; + } + (void)printf("(New addr: %s)\n", + inet_ntoa(icp->icmp_gwaddr)); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_ECHO: + (void)printf("Echo Request\n"); + /* XXX ID + Seq + Data */ + break; +// case ICMP_ROUTERADVERT: + /* RFC1256 */ +/* + (void)printf("Router Discovery Advertisement\n"); + (void)printf("(%d entries, lifetime %d seconds)\n", + icp->icmp_num_addrs, ntohs(icp->icmp_lifetime)); + break; +*/ +// case ICMP_ROUTERSOLICIT: + /* RFC1256 */ +/* (void)printf("Router Discovery Solicitation\n"); + break; +*/ + case ICMP_TIMXCEED: + switch(icp->icmp_code) { + case ICMP_TIMXCEED_INTRANS: + (void)printf("Time to live exceeded\n"); + break; + case ICMP_TIMXCEED_REASS: + (void)printf("Frag reassembly time exceeded\n"); + break; + default: + (void)printf("Time exceeded, Unknown Code: %d\n", + icp->icmp_code); + break; + } +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_PARAMPROB: + switch(icp->icmp_code) { + case ICMP_PARAMPROB_OPTABSENT: + (void)printf( + "Parameter problem, required option absent: pointer = 0x%02x\n", + ntohs(icp->icmp_hun.ih_pptr)); + break; + default: + (void)printf("Parameter problem: pointer = 0x%02x\n", + ntohs(icp->icmp_hun.ih_pptr)); + break; + } +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_TSTAMP: + (void)printf("Timestamp\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_TSTAMPREPLY: + (void)printf("Timestamp Reply\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_IREQ: + (void)printf("Information Request\n"); + /* XXX ID + Seq */ + break; + case ICMP_IREQREPLY: + (void)printf("Information Reply\n"); + /* XXX ID + Seq */ + break; +#ifdef ICMP_MASKREQ + case ICMP_MASKREQ: + (void)printf("Address Mask Request\n"); + break; +#endif +#ifdef ICMP_MASKREPLY + case ICMP_MASKREPLY: + (void)printf("Address Mask Reply (Mask 0x%08ld)\n", + ntohl(icp->icmp_mask)); + break; +#endif + default: + (void)printf("Unknown ICMP type: %u\n", icp->icmp_type); + } +} + +/* + * pr_iph -- + * Print an IP header with options. + */ +void +pr_iph(ip) + struct ip *ip; +{ + int hlen; + u_char *cp; + + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + 20; /* point to options */ + + (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); + (void)printf(" %1x %1x %02x %04x %04x", + ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); + (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, + (ip->ip_off) & 0x1fff); + (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); + /* dump and option bytes */ + while (hlen-- > 20) { + (void)printf("%02x", *cp++); + } + (void)putchar('\n'); +} + +/* + * pr_addr -- + * Return an ascii host address as a dotted quad and optionally with + * a hostname. + */ +char * +pr_addr(a) + in_addr_t a; +{ + struct hostent *hp; + struct in_addr in; + static char buf[16+3+MAXHOSTNAMELEN]; + + in.s_addr = a; + if ((options & F_NUMERIC) || + !(hp = gethostbyaddr((char *)&in.s_addr, sizeof(in.s_addr), AF_INET))) + (void)snprintf(buf, sizeof buf, "%s", inet_ntoa(in)); + else + (void)snprintf(buf, sizeof buf, "%s (%s)", hp->h_name, + inet_ntoa(in)); + return(buf); +} + +/* + * pr_retip -- + * Dump some info on a returned (via ICMP) IP packet. + */ +void +pr_retip(ip) + struct ip *ip; +{ + int hlen; + u_char *cp; + + pr_iph(ip); + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + hlen; + + if (ip->ip_p == 6) + (void)printf("TCP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); + else if (ip->ip_p == 17) + (void)printf("UDP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); +} + +void +fill(bp, patp) + char *bp, *patp; +{ + int ii, jj, kk; + int pat[16]; + char *cp; + + for (cp = patp; *cp; cp++) + if (!isxdigit(*cp)) + err(1, "patterns must be specified as hex digits"); + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], + &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], + &pat[13], &pat[14], &pat[15]); + + if (ii > 0) + for (kk = 0; + kk <= MAXPAYLOAD - (8 + sizeof(uint64) + ii); + kk += ii) + for (jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + if (!(options & F_QUIET)) { + (void)printf("PATTERN: 0x"); + for (jj = 0; jj < ii; ++jj) + (void)printf("%02x", bp[jj] & 0xFF); + (void)printf("\n"); + } +} + +/* + * when we get types of ICMP message with parts of the orig. datagram + * we want to try to assure ourselves that it is from this instance + * of ping, and not say, a refused finger connection or something + */ +int +check_icmph(iph) + struct ip *iph; +{ + struct icmp *icmph; + + /* only allow IP version 4 */ + if (iph->ip_v != 4) + return 0; + + /* Only allow ICMP */ + if (iph->ip_p != IPPROTO_ICMP) + return 0; + + icmph = (struct icmp *) (iph + (4 * iph->ip_hl)); + + /* make sure it is in response to an ECHO request */ + if (icmph->icmp_type != 8) + return 0; + + /* ok, make sure it has the right id on it */ + if (icmph->icmp_hun.ih_idseq.icd_id != ident) + return 0; + + return 1; +} + +void +usage() +{ + (void)fprintf(stderr, + "ping for OpenBeOS\n" + "usage: ping [-DdfLnqRrv] [-c count] [-I ifaddr] [-i wait]\n" + "\t[-l preload] [-p pattern] [-s packetsize] [-t ttl]" + " [-w maxwait] host\n"); + exit(1); +} diff --git a/src/apps/bin/route/Jamfile b/src/apps/bin/route/Jamfile new file mode 100644 index 0000000000..19e4450e32 --- /dev/null +++ b/src/apps/bin/route/Jamfile @@ -0,0 +1,2 @@ +SubDir OBOS_TOP src apps bin route ; + diff --git a/src/apps/bin/route/keywords.c b/src/apps/bin/route/keywords.c new file mode 100644 index 0000000000..ba38d6ab28 --- /dev/null +++ b/src/apps/bin/route/keywords.c @@ -0,0 +1,58 @@ +/* $OpenBSD: keywords.c,v 1.6 2000/07/27 20:12:24 angelos Exp $ */ + +/* WARNING! This file was generated by keywords.sh */ + +#include "keywords.h" + +struct keytab keywords[] = { + + {"add", K_ADD}, + {"blackhole", K_BLACKHOLE}, + {"change", K_CHANGE}, + {"cloning", K_CLONING}, + {"delete", K_DELETE}, + {"dst", K_DST}, + {"expire", K_EXPIRE}, + {"flush", K_FLUSH}, + {"gateway", K_GATEWAY}, + {"genmask", K_GENMASK}, + {"get", K_GET}, + {"host", K_HOST}, + {"hopcount", K_HOPCOUNT}, + {"iface", K_IFACE}, + {"interface", K_INTERFACE}, + {"ifa", K_IFA}, + {"ifp", K_IFP}, + {"inet", K_INET}, + {"inet6", K_INET6}, + {"ipx", K_IPX}, + {"iso", K_ISO}, + {"link", K_LINK}, + {"llinfo", K_LLINFO}, + {"lock", K_LOCK}, + {"lockrest", K_LOCKREST}, + {"mask", K_MASK}, + {"monitor", K_MONITOR}, + {"mtu", K_MTU}, + {"net", K_NET}, + {"netmask", K_NETMASK}, + {"nostatic", K_NOSTATIC}, + {"osi", K_OSI}, + {"prefixlen", K_PREFIXLEN}, + {"proto1", K_PROTO1}, + {"proto2", K_PROTO2}, + {"recvpipe", K_RECVPIPE}, + {"reject", K_REJECT}, + {"rtt", K_RTT}, + {"rttvar", K_RTTVAR}, + {"sa", K_SA}, + {"sendpipe", K_SENDPIPE}, + {"show", K_SHOW}, + {"ssthresh", K_SSTHRESH}, + {"static", K_STATIC}, + {"x25", K_X25}, + {"xns", K_XNS}, + {"xresolve", K_XRESOLVE}, + {0, 0} +}; + diff --git a/src/apps/bin/route/keywords.h b/src/apps/bin/route/keywords.h new file mode 100644 index 0000000000..81c547bf98 --- /dev/null +++ b/src/apps/bin/route/keywords.h @@ -0,0 +1,57 @@ +/* $OpenBSD: keywords.h,v 1.6 2000/07/27 20:12:25 angelos Exp $ */ + +/* WARNING! This file was generated by keywords.sh */ + +extern struct keytab { + char *kt_cp; + int kt_i; +} keywords[]; + + +#define K_ADD 1 +#define K_BLACKHOLE 2 +#define K_CHANGE 3 +#define K_CLONING 4 +#define K_DELETE 5 +#define K_DST 6 +#define K_EXPIRE 7 +#define K_FLUSH 8 +#define K_GATEWAY 9 +#define K_GENMASK 10 +#define K_GET 11 +#define K_HOST 12 +#define K_HOPCOUNT 13 +#define K_IFACE 14 +#define K_INTERFACE 15 +#define K_IFA 16 +#define K_IFP 17 +#define K_INET 18 +#define K_INET6 19 +#define K_IPX 20 +#define K_ISO 21 +#define K_LINK 22 +#define K_LLINFO 23 +#define K_LOCK 24 +#define K_LOCKREST 25 +#define K_MASK 26 +#define K_MONITOR 27 +#define K_MTU 28 +#define K_NET 29 +#define K_NETMASK 30 +#define K_NOSTATIC 31 +#define K_OSI 32 +#define K_PREFIXLEN 33 +#define K_PROTO1 34 +#define K_PROTO2 35 +#define K_RECVPIPE 36 +#define K_REJECT 37 +#define K_RTT 38 +#define K_RTTVAR 39 +#define K_SA 40 +#define K_SENDPIPE 41 +#define K_SHOW 42 +#define K_SSTHRESH 43 +#define K_STATIC 44 +#define K_X25 45 +#define K_XNS 46 +#define K_XRESOLVE 47 diff --git a/src/apps/bin/route/route.c b/src/apps/bin/route/route.c new file mode 100644 index 0000000000..ca319280db --- /dev/null +++ b/src/apps/bin/route/route.c @@ -0,0 +1,1498 @@ +/* + * Copyright (c) 1983, 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include "sys/socket.h" +#include "sys/mbuf.h" + +#include "net/if.h" +#include "net/route.h" +#include "net/if_dl.h" +#include "netinet/in.h" +#include "arpa/inet.h" +#include "netdb.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "keywords.h" + +int getaddr(int which, char *s, struct hostent **hpp); + +union sockunion { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_dl sdl; +} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp; + +typedef union sockunion *sup; +pid_t pid; +int rtm_addrs, s; +int forcehost, forcenet, doflush, nflag, af, qflag, tflag, keyword(); +int iflag, verbose, aflen = sizeof (struct sockaddr_in); +int locking, lockrest, debugonly; +struct rt_metrics rt_metrics; +u_long rtm_inits; +uid_t uid; + +char *routename __P((struct sockaddr *)); +char *netname __P((struct sockaddr *)); +void flushroutes __P((int, char **)); +int newroute __P((int, char **)); +void monitor __P((void)); +#ifdef INET6 +static int prefixlen __P((char *)); +#endif +void sockaddr __P((char *, struct sockaddr *)); +void sodump __P((sup, char *)); +void print_getmsg __P((struct rt_msghdr *, int)); +void print_rtmsg __P((struct rt_msghdr *, int)); +void pmsg_common __P((struct rt_msghdr *)); +void pmsg_addrs __P((char *, int)); +void bprintf __P((FILE *, int, u_char *)); +void mask_addr __P((void)); +//int getaddr __P((int, char *, struct hostent **)); +int rtmsg __P((int, int)); +int x25_makemask __P((void)); + +extern void show __P((int, char **)); /* XXX - from show.c */ + + +int opterr = 1; +int optind = 1; +int optopt = 0; +int optreset = 0; +char *optarg; + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +int _getopt(int nargc, char *const *nargv, const char *ostr) +{ + static char *place = EMSG; + char *oli; + + if (optreset || !*place) { + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return -1; + } + if (place[1] && *++place=='-') { + ++optind; + place=EMSG; + return -1; + } + } + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + if (optopt==(int)'-') + return -1; + if (!*place) + ++optind; + if (opterr && *ostr != ':') + fprintf(stderr, "illegal option --%c\n", optopt); + return BADCH; + } + if (*++oli != ':') { + optarg = NULL; + if (!*place) + ++optind; + } else { + if (*place) + optarg = place; + else if (nargc <= ++optind) { + place = EMSG; + if (*ostr==':') + return BADARG; + if (opterr) + printf("option requires an argument -- %c\n", optopt); + return BADCH; + } else + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return optopt; +} + +void usage(char *cp) +{ + printf("route for OpenBeOS!\n"); + if (cp) + (void) fprintf(stderr, "route: botched keyword: %s\n", cp); + (void) fprintf(stderr, + "usage: route [ -nqv ] cmd [[ - ] args ]\n"); + (void) fprintf(stderr, + "keywords: get, add, change, delete, show, flush, monitor.\n"); + exit(1); + /* NOTREACHED */ +} + +void quit(char *s) +{ + int sverrno = errno; + + (void) fprintf(stderr, "route: "); + if (s) + (void) fprintf(stderr, "%s: ", s); + (void) fprintf(stderr, "%s\n", strerror(sverrno)); + exit(1); + /* NOTREACHED */ +} + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +int +main(argc, argv) + int argc; + char **argv; +{ + int ch; + int rval = 0; + + if (argc < 2) + usage(NULL); + + while ((ch = _getopt(argc, argv, "nqdtv")) != -1) { + switch(ch) { + case (int)'n': + nflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'v': + verbose = 1; + break; + case 't': + tflag = 1; + break; + case 'd': + debugonly = 1; + break; + default: + usage(NULL); + } + } + argc -= optind; + argv += optind; + + pid = getpid(); + uid = getuid(); + if (tflag) + s = open("/dev/null", O_WRONLY); + else + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + quit("socket"); + if (*argv == NULL) + goto no_cmd; + switch (keyword(*argv)) { + case K_GET: + uid = 0; + /* FALLTHROUGH */ + case K_CHANGE: + case K_ADD: + case K_DELETE: + rval = newroute(argc, argv); + break; + case K_SHOW: + uid = 0; + show(argc, argv); + break; + case K_MONITOR: + monitor(); + break; + case K_FLUSH: + flushroutes(argc, argv); + break; + no_cmd: + default: + usage(*argv); + } + exit(rval); +} + +/* + * Purge all entries in the routing tables not + * associated with network interfaces. + */ +void +flushroutes(argc, argv) + int argc; + char *argv[]; +{ + size_t needed; + int mib[6], rlen, seqno; + char *buf = NULL, *next, *lim = NULL; + struct rt_msghdr *rtm; + struct sockaddr *sa; + + if (uid) { + errno = EACCES; + quit("must be root to alter routing table"); + } + shutdown(s, 0); /* Don't want to read back our messages */ + if (argc > 1) { + argv++; + if (argc == 2 && **argv == '-') + switch (keyword(*argv + 1)) { + case K_INET: + af = AF_INET; + break; +#ifdef INET6 + case K_INET6: + af = AF_INET6; + break; +#endif + case K_LINK: + af = AF_LINK; + break; + default: + goto bad; + } else +bad: usage(*argv); + } + mib[0] = PF_ROUTE; + mib[1] = 0; /* protocol */ + mib[2] = 0; /* wildcard address family */ + mib[3] = NET_RT_DUMP; + mib[4] = 0; /* no flags */ + if (sysctl(mib, 5, NULL, &needed, NULL, 0) < 0) + quit("route-sysctl-estimate"); + if (needed) { + if ((buf = malloc(needed)) == NULL) + quit("malloc"); + if (sysctl(mib, 5, buf, &needed, NULL, 0) < 0) + quit("actual retrieval of routing table"); + lim = buf + needed; + } + if (verbose) { + (void) printf("Examining routing table from sysctl\n"); + if (af) + printf("(address family %s)\n", (*argv + 1)); + } + if (buf == NULL) + return; + + seqno = 0; /* ??? */ + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (verbose) + print_rtmsg(rtm, rtm->rtm_msglen); + if ((rtm->rtm_flags & (RTF_GATEWAY|RTF_STATIC|RTF_LLINFO)) == 0) + continue; + sa = (struct sockaddr *)(rtm + 1); + if (af) { + if (sa->sa_family != af) + continue; + } + if (debugonly) + continue; + rtm->rtm_type = RTM_DELETE; + rtm->rtm_seq = seqno; + rlen = write(s, next, rtm->rtm_msglen); +printf("write gave %d\n", rlen); + if (rlen < (int)rtm->rtm_msglen) { + (void) fprintf(stderr, + "route: write to routing socket: %s\n", + strerror(errno)); + (void) printf("got only %d for rlen\n", rlen); + break; + } + seqno++; + if (qflag) + continue; + if (verbose) + print_rtmsg(rtm, rlen); + else { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + (void) printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ? + routename(sa) : netname(sa)); + sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa); + (void) printf("%-20.20s ", routename(sa)); + (void) printf("done\n"); + } + } + free(buf); +} + +static char hexlist[] = "0123456789abcdef"; + +char * +any_ntoa(sa) + const struct sockaddr *sa; +{ + static char obuf[240]; + const char *in = sa->sa_data; + char *out = obuf; + int len = sa->sa_len; + + *out++ = 'Q'; + do { + *out++ = hexlist[(*in >> 4) & 15]; + *out++ = hexlist[(*in++) & 15]; + *out++ = '.'; + } while (--len > 0 && (out + 3) < &obuf[sizeof obuf-1]); + out[-1] = '\0'; + return (obuf); +} + +char *routename(struct sockaddr *sa) +{ + char *cp = NULL; + static char line[MAXHOSTNAMELEN]; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN]; + static int first = 1; + char *ns_print(); + char *ipx_print(); + + if (first) { + first = 0; + if (gethostname(domain, sizeof domain) == 0 && + (cp = strchr(domain, '.'))) + (void) strcpy(domain, cp + 1); + else + domain[0] = 0; + cp = NULL; + } + if (sa->sa_len == 0) + strcpy(line, "default"); + else switch (sa->sa_family) { + + case AF_INET: + { struct in_addr in; + in = ((struct sockaddr_in *)sa)->sin_addr; + + if (in.s_addr == INADDR_ANY || sa->sa_len < 4) + cp = "default"; + if (!cp && !nflag) { + if ((hp = gethostbyaddr((char *)&in.s_addr, + sizeof (in.s_addr), AF_INET)) != NULL) { + if ((cp = strchr(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = hp->h_name; + } + } + strncpy(line, cp ? cp : inet_ntoa(in), sizeof line); + break; + } + +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sin6; + int niflags; + +#ifdef NI_WITHSCOPEID + niflags = NI_WITHSCOPEID; +#else + niflags = 0; +#endif + if (nflag) + niflags |= NI_NUMERICHOST; + memset(&sin6, 0, sizeof(sin6)); + memcpy(&sin6, sa, sa->sa_len); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; +#ifdef __KAME__ + if (sa->sa_len == sizeof(struct sockaddr_in6) && + (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) && + sin6.sin6_scope_id == 0) { + sin6.sin6_scope_id = + ntohs(*(uint16 *)&sin6.sin6_addr.s6_addr[2]); + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } +#endif + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + line, sizeof(line), NULL, 0, niflags) != 0) + strncpy(line, "invalid", sizeof(line)); + break; + } +#endif + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + default: + (void) snprintf(line, sizeof line, "(%d) %s", + sa->sa_family, any_ntoa(sa)); + break; + } + return (line); +} + +/* + * Return the name of the network whose address is given. + * The address is assumed to be that of a net or subnet, not a host. + */ +char * +netname(sa) + struct sockaddr *sa; +{ + char *cp = NULL; + static char line[MAXHOSTNAMELEN]; + struct netent *np = 0; + in_addr_t net, mask, subnetshift; + char *ns_print(); + char *ipx_print(); + + switch (sa->sa_family) { + case AF_INET: + { + struct in_addr in = ((struct sockaddr_in *)sa)->sin_addr; + in.s_addr = ntohl(in.s_addr); + if (in.s_addr == 0) + cp = "default"; + else if (!nflag) { + if (IN_CLASSA(in.s_addr)) { + mask = IN_CLASSA_NET; + subnetshift = 8; + } else if (IN_CLASSB(in.s_addr)) { + mask = IN_CLASSB_NET; + subnetshift = 8; + } else { + mask = IN_CLASSC_NET; + subnetshift = 4; + } + /* + * If there are more bits than the standard mask + * would suggest, subnets must be in use. + * Guess at the subnet mask, assuming reasonable + * width subnet fields. + */ + while (in.s_addr &~ mask) + mask = (int)mask >> subnetshift; + + net = in.s_addr & mask; + while ((mask & 1) == 0) + mask >>= 1, net >>= 1; + np = getnetbyaddr(net, AF_INET); + if (np) + cp = np->n_name; + } + in = ((struct sockaddr_in *)sa)->sin_addr; + strncpy(line, cp ? cp : inet_ntoa(in), sizeof line); + break; + } +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sin6; + int niflags; + +#ifdef NI_WITHSCOPEID + niflags = NI_WITHSCOPEID; +#else + niflags = 0; +#endif + if (nflag) + niflags |= NI_NUMERICHOST; + memset(&sin6, 0, sizeof(sin6)); + memcpy(&sin6, sa, sa->sa_len); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; +#ifdef __KAME__ + if (sa->sa_len == sizeof(struct sockaddr_in6) && + (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) && + sin6.sin6_scope_id == 0) { + sin6.sin6_scope_id = + ntohs(*(uint16 *)&sin6.sin6_addr.s6_addr[2]); + sin6.sin6_addr.s6_addr[2] = 0; + sin6.sin6_addr.s6_addr[3] = 0; + } +#endif + + if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, + line, sizeof(line), NULL, 0, niflags) != 0) + strncpy(line, "invalid", sizeof(line)); + break; + } +#endif + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + default: + snprintf(line, sizeof line, "af %d: %s", + sa->sa_family, any_ntoa(sa)); + break; + } + return (line); +} + +void +set_metric(value, key) + char *value; + int key; +{ + int flag = 0; + u_long noval, *valp = &noval; + + switch (key) { +#define caseof(x, y, z) case x: valp = &rt_metrics.z; flag = y; break + caseof(K_MTU, RTV_MTU, rmx_mtu); + caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount); + caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire); + caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe); + caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe); + caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh); + caseof(K_RTT, RTV_RTT, rmx_rtt); + caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar); + } + rtm_inits |= flag; + if (lockrest || locking) + rt_metrics.rmx_locks |= flag; + if (locking) + locking = 0; + *valp = atoi(value); +} + +int newroute(int argc, char **argv) +{ + char *cmd, *dest = "", *gateway = "", *err; + int ishost = 0, ret = 0, attempts, oerrno, flags = RTF_STATIC; + int key; + struct hostent *hp = 0; + + if (uid) { + errno = EACCES; + quit("must be root to alter routing table"); + } + cmd = argv[0]; + if (*cmd != 'g') + shutdown(s, 0); /* Don't want to read back our messages */ + while (--argc > 0) { + if (**(++argv)== '-') { + switch (key = keyword(1 + *argv)) { + case K_LINK: + af = AF_LINK; + aflen = sizeof(struct sockaddr_dl); + break; + case K_INET: + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + break; +#ifdef INET6 + case K_INET6: + af = AF_INET6; + aflen = sizeof(struct sockaddr_in6); + break; +#endif + case K_SA: + af = PF_ROUTE; + aflen = sizeof(union sockunion); + break; + case K_IFACE: + case K_INTERFACE: + iflag++; + break; + case K_NOSTATIC: + flags &= ~RTF_STATIC; + break; + case K_LLINFO: + flags |= RTF_LLINFO; + break; + case K_LOCK: + locking = 1; + break; + case K_LOCKREST: + lockrest = 1; + break; + case K_HOST: + forcehost++; + break; + case K_REJECT: + flags |= RTF_REJECT; + break; + case K_BLACKHOLE: + flags |= RTF_BLACKHOLE; + break; + case K_PROTO1: + flags |= RTF_PROTO1; + break; + case K_PROTO2: + flags |= RTF_PROTO2; + break; + case K_CLONING: + flags |= RTF_CLONING; + break; + case K_XRESOLVE: + flags |= RTF_XRESOLVE; + break; + case K_STATIC: + flags |= RTF_STATIC; + break; + case K_IFA: + if (!--argc) + usage(1+*argv); + (void) getaddr(RTA_IFA, *++argv, 0); + break; + case K_IFP: + if (!--argc) + usage(1+*argv); + (void) getaddr(RTA_IFP, *++argv, 0); + break; + case K_GENMASK: + if (!--argc) + usage(1+*argv); + (void) getaddr(RTA_GENMASK, *++argv, 0); + break; + case K_GATEWAY: + if (!--argc) + usage(1+*argv); + (void) getaddr(RTA_GATEWAY, *++argv, 0); + break; + case K_DST: + if (!--argc) + usage(1+*argv); + ishost = getaddr(RTA_DST, *++argv, &hp); + dest = *argv; + break; + case K_NETMASK: + if (!--argc) + usage(1+*argv); + (void) getaddr(RTA_NETMASK, *++argv, 0); + /* FALLTHROUGH */ + case K_NET: + forcenet++; + break; +#ifdef INET6 + case K_PREFIXLEN: + argc--; + if (prefixlen(*++argv) == 128) { + forcenet = 0; + ishost = 1; + } else { + forcenet = 1; + ishost = 0; + } + break; +#endif + case K_MTU: + case K_HOPCOUNT: + case K_EXPIRE: + case K_RECVPIPE: + case K_SENDPIPE: + case K_SSTHRESH: + case K_RTT: + case K_RTTVAR: + if (!--argc) + usage(1+*argv); + set_metric(*++argv, key); + break; + default: + usage(1+*argv); + } + } else { + if ((rtm_addrs & RTA_DST) == 0) { + dest = *argv; + ishost = getaddr(RTA_DST, *argv, &hp); + } else if ((rtm_addrs & RTA_GATEWAY) == 0) { + gateway = *argv; + (void) getaddr(RTA_GATEWAY, *argv, &hp); + } else { + int hops = atoi(*argv); + + if (hops == 0) { + if (!qflag && strcmp(*argv, "0") == 0) + printf("%s,%s", + "old usage of trailing 0", + "assuming route to if\n"); + else + usage(NULL); + iflag = 1; + continue; + } else if (hops > 0 && hops < 10) { + if (!qflag) { + printf("old usage of trailing digit, "); + printf("assuming route via gateway\n"); + } + iflag = 0; + continue; + } + (void) getaddr(RTA_NETMASK, *argv, 0); + } + } + } + if (forcehost) + ishost = 1; + if (forcenet) + ishost = 0; + flags |= RTF_UP; + if (ishost) + flags |= RTF_HOST; + if (iflag == 0) + flags |= RTF_GATEWAY; + for (attempts = 1; ; attempts++) { + errno = 0; + if ((ret = rtmsg(*cmd, flags)) == 0) + break; + if (errno != ENETUNREACH && errno != ESRCH) + break; + if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) { + hp->h_addr_list++; + memcpy(&so_gate.sin.sin_addr, hp->h_addr_list[0], + hp->h_length); + } else + break; + } + if (*cmd == 'g') + exit(0); + oerrno = errno; + if (!qflag) { + (void) printf("%s %s %s", cmd, ishost? "host" : "net", dest); + if (*gateway) { + (void) printf(": gateway %s", gateway); + if (attempts > 1 && ret == 0 && af == AF_INET) + (void) printf(" (%s)", + inet_ntoa(so_gate.sin.sin_addr)); + } + if (ret == 0) + (void) printf("\n"); + } + if (ret != 0) { + switch (oerrno) { + case ESRCH: + err = "not in table"; + break; + case EBUSY: + err = "entry in use"; + break; + case ENOBUFS: + err = "routing table overflow"; + break; + default: + err = strerror(oerrno); + break; + } + (void) printf(": %s\n", err); + } + return (ret != 0); +} + +void inet_makenetandmask(uint32 net, struct sockaddr_in *sin, int bits) +{ + uint32 addr, mask = 0; + char *cp; + + rtm_addrs |= RTA_NETMASK; + if (net == 0) + mask = addr = 0; + else if (bits) { + addr = net; + mask = 0xffffffff << (32 - bits); + } else if (net < 128) { + addr = net << IN_CLASSA_NSHIFT; + mask = IN_CLASSA_NET; + } else if (net < 65536) { + addr = net << IN_CLASSB_NSHIFT; + mask = IN_CLASSB_NET; + } else if (net < 16777216L) { + addr = net << IN_CLASSC_NSHIFT; + mask = IN_CLASSC_NET; + } else { + addr = net; + if ((addr & IN_CLASSA_HOST) == 0) + mask = IN_CLASSA_NET; + else if ((addr & IN_CLASSB_HOST) == 0) + mask = IN_CLASSB_NET; + else if ((addr & IN_CLASSC_HOST) == 0) + mask = IN_CLASSC_NET; + else + mask = -1; + } + sin->sin_addr.s_addr = htonl(addr); + sin = &so_mask.sin; + sin->sin_addr.s_addr = htonl(mask); + sin->sin_len = 0; + sin->sin_family = 0; + cp = (char *)(&sin->sin_addr + 1); + while (*--cp == 0 && cp > (char *)sin) + ; + sin->sin_len = 1 + cp - (char *)sin; +} + +/* + * Interpret an argument as a network address of some kind, + * returning 1 if a host address, 0 if a network address. + */ +int getaddr(int which, char *s, struct hostent **hpp) +{ + sup su = NULL; + struct hostent *hp; + struct netent *np; + u_long val; + char *q, qs; + int afamily; + + if (af == 0) { + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + } + afamily = af; /* local copy of af so we can change it */ + rtm_addrs |= which; + switch (which) { + case RTA_DST: + su = &so_dst; + break; + case RTA_GATEWAY: + su = &so_gate; + break; + case RTA_NETMASK: + su = &so_mask; + break; + case RTA_GENMASK: + su = &so_genmask; + break; + case RTA_IFP: + su = &so_ifp; + afamily = AF_LINK; + break; + case RTA_IFA: + su = &so_ifa; + su->sa.sa_family = af; + break; + default: + usage("Internal Error"); + /*NOTREACHED*/ + } + su->sa.sa_len = aflen; + su->sa.sa_family = afamily; /* cases that don't want it have left already */ + if (strcmp(s, "default") == 0) { + switch (which) { + case RTA_DST: + forcenet++; + (void) getaddr(RTA_NETMASK, s, 0); + break; + case RTA_NETMASK: + case RTA_GENMASK: + su->sa.sa_len = 0; + } + return (0); + } + switch (afamily) { +#ifdef INET6 + case AF_INET6: + { + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = afamily; /*AF_INET6*/ + hints.ai_flags = AI_NUMERICHOST; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + if (getaddrinfo(s, "0", &hints, &res) != 0 || + res->ai_family != AF_INET6 || + res->ai_addrlen != sizeof(su->sin6)) { + (void) fprintf(stderr, "%s: bad value\n", s); + exit(1); + } + memcpy(&su->sin6, res->ai_addr, sizeof(su->sin6)); +#ifdef __KAME__ + if ((IN6_IS_ADDR_LINKLOCAL(&su->sin6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&su->sin6.sin6_addr)) && + su->sin6.sin6_scope_id) { + *(uint16 *)&su->sin6.sin6_addr.s6_addr[2] = + htons(su->sin6.sin6_scope_id); + su->sin6.sin6_scope_id = 0; + } +#endif + if (which == RTA_DST) + inet6_makenetandmask(&su->sin6); + freeaddrinfo(res); + return (0); + } +#endif + + case AF_LINK: + link_addr(s, &su->sdl); + return (1); + + case PF_ROUTE: + su->sa.sa_len = sizeof(*su); + sockaddr(s, &su->sa); + return (1); + + case AF_INET: + default: + break; + } + + if (hpp == NULL) + hpp = &hp; + *hpp = NULL; + + q = strchr(s,'/'); + if (q && which == RTA_DST) { + qs = *q; + *q = '\0'; + val = inet_addr(s); + if (val != INADDR_NONE) { + inet_makenetandmask(htonl(val), &su->sin, + strtoul(q+1, 0, 0)); + return (0); + } + *q =qs; + } + if (((val = inet_addr(s)) != INADDR_NONE) && + (which != RTA_DST || forcenet == 0)) { + su->sin.sin_addr.s_addr = val; + if (inet_lnaof(su->sin.sin_addr) != INADDR_ANY) + return (1); + else { + val = ntohl(val); + goto netdone; + } + } + if ((val = inet_network(s)) != INADDR_NONE || + (forcehost == 0 && (np = getnetbyname(s)) != NULL && + (val = np->n_net) != 0)) { +netdone: + if (which == RTA_DST) + inet_makenetandmask(val, &su->sin, 0); + return (0); + } + hp = gethostbyname(s); + if (hp) { + *hpp = hp; + su->sin.sin_family = hp->h_addrtype; + memcpy(&su->sin.sin_addr, hp->h_addr, hp->h_length); + return (1); + } + (void) fprintf(stderr, "route: %s: bad value\n", s); + exit(1); +} + +#ifdef INET6 +int +prefixlen(s) + char *s; +{ + int len = atoi(s), q, r; + + rtm_addrs |= RTA_NETMASK; + if (len < -1 || len > 129) { + (void) fprintf(stderr, "%s: bad value\n", s); + exit(1); + } + + q = len >> 3; + r = len & 7; + so_mask.sin6.sin6_family = AF_INET6; + so_mask.sin6.sin6_len = sizeof(struct sockaddr_in6); + memset((void *)&so_mask.sin6.sin6_addr, 0, + sizeof(so_mask.sin6.sin6_addr)); + if (q > 0) + memset((void *)&so_mask.sin6.sin6_addr, 0xff, q); + if (r > 0) + *((u_char *)&so_mask.sin6.sin6_addr + q) = (0xff00 >> r) & 0xff; + return (len); +} +#endif + +short ns_nullh[] = {0,0,0}; +short ns_bh[] = {-1,-1,-1}; + + +short ipx_nullh[] = {0,0,0}; +short ipx_bh[] = {-1,-1,-1}; + +void +interfaces() +{ + size_t needed; + int mib[6]; + char *buf = NULL, *lim, *next; + struct rt_msghdr *rtm; + + mib[0] = PF_ROUTE; + mib[1] = 0; /* protocol */ + mib[2] = 0; /* wildcard address family */ + mib[3] = NET_RT_IFLIST; + mib[4] = 0; /* no flags */ + if (sysctl(mib, 5, NULL, &needed, NULL, 0) < 0) + quit("route-sysctl-estimate"); + if (needed) { + if ((buf = malloc(needed)) == NULL) + quit("malloc"); + if (sysctl(mib, 5, buf, &needed, NULL, 0) < 0) + quit("actual retrieval of interface table"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + print_rtmsg(rtm, rtm->rtm_msglen); + } + free(buf); + } +} + +void +monitor() +{ + int n; + char msg[2048]; + + verbose = 1; + if (debugonly) { + interfaces(); + exit(0); + } + for(;;) { + time_t now; + n = read(s, msg, 2048); + now = time(NULL); + (void) printf("got message of size %d on %s", n, ctime(&now)); + print_rtmsg((struct rt_msghdr *)msg, n); + } +} + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +int +rtmsg(cmd, flags) + int cmd, flags; +{ + static int seq; + int rlen; + char *cp = m_rtmsg.m_space; + int l; + +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = ROUNDUP(u.sa.sa_len); memcpy(cp, &(u), l); cp += l;\ + if (verbose) sodump(&(u),"u");\ + } + + errno = 0; + memset(&m_rtmsg, 0, sizeof(m_rtmsg)); + if (cmd == 'a') + cmd = RTM_ADD; + else if (cmd == 'c') + cmd = RTM_CHANGE; + else if (cmd == 'g') { + cmd = RTM_GET; + if (so_ifp.sa.sa_family == 0) { + so_ifp.sa.sa_family = AF_LINK; + so_ifp.sa.sa_len = sizeof(struct sockaddr_dl); + rtm_addrs |= RTA_IFP; + } + } else + cmd = RTM_DELETE; +#define rtm m_rtmsg.m_rtm + rtm.rtm_type = cmd; + rtm.rtm_flags = flags; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + rtm.rtm_rmx = rt_metrics; + rtm.rtm_inits = rtm_inits; + + if (rtm_addrs & RTA_NETMASK) + mask_addr(); + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_GATEWAY, so_gate); + NEXTADDR(RTA_NETMASK, so_mask); + NEXTADDR(RTA_GENMASK, so_genmask); + NEXTADDR(RTA_IFP, so_ifp); + NEXTADDR(RTA_IFA, so_ifa); + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + if (verbose) + print_rtmsg(&rtm, l); + if (debugonly) + return (0); + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + perror("writing to routing socket"); + return (-1); + } + if (cmd == RTM_GET) { + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq)); + if (l < 0) + (void) fprintf(stderr, + "route: read from routing socket: %s\n", + strerror(errno)); + else + print_getmsg(&rtm, l); + } +#undef rtm + return (0); +} + +void mask_addr() +{ + int olen = so_mask.sa.sa_len; + char *cp1 = olen + (char *)&so_mask, *cp2; + + for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; ) + if (*--cp1 != 0) { + so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask; + break; + } + if ((rtm_addrs & RTA_DST) == 0) + return; + switch (so_dst.sa.sa_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + case 0: + return; + } + cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst; + cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst; + while (cp2 > cp1) + *--cp2 = 0; + cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask; + while (cp1 > (char*)so_dst.sa.sa_data) + *--cp1 &= *--cp2; +} + +char *msgtypes[] = { + "", + "RTM_ADD: Add Route", + "RTM_DELETE: Delete Route", + "RTM_CHANGE: Change Metrics or flags", + "RTM_GET: Report Metrics", + "RTM_LOSING: Kernel Suspects Partitioning", + "RTM_REDIRECT: Told to use different route", + "RTM_MISS: Lookup failed on this address", + "RTM_LOCK: fix specified metrics", + "RTM_OLDADD: caused by SIOCADDRT", + "RTM_OLDDEL: caused by SIOCDELRT", + "RTM_RESOLVE: Route created by cloning", + "RTM_NEWADDR: address being added to iface", + "RTM_DELADDR: address being removed from iface", + "RTM_IFINFO: iface status change", + 0, +}; + +char metricnames[] = +"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount\1mtu"; +char routeflags[] = +"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT\011CLONING\012XRESOLVE\013LLINFO\014STATIC\017PROTO2\020PROTO1"; +char ifnetflags[] = +"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1\017LINK2\020MULTICAST"; +char addrnames[] = +"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; + +void print_rtmsg(struct rt_msghdr *rtm, int msglen) +{ + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + + if (verbose == 0) + return; + if (rtm->rtm_version != RTM_VERSION) { + (void) printf("routing message version %d not understood\n", + rtm->rtm_version); + return; + } + (void)printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen); + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + (void) printf("if# %d, flags:", ifm->ifm_index); + bprintf(stdout, ifm->ifm_flags, ifnetflags); + pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + (void) printf("metric %d, flags:", ifam->ifam_metric); + bprintf(stdout, ifam->ifam_flags, routeflags); + pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs); + break; + default: + (void) printf("seq %d, errno %d, flags:", + rtm->rtm_seq, rtm->rtm_errno); + bprintf(stdout, rtm->rtm_flags, routeflags); + pmsg_common(rtm); + } +} + +void print_getmsg(struct rt_msghdr *rtm, int msglen) +{ + struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL; + struct sockaddr_dl *ifp = NULL; + struct sockaddr *sa; + char *cp; + int i; + + (void) printf(" route to: %s\n", routename(&so_dst.sa)); + if (rtm->rtm_version != RTM_VERSION) { + (void)fprintf(stderr, + "routing message version %d not understood\n", + rtm->rtm_version); + return; + } + if (rtm->rtm_msglen > msglen) { + (void)fprintf(stderr, + "message length mismatch, in packet %d, returned %d\n", + rtm->rtm_msglen, msglen); + } + if (rtm->rtm_errno) { + (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n", + strerror(rtm->rtm_errno), rtm->rtm_errno); + return; + } + cp = ((char *)(rtm + 1)); + if (rtm->rtm_addrs) + for (i = 1; i; i <<= 1) + if (i & rtm->rtm_addrs) { + sa = (struct sockaddr *)cp; + switch (i) { + case RTA_DST: + dst = sa; + break; + case RTA_GATEWAY: + gate = sa; + break; + case RTA_NETMASK: + mask = sa; + break; + case RTA_IFP: + if (sa->sa_family == AF_LINK && + ((struct sockaddr_dl *)sa)->sdl_nlen) + ifp = (struct sockaddr_dl *)sa; + break; + } + ADVANCE(cp, sa); + } + if (dst && mask) + mask->sa_family = dst->sa_family; /* XXX */ + if (dst) + (void)printf("destination: %s\n", routename(dst)); + if (mask) { + int savenflag = nflag; + + nflag = 1; + (void)printf(" mask: %s\n", routename(mask)); + nflag = savenflag; + } + if (gate && rtm->rtm_flags & RTF_GATEWAY) + (void)printf(" gateway: %s\n", routename(gate)); + if (ifp) + (void)printf(" interface: %.*s\n", + ifp->sdl_nlen, ifp->sdl_data); + (void)printf(" flags: "); + bprintf(stdout, rtm->rtm_flags, routeflags); + +#define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') +#define msec(u) (((u) + 500) / 1000) /* usec to msec */ + + (void) printf("\n%s\n", "\ + recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire"); + printf("%8d%c ", (int)rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); + printf("%8d%c ", (int)rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); + printf("%8d%c ", (int)rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); + printf("%8d%c ", (int)msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); + printf("%8d%c ", (int)msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR)); + printf("%8d%c ", (int)rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT)); + printf("%8d%c ", (int)rtm->rtm_rmx.rmx_mtu, lock(MTU)); + if (rtm->rtm_rmx.rmx_expire) + rtm->rtm_rmx.rmx_expire -= time(0); + printf("%8d%c\n", (int)rtm->rtm_rmx.rmx_expire, lock(EXPIRE)); +#undef lock +#undef msec +#define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) + if (verbose) + pmsg_common(rtm); + else if (rtm->rtm_addrs &~ RTA_IGN) { + (void) printf("sockaddrs: "); + bprintf(stdout, rtm->rtm_addrs, addrnames); + putchar('\n'); + } +#undef RTA_IGN +} + +void pmsg_common(struct rt_msghdr *rtm) +{ + (void) printf("\nlocks: "); + bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames); + (void) printf(" inits: "); + bprintf(stdout, rtm->rtm_inits, metricnames); + pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs); +} + +void pmsg_addrs(char *cp, int addrs) +{ + struct sockaddr *sa; + int i; + + if (addrs == 0) + return; + (void) printf("\nsockaddrs: "); + bprintf(stdout, addrs, addrnames); + (void) putchar('\n'); + for (i = 1; i; i <<= 1) + if (i & addrs) { + sa = (struct sockaddr *)cp; + (void) printf(" %s", routename(sa)); + ADVANCE(cp, sa); + } + (void) putchar('\n'); + (void) fflush(stdout); +} + +void bprintf(FILE *fp, int b, u_char *s) +{ + int i; + int gotsome = 0; + + if (b == 0) + return; + while ((i = *s++)) { + if ((b & (1 << (i-1)))) { + if (gotsome == 0) + i = '<'; + else + i = ','; + (void) putc(i, fp); + gotsome = 1; + for (; (i = *s) > 32; s++) + (void) putc(i, fp); + } else + while (*s > 32) + s++; + } + if (gotsome) + (void) putc('>', fp); +} + +int keyword(char *cp) +{ + struct keytab *kt = keywords; + + while (kt->kt_cp && strcmp(kt->kt_cp, cp)) + kt++; + return (kt->kt_i); +} + +void sodump(sup su, char *which) +{ + char ntop_buf[NI_MAXHOST]; /*for inet_ntop()*/ + + switch (su->sa.sa_family) { + case AF_LINK: + (void) printf("%s: link %s; ", + which, link_ntoa(&su->sdl)); + break; + case AF_INET: + (void) printf("%s: inet %s; ", + which, inet_ntop(AF_INET, &su->sin.sin_addr, + ntop_buf, sizeof(ntop_buf))); + break; +#ifdef INET6 + case AF_INET6: + (void) printf("%s: inet6 %s; ", + which, inet_ntop(AF_INET6, &su->sin6.sin6_addr, + ntop_buf, sizeof(ntop_buf))); + break; +#endif + } + (void) fflush(stdout); +} + +/* States*/ +#define VIRGIN 0 +#define GOTONE 1 +#define GOTTWO 2 +/* Inputs */ +#define DIGIT (4*0) +#define END (4*1) +#define DELIM (4*2) + +void sockaddr(char *addr, struct sockaddr *sa) +{ + char *cp = (char *)sa; + int size = sa->sa_len; + char *cplim = cp + size; + int byte = 0, state = VIRGIN, new = 0; + + memset(cp, 0, size); + cp++; + do { + if ((*addr >= '0') && (*addr <= '9')) { + new = *addr - '0'; + } else if ((*addr >= 'a') && (*addr <= 'f')) { + new = *addr - 'a' + 10; + } else if ((*addr >= 'A') && (*addr <= 'F')) { + new = *addr - 'A' + 10; + } else if (*addr == 0) + state |= END; + else + state |= DELIM; + addr++; + switch (state /* | INPUT */) { + case GOTTWO | DIGIT: + *cp++ = byte; /*FALLTHROUGH*/ + case VIRGIN | DIGIT: + state = GOTONE; byte = new; continue; + case GOTONE | DIGIT: + state = GOTTWO; byte = new + (byte << 4); continue; + default: /* | DELIM */ + state = VIRGIN; *cp++ = byte; byte = 0; continue; + case GOTONE | END: + case GOTTWO | END: + *cp++ = byte; /* FALLTHROUGH */ + case VIRGIN | END: + break; + } + break; + } while (cp < cplim); + sa->sa_len = cp - (char *)sa; +} diff --git a/src/apps/bin/route/show.c b/src/apps/bin/route/show.c new file mode 100644 index 0000000000..1681b585e6 --- /dev/null +++ b/src/apps/bin/route/show.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "sys/mbuf.h" +#include "sys/socket.h" +#include "sys/socketvar.h" +#include "sys/protosw.h" + +#include "net/if.h" +#include "net/if_dl.h" +#include "net/if_types.h" +#include "net/route.h" +#include "netinet/in.h" +#include "arpa/inet.h" + +#include "keywords.h" + +#include +#include +#include +#include + +/* XXX: things from route.c */ +extern char *routename __P((struct sockaddr *)); +extern char *netname __P((struct sockaddr *)); +extern int nflag; + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +/* + * Definitions for showing gateway flags. + */ +struct bits { + short b_mask; + char b_val; +}; +static const struct bits bits[] = { + { RTF_UP, 'U' }, + { RTF_GATEWAY, 'G' }, + { RTF_HOST, 'H' }, + { RTF_REJECT, 'R' }, + { RTF_BLACKHOLE, 'B' }, + { RTF_DYNAMIC, 'D' }, + { RTF_MODIFIED, 'M' }, + { RTF_DONE, 'd' }, /* Completed -- for routing messages only */ + { RTF_MASK, 'm' }, /* Mask Present -- for routing messages only */ + { RTF_CLONING, 'C' }, + { RTF_XRESOLVE, 'X' }, + { RTF_LLINFO, 'L' }, + { RTF_STATIC, 'S' }, + { RTF_PROTO1, '1' }, + { RTF_PROTO2, '2' }, + { 0 } +}; + +static void p_rtentry __P((struct rt_msghdr *)); +static void p_sockaddr __P((struct sockaddr *, int, int)); +static void p_flags __P((int, char *)); +static void pr_rthdr __P((void)); +static void pr_family __P((int)); + +int keyword(char *); +void usage(char *); + +/* + * Print routing tables. + */ +void show(int argc, char **argv) +{ + struct rt_msghdr *rtm; + char *buf = NULL, *next, *lim = NULL; + size_t needed; + int mib[6], af = 0; + struct sockaddr *sa; + + if (argc > 1) { + argv++; + if (argc == 2 && **argv == '-') + switch (keyword(*argv + 1)) { + case K_INET: + af = AF_INET; + break; +#ifdef INET6 + case K_INET6: + af = AF_INET6; + break; +#endif + case K_IPX: + af = AF_IPX; + break; + case K_LINK: + af = AF_LINK; + break; + default: + goto bad; + } else +bad: usage(*argv); + } +// mib[0] = CTL_NET; + mib[0] = PF_ROUTE; + mib[1] = 0; + mib[2] = 0; + mib[3] = NET_RT_DUMP; + mib[4] = 0; + if (sysctl(mib, 5, NULL, &needed, NULL, 0) < 0) { + perror("route-sysctl-estimate"); + exit(1); + } + if (needed > 0) { + if ((buf = malloc(needed)) == 0) { + printf("out of space\n"); + exit(1); + } + if (sysctl(mib, 5, buf, &needed, NULL, 0) < 0) { + perror("sysctl of routing table"); + exit(1); + } + lim = buf + needed; + } + + printf("Routing tables\n"); + + if (buf) { + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + sa = (struct sockaddr *)(rtm + 1); + if (af && sa->sa_family != af) + continue; + p_rtentry(rtm); + } + free(buf); + } +} + +/* column widths; each followed by one space */ +#define WID_DST 16 /* width of destination column */ +#define WID_GW 18 /* width of gateway column */ + +/* + * Print header for routing table columns. + */ +static void +pr_rthdr() +{ + printf("%-*.*s %-*.*s %-6.6s\n", + WID_DST, WID_DST, "Destination", + WID_GW, WID_GW, "Gateway", + "Flags"); +} + +/* + * Print a routing table entry. + */ +static void +p_rtentry(rtm) + struct rt_msghdr *rtm; +{ + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); +#ifdef notdef + static int masks_done, banner_printed; +#endif + static int old_af; + int af = 0, interesting = RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_MASK; + +#ifdef notdef + /* for the moment, netmasks are skipped over */ + if (!banner_printed) { + printf("Netmasks:\n"); + banner_printed = 1; + } + if (masks_done == 0) { + if (rtm->rtm_addrs != RTA_DST ) { + masks_done = 1; + af = sa->sa_family; + } + } else +#endif + af = sa->sa_family; + if (old_af != af) { + old_af = af; + pr_family(af); + pr_rthdr(); + } + if (rtm->rtm_addrs == RTA_DST) { + p_sockaddr(sa, 0, 36); + } else { + p_sockaddr(sa, rtm->rtm_flags, 16); + sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa); + p_sockaddr(sa, 0, 18); + } + p_flags(rtm->rtm_flags & interesting, "%-6.6s "); + putchar('\n'); +} + +/* + * Print address family header before a section of the routing table. + */ +static void +pr_family(af) + int af; +{ + char *afname; + + switch (af) { + case AF_INET: + afname = "Internet"; + break; +#ifdef INET6 + case AF_INET6: + afname = "Internet6"; + break; +#endif /* INET6 */ +/* + case AF_NS: + afname = "XNS"; + break; +*/ + case AF_IPX: + afname = "IPX"; + break; +/* + case AF_APPLETALK: + afname = "AppleTalk"; + break; +*/ + default: + afname = NULL; + break; + } + if (afname) + printf("\n%s:\n", afname); + else + printf("\nProtocol Family %d:\n", af); +} + + +static void +p_sockaddr(sa, flags, width) + struct sockaddr *sa; + int flags, width; +{ + char workbuf[128], *cplim; + char *cp = workbuf; + + switch(sa->sa_family) { + + case AF_LINK: + { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && + sdl->sdl_slen == 0) + (void) sprintf(workbuf, "link#%d", sdl->sdl_index); + else switch (sdl->sdl_type) { + case IFT_ETHER: + { + int i; + u_char *lla = (u_char *)sdl->sdl_data + + sdl->sdl_nlen; + + cplim = ""; + for (i = 0; i < sdl->sdl_alen; i++, lla++) { + cp += sprintf(cp, "%s%x", cplim, *lla); + cplim = ":"; + } + cp = workbuf; + break; + } + default: + cp = link_ntoa(sdl); + break; + } + break; + } + + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + if (sin->sin_addr.s_addr == 0) + cp = "default"; + else + cp = (flags & RTF_HOST) ? routename(sa) : netname(sa); + break; + } + +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa; + + cp = IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "default" : + ((flags & RTF_HOST) ? + routename(sa) : netname(sa)); + /* make sure numeric address is not truncated */ + if (strchr(cp, ':') != NULL && strlen(cp) > width) + width = strlen(cp); + break; + } +#endif /* INET6 */ + +/* + case AF_NS: + cp = ns_print((struct sockaddr_ns *)sa); + break; +*/ + default: + { + u_char *s = (u_char *)sa->sa_data, *slim; + + slim = sa->sa_len + (u_char *) sa; + cplim = cp + sizeof(workbuf) - 6; + cp += sprintf(cp, "(%d)", sa->sa_family); + while (s < slim && cp < cplim) { + cp += sprintf(cp, " %02x", *s++); + if (s < slim) + cp += sprintf(cp, "%02x", *s++); + } + cp = workbuf; + } + } + if (width < 0 ) + printf("%s ", cp); + else { + if (nflag) + printf("%-*s ", width, cp); + else + printf("%-*.*s ", width, width, cp); + } +} + +static void +p_flags(f, format) + int f; + char *format; +{ + char name[33], *flags; + const struct bits *p = bits; + + for (flags = name; p->b_mask && flags < &name[sizeof name-2]; p++) + if (p->b_mask & f) + *flags++ = p->b_val; + *flags = '\0'; + printf(format, name); +} diff --git a/src/apps/bin/traceroute/Jamfile b/src/apps/bin/traceroute/Jamfile new file mode 100644 index 0000000000..46c198009b --- /dev/null +++ b/src/apps/bin/traceroute/Jamfile @@ -0,0 +1,2 @@ +SubDir OBOS_TOP src apps bin traceroute ; + diff --git a/src/apps/bin/traceroute/traceroute.c b/src/apps/bin/traceroute/traceroute.c new file mode 100644 index 0000000000..97133c9a16 --- /dev/null +++ b/src/apps/bin/traceroute/traceroute.c @@ -0,0 +1,1030 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * traceroute host - trace the route ip packets follow going to "host". + * + * Attempt to trace the route an ip packet would follow to some + * internet host. We find out intermediate hops by launching probe + * packets with a small ttl (time to live) then listening for an + * icmp "time exceeded" reply from a gateway. We start our probes + * with a ttl of one and increase by one until we get an icmp "port + * unreachable" (which means we got to "host") or hit a max (which + * defaults to 64 hops & can be changed with the -m flag). Three + * probes (change with -q flag) are sent at each ttl setting and a + * line is printed showing the ttl, address of the gateway and + * round trip time of each probe. If the probe answers come from + * different gateways, the address of each responding system will + * be printed. If there is no response within a 5 sec. timeout + * interval (changed with the -w flag), a "*" is printed for that + * probe. + * + * Probe packets are UDP format. We don't want the destination + * host to process them so the destination port is set to an + * unlikely value (if some clod on the destination is using that + * value, it can be changed with the -p flag). + * + * A sample use might be: + * + * [yak 71]% traceroute nis.nsf.net. + * traceroute to nis.nsf.net (35.1.1.48), 64 hops max, 56 byte packet + * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms + * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms + * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms + * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms + * + * Note that lines 2 & 3 are the same. This is due to a buggy + * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards + * packets with a zero ttl. + * + * A more interesting example is: + * + * [yak 72]% traceroute allspice.lcs.mit.edu. + * traceroute to allspice.lcs.mit.edu (18.26.0.115), 64 hops max + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms + * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms + * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms + * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms + * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms + * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms + * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms + * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms + * 12 * * * + * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms + * 14 * * * + * 15 * * * + * 16 * * * + * 17 * * * + * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms + * + * (I start to see why I'm having so much trouble with mail to + * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away + * either don't send ICMP "time exceeded" messages or send them + * with a ttl too small to reach us. 14 - 17 are running the + * MIT C Gateway code that doesn't send "time exceeded"s. God + * only knows what's going on with 12. + * + * The silent gateway 12 in the above may be the result of a bug in + * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) + * sends an unreachable message using whatever ttl remains in the + * original datagram. Since, for gateways, the remaining ttl is + * zero, the icmp "time exceeded" is guaranteed to not make it back + * to us. The behavior of this bug is slightly more interesting + * when it appears on the destination system: + * + * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms + * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms + * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms + * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms + * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms + * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms + * 7 * * * + * 8 * * * + * 9 * * * + * 10 * * * + * 11 * * * + * 12 * * * + * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! + * + * Notice that there are 12 "gateways" (13 is the final + * destination) and exactly the last half of them are "missing". + * What's really happening is that rip (a Sun-3 running Sun OS3.5) + * is using the ttl from our arriving datagram as the ttl in its + * icmp reply. So, the reply will time out on the return path + * (with no notice sent to anyone since icmp's aren't sent for + * icmp's) until we probe with a ttl that's at least twice the path + * length. I.e., rip is really only 7 hops away. A reply that + * returns with a ttl of 1 is a clue this problem exists. + * Traceroute prints a "!" after the time if the ttl is <= 1. + * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or + * non-standard (HPUX) software, expect to see this problem + * frequently and/or take care picking the target host of your + * probes. + * + * Other possible annotations after the time are !H, !N, !P (got a host, + * network or protocol unreachable, respectively), !S or !F (source + * route failed or fragmentation needed -- neither of these should + * ever occur and the associated gateway is busted if you see one). If + * almost all the probes result in some kind of unreachable, traceroute + * will give up and exit. + * + * Notes + * ----- + * This program must be run by root or be setuid. (I suggest that + * you *don't* make it setuid -- casual use could result in a lot + * of unnecessary traffic on our poor, congested nets.) + * + * This program requires a kernel mod that does not appear in any + * system available from Berkeley: A raw ip socket using proto + * IPPROTO_RAW must interpret the data sent as an ip datagram (as + * opposed to data to be wrapped in a ip datagram). See the README + * file that came with the source to this program for a description + * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may + * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE + * MODIFIED TO RUN THIS PROGRAM. + * + * The udp port usage may appear bizarre (well, ok, it is bizarre). + * The problem is that an icmp message only contains 8 bytes of + * data from the original datagram. 8 bytes is the size of a udp + * header so, if we want to associate replies with the original + * datagram, the necessary information must be encoded into the + * udp header (the ip id could be used but there's no way to + * interlock with the kernel's assignment of ip id's and, anyway, + * it would have taken a lot more kernel hacking to allow this + * code to set the ip id). So, to allow two or more users to + * use traceroute simultaneously, we use this task's pid as the + * source port (the high bit is set to move the port number out + * of the "likely" range). To keep track of which probe is being + * replied to (so times and/or hop counts don't get confused by a + * reply that was delayed in transit), we increment the destination + * port number before each probe. + * + * Don't use this as a coding example. I was trying to find a + * routing problem and this code sort-of popped out after 48 hours + * without sleep. I was amazed it ever compiled, much less ran. + * + * I stole the idea for this program from Steve Deering. Since + * the first release, I've learned that had I attended the right + * IETF working group meetings, I also could have stolen it from Guy + * Almes or Matt Mathis. I don't know (or care) who came up with + * the idea first. I envy the originators' perspicacity and I'm + * glad they didn't keep the idea a secret. + * + * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or + * enhancements to the original distribution. + * + * I've hacked up a round-trip-route version of this that works by + * sending a loose-source-routed udp datagram through the destination + * back to yourself. Unfortunately, SO many gateways botch source + * routing, the thing is almost worthless. Maybe one day... + * + * -- Van Jacobson (van@helios.ee.lbl.gov) + * Tue Dec 20 03:50:13 PST 1988 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "sys/select.h" + +#include + +#include +//#include +#include +#include +#include +#include +#include +#include + +#define Fprintf (void)fprintf +#define Printf (void)printf + +#define MAX_LSRR ((MAX_IPOPTLEN - 4) / 4) + +/* + * Format of the data in a (udp) probe packet. + */ +struct packetdata { + u_char seq; /* sequence number of this packet */ + u_char ttl; /* ttl packet left with */ + uint32 sec; /* time packet left */ + uint32 usec; +}; + +struct in_addr gateway[MAX_LSRR + 1]; +int lsrrlen = 0; +int32 sec_perturb; +int32 usec_perturb; + +u_char packet[512], *outpacket; /* last inbound (icmp) packet */ + +int wait_for_reply (int, struct sockaddr_in *, struct timeval *); +void send_probe (int, int, int, struct sockaddr_in *); +int packet_ok (u_char *, int, struct sockaddr_in *, int, int); +void print (u_char *, int, struct sockaddr_in *); +char *inetname (struct in_addr); +u_short in_cksum (uint16 *, int); +void usage (void); + +int s; /* receive (icmp) socket file descriptor */ +int sndsock; /* send (udp) socket file descriptor */ +struct timezone tz; /* leftover */ + +int datalen; /* How much data */ +int headerlen; /* How long packet's header is */ + +char *source = 0; +char *hostname; + +int nprobes = 3; +int max_ttl = IPDEFTTL; +int first_ttl = 1; +u_short ident; +u_short port = 32768+666; /* start udp dest port # for probe packets */ +u_char proto = IPPROTO_UDP; +u_char icmp_type = ICMP_ECHO; /* default ICMP code/type */ +u_char icmp_code = 0; +int options; /* socket options */ +int verbose; +int waittime = 5; /* time to wait for response (in seconds) */ +int nflag; /* print addresses numerically */ +int dump; + +/* arc4random is defined in stdlib.h on OpenBSD, so we'll just forward + * declare the prototype here to stop the compiler moaning. + */ +uint32 arc4random(void); + +static void err(int exitval, char *where) +{ + printf("error: %s: error %d [%s]\n", where, errno, strerror(errno)); + exit(exitval); +} + +static void errx(int exitval, char *fmt_string, char *value) +{ + printf("error: "); + printf(fmt_string, value); + exit(exitval); +} + + +int main(int argc, char **argv) +{ + struct hostent *hp; + struct protoent *pe; + struct sockaddr_in from, to; + int ch, i, lsrr, on, probe, seq, tos, ttl, ttl_flag, incflag = 1; + struct ip *ip; + uint32 tmprnd; + int sump = 0; + + if ((pe = getprotobyname("icmp")) == NULL) { + Fprintf(stderr, "icmp: unknown protocol\n"); + exit(10); + } + if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0) + err(5, "icmp socket"); + if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + err(5, "raw socket"); + + /* revoke privs */ +// seteuid(getuid()); +// setuid(getuid()); + + ttl_flag = 0; + lsrr = 0; + on = 1; + seq = tos = 0; + while ((ch = getopt(argc, argv, "SDIdg:f:m:np:q:rs:t:w:vlP:c")) != -1) + switch (ch) { + case 'S': + sump = 1; + break; + case 'f': + first_ttl = atoi(optarg); + if (first_ttl < 1 || first_ttl > max_ttl) { + printf("min ttl must be 1 to %d.", max_ttl); + exit(1); + } + break; + case 'c': + incflag = 0; + break; + case 'd': + options |= SO_DEBUG; + break; + case 'D': + dump = 1; + break; + case 'g': + if (lsrr >= MAX_LSRR) { + printf("too many gateways; max %d", MAX_LSRR); + exit(1); + } + if (inet_aton(optarg, &gateway[lsrr]) == 0) { + hp = gethostbyname(optarg); + if (hp == 0) + errx(1, "unknown host %s", optarg); + memcpy(&gateway[lsrr], hp->h_addr, hp->h_length); + } + if (++lsrr == 1) + lsrrlen = 4; + lsrrlen += 4; + break; + case 'I': + proto = IPPROTO_ICMP; + break; + case 'l': + ttl_flag++; + break; + case 'm': + max_ttl = atoi(optarg); + if (max_ttl < first_ttl || max_ttl > MAXTTL) { + printf("max ttl must be %d to %d.", first_ttl, + MAXTTL); + exit(1); + } + break; + case 'n': + nflag++; + break; + case 'p': + port = atoi(optarg); + if (port < 1) + err(1, "port must be >0."); + break; + case 'P': + proto = atoi(optarg); + if (proto < 1) { + struct protoent *pent; + pent = getprotobyname(optarg); + if (pent) + proto = pent->p_proto; + else + err(1, "proto must be >=1, or a name."); + } + break; + case 'q': + nprobes = atoi(optarg); + if (nprobes < 1) + err(1, "nprobes must be >0."); + break; + case 'r': + options |= SO_DONTROUTE; + break; + case 's': + /* + * set the ip source address of the outbound + * probe (e.g., on a multi-homed host). + */ + source = optarg; + break; + case 't': + tos = atoi(optarg); + if (tos < 0 || tos > 255) + err(1, "tos must be 0 to 255."); + break; + case 'v': + verbose++; + break; + case 'w': + waittime = atoi(optarg); + if (waittime <= 1) + err(1, "wait must be >1 sec."); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + setlinebuf (stdout); + + (void) memset(&to, 0, sizeof(struct sockaddr)); + to.sin_family = AF_INET; + if (inet_aton(*argv, &to.sin_addr) != 0) + hostname = *argv; + else { + hp = gethostbyname(*argv); + if (hp == 0) + errx(1, "unknown host %s", *argv); + to.sin_family = hp->h_addrtype; + memcpy(&to.sin_addr, hp->h_addr, hp->h_length); + if ((hostname = strdup(hp->h_name)) == NULL) + err(1, "malloc"); + } + if (*++argv) + datalen = atoi(*argv); + + switch (proto) { + case IPPROTO_UDP: + headerlen = (sizeof(struct ip) + lsrrlen + + sizeof(struct udphdr) + sizeof(struct packetdata)); + break; + case IPPROTO_ICMP: + headerlen = (sizeof(struct ip) + lsrrlen + + sizeof(struct icmp) + sizeof(struct packetdata)); + break; + default: + headerlen = (sizeof(struct ip) + lsrrlen + + sizeof(struct packetdata)); + } + + if (datalen < 0 || datalen > IP_MAXPACKET - headerlen) { + printf("packet size must be 0 to %d.", + IP_MAXPACKET - headerlen); + exit(1); + } + + datalen += headerlen; + + outpacket = (u_char *)malloc(datalen); + if (outpacket == 0) + err(1, "malloc"); + (void) memset(outpacket, 0, datalen); + + ip = (struct ip *)outpacket; + if (lsrr != 0) { + u_char *p; + p = (u_char *)(ip + 1); + *p++ = IPOPT_NOP; + *p++ = IPOPT_LSRR; + *p++ = lsrrlen - 1; + *p++ = IPOPT_MINOFF; + gateway[lsrr] = to.sin_addr; + for (i = 1; i <= lsrr; i++) + *((struct in_addr *)p)++ = gateway[i]; + ip->ip_dst = gateway[0]; + } else + ip->ip_dst = to.sin_addr; + ip->ip_off = htons(0); + ip->ip_hl = (sizeof(struct ip) + lsrrlen) >> 2; + ip->ip_p = proto; + ip->ip_v = IPVERSION; + ip->ip_tos = tos; + + ident = (getpid() & 0xffff) | 0x8000; + tmprnd = arc4random(); + sec_perturb = (tmprnd & 0x80000000) ? -(tmprnd & 0x7ff) : + (tmprnd & 0x7ff); + usec_perturb = arc4random(); + + if (options & SO_DEBUG) + (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + if (options & SO_DONTROUTE) + (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); +#ifdef SO_SNDBUF + if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen, + sizeof(datalen)) < 0) + err(6, "SO_SNDBUF"); +#endif /* SO_SNDBUF */ +#ifdef IP_HDRINCL + if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on, + sizeof(on)) < 0) + err(6, "IP_HDRINCL"); +#endif /* IP_HDRINCL */ + if (options & SO_DEBUG) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + if (options & SO_DONTROUTE) + (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, + (char *)&on, sizeof(on)); + + if (source) { + (void) memset(&from, 0, sizeof(struct sockaddr)); + from.sin_family = AF_INET; + if (inet_aton(source, &from.sin_addr) == 0) + errx(1, "unknown host %s", source); + ip->ip_src = from.sin_addr; + if (getuid() != 0 && + (ntohl(from.sin_addr.s_addr) & 0xff000000U) == 0x7f000000U && + (ntohl(to.sin_addr.s_addr) & 0xff000000U) != 0x7f000000U) + err(1, "source is on 127/8, destination is not"); + + if (getuid() && + bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0) + err(1, "bind"); + } + + Fprintf(stderr, "traceroute to %s (%s)", hostname, + inet_ntoa(to.sin_addr)); + if (source) + Fprintf(stderr, " from %s", source); + Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen); + (void) fflush(stderr); + + if (first_ttl > 1) + Printf("Skipping %d intermediate hops\n", first_ttl - 1); + + for (ttl = first_ttl; ttl <= max_ttl; ++ttl) { + in_addr_t lastaddr = 0; + int got_there = 0; + int unreachable = 0; + int timeout = 0; + uint64 dt; + int loss; + + Printf("%2d ", ttl); + for (probe = 0, loss = 0; probe < nprobes; ++probe) { + int cc; + struct timeval t1, t2; + struct timezone tz; + int code; + + (void) gettimeofday(&t1, &tz); + send_probe(++seq, ttl, incflag, &to); + while ((cc = wait_for_reply(s, &from, &t1))) { + (void) gettimeofday(&t2, &tz); + if (t2.tv_sec - t1.tv_sec > waittime) { + cc = 0; + break; + } + i = packet_ok(packet, cc, &from, seq, incflag); + /* Skip short packet */ + if (i == 0) + continue; + if (from.sin_addr.s_addr != lastaddr) { + print(packet, cc, &from); + lastaddr = from.sin_addr.s_addr; + } + dt = (uint64)(t2.tv_sec - t1.tv_sec) * 1000000 + + (uint64)(t2.tv_usec - t1.tv_usec); + Printf(" %u", (u_int)(dt / 1000)); + if (dt % 1000) + Printf(".%u", (u_int)(dt % 1000)); + Printf(" ms"); + ip = (struct ip *)packet; + if (ttl_flag) + Printf(" (%d)", ip->ip_ttl); + if (i == -2) { +#ifndef ARCHAIC + ip = (struct ip *)packet; + if (ip->ip_ttl <= 1) + Printf(" !"); +#endif + ++got_there; + break; + } + /* time exceeded in transit */ + if (i == -1) + break; + code = i - 1; + switch (code) { + case ICMP_UNREACH_PORT: +#ifndef ARCHAIC + ip = (struct ip *)packet; + if (ip->ip_ttl <= 1) + Printf(" !"); +#endif /* ARCHAIC */ + ++got_there; + break; + case ICMP_UNREACH_NET: + ++unreachable; + Printf(" !N"); + break; + case ICMP_UNREACH_HOST: + ++unreachable; + Printf(" !H"); + break; + case ICMP_UNREACH_PROTOCOL: + ++got_there; + Printf(" !P"); + break; + case ICMP_UNREACH_NEEDFRAG: + ++unreachable; + Printf(" !F"); + break; + case ICMP_UNREACH_SRCFAIL: + ++unreachable; + Printf(" !S"); + break; + case ICMP_UNREACH_FILTER_PROHIB: + case ICMP_UNREACH_NET_PROHIB: /*misuse*/ + ++unreachable; + Printf(" !A"); + break; + case ICMP_UNREACH_HOST_PROHIB: + ++unreachable; + Printf(" !C"); + break; + case ICMP_UNREACH_NET_UNKNOWN: + case ICMP_UNREACH_HOST_UNKNOWN: + ++unreachable; + Printf(" !U"); + break; + case ICMP_UNREACH_ISOLATED: + ++unreachable; + Printf(" !I"); + break; + case ICMP_UNREACH_TOSNET: + case ICMP_UNREACH_TOSHOST: + ++unreachable; + Printf(" !T"); + break; + default: + ++unreachable; + Printf(" !<%d>", i - 1); + break; + } + break; + } + if (cc == 0) { + Printf(" *"); + timeout++; + loss++; + } + (void) fflush(stdout); + } + if (sump) + Printf(" (%d%% loss)", (loss * 100) / nprobes); + putchar('\n'); + if (got_there || (unreachable && (unreachable + timeout) >= nprobes)) + break; + } + exit(0); +} + +int +wait_for_reply(sock, from, sent) + int sock; + struct sockaddr_in *from; + struct timeval *sent; +{ + struct timeval now, wait; + int cc = 0, fdsn; + size_t fromlen = sizeof (*from); + fd_set *fdsp; + + fdsn = howmany(sock+1, NFDBITS) * sizeof(fd_mask); + if ((fdsp = (fd_set *)malloc(fdsn)) == NULL) + err(1, "malloc"); + memset(fdsp, 0, fdsn); + FD_SET(sock, fdsp); + gettimeofday(&now, NULL); + wait.tv_sec = (sent->tv_sec + waittime) - now.tv_sec; + wait.tv_usec = sent->tv_usec - now.tv_usec; + if (wait.tv_usec < 0) { + wait.tv_usec += 1000000; + wait.tv_sec--; + } + if (wait.tv_sec < 0) + wait.tv_sec = wait.tv_usec = 0; + + if (select(sock+1, fdsp, (fd_set *)0, (fd_set *)0, &wait) > 0) + cc=recvfrom(s, (char *)packet, sizeof(packet), 0, + (struct sockaddr *)from, &fromlen); + + free(fdsp); + return (cc); +} + +void +dump_packet() +{ + u_char *p; + int i; + + Fprintf(stderr, "packet data:"); + for (p = outpacket, i = 0; i < datalen; i++) { + if ((i % 24) == 0) + Fprintf(stderr, "\n "); + Fprintf(stderr, " %02x", *p++); + } + Fprintf(stderr, "\n"); +} + +void +send_probe(seq, ttl, iflag, to) + int seq, ttl, iflag; + struct sockaddr_in *to; +{ + struct ip *ip = (struct ip *)outpacket; + u_char *p = (u_char *)(ip + 1); + struct udphdr *up = (struct udphdr *)(p + lsrrlen); + struct icmp *icmpp = (struct icmp *)(p + lsrrlen); + struct packetdata *op; + int i; + struct timeval tv; + + ip->ip_len = htons(datalen); + ip->ip_ttl = ttl; + ip->ip_id = htons(ident+seq); + + switch(proto) { + case IPPROTO_ICMP: + icmpp->icmp_type = icmp_type; + icmpp->icmp_code = icmp_code; + icmpp->icmp_seq = htons(seq); + icmpp->icmp_id = htons(ident); + op = (struct packetdata *)(icmpp + 1); + break; + case IPPROTO_UDP: + up->uh_sport = htons(ident); + if (iflag) + up->uh_dport = htons(port+seq); + else + up->uh_dport = htons(port); + up->uh_ulen = htons((u_short)(datalen - sizeof(struct ip) - + lsrrlen)); + up->uh_sum = 0; + op = (struct packetdata *)(up + 1); + break; + default: + op = (struct packetdata *)(ip + 1); + break; + } + op->seq = seq; + op->ttl = ttl; + (void) gettimeofday(&tv, &tz); + + /* + * We don't want hostiles snooping the net to get any useful + * information about us. Send the timestamp in network byte order, + * and perturb the timestamp enough that they won't know our + * real clock ticker. We don't want to perturb the time by too + * much: being off by a suspiciously large amount might indicate + * OpenBSD. + * + * The timestamps in the packet are currently unused. If future + * work wants to use them they will have to subtract out the + * perturbation first. + */ + (void) gettimeofday(&tv, &tz); + op->sec = htonl(tv.tv_sec + sec_perturb); + op->usec = htonl((tv.tv_usec + usec_perturb) % 1000000); + + if (proto == IPPROTO_ICMP && icmp_type == ICMP_ECHO) { + icmpp->icmp_cksum = 0; + icmpp->icmp_cksum = in_cksum((u_short *)icmpp, + datalen - sizeof(struct ip) - lsrrlen); + if (icmpp->icmp_cksum == 0) icmpp->icmp_cksum = 0xffff; + } + + if (dump) + dump_packet(); + + i = sendto(sndsock, outpacket, datalen, 0, (struct sockaddr *)to, + sizeof(struct sockaddr_in)); + if (i < 0 || i != datalen) { + if (i<0) + perror("sendto"); + //Printf("traceroute: wrote %s %d chars, ret=%d\n", hostname, + // datalen, i); + (void) fflush(stdout); + } +} + +/* + * Convert an ICMP "type" field to a printable string. + */ +char * +pr_type(t) + u_char t; +{ + static char *ttab[] = { + "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable", + "Source Quench", "Redirect", "ICMP 6", "ICMP 7", + "Echo", "Router Advert", "Router Solicit", "Time Exceeded", + "Param Problem", "Timestamp", "Timestamp Reply", "Info Request", + "Info Reply", "Mask Request", "Mask Reply" + }; + + if (t > 18) + return ("OUT-OF-RANGE"); + + return (ttab[t]); +} + + +int +packet_ok(buf, cc, from, seq, iflag) + u_char *buf; + int cc; + struct sockaddr_in *from; + int seq; + int iflag; +{ + register struct icmp *icp; + u_char type, code; + int hlen; +#ifndef ARCHAIC + struct ip *ip; + + ip = (struct ip *) buf; + hlen = ip->ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) { + if (verbose) + Printf("packet too short (%d bytes) from %s\n", cc, + inet_ntoa(from->sin_addr)); + return (0); + } + cc -= hlen; + icp = (struct icmp *)(buf + hlen); +#else + icp = (struct icmp *)buf; +#endif /* ARCHAIC */ + type = icp->icmp_type; code = icp->icmp_code; + if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) || + type == ICMP_UNREACH || type == ICMP_ECHOREPLY ) { + struct ip *hip; + struct udphdr *up; + struct icmp *icmpp; + + hip = &icp->icmp_ip; + hlen = hip->ip_hl << 2; + + switch(proto) { + case IPPROTO_ICMP: + + if (icmp_type == ICMP_ECHO && + type == ICMP_ECHOREPLY && + icp->icmp_id == htons(ident) && + icp->icmp_seq == htons(seq)) + return (-2); /* we got there */ + + icmpp = (struct icmp *)((u_char *)hip + hlen); + if (hlen + 8 <= cc && hip->ip_p == IPPROTO_ICMP && + icmpp->icmp_id == htons(ident) && + icmpp->icmp_seq == htons(seq)) + return (type == ICMP_TIMXCEED? -1 : code + 1); + break; + + case IPPROTO_UDP: + up = (struct udphdr *)((u_char *)hip + hlen); + if (hlen + 12 <= cc && hip->ip_p == proto && + up->uh_sport == htons(ident) && + ((iflag && up->uh_dport == htons(port + seq)) || + (!iflag && up->uh_dport == htons(port)))) + return (type == ICMP_TIMXCEED? -1 : code + 1); + break; + default: + /* this is some odd, user specified proto, + * how do we check it? + */ + if (hip->ip_p == proto) + return (type == ICMP_TIMXCEED? -1 : code + 1); + } + } +#ifndef ARCHAIC + if (verbose) { + int i; + in_addr_t *lp = (in_addr_t *)&icp->icmp_ip; + + Printf("\n%d bytes from %s", cc, inet_ntoa(from->sin_addr)); + Printf(" to %s", inet_ntoa(ip->ip_dst)); + Printf(": icmp type %d (%s) code %d\n", type, pr_type(type), + icp->icmp_code); + for (i = 4; i < cc ; i += sizeof(in_addr_t)) + Printf("%2d: x%8.8lx\n", i, (unsigned long)*lp++); + } +#endif /* ARCHAIC */ + return (0); +} + + +void +print(buf, cc, from) + u_char *buf; + int cc; + struct sockaddr_in *from; +{ + struct ip *ip; + int hlen; + + ip = (struct ip *) buf; + hlen = ip->ip_hl << 2; + cc -= hlen; + + if (nflag) + Printf(" %s", inet_ntoa(from->sin_addr)); + else + Printf(" %s (%s)", inetname(from->sin_addr), + inet_ntoa(from->sin_addr)); + + if (verbose) + Printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst)); +} + + +/* + * Checksum routine for Internet Protocol family headers (C Version) + */ +u_short +in_cksum(addr, len) + u_short *addr; + int len; +{ + register int nleft = len; + register u_short *w = addr; + register u_short answer; + register int sum = 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; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) + sum += *(u_char *)w; + + /* + * add 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); +} + +/* + * Construct an Internet address representation. + * If the nflag has been supplied, give + * numeric value, otherwise try for symbolic name. + */ +char * +inetname(in) + struct in_addr in; +{ + register char *cp; + register struct hostent *hp; + static int first = 1; + static char domain[MAXHOSTNAMELEN], line[MAXHOSTNAMELEN]; + + if (first && !nflag) { + first = 0; + if (gethostname(domain, sizeof domain) == 0 && + (cp = strchr(domain, '.')) != NULL) { + strncpy(cp + 1, domain, sizeof(domain)); + } + } + if (!nflag && in.s_addr != INADDR_ANY) { + hp = gethostbyaddr((char *)&in, sizeof(in), AF_INET); + if (hp != NULL) { + if ((cp = strchr(hp->h_name, '.')) != NULL && + strcmp(cp + 1, domain) == 0) + *cp = '\0'; + strncpy(hp->h_name, line, sizeof(line)); + return (line); + } + } + return (inet_ntoa(in)); +} + +void +usage() +{ + extern char *__progname; + fprintf(stderr, +"traceroute for OpenBeOS\n" +"usage: %s [-SDIdnrvc] [-g gateway_addr] ... [-m max_ttl] [-p port#]\n\t\ +[-P proto] [-q nqueries] [-s src_addr] [-t tos]\n\t\ +[-w wait] [-f first_ttl] host [data size]\n", __progname); + exit(1); +}