/* Internet Control Message Protocol */ #include "machdep.h" #include "internet.h" #include "timer.h" #include "ip.h" #include "icmp.h" #include "mbuf.h" int (*echo_proc)(); /* Handler for Echo Reply messages */ struct icmp_errors icmp_errors; struct icmp_stats icmp_stats; /* Process an incoming ICMP packet */ void icmp_input(bp,protocol,source,dest,tos,length,rxbroadcast) struct mbuf *bp; /* Pointer to ICMP message */ char protocol; /* Should always be ICMP_PTCL */ int32 source; /* Sender of ICMP message */ int32 dest; /* Us */ char tos; /* Type of Service */ int16 length; /* Length of ICMP message */ char rxbroadcast; { struct icmp *icmph; /* Pointer to ICMP message */ struct ip_header *iph; /* Offending datagram header */ int16 type; /* Type of ICMP message */ int16 ip_len; if(rxbroadcast){ /* Broadcast ICMP packets are to be IGNORED !! */ icmp_errors.bdcsts++; free_p(bp); return; } if(cksum(NULLHEADER,bp,length) != 0){ /* Bad ICMP checksum; discard */ icmp_errors.checksum++; free_p(bp); return; } /* If the message is fragmented, copy to a contiguous mbuf */ if(bp->next != NULLBUF){ struct mbuf *nbp; nbp = copy_p(bp,length); free_p(bp); if(nbp == NULLBUF){ icmp_errors.nospace++; return; } bp = nbp; } icmph = (struct icmp *)bp->data; /* Process the message. Some messages are passed up to the protocol * module for handling, others are handled here. */ type = icmph->type & 0xff; if(type < ICMP_TYPES) icmp_stats.input[type]++; switch(type){ case TIME_EXCEED: /* Time-to-live Exceeded */ case DEST_UNREACH: /* Destination Unreachable */ case QUENCH: /* Source Quench */ iph = (struct ip_header *)(icmph + 1); ip_len = (iph->v_ihl & 0xf) * sizeof(int32); switch(iph->protocol){ case TCP_PTCL: tcp_icmp(ntohl(iph->source),ntohl(iph->dest), icmph->type,icmph->code,(char *)iph + ip_len); break; } break; case ECHO: /* Echo Request */ /* Change type to ECHO_REPLY, recompute checksum, * and return datagram. */ icmph->type = ECHO_REPLY; icmph->checksum = 0; icmph->checksum = cksum(NULLHEADER,bp,length); icmp_stats.output[ECHO_REPLY]++; ip_send(dest,source,ICMP_PTCL,tos,0,bp,length,0,0); return; case REDIRECT: /* Redirect */ case PARAM_PROB: /* Parameter Problem */ break; case ECHO_REPLY: /* Echo Reply */ if(echo_proc){ (*echo_proc)(ntohl(iph->source),ntohl(iph->dest), icmph->type,icmph->code,(char *)iph + ip_len); } break; case TIMESTAMP: /* Timestamp */ case TIME_REPLY: /* Timestamp Reply */ case INFO_RQST: /* Information Request */ case INFO_REPLY: /* Information Reply */ break; } free_p(bp); } /* Return an ICMP response to the sender of a datagram */ icmp_output(bp,type,code,args) struct mbuf *bp; /* Pointer to offending IP header + data */ char type,code; /* Codes to send */ union icmp_args *args; { struct ip_header *iph; /* Offending IP header */ int16 ip_len; /* Length of offending IP header */ struct mbuf *reply; /* Buffer with ICMP reply */ struct icmp *icmph; /* ICMP protocol header */ struct mbuf *data; /* Returned portion of offending packet */ int16 dlen; /* Length of data portion of offending pkt */ int16 length; /* Total length of reply */ extern int32 ip_addr; /* Our IP address */ if(type < ICMP_TYPES) icmp_stats.output[type]++; iph = (struct ip_header *)bp->data; if(iph->protocol == ICMP_PTCL){ icmp_errors.noloop++; return; /* Never send an ICMP message about another ICMP message */ } /* Compute amount of original datagram to return. * We return the original IP header, and up to 8 bytes past that. */ ip_len = (iph->v_ihl & 0xf) * sizeof(int32); dlen = ntohs(iph->length); if(dlen > ip_len + 8) dlen = ip_len + 8; length = sizeof(struct icmp) + dlen; /* Allocate ICMP header and fill in */ if((reply = alloc_mbuf(sizeof(struct icmp))) == NULLBUF){ /* No space; don't bother */ icmp_errors.nospace++; return; } reply->cnt = sizeof(struct icmp); icmph = (struct icmp *)reply->data; icmph->type = type; icmph->code = code; if(args != (union icmp_args *)NULL) icmph->args.unused = args->unused; /* copies whole union */ else icmph->args.unused = 0; /* Link in a copy of the beginning of the original datagram */ data = copy_p(bp,dlen); reply->next = data; /* Could be NULL if copy fails */ /* Compute ICMP checksum and send */ icmph->checksum = 0; icmph->checksum = cksum(NULLHEADER,reply,length); ip_send(ip_addr,ntohl(iph->source),ICMP_PTCL,iph->tos,0,reply,length,0,0); } #ifdef TRACE /* ICMP message types */ char *icmptypes[] = { "Echo Reply", NULLCHAR, NULLCHAR, "Unreachable", "Source Quench", "Redirect", NULLCHAR, NULLCHAR, "Echo Request", NULLCHAR, NULLCHAR, "Time Exceeded", "Parameter Problem", "Timestamp", "Timestamp Reply", "Information Request", "Information Reply" }; /* ICMP unreachable messages */ char *unreach[] = { "Network", "Host", "Protocol", "Port", "Fragmentation", "Source route" }; /* ICMP Time exceeded messages */ char *exceed[] = { "Time-to-live", "Fragment reassembly" }; /* ICMP redirect messages */ char *redirect[] = { "Network", "Host", "TOS & Network", "TOS & Host" }; int doicmpstat(argc,argv) int argc; char *argv[]; { extern struct icmp_errors icmp_errors; extern struct icmp_stats icmp_stats; register int i; printf("chksum err %u no space %u icmp %u bdcsts %u\r\n", icmp_errors.checksum,icmp_errors.nospace,icmp_errors.noloop, icmp_errors.bdcsts); printf("type rcvd sent\r\n"); for(i=0;inext != NULLBUF){ bp = copy_p(bp,len_mbuf(bp)); tmpbuf = 1; } else tmpbuf = 0; codemsg = NULLCHAR; icmp = (struct icmp *)bp->data; if(icmp->type <= 16 && icmptypes[icmp->type] != NULLCHAR) printf("ICMP: %s",icmptypes[icmp->type]); else printf("ICMP: type %u",icmp->type); switch(icmp->type){ case DEST_UNREACH: if(icmp->code <= 5) codemsg = unreach[icmp->code]; break; case REDIRECT: if(icmp->code <= 3) codemsg = redirect[icmp->code]; break; case TIME_EXCEED: if(icmp->code <= 1) codemsg = exceed[icmp->code]; break; } if(codemsg != NULLCHAR) printf(" %s",codemsg); else printf(" code %u",icmp->code); /* Special case for parameter problem message */ if(icmp->type == PARAM_PROB) printf(" pointer = 0x%x",icmp->args.pointer); if(check){ /* Verify checksum */ if((i = cksum(NULLHEADER,bp,len_mbuf(bp))) != 0) printf(" CHECKSUM ERROR (%u)",i); } printf("\r\n"); /* Dump the offending IP header, if any */ switch(icmp->type){ case DEST_UNREACH: case TIME_EXCEED: case PARAM_PROB: case QUENCH: case REDIRECT: printf("Returned "); dup_p(&ibp,bp,sizeof(struct icmp), len_mbuf(bp) - sizeof(struct icmp)); ip_dump(ibp); free_p(ibp); } if(tmpbuf) free_p(bp); } #endif