BBN-Vax-TCP/bbnnet/icmp.c
#include "../h/param.h"
#include "../h/systm.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/ifcb.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/icmp.h"
#include "../bbnnet/ucb.h"
extern int nosum;
#define NICTYPE 17
/* ICMP message formats */
#define ICBAD 0 /* unimplemented */
#define ICERR 1 /* error format (use header) */
#define ICDAT 2 /* data format (use id) */
#define ICINT 3 /* data format (handle internally) */
char icaction[NICTYPE] = { ICDAT, ICBAD, ICBAD, ICERR, ICERR, ICERR, ICBAD,
ICBAD, ICINT, ICBAD, ICBAD, ICERR, ICERR, ICINT,
ICDAT, ICINT, ICDAT };
/*
* Process ICMP messages. Called directly from ip_input processor.
*/
icmp(mp, ifp)
register struct mbuf *mp;
struct ifcb *ifp;
{
register struct ip *ip;
register struct icmp *icp;
register struct ucb *up;
unsigned short i;
int op, ilen;
struct socket temp;
struct ucb *icfind();
ip = (struct ip *)((int)mp + mp->m_off);
mp->m_off += sizeof(struct ip);
mp->m_len -= sizeof(struct ip);
icp = (struct icmp *)((int)ip + sizeof(struct ip));
ilen = ip->ip_len;
i = (unsigned short)icp->ic_sum;
icp->ic_sum = 0;
if (i != (unsigned short)cksum(mp, ilen)) {
netstat.ic_badsum++;
if (nosum)
goto ignore;
netlog(mp);
} else {
ignore:
/* decode message format, and associate msg with connection */
if (icp->ic_type < NICTYPE)
op = icaction[icp->ic_type];
else
op = ICBAD;
switch (op) {
case ICERR: /* error format -- get conn from orig hdr */
up = icfind(&icp->ic_data.hdr);
break;
case ICDAT: /* data format -- get conn from id */
/* Assume we sent the orig packet with id: id
is index in connection table (plus one). Verify
by bounds checking. */
up = NULL;
if ((i = icp->ic_misc.ic_iseq.id) != 0) {
up = contab + (i-1);
if (up >= conNCON || up->uc_proc == NULL)
up = NULL;
}
break;
case ICINT: /* internal format -- don't need conn */
break;
default: /* drop bad messages */
netstat.ic_drops++;
goto badret;
}
/* Now do any processing. Internal messages get handled
here. Others may be counted, or cause some other action */
switch (icp->ic_type) {
case 3: /* destination unreachable */
if (up != NULL)
to_user(up, UNRCH);
break;
case 4: /* source quench */
netstat.ic_quenches++;
break;
case 5: /* redirect */
if (up != NULL) {
/* free the old route and insert the new */
if (up->uc_route != NULL)
h_free(up->uc_route);
up->uc_route = h_make(&icp->ic_misc.ic_gaddr);
netstat.ic_redirects++;
}
break;
case 8: /* echo */
/* reply to echo: swap src and dest addrs, change
type to zero, recompute checksum */
icp->ic_type = 0;
temp = ip->ip_src;
ip->ip_src = ip->ip_dst;
ip->ip_dst = temp;
icp->ic_sum = cksum(mp, ilen);
mp->m_off -= sizeof(struct ip);
mp->m_len += sizeof(struct ip);
icsend(mp, ifp, ip, ilen);
netstat.ic_echoes++;
return;
case 11: /* time exceeded */
netstat.ic_timex++;
break;
case 13: /* timestamp */
/* reply by changing type, swapping addrs, and
adding r/t timestamps. Timestamps are not UT
so high order bit is set. */
if (icp->ic_code == 0) {
icp->ic_type = 14;
icp->ic_data.ic_time.trecv = 0x80000000 |
(long)time;
icp->ic_data.ic_time.ttrans = 0x80000000 |
(long)time;
temp = ip->ip_src;
ip->ip_src = ip->ip_dst;
ip->ip_dst = temp;
icp->ic_sum = cksum(mp, ilen);
mp->m_off -= sizeof(struct ip);
mp->m_len += sizeof(struct ip);
icsend(mp, ifp, ip, ilen);
}
return;
case 15: /* info request */
/* reply by changing type, swapping addrs, and
filling in net part of addr */
if (icp->ic_code == 0) {
icp->ic_type = 16;
temp = ip->ip_src;
ip->ip_src.s_addr = ip->ip_dst.s_addr |
(long)ifp->if_net;
ip->ip_dst.s_addr = temp.s_addr |
(long)ifp->if_net;
icp->ic_sum = cksum(mp, ilen);
mp->m_off -= sizeof(struct ip);
mp->m_len += sizeof(struct ip);
icsend(mp, ifp, ip, ilen);
}
return;
}
/* Dispatch messages to raw connections, if wanted */
if (up != NULL && up->uc_flags&(UIP+URAW) &&
((op == ICERR && up->uc_flags&RAWERR) || op == ICDAT)) {
mp->m_off -= sizeof(struct ip);
mp->m_len += sizeof(struct ip);
raw_queue(mp, up);
} else
badret:
netlog(mp);
}
}
/*
* Process GGP messages.
*/
ggp(mp, ifp)
register struct mbuf *mp;
struct ifcb *ifp;
{
register struct ip *ip;
register struct ggp *gp;
register struct ucb *up;
unsigned short i;
int ilen;
struct socket temp;
struct ucb *icfind();
ip = (struct ip *)((int)mp + mp->m_off);
gp = (struct ggp *)((int)ip + sizeof(struct ip));
ilen = ip->ip_len;
switch (gp->gg_type) {
case 3: /* destination unreachable */
if ((up = icfind(&gp->gg_data.gg_hdr)) != NULL)
to_user(up, UNRCH);
break;
case 4: /* source quench */
up = icfind(&gp->gg_data.gg_hdr);
netstat.ic_quenches++;
break;
case 5: /* redirect */
if ((up = icfind(&gp->gg_data.gg_redir.rhdr)) != NULL) {
/* free the old route and insert the new */
if (up->uc_route != NULL)
h_free(up->uc_route);
up->uc_route = h_make(&gp->gg_data.gg_redir.raddr);
netstat.ic_redirects++;
}
break;
case 8: /* echo */
/* reply to echo: swap src and dest addrs, change
type to zero, recompute checksum */
gp->gg_type = 0;
temp = ip->ip_src;
ip->ip_src = ip->ip_dst;
ip->ip_dst = temp;
icsend(mp, ifp, ip, ilen);
netstat.ic_echoes++;
return;
default:
netstat.ic_drops++;
}
/* Dispatch messages to raw connections, if wanted */
if (up != NULL && up->uc_flags&(UIP+URAW) &&
((i == ICERR && up->uc_flags&RAWERR) || i == ICDAT))
raw_queue(mp, up);
else
netlog(mp);
}
/*
* Given pertinent fields from the original datagram header portion of
* a GGP or ICMP packet, find the corresponding connection that originated
* it (if any).
*/
struct ucb *icfind(hdr)
register struct th *hdr;
{
register struct ucb *up;
register struct tcb *tp;
unsigned short src, dst;
src = short_from_net(hdr->t_src);
dst = short_from_net(hdr->t_dst);
/* match ip/tcp header fields with open connection */
for (up=contab; up < conNCON; up++) {
if (up->uc_proc == NULL)
continue;
if (up->uc_flags & UTCP) { /* tcp connection */
if ((tp = up->uc_tcb) == NULL)
continue;
if (tp->t_lport == src &&
tp->t_fport == dst &&
up->uc_host.s_addr == hdr->t_d.s_addr &&
up->uc_srcif->if_addr.s_addr == hdr->t_s.s_addr &&
hdr->t_pr == TCPROTO)
return(up);
} else if (up->uc_flags & UIP) { /* ip connection */
if ((up->uc_host.s_addr == hdr->t_d.s_addr ||
up->uc_host.s_addr == NULL) &&
(up->uc_flags & RAWRALL ||
up->uc_srcif->if_addr.s_addr == hdr->t_s.s_addr) &&
hdr->t_pr >= up->uc_lo &&
hdr->t_pr <= up->uc_hi)
return(up);
} else if (up->uc_flags & URAW) { /* raw connection */
if ((up->uc_host.s_addr == hdr->t_d.s_addr ||
up->uc_host.s_addr == NULL) &&
(up->uc_flags & RAWRALL ||
up->uc_srcif->if_addr.s_addr == hdr->t_s.s_addr) &&
up->uc_lo <= IPLINK && up->uc_hi >= IPLINK)
return(up);
}
}
netstat.ic_drops++;
return(NULL); /* not found */
}
/*
* Turn around an ICMP packet.
*/
icsend(mp, ifp, ip, ilen)
register struct mbuf *mp;
register struct ifcb *ifp;
register struct ip *ip;
register ilen;
{
register hlen;
struct socket temp;
struct socket ip_route();
/* fix ip header and recompute ip checksum */
hlen = ip->ip_hl << 2;
ilen += hlen;
ip->ip_len = short_to_net(ilen);
ip->ip_off = 0;
ip->ip_id = short_to_net(ip->ip_id);
ip->ip_sum = cksum(mp, hlen);
/* pass packet to local net driver */
temp = ip_route(&ip->ip_src, &ip->ip_dst);
(*ifp->if_send)(mp, ifp, &temp, ilen, IPLINK, TRUE);
}