# /* icmp.c */ /* EMACS_MODES: c !fill */ /* This file contains the routines to format, send, receive, and process * icmp and ggp packets. Most of the routines which need to be concerned * about the format of icmp and ggp packets are contained in this file. * The following routines are included: * do_icmp Receive and process all pending icmp packets * do_ggp Receive and process all pending ggp packets * ggp_send_echo Send a ggp echo packet to a specified host * icmp_send_echo Send an icmp echo packet to a specified host * icmp_timestamp Send an icmp timestamp request to a specified host * The routines in this file depend on the internet packet handling routines * in the file internet.c */ #include #include "params.h" #include "extern.h" #include "icmp_codes.h" #include "ip.h" #include "icmp.h" /* The following definitions for net equality comparison should be elsewhere * but aren't. The NET_MASK defines which bits of the internet address * comprise the network number on my local net. For example, on a Class A * network, NET_MASK would be (long_to_net(0xFF000000)), while on a Class * B net (or class A net with subnet routing) it would be * (long_to_net(0xFFFF0000)). It must be modified as appropriate for the * particular local network in use. * Because of the crockitude of long arithmetic on pdp11's, the long_to_net's * have been done by hand in the code below (sigh...) */ #define A_MASK (0x00FF0000L) /* Class A network */ #define B_MASK (0xFFFF0000L) /* Class B network or Class * A with subnet routing */ #define C_MASK (0xFFFF00FFL) /* Class C network or Class * B with subnet routing */ #define NET_MASK B_MASK /* for MIT-CSR, Class A with subnet */ /* This macro determines if the specified address is on my net, using the * NET_MASK defined above. */ #define my_net(a) ((*((long *)(a)) & NET_MASK) == ((*((long *)&myhost)) \ & NET_MASK)) static int icfd; /* icmp file descriptor */ static int ggfd; /* ggp file descriptor */ static int icinfd; /* input icmp file (testing) */ static int gginfd; /* input ggp file (testing) */ long ic_rcvd; /* number of rcvd. icmp pkts */ long ic_sent; /* number of sent icmp pkts */ long ic_dropped; /* number of dropped icmp pkts */ long gg_rcvd; /* rcvd. ggp pkts. */ long gg_sent; /* sent ggp pkts */ long gg_dropped; /* number of dropped ggp pkts */ do_icmp () /* Receive and process all pending icmp packets. At present this routine * handles only packets of ECHO and TIMESTAMP types and their replies, * plus redirect requests (handled by updating the kernel routing table). * If the packet is a foreign request, respond to it. Otherwise, it * must be a reply to a local request; try to find the request in * the timeout queue and call the completion routine for it. */ { reg caddr_t ppkt; /* ptr. to received packet */ reg struct icmp *picmp; /* ptr. to rcvd. icmp data */ unshort plen; /* total packet length */ unshort iclen; /* icmp data length */ unshort csum; /* checksum */ int stat; /* write status */ reg struct ip *pip; /* ptr. to in. hdr. of pkt */ int tvec[3]; /* vector for qtime call */ struct ip *ptp; /* ptr. to ip hdr of orig d'gram */ struct in_name *prd; /* ptr. to redirect gw. address */ long *netp,*mynetp; /* sigh. can't get my_net() to work */ struct { /* structure for redirect ioctl */ struct in_name fname; /* for packets to this host, */ #ifdef TEMPORARY int gate; /* redirect to this gateway */ #else struct in_name gate; /* redirect to this gateway */ #endif } ictl; while ((ppkt = in_read (icinfd, &plen)) != NULL) { ic_rcvd++; picmp = (struct icmp *)in_find_data (ppkt, &iclen); if (log & LOG_RCV) { logdate (); in_logpkt (ppkt, iclen, INPKT); } if (iclen & 01) /* odd-length packet; null-pad */ ((char *)picmp)[iclen] = 0; csum = picmp->ic_sum; picmp->ic_sum = 0; if (csum != ~cksum (picmp, (iclen + 1) >> 1, 0)) { if (log & LOG_ERR) { logdate (); printf ("icmp checksum error\n"); } ic_dropped++; } else switch (picmp->ic_type) { case IC_DUR: /* dest. unreachable */ case IC_SRQ: /* source quench */ case IC_TIME: /* timeout */ case IC_PARAM: /* parameter problem */ case IC_IRQ: /* information request */ case IC_IRPLY: /* information reply */ if (log & LOG_FOR) { logdate (); printf ("foreign request type %d ignored\n", picmp->ic_type); } ic_dropped++; break; case IC_ERPLY: /* echo reply packet */ picmp->ic_misc.ic_iseq.id_ident = short_to_net(picmp->ic_misc.ic_iseq.id_ident); picmp->ic_misc.ic_iseq.id_seq = short_to_net(picmp->ic_misc.ic_iseq.id_seq); if (log & LOG_LOC) { logdate (); printf ("Echo reply received, trans = %d\n", picmp->ic_misc.ic_iseq.id_ident); } if (!deq_timeout (picmp->ic_misc.ic_iseq.id_ident, ppkt)) { if (log & LOG_ERR) { logdate (); printf ("unsolicited reply ignored, trans = %d\n", picmp->ic_misc.ic_iseq.id_ident); } ic_dropped++; } break; case IC_TMRPLY: /* timestamp reply packet */ picmp->ic_misc.ic_iseq.id_ident = short_to_net(picmp->ic_misc.ic_iseq.id_ident); picmp->ic_misc.ic_iseq.id_seq = short_to_net(picmp->ic_misc.ic_iseq.id_seq); picmp->ic_data.ic_time.tm_orig = long_to_net(picmp->ic_data.ic_time.tm_orig); picmp->ic_data.ic_time.tm_recv = long_to_net(picmp->ic_data.ic_time.tm_recv); picmp->ic_data.ic_time.tm_trans = long_to_net(picmp->ic_data.ic_time.tm_trans); if (log & LOG_LOC) { logdate (); printf ("Timestamp reply received, trans = %d\n", picmp->ic_misc.ic_iseq.id_ident); } if (!deq_timeout (picmp->ic_misc.ic_iseq.id_ident, ppkt)) { if (log & LOG_ERR) { logdate (); printf ("unsolicited reply ignored, trans = %d\n", picmp->ic_misc.ic_iseq.id_ident); } ic_dropped++; } break; case IC_ECHO: /* echo request */ pip = in_header (ppkt); /* find in header */ if (log & LOG_FOR) { logdate (); printf ("foreign icmp echo request from %o,%o,%o,%o\n", pip->ip_src.in_net, pip->ip_src.in_hhost, pip->ip_src.in_mhost, pip->ip_src.in_lhost); } inn_copy (&pip->ip_src, &pip->ip_dest); pip->ip_len = short_to_net(pip->ip_len); picmp->ic_type = IC_ERPLY; picmp->ic_sum = 0; picmp->ic_sum = ~cksum(picmp, (iclen + 1) >> 1, 0); ic_sent++; if (in_write (icfd, ppkt, iclen, NULL, NULL, NOFILL) <= 0) { stat = errno; if (log & LOG_ERR) { logdate (); printf ("net write error, stat = %d\n", stat); } } else if (log & LOG_XMT) in_logpkt (ppkt, iclen, OUTPKT); break; case IC_TMSTMP: /* timestamp request */ pip = in_header (ppkt); /* find in header */ if (log & LOG_FOR) { logdate (); printf ("foreign timestamp request from %o,%o,%o,%o\n", pip->ip_src.in_net, pip->ip_src.in_hhost, pip->ip_src.in_mhost, pip->ip_src.in_lhost); } inn_copy (&pip->ip_src, &pip->ip_dest); pip->ip_len = short_to_net(pip->ip_len); picmp->ic_type = IC_TMRPLY; qtime (&tvec); /* get time to 60ths */ picmp->ic_data.ic_time.tm_recv = tvec_to_ut(tvec); picmp->ic_data.ic_time.tm_recv = long_to_net(picmp->ic_data.ic_time.tm_recv); picmp->ic_data.ic_time.tm_trans = picmp->ic_data.ic_time.tm_recv; picmp->ic_sum = 0; picmp->ic_sum = ~cksum(picmp, (iclen + 1) >> 1, 0); ic_sent++; if (in_write (icfd, ppkt, iclen, NULL, NULL, NOFILL) <= 0) { stat = errno; if (log & LOG_ERR) { logdate (); printf ("net write error, stat = %d\n", stat); } } else if (log & LOG_XMT) in_logpkt (ppkt, iclen, OUTPKT); break; case IC_REDIR: /* redirect */ prd = &picmp->ic_misc.ic_gaddr; /* gw addr */ ptp = &picmp->ic_data.ic_hdr.th_ip; inn_copy (&ptp->ip_dest, &ictl.fname); #ifdef TEMPORARY ictl.gate = prd->in_lhost; #else inn_copy (prd, &ictl.gate); #endif netp = (long *)prd; mynetp = (long *)&myhost; if ((*netp & NET_MASK) == (*mynetp & NET_MASK)) ioctl (icfd, NIOCHGADD, &ictl); if (log & LOG_FOR) { logdate (); printf ("icmp redirect, host %o,%o,%o,%o --> gw %o,%o,%o,%o\n", ptp->ip_dest.in_net, ptp->ip_dest.in_hhost, ptp->ip_dest.in_mhost, ptp->ip_dest.in_lhost, prd->in_net, prd->in_hhost, prd->in_mhost, prd->in_lhost); } break; default: /* unexpected packet */ if (log & LOG_ERR) { logdate (); printf ("unknown packet type %d ignored\n", picmp->ic_type); } ic_dropped++; break; } in_free (ppkt); } } do_ggp () /* Receive and process all pending ggp packets. At present this routine * handles only packets of ECHO type and their replies. * If the packet is a foreign request, respond to it. Otherwise, it * must be a reply to a local request; try to find the request in * the timeout queue and call the completion routine for it. */ { reg caddr_t ppkt; /* ptr. to received packet */ reg struct ggp *pggp; /* ptr. to rcvd. ggp data */ unshort plen; /* total packet length */ unshort gglen; /* ggp data length */ int stat; /* net write status */ reg struct ip *pip; /* ptr. to in header */ while ((ppkt = in_read (gginfd, &plen)) != NULL) { gg_rcvd++; pggp = (struct ggp *)in_find_data (ppkt, &gglen); if (log & LOG_RCV) { logdate (); in_logpkt (ppkt, gglen, INPKT); } switch (pggp->ic_type) { case GG_DUR: /* dest. unreachable */ case GG_SRQ: /* source quench */ case GG_REDIR: /* redirect */ if (log & LOG_FOR) { logdate (); printf ("foreign request type %d ignored\n", pggp->ic_type); } gg_dropped++; break; case GG_ERPLY: /* echo reply packet */ pggp->gg_seq = short_to_net(pggp->gg_seq); if (log & LOG_LOC) { logdate (); printf ("Echo reply received, trans = %d\n", pggp->gg_seq); } if (!deq_timeout (pggp->gg_seq, ppkt)) { if (log & LOG_ERR) { logdate (); printf ("unsolicited reply ignored, trans = %d\n", pggp->gg_seq); } gg_dropped++; } break; case GG_ECHO: /* echo request */ pip = in_header (ppkt); /* find in header */ if (log & LOG_FOR) { logdate (); printf ("foreign ggp echo request from %o,%o,%o,%o\n", pip->ip_src.in_net, pip->ip_src.in_hhost, pip->ip_src.in_mhost, pip->ip_src.in_lhost); } inn_copy (&pip->ip_src, &pip->ip_dest); pip->ip_len = short_to_net(pip->ip_len); pggp->gg_type = GG_ERPLY; gg_sent++; if (in_write (ggfd, ppkt, gglen, NULL, NULL, NOFILL) <= 0) { stat = errno; if (log & LOG_ERR) { logdate (); printf ("net write error, stat = %d\n", stat); } } else if (log & LOG_XMT) in_logpkt (ppkt, gglen, OUTPKT); break; default: /* unexpected packet */ if (log & LOG_ERR) { logdate (); printf ("unknown packet type %d ignored\n", pggp->gg_type); } gg_dropped++; break; } in_free (ppkt); } } ggp_send_echo (fhost, retries, timeout, comp, arg1, arg2) /* Send a ggp echo request to the specified foreign host. * Then enqueue the resulting packet on the timeout/retry queue, * with the specified number of retries and timeout interval, and * the specified completion routine and arguments. If an error * occurs in the send, call the completion routine directly * with the error status. * * Arguments: */ struct in_name *fhost; /* dest. host address */ int retries; /* number of retries */ int timeout; /* timeout interval in seconds */ reg int (*comp)(); /* completion routine */ caddr_t arg1; /* first arg to comp routine */ caddr_t arg2; /* second arg to comp routine */ { reg caddr_t ppkt; /* ptr. to echo packet */ reg struct ggp *pggp; /* ptr. to ggp portion of packet */ unshort psize; /* data area size in bytes */ int stat; /* write status */ unshort gglen; /* length of ggp data */ unshort uid; /* unique trans. id */ gglen = sizeof (struct ggp); if ((ppkt = in_alloc (gglen)) == NULL) { logdate (); printf ("out of space for packet, aborting!\n"); abort (); } uid = ident++; /* get unique id */ pggp = in_find_data (ppkt, &psize); pggp->gg_type = GG_ECHO; pggp->gg_code = 0; pggp->gg_seq = uid; pggp->gg_seq = short_to_net(pggp->gg_seq); /* byte swap if needed */ gg_sent++; if (in_write (ggfd, ppkt, gglen, 0, fhost, FILL) <= 0) { stat = errno; if (log & LOG_ERR) { logdate (); printf ("net write error %d, trans = %d\n", stat, uid); } in_free (ppkt); (*comp)(NULL, arg1, arg2, stat); return; } else { if (log & LOG_XMT) in_logpkt (ppkt, gglen, OUTPKT); enq_timeout (timeout, retries, ppkt, psize, ggfd, uid, comp, arg1, arg2); } } icmp_send_echo (fhost, retries, timeout, comp, arg1, arg2) /* Send a icmp echo request to the specified foreign host. * Then enqueue the resulting packet on the timeout/retry queue, * with the specified number of retries and timeout interval, and * the specified completion routine and arguments. If an error * occurs in the send, call the completion routine directly * with the error status. * * Arguments: */ struct in_name *fhost; /* dest. host address */ int retries; /* number of retries */ int timeout; /* timeout interval in seconds */ reg int (*comp)(); /* completion routine */ caddr_t arg1; /* first arg to comp routine */ caddr_t arg2; /* second arg to comp routine */ { reg caddr_t ppkt; /* ptr. to echo packet */ reg struct icmp *picmp; /* ptr. to icmp portion of packet */ unshort psize; /* data area size in bytes */ int stat; /* write status */ unshort uid; /* unique trans. id */ unshort iclen; /* length of icmp data */ iclen = sizeof (struct icmp); if ((ppkt = in_alloc (iclen)) == NULL) { logdate (); printf ("out of space for packet, aborting!\n"); abort (); } uid = ident++; /* get a unique id */ picmp = in_find_data (ppkt, &psize); picmp->ic_type = IC_ECHO; picmp->ic_code = 0; picmp->ic_misc.ic_iseq.id_ident = uid; picmp->ic_misc.ic_iseq.id_seq = 0; picmp->ic_misc.ic_iseq.id_ident = short_to_net(picmp->ic_misc.ic_iseq.id_ident); picmp->ic_misc.ic_iseq.id_seq = short_to_net(picmp->ic_misc.ic_iseq.id_seq); picmp->ic_sum = 0; picmp->ic_sum = ~cksum (picmp, iclen >> 1, 0); ic_sent++; if (in_write (icfd, ppkt, iclen, 0, fhost, FILL) <= 0) { stat = errno; if (log & LOG_ERR) { logdate (); printf ("net write error %d, trans = %d\n", stat, uid); } in_free (ppkt); (*comp)(NULL, arg1, arg2, stat); return; } else { if (log & LOG_XMT) in_logpkt (ppkt, iclen, OUTPKT); enq_timeout (timeout, retries, ppkt, psize, icfd, uid, comp, arg1, arg2); } } icmp_timestamp (fhost, retries, timeout, comp, arg1, arg2) /* Send a icmp timestamp request to the specified foreign host. * Then enqueue the resulting packet on the timeout/retry queue, * with the specified number of retries and timeout interval, and * the specified completion routine and arguments. If an error * occurs in the send, call the completion routine directly * with the error status. * * Arguments: */ struct in_name *fhost; /* dest. host address */ int retries; /* number of retries */ int timeout; /* timeout interval in seconds */ reg int (*comp)(); /* completion routine */ caddr_t arg1; /* first arg to comp routine */ caddr_t arg2; /* second arg to comp routine */ { reg caddr_t ppkt; /* ptr. to echo packet */ reg struct icmp *picmp; /* ptr. to icmp portion of packet */ unshort psize; /* data area size in bytes */ int stat; /* write status */ unshort iclen; /* length of icmp data */ unshort uid; /* unique trans. id */ int tvec[3]; /* vector for qtime */ iclen = sizeof (struct icmp); if ((ppkt = in_alloc (iclen)) == NULL) { logdate (); printf ("out of space for packet, aborting!\n"); abort (); } uid = ident++; /* get a unique id */ picmp = in_find_data (ppkt, &psize); picmp->ic_type = IC_TMSTMP; picmp->ic_code = 0; picmp->ic_misc.ic_iseq.id_ident = uid; picmp->ic_misc.ic_iseq.id_seq = 0; qtime (&tvec); picmp->ic_data.ic_time.tm_orig = tvec_to_ut(tvec); picmp->ic_misc.ic_iseq.id_ident = short_to_net(picmp->ic_misc.ic_iseq.id_ident); picmp->ic_misc.ic_iseq.id_seq = short_to_net(picmp->ic_misc.ic_iseq.id_seq); picmp->ic_data.ic_time.tm_orig = long_to_net(picmp->ic_data.ic_time.tm_orig); picmp->ic_data.ic_time.tm_recv = picmp->ic_data.ic_time.tm_orig; picmp->ic_data.ic_time.tm_trans = picmp->ic_data.ic_time.tm_orig; picmp->ic_sum = 0; picmp->ic_sum = ~cksum (picmp, iclen >> 1, 0); ic_sent++; if (in_write (icfd, ppkt, iclen, 0, fhost, FILL) <= 0) { stat = errno; if (log & LOG_ERR) { logdate (); printf ("net write error %d, trans = %d\n", stat, uid); } in_free (ppkt); (*comp)(NULL, arg1, arg2, stat); return; } else { if (log & LOG_XMT) in_logpkt (ppkt, iclen, OUTPKT); enq_timeout (timeout, retries, ppkt, psize, icfd, uid, comp, arg1, arg2); } } #ifndef DBG icmp_init () { icfd = in_open (ICMPROTO, NULL, NULL, NULL); icinfd = icfd; return (icfd); } ggp_init () { ggfd = in_open (GGPROTO, NULL, NULL, NULL); gginfd = ggfd; return (ggfd); } #else icmp_init () { icinfd = open ("icmp_in", 0); icfd = creat ("icmp_out", 0777); return (icfd); } ggp_init () { gginfd = open ("ggp_in", 0); ggfd = creat ("ggp_out", 0777); return (ggfd); } #endif