/* ICMP-related user commands * Copyright 1991 Phil Karn, KA9Q */ #include #include "global.h" #include "icmp.h" #include "ip.h" #include "mbuf.h" #include "netuser.h" #include "internet.h" #include "timer.h" #include "socket.h" #include "proc.h" #include "session.h" #include "cmdparse.h" #include "commands.h" static int doicmpec __ARGS((int argc, char *argv[],void *p)); static int doicmpstat __ARGS((int argc, char *argv[],void *p)); static int doicmptr __ARGS((int argc, char *argv[],void *p)); static void pingtx __ARGS((int s,void *ping1,void *p)); static void pinghdr __ARGS((struct session *sp,struct ping *ping)); static struct cmds Icmpcmds[] = { "echo", doicmpec, 0, 0, NULLCHAR, "status", doicmpstat, 0, 0, NULLCHAR, "trace", doicmptr, 0, 0, NULLCHAR, NULLCHAR }; int Icmp_trace; static int Icmp_echo = 1; int doicmp(argc,argv,p) int argc; char *argv[]; void *p; { return subcmd(Icmpcmds,argc,argv,p); } static int doicmpstat(argc,argv,p) int argc; char *argv[]; void *p; { register int i; int lim; /* Note that the ICMP variables are shown in column order, because * that lines up the In and Out variables on the same line */ lim = NUMICMPMIB/2; for(i=1;i<=lim;i++){ tprintf("(%2u)%-20s%10lu",i,Icmp_mib[i].name, Icmp_mib[i].value.integer); tprintf(" (%2u)%-20s%10lu\n",i+lim,Icmp_mib[i+lim].name, Icmp_mib[i+lim].value.integer); } return 0; } static int doicmptr(argc,argv,p) int argc; char *argv[]; void *p; { return setbool(&Icmp_trace,"ICMP tracing",argc,argv); } static int doicmpec(argc,argv,p) int argc; char *argv[]; void *p; { return setbool(&Icmp_echo,"ICMP echo response accept",argc,argv); } /* Send ICMP Echo Request packets */ int doping(argc,argv,p) int argc; char *argv[]; void *p; { struct proc *pinger = NULLPROC; /* Transmit process */ struct sockaddr_in from; struct icmp icmp; struct mbuf *bp; int32 timestamp,rtt,abserr; int s,fromlen; struct ping ping; struct session *sp; memset((char *)&ping,0,sizeof(ping)); /* Allocate a session descriptor */ if((sp = ping.sp = newsession(argv[1],PING,1)) == NULLSESSION){ tprintf("Too many sessions\n"); return 1; } if((sp->s = s = socket(AF_INET,SOCK_RAW,ICMP_PTCL)) == -1){ tprintf("Can't create socket\n"); keywait(NULLCHAR,1); freesession(sp); return 1; } tprintf("Resolving %s... ",sp->name); if((ping.target = resolve(sp->name)) == 0){ tprintf("Host %s unknown\n",sp->name); keywait(NULLCHAR,1); freesession(sp); return 1; } if(argc > 2) ping.len = atoi(argv[2]); if(argc > 3) ping.interval = atol(argv[3]); /* Optionally ping a range of IP addresses */ if(argc > 4) ping.incflag = 1; if(ping.interval != 0){ pinger = newproc("pingtx",300,pingtx,s,&ping,NULL,0); } else { /* One shot ping; let echo_proc hook handle response. * An ID of MAXINT16 will not be confused with a legal socket * number, which is used to identify repeated pings */ pingem(s,ping.target,0,MAXINT16,ping.len); freesession(sp); return 0; } /* Now collect the replies */ pinghdr(sp,&ping); for(;;){ fromlen = sizeof(from); if(recv_mbuf(s,&bp,0,(char *)&from,&fromlen) == -1) break; ntohicmp(&icmp,&bp); if(icmp.type != ICMP_ECHO_REPLY || icmp.args.echo.id != s){ /* Ignore other people's responses */ free_p(bp); continue; } /* Get stamp */ if(pullup(&bp,(char *)×tamp,sizeof(timestamp)) != sizeof(timestamp)){ /* The timestamp is missing! */ free_p(bp); /* Probably not necessary */ continue; } free_p(bp); ping.responses++; /* Compute round trip time, update smoothed estimates */ rtt = msclock() - timestamp; abserr = (rtt > ping.srtt) ? (rtt-ping.srtt) : (ping.srtt-rtt); if(ping.responses == 1){ /* First response, base entire SRTT on it */ ping.srtt = rtt; ping.mdev = 0; } else { ping.srtt = (7*ping.srtt + rtt + 4) >> 3; ping.mdev = (3*ping.mdev + abserr + 2) >> 2; } if((ping.responses % 20) == 0) pinghdr(sp,&ping); tprintf("%10lu%10lu%5lu%10lu%10lu%10lu\n", ping.sent,ping.responses, (ping.responses*100 + ping.sent/2)/ping.sent, rtt,ping.srtt,ping.mdev); } if(pinger != NULLPROC) killproc(pinger); freesession(sp); return 0; } static void pinghdr(sp,ping) struct session *sp; struct ping *ping; { tprintf("Pinging %s (%s); data %d interval %lu ms:\n", sp->name,inet_ntoa(ping->target),ping->len,ping->interval); tprintf(" sent rcvd % rtt avg rtt mdev\n"); } void echo_proc(source,dest,icmp,bp) int32 source; int32 dest; struct icmp *icmp; struct mbuf *bp; { int32 timestamp,rtt; if(Icmp_echo && icmp->args.echo.id == MAXINT16 && pullup(&bp,(char *)×tamp,sizeof(timestamp)) == sizeof(timestamp)){ /* Compute round trip time */ rtt = msclock() - timestamp; tprintf("%s: rtt %lu\n",inet_ntoa(source),rtt); } free_p(bp); } /* Ping transmit process. Runs until killed */ static void pingtx(s,ping1,p) int s; /* Socket to use */ void *ping1; void *p; { struct ping *ping; ping = (struct ping *)ping1; ping->sent = 0; if(ping->incflag){ for(;;){ pingem(s,ping->target++,0,MAXINT16,ping->len); ping->sent++; pause(ping->interval); } } else { for(;;){ pingem(s,ping->target,(int16)ping->sent++,(int16)s,ping->len); pause(ping->interval); } } } /* Send ICMP Echo Request packet */ int pingem(s,target,seq,id,len) int s; /* Raw socket on which to send ping */ int32 target; /* Site to be pinged */ int16 seq; /* ICMP Echo Request sequence number */ int16 id; /* ICMP Echo Request ID */ int16 len; /* Length of optional data field */ { struct mbuf *data; struct mbuf *bp; struct icmp icmp; struct sockaddr_in to; int32 clock; clock = msclock(); data = ambufw((int16)(len+sizeof(clock))); data->cnt = len+sizeof(clock); /* Set optional data field, if any, to all 55's */ if(len != 0) memset(data->data+sizeof(clock),0x55,len); /* Insert timestamp and build ICMP header */ memcpy(data->data,(char *)&clock,sizeof(clock)); icmpOutEchos++; icmpOutMsgs++; icmp.type = ICMP_ECHO; icmp.code = 0; icmp.args.echo.seq = seq; icmp.args.echo.id = id; if((bp = htonicmp(&icmp,data)) == NULLBUF){ free_p(data); return 0; } to.sin_family = AF_INET; to.sin_addr.s_addr = target; send_mbuf(s,bp,0,(char *)&to,sizeof(to)); return 0; }