203 lines
5.0 KiB
C
203 lines
5.0 KiB
C
/* Internet Control Message Protocol */
|
|
#include "global.h"
|
|
#include "mbuf.h"
|
|
#include "internet.h"
|
|
#include "timer.h"
|
|
#include "iface.h"
|
|
#include "ip.h"
|
|
#include "icmp.h"
|
|
|
|
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 mbuf *htonicmp(),*tbp;
|
|
struct icmp icmp; /* ICMP header */
|
|
struct ip ip; /* Offending datagram header */
|
|
int16 type, cksum(); /* Type of ICMP message */
|
|
void ntohicmp(),echo_proc(),tcp_icmp();
|
|
int ntohip();
|
|
|
|
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;
|
|
}
|
|
ntohicmp(&icmp,&bp);
|
|
|
|
/* Process the message. Some messages are passed up to the protocol
|
|
* module for handling, others are handled here.
|
|
*/
|
|
type = icmp.type;
|
|
if(type < ICMP_TYPES)
|
|
icmp_stats.input[type]++;
|
|
|
|
switch(uchar(type)){
|
|
case TIME_EXCEED: /* Time-to-live Exceeded */
|
|
case DEST_UNREACH: /* Destination Unreachable */
|
|
case QUENCH: /* Source Quench */
|
|
ntohip(&ip,&bp); /* Extract offending IP header */
|
|
switch(uchar(ip.protocol)){
|
|
case TCP_PTCL:
|
|
tcp_icmp(ip.source,ip.dest,icmp.type,icmp.code,&bp);
|
|
break;
|
|
}
|
|
break;
|
|
case ECHO_RQST: /* Echo Request */
|
|
/* Change type to ECHO_REPLY, recompute checksum,
|
|
* and return datagram.
|
|
*/
|
|
icmp.type = ECHO_REPLY;
|
|
if((tbp = htonicmp(&icmp,bp)) == NULLBUF){
|
|
free_p(bp);
|
|
return;
|
|
}
|
|
icmp_stats.output[ECHO_REPLY]++;
|
|
ip_send(dest,source,ICMP_PTCL,tos,0,tbp,length,0,0);
|
|
return;
|
|
case REDIRECT: /* Redirect */
|
|
case PARAM_PROB: /* Parameter Problem */
|
|
break;
|
|
case ECHO_REPLY: /* Echo Reply */
|
|
echo_proc(source,dest,&icmp);
|
|
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.
|
|
* Unlike most routines, the callER frees the mbuf.
|
|
*/
|
|
int
|
|
icmp_output(ip,data,type,code,args)
|
|
struct ip *ip; /* Header of offending datagram */
|
|
struct mbuf *data; /* Data portion of datagram */
|
|
char type,code; /* Codes to send */
|
|
union icmp_args *args;
|
|
{
|
|
struct mbuf *htonicmp();
|
|
struct mbuf *htonip();
|
|
struct mbuf *bp;
|
|
struct icmp icmp; /* ICMP protocol header */
|
|
int16 dlen; /* Length of data portion of offending pkt */
|
|
int16 length; /* Total length of reply */
|
|
extern int32 ip_addr; /* Our IP address */
|
|
|
|
if(ip == NULLIP)
|
|
return -1;
|
|
if(type < ICMP_TYPES)
|
|
icmp_stats.output[type]++;
|
|
|
|
if(uchar(ip->protocol) == ICMP_PTCL){
|
|
/* Never send an ICMP message about another ICMP message */
|
|
icmp_errors.noloop++;
|
|
return -1;
|
|
}
|
|
/* Compute amount of original datagram to return.
|
|
* We return the original IP header, and up to 8 bytes past that.
|
|
*/
|
|
dlen = min(8,len_mbuf(data));
|
|
length = dlen + ICMPLEN + IPLEN + ip->optlen;
|
|
/* Take excerpt from data portion */
|
|
if(data != NULLBUF && (bp = copy_p(data,dlen)) == NULLBUF)
|
|
return -1; /* The caller will free data */
|
|
|
|
/* Recreate and tack on offending IP header */
|
|
if((data = htonip(ip,bp)) == NULLBUF){
|
|
free_p(bp);
|
|
return -1;
|
|
}
|
|
icmp.type = type;
|
|
icmp.code = code;
|
|
switch(uchar(icmp.type)){
|
|
case PARAM_PROB:
|
|
icmp.args.pointer = args->pointer;
|
|
break;
|
|
case REDIRECT:
|
|
icmp.args.address = args->address;
|
|
break;
|
|
case ECHO_RQST:
|
|
case ECHO_REPLY:
|
|
case INFO_RQST:
|
|
case INFO_REPLY:
|
|
case TIMESTAMP:
|
|
case TIME_REPLY:
|
|
icmp.args.echo.id = args->echo.id;
|
|
icmp.args.echo.seq = args->echo.seq;
|
|
break;
|
|
default:
|
|
icmp.args.unused = 0;
|
|
break;
|
|
}
|
|
/* Now stick on the ICMP header */
|
|
if((bp = htonicmp(&icmp,data)) == NULLBUF){
|
|
free_p(data);
|
|
return -1;
|
|
}
|
|
return ip_send(ip_addr,ip->source,ICMP_PTCL,ip->tos,0,bp,length,0,0);
|
|
}
|
|
/* Generate ICMP header in network byte order, link data, compute checksum */
|
|
struct mbuf *
|
|
htonicmp(icmp,data)
|
|
struct icmp *icmp;
|
|
struct mbuf *data;
|
|
{
|
|
struct mbuf *bp;
|
|
register char *cp;
|
|
int16 checksum;
|
|
|
|
if((bp = pushdown(data,ICMPLEN)) == NULLBUF)
|
|
return NULLBUF;
|
|
cp = bp->data;
|
|
|
|
*cp++ = icmp->type;
|
|
*cp++ = icmp->code;
|
|
cp = put16(cp,0); /* Clear checksum */
|
|
cp = put16(cp,icmp->args.echo.id);
|
|
cp = put16(cp,icmp->args.echo.seq);
|
|
|
|
/* Compute checksum, and stash result */
|
|
checksum = cksum(NULLHEADER,bp,len_mbuf(bp));
|
|
cp = &bp->data[2];
|
|
cp = put16(cp,checksum);
|
|
|
|
return bp;
|
|
}
|
|
/* Pull off ICMP header */
|
|
void
|
|
ntohicmp(icmp,bpp)
|
|
struct icmp *icmp;
|
|
struct mbuf **bpp;
|
|
{
|
|
if(icmp == (struct icmp *)0)
|
|
return;
|
|
/* return -1; this code is full of stuff that ain't used - K5JB */
|
|
icmp->type = pullchar(bpp);
|
|
icmp->code = pullchar(bpp);
|
|
(void) pull16(bpp); /* Toss checksum */
|
|
icmp->args.echo.id = pull16(bpp);
|
|
icmp->args.echo.seq = pull16(bpp);
|
|
/* return 0; */
|
|
}
|