#include <stdio.h> #include <sys/param.h> #include <sys/stream.h> #include <sys/inet/in.h> #define KERNEL 1 /* get kernel definitions */ #include <sys/inet/tcp.h> #include <sys/inet/socket.h> #include <sys/inet/udp.h> #include <sys/inet/ip_var.h> #include <sys/inet/tcp_timer.h> #define TCPSTATES 1 #include <sys/inet/tcp_fsm.h> #include <sys/inet/tcp_var.h> #include <sys/inet/tcpip.h> #include <sys/inet/tcpdebug.h> #include <sys/ethernet.h> static int strip = 0; /* true if we're stripping the high bit from kernel addrs */ /* predeclared */ doseek(); void doseekoff(); void doread(); main(argc, argv) char *argv[]; { char *xinu, *kmem; char action = '\0'; char *operand; int ai=1; xinu = "/unix"; kmem = "/dev/kmem"; if(argc > 1 && argv[ai][0] == '-') { action = argv[ai][1]; operand = &argv[ai++][2]; } if (argc > ai+1) xinu = argv[ai++]; if (argc > ai) kmem = argv[ai]; kern_init(xinu, kmem); switch(action) { case '\0': case 'c': tcps(0); udps(); break; case 'i': interfaces(); break; case 's': statistics(); break; case 'r': routes(0); break; case 'R': routes(1); break; case 'a': arps(); break; case 'C': tcps(1); break; case 't': if (*operand == '\0' || strcmp(operand, "tcp")==0) debugtcp(); else if (strcmp(operand, "il")==0) debugil(); else if (strcmp(operand, "qe")==0) debugqe(); break; default: fprintf(stderr, "Usage: %s -icCrat [unix [vmcore]]\n", argv[0]); break; } } /* * truncate name to rightmost n chars */ trunc(name, n) char *name; int n; { register int i=strlen(name); return ((i<=n) ? name : name+(i-n)); } #include "nlist.h" struct nlist nl[] = { {"_ipif", 0}, #define NL_INET 0 {"_ipstat", 0}, #define NL_IPSTAT 1 {"_tcpstat", 0}, #define NL_TCPSTAT 2 {"_Ntcp", 0}, #define NL_NTCP 3 {"_tcpsocks", 0}, #define NL_TCP 4 {"_ip_routes", 0}, #define NL_ROUTE 5 {"_ip_arps", 0}, #define NL_ARP 6 {"_Ninet", 0}, #define NL_NINET 7 {"_Nip_route", 0}, #define NL_NROUTE 8 {"_Nip_arp", 0}, #define NL_NARP 9 {"_ip_default_route", 0}, #define NL_DR 10 {"_bugarr", 0}, #define NL_TCPDEB 11 {"_Nbugarr", 0}, #define NL_NBUGARR 12 {"_Nudp", 0}, #define NL_NUDP 13 {"_udpconn", 0}, #define NL_UDP 14 {"_ild", 0}, #define NL_ILDEB 15 {"_qed", 0}, #define NL_QEDEB 16 {0, 0} }; int kern_fd; kern_init(xinu, kmem) char *xinu, *kmem; { int i; nlist(xinu, nl); if((long)nl[0].n_value == 0){ fprintf(stderr, "nlist %s failed\n", xinu); exit(1); } if(strcmp(kmem, "/dev/kmem") != 0){ for(i = 0; nl[i].n_name; i++){ nl[i].n_value &= 0xffffff; } strip = 1; } kern_fd = open(kmem, 0); if(kern_fd < 0){ perror(kmem); exit(1); } } interfaces() { extern char *xflags(); struct ipif ipif[128]; int i, ninet; if (doseek(NL_NINET) < 0) { printf("Internet not compiled into this kernel\n"); return; } doread((char *)&ninet, sizeof ninet); if (ninet > (sizeof(ipif)/sizeof(struct ipif))) ninet = sizeof(ipif)/sizeof(struct ipif); printf("%-4s %-12s %-12s %5s %6s %6s %6s %6s\n", "Mtu", "Network", "Address", "Flags", "Ipkts", "Ierrs", "Opkts", "Oerrs"); doseek(NL_INET); doread((char *)ipif, ninet*sizeof(struct ipif)); for(i = 0; i < ninet; i++){ if((ipif[i].flags&IFF_UP) == 0) continue; printf("%-4d %-12s ", ipif[i].mtu, trunc(in_host(ipif[i].that),12)); printf("%-12s %5s %6d %6d %6d %6d\n", trunc(in_host(ipif[i].thishost),12), xflags(ipif[i].flags, "UHA?", " "), ipif[i].ipackets, ipif[i].ierrors, ipif[i].opackets, ipif[i].oerrors); } } statistics() { struct ipstat stats; struct tcpstat tcpstat; if (doseek(NL_IPSTAT) < 0) { printf("Internet not compiled into this kernel\n"); return; } doread((char *)&stats, sizeof(stats)); printf("IP:\n"); printf("%6d bad sums\n", stats.ips_badsum); printf("%6d short packets\n", stats.ips_tooshort); printf("%6d short data\n", stats.ips_toosmall); printf("%6d bad header lengths\n", stats.ips_badhlen); printf("%6d real bad header lengths\n", stats.ips_badlen); printf("%6d queue overflows\n", stats.ips_qfull); printf("%6d output routing errors\n", stats.ips_route); printf("%6d fragmented packets\n", stats.ips_fragout); if (doseek(NL_TCPSTAT) < 0) { printf("Tcp not compiled into this kernel\n"); return; } doread((char *)&tcpstat, sizeof(tcpstat)); printf("TCP:\n"); printf("%6d bad sums\n", tcpstat.tcps_badsum); printf("%6d bad offsets\n", tcpstat.tcps_badoff); printf("%6d header drops\n", tcpstat.tcps_hdrops); printf("%6d bad segments\n", tcpstat.tcps_badsegs); printf("%6d retransmit timeouts\n", tcpstat.tcps_timeouts[0]); printf("%6d persist timeouts\n", tcpstat.tcps_timeouts[1]); printf("%6d keep-alive timeouts\n", tcpstat.tcps_timeouts[2]); printf("%6d quiet time timeouts\n", tcpstat.tcps_timeouts[3]); printf("%6d duplicate packets received\n", tcpstat.tcps_duplicates); printf("%6d possibly late packets received\n", tcpstat.tcps_delayed); } tcps(flag) { int i, ntcp; struct socket so; struct tcpcb tcpcb; extern char *xflags(); char b1[100], b2[100]; struct in_service *sp; #define SS_INTERESTING (SS_RCVATMARK|SS_OPEN|SS_ACTIVE|SS_WAITING|SS_PLEASEOPEN) if (doseek(NL_NTCP) < 0) { printf("Tcp not compiled into this kernel\n"); return; } doread((char *)&ntcp, sizeof ntcp); printf("Proto Dev Wque Rque State %18s %18s %14s\n", "Remote Addr", "Local Addr", "Cstate"); for(i = 0; i < ntcp; i++){ doseekoff(NL_TCP, sizeof(so)*i); doread((char *)&so, sizeof(so)); if((so.so_state&SS_INTERESTING) == 0) continue; if(so.so_tcpcb){ if(strip) so.so_tcpcb = (struct tcpcb *)((int)(so.so_tcpcb)&0xffffff); lseek(kern_fd, so.so_tcpcb, 0); doread((char *)&tcpcb, sizeof(tcpcb)); } else { tcpcb.t_state = 0; } sp = in_service(0, "tcp", so.so_fport); sprintf(b1, "%11s.%s", trunc(in_host(so.so_faddr),11), sp->name); sp = in_service(0, "tcp", so.so_lport); sprintf(b2, "%11s.%s", trunc(in_host(so.so_laddr),11), sp->name); if(!flag || tcpcb.t_state != TCPS_LISTEN) printf(" tcp %02d %4d %4d %5s %18.18s %18.18s %14s\n", so.so_dev, so.so_wcount, so.so_rcount, xflags(so.so_state, "OPRWA?", " "), b1, b2, tcpstates[tcpcb.t_state]); #ifdef ALL if(!flag || tcpcb.t_state != TCPS_LISTEN) printf("template %x\n", tcpcb.t_template); #endif if(flag && tcpcb.t_state != TCPS_LISTEN){ printf(" Timers:\n"); printf("\tretransmit %d\n\tpersist %d\n\tkeepalive %d\n\t2msl %d\n", tcpcb.t_timer[0], tcpcb.t_timer[1], tcpcb.t_timer[2], tcpcb.t_timer[3]); printf(" Send sequence variables:"); printf("\n\tunacked %d\n\tnext %d\n\turgent ptr %d", tcpcb.snd_una, tcpcb.snd_nxt, tcpcb.snd_up); printf("\n\tinitial number %d\n\twindow %d\n\thighest sent %d\n", tcpcb.iss, tcpcb.snd_wnd, tcpcb.snd_max); printf(" Receive sequence variables:"); printf("\n\tnext %d\n\turgent ptr %d", tcpcb.rcv_nxt, tcpcb.rcv_up); printf("\n\tinitial number %d\n\twindow %d\n\tadvertised %d\n", tcpcb.irs, tcpcb.rcv_wnd, tcpcb.rcv_adv); printf(" Transmit timing:"); printf("\n\tinactive %d\n\tround trip %d\n\tseq # timed %d\n\tsmoothed round trip %f\n", tcpcb.t_idle, tcpcb.t_rtt, tcpcb.t_rtseq, tcpcb.t_srtt); printf(" Status:"); printf("\n\tmax segment size %d", tcpcb.t_maxseg); printf("\n\tforcing out byte %d", tcpcb.t_force); printf("\n\tflags 0x%x\n", tcpcb.t_flags); printf("\n"); } } } udps() { int i, nudp; struct udp udp[132]; extern char *xflags(); char b1[100], b2[100]; struct in_service *sp; if (doseek(NL_NUDP) < 0) { printf("Udp not compiled into this kernel\n"); return; } doread((char *)&nudp, sizeof nudp); printf("\nProto Dev State %18s %18s\n", "Remote Addr", "Local Addr"); doseek(NL_UDP); doread((char *)udp, nudp*sizeof(struct udp)); for (i = 0; i < nudp; i++) { if(udp[i].rq == 0) continue; sp = in_service(0, "udp", udp[i].sport); sprintf(b1, "%s.%.6s", trunc(in_host(udp[i].src),11), sp->name); sp = in_service(0, "udp", udp[i].dport); sprintf(b2, "%s.%.6s", trunc(in_host(udp[i].dst),11), sp->name); printf(" udp %02d %5s %18.18s %18.18s\n", i, xflags(udp[i].flags, "ILC?", " "), b2, b1); } } char * xflags(fl, fs, buf) char *fs, *buf; { int i, len; char *os; os = buf; len = strlen(fs); for(i = 0; i < len; i++){ if(fl & (1<<i)) *buf++ = fs[i]; } *buf++ = '\0'; return(os); } routes(all) int all; { struct ip_route r[256]; int i; int nroute; doseek(NL_DR); doread((char *)r, sizeof(struct ip_route)); if (r[0].gate != 0) printf("Default route is %s\n\n", in_host(r[0].gate)); doseek(NL_NROUTE); doread((char *)&nroute, sizeof nroute); if (nroute > (sizeof(r)/sizeof(struct ip_route))) nroute = sizeof(r)/sizeof(struct ip_route); doseek(NL_ROUTE); doread((char *)r, nroute*sizeof(struct ip_route)); printf("%-14s %-14s\n", "Destination", "Gateway"); for(i = 0; i < nroute; i++){ if(all || r[i].dst!=r[i].gate){ printf("%-14s ", in_host(r[i].dst)); printf("%-14s\n", in_host(r[i].gate)); } } } arps() { struct ip_arp a[256]; int i, j, narp; if (doseek(NL_NARP) < 0) { printf("Arp not compiled into this kernel\n"); return; } doread((char *)&narp, sizeof narp); if (narp > (sizeof(a)/sizeof(struct ip_arp))) narp = sizeof(a)/sizeof(struct ip_arp); doseek(NL_ARP); doread((char *)a, narp*sizeof(struct ip_arp)); printf("%-11s Ether-Address\n", "Host"); for(i = 0; i < narp; i++){ if(a[i].inaddr){ printf("%-11s ", in_host(a[i].inaddr)); for(j = 0; j < 6; j++){ printf("%02x ", a[i].enaddr[j]); } printf("\n"); } } } #define NEXT(i) ((i+1)%SIZDEBUG) #define PREV(i) ((i+SIZDEBUG-1)%SIZDEBUG) #define HEADER\ "\ntime src dst seq bytes ack wind cksm urg flags\n" #define FORMAT "%4d %c %8x.%-4d %8x.%-4d %8x %3x %8x %4x %04x %8x %s\n" debugtcp() { struct tcpdebug d[SIZDEBUG]; int i, lasti= 0; time_t t, lastt = 0; time_t base = 0; int line = 0; char *xflags(); char xbuf[9]; for(;;) { /* get array */ if (doseek(NL_TCPDEB) < 0) { printf("Tcp debugging not compiled into this kernel\n"); return; } doread((char *)d, sizeof(d)); /* see if we've overflowed the buffer (or first time around) */ if (d[lasti].stamp != lastt) { if (lastt != 0) printf("** missing messages **\n"); /* find last(highest) timestamped entry */ for (i = NEXT(lasti); i != lasti; i = NEXT(i)) { if (d[i].stamp < lastt) { if (base == 0) base = d[i].stamp; lasti = PREV(i); lastt = 0; /* for start of next loop */ break; } lastt = d[i].stamp; } } /* print all new entries */ for (i = NEXT(lasti); i != lasti; i = NEXT(i)) { if (d[i].stamp < lastt) break; if (d[i].stamp == 0) continue; if ((line++ % 22) == 0) printf(HEADER); printf(FORMAT, d[i].stamp-base, d[i].inout ? 'o' : 'i', ntohl(d[i].hdr.ti_src), ntohs(d[i].hdr.ti_sport), ntohl(d[i].hdr.ti_dst), ntohs(d[i].hdr.ti_dport), ntohl(d[i].hdr.ti_seq), ntohs(d[i].hdr.ti_len) - sizeof(struct tcphdr), ntohl(d[i].hdr.ti_ack), ntohs(d[i].hdr.ti_win), ntohs(d[i].hdr.ti_sum), ntohs(d[i].hdr.ti_urp), xflags(d[i].hdr.ti_flags&0xff, "FSRPAU??", xbuf) ); lastt = d[i].stamp; lasti = i; } fflush(stdout); sleep(1); } } #define QEDEBSIZE 64 #define QENEXT(i) ((i+1)%QEDEBSIZE) #define QEPREV(i) ((i+QEDEBSIZE-1)%QEDEBSIZE) #define QEHEADER\ "\n time src dst type\n" #define QEFORMAT "%7d %c %02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x %4x\n" #define ILDEBSIZE 64 #define ILNEXT(i) ((i+1)%ILDEBSIZE) #define ILPREV(i) ((i+ILDEBSIZE-1)%ILDEBSIZE) #define ILHEADER\ "\n time dst arp type\n" #define ILFORMAT "%7d %c %02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x %4x\n" debugil() { struct { time_t time; unsigned short code; struct etherpup pup; } d[ILDEBSIZE]; int i, lasti= 0; time_t t, lastt = 0; time_t base = 0; int line = 0; char *xflags(); char xbuf[9]; for(;;) { /* get array */ if (doseek(NL_ILDEB) < 0) { printf("Interlan debugging not compiled into this kernel\n"); return; } doread((char *)d, sizeof(d)); /* see if we've overflowed the buffer (or first time around) */ if (d[lasti].time != lastt) { if (lastt != 0) printf("** missing messages **\n"); /* find last(highest) timestamped entry */ for (i = ILNEXT(lasti); i != lasti; i = ILNEXT(i)) { if (d[i].time < lastt) { if (base == 0) base = d[i].time; lasti = ILPREV(i); lastt = 0; /* for start of next loop */ break; } lastt = d[i].time; } } /* print all new entries */ for (i = ILNEXT(lasti); i != lasti; i = ILNEXT(i)) { if (d[i].time < lastt) break; if (d[i].time == 0) continue; if ((line++ % 22) == 0) printf(ILHEADER); printf(ILFORMAT, d[i].time-base, d[i].code?'o':'i', d[i].pup.dhost[0], d[i].pup.dhost[1], d[i].pup.dhost[2], d[i].pup.dhost[3], d[i].pup.dhost[4], d[i].pup.dhost[5], d[i].pup.shost[0], d[i].pup.shost[1], d[i].pup.shost[2], d[i].pup.shost[3], d[i].pup.shost[4], d[i].pup.shost[5], d[i].pup.type ); lastt = d[i].time; lasti = i; } fflush(stdout); sleep(1); } } debugqe() { struct { time_t time; unsigned short code; struct etherpup pup; } d[QEDEBSIZE]; int i, lasti= 0; time_t t, lastt = 0; time_t base = 0; int line = 0; char *xflags(); char xbuf[9]; for(;;) { /* get array */ if (doseek(NL_QEDEB) < 0) { printf("Interlan debugging not compqeed into this kernel\n"); return; } doread((char *)d, sizeof(d)); /* see if we've overflowed the buffer (or first time around) */ if (d[lasti].time != lastt) { if (lastt != 0) printf("** missing messages **\n"); /* find last(highest) timestamped entry */ for (i = QENEXT(lasti); i != lasti; i = QENEXT(i)) { if (d[i].time < lastt) { if (base == 0) base = d[i].time; lasti = QEPREV(i); lastt = 0; /* for start of next loop */ break; } lastt = d[i].time; } } /* print all new entries */ for (i = QENEXT(lasti); i != lasti; i = QENEXT(i)) { if (d[i].time < lastt) break; if (d[i].time == 0) continue; if ((line++ % 22) == 0) printf(QEHEADER); printf(QEFORMAT, d[i].time-base, d[i].code?'o':'i', d[i].pup.dhost[0], d[i].pup.dhost[1], d[i].pup.dhost[2], d[i].pup.dhost[3], d[i].pup.dhost[4], d[i].pup.dhost[5], d[i].pup.shost[0], d[i].pup.shost[1], d[i].pup.shost[2], d[i].pup.shost[3], d[i].pup.shost[4], d[i].pup.shost[5], d[i].pup.type ); lastt = d[i].time; lasti = i; } fflush(stdout); sleep(1); } } doseek(nlitem) unsigned int nlitem; { if (nl[nlitem].n_value == 0) return -1; if(lseek(kern_fd, (long)nl[nlitem].n_value, 0) == -1){ perror("seek"); exit(1); } return 0; } void doseekoff(nlitem, offset) unsigned int nlitem; unsigned int offset; { if(lseek(kern_fd, (long)(nl[nlitem].n_value+offset), 0) == -1){ perror("seek"); exit(1); } } void doread(addr, size) char *addr; unsigned int size; { if(read(kern_fd, addr, size) < 0){ perror("read"); exit(1); } }