home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GRIPS 2: Government Rast…rocessing Software & Data
/
GRIPS_2.cdr
/
dos
/
ncsa_tel
/
tel_2_2_
/
source
/
ip.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-07-15
|
12KB
|
497 lines
/*
* IP.C
* IP level routines, including ICMP
* also includes a basic version of UDP, not generalized yet
*
****************************************************************************
* *
* part of: *
* TCP/IP kernel for NCSA Telnet *
* by Tim Krauskopf *
* *
* National Center for Supercomputing Applications *
* 152 Computing Applications Building *
* 605 E. Springfield Ave. *
* Champaign, IL 61820 *
* *
****************************************************************************
* Revision history:
*
* 10/87 Initial source release, Tim Krauskopf
* 2/88 typedefs of integer lengths, TK
*/
#include "stdio.h"
#include "protocol.h"
#include "data.h"
uint16 ipcheck(),tcpcheck();
/***************************************************************************/
/* ipinterpret
* Called by the reception routine with a new IP packet. Check the checksum,
* addressing and protocol type and call appropriate routines.
*/
ipinterpret(p)
IPKT *p;
{
int hischeck,iplen,i;
/*
* We cannot handle fragmented IP packets yet, return an error
*/
if (p->i.frags & 0x20) {
netposterr(304);
return(1);
}
/*
* checksum verification of IP header
*/
if (p->i.check) { /* no IP checksumming if check=0 */
if (ipcheck(&p->i.versionandhdrlen,(p->i.versionandhdrlen & 0x0f) << 1)) {
netposterr(300); /* bad IP checksum */
return(1); /* drop packet */
}
}
/*
* check to make sure that the packet is for me.
* Throws out all packets which are not directed to my IP address.
*
* This code is incomplete. It does not pass broadcast IP addresses up
* to higher layers. It used to report packets which were incorrectly
* addressed, but no longer does. Needs proper check for broadcast
* addresses.
*/
if (!comparen(nnipnum,p->i.ipdest,4)) { /* potential non-match */
return(1); /* drop packet */
}
/*
* Extract total length of packet
*/
iplen = intswap(p->i.tlen);
/*
* See if there are any IP options to be handled.
* We don't understand IP options, post a warning to the user and drop
* the packet.
*/
i = (p->i.versionandhdrlen & 0x0f)<<2;
if (i > 20) {
netposterr(302); /* packet with options */
return(1);
}
switch (p->i.protocol) { /* which protocol to handle this packet? */
case PROTUDP:
return(udpinterpret(p,iplen-i));
break;
case PROTTCP:
return(tcpinterpret(p,iplen-i)); /* pass tcplen on to TCP */
case PROTICMP:
return(icmpinterpret(p,iplen-i));
default:
netposterr(303);
return(1);
}
return(0);
}
#ifdef NNDEBUG
ipdump(p)
IPKT *p;
{
uint16 iplen,iid;
iid = intswap(p->i.ident);
iplen = intswap(p->i.tlen);
puts("found IP packet:");
printf("Version+hdr: %x service %d tlen %u \n",
p->i.versionandhdrlen,p->i.service,iplen);
printf("Ident: %u frags: %4x ttl: %d prot: %d \n",
iid,p->i.frags,p->i.ttl,p->i.protocol);
printf("addresses: s: %d.%d.%d.%d t: %d.%d.%d.%d \n",
p->i.ipsource[0],p->i.ipsource[1],p->i.ipsource[2],p->i.ipsource[3],
p->i.ipdest[0],p->i.ipdest[1],p->i.ipdest[2],p->i.ipdest[3]);
puts("\n");
}
/***************************************************************************/
/* ipsend THIS ROUTINE HAS NOT BEEN TESTED, NEVER USED!
*
* generic send of an IP packet according to parameters. Use of this
* procedure is discouraged. Terribly inefficient, but may be useful for
* tricky or diagnostic situations. Unused for TCP.
*
* usage: ipsend(data,ident,prot,options,hdrlen)
* data is a pointer to the data to be sent
* ident is the 16 bit identifier
* prot is the protocol type, PROTUDP or PROTTCP or other
* hlen is in bytes, total header length, 20 is minimum
* dlen is the length of the data field, in bytes
* who is ip address of recipient
* options must be included in hlen and hidden in the data stream
*/
ipsend(data,dlen,iid,iprot,who,hlen)
unsigned char *data,iprot,*who;
int iid,dlen,hlen;
{
int iplen;
if (dlen > 512)
dlen = 512;
iplen = hlen+dlen; /* total length of packet */
blankip.i.tlen = intswap(iplen); /* byte swap */
blankip.i.versionandhdrlen = 0x40 | (hlen>>2);
blankip.i.ident = intswap(iid); /* byte swap */
blankip.i.protocol = iprot;
blankip.i.check = 0; /* set to 0 before calculating */
movebytes(blankip.i.ipdest,who,4);
movebytes(blankip.d.me,myaddr,DADDLEN);
movenbytes(blankip.x.data,data,dlen); /* might be header options data */
blankip.i.check = ipcheck(&blankip.i.versionandhdrlen,hlen>>1);
/* checks based on words */
/* resolve knowledge of Ethernet hardware addresses */
/*
* This is commented out because I know that this procedure is broken!
* If you use it, debug it first.
dlayersend(&blankip,iplen+14);
*/
return(0);
}
#endif
/****************************************************************************/
/* icmpinterpret
* interpret the icmp message that just came in
*/
icmpinterpret(p,icmplen)
ICMPKT *p;
int icmplen;
{
uint i,cksum,hisck;
IPLAYER *iptr;
i = p->c.type;
netposterr(600 + i); /* provide info for higher layer user */
if (p->c.check) { /* ignore if chksum = 0 */
if (ipcheck(&p->c,icmplen>>1)) {
netposterr(699);
return(-1);
}
}
switch (i) {
case 8: /* ping request sent to me */
p->c.type = 0; /* echo reply type */
neticmpturn(p,icmplen); /* send back */
break;
case 5: /* ICMP redirect */
iptr = (IPLAYER *)p->data;
netputuev(ICMPCLASS,IREDIR,0); /* event to be picked up */
movebytes(nnicmpsave,iptr->ipdest,4); /* dest address */
movebytes(nnicmpnew,&p->c.part1,4); /* new gateway */
break;
default:
break;
}
return(0);
}
/****************************************************************************/
/* udpinterpret
* take incoming UDP packets and make them available to the user level
* routines. Currently keeps the last packet coming in to a port.
*
* Limitations:
* Can only listen to one UDP port at a time. Only saves the last packet
* received on that port.
* Port numbers should be assigned like TCP ports are (future).
*/
udpinterpret(p,ulen)
UDPKT *p;
int ulen;
{
uint hischeck,mycheck;
/*
* did we want this data ? If not, then let it go, no comment
* If we want it, copy the relevent information into our structure
*/
if (intswap(p->u.dest) != ulist.listen)
return(1);
/*
* first compute the checksum to see if it is a valid packet
*/
hischeck = p->u.check;
p->u.check = 0;
if (hischeck) {
movebytes(tcps.source,p->i.ipsource,8);
tcps.z = 0;
tcps.proto = p->i.protocol;
tcps.tcplen = intswap(ulen);
mycheck = tcpcheck(&tcps,&p->u,ulen);
if (hischeck != mycheck) {
netposterr(700);
return(2);
}
p->u.check = hischeck; /* put it back */
}
ulen -= 8; /* account for header */
if (ulen > UMAXLEN) /* most data that we can accept */
ulen = UMAXLEN;
movebytes(ulist.who,p->i.ipsource,4);
movebytes(ulist.data,p->data,ulen);
ulist.length = ulen;
ulist.stale = 0;
netputuev(USERCLASS,UDPDATA,ulist.listen); /* post that it is here */
return(0);
}
/****************************************************************************/
/* neturead
* get the data from the UDP buffer
* Returns the number of bytes transferred into your buffer, -1 if none here
* This needs work.
*/
neturead(buffer)
char *buffer;
{
if (ulist.stale)
return(-1);
movebytes(buffer,ulist.data,ulist.length);
ulist.stale = 1;
return(ulist.length);
}
/***************************************************************************/
/* netulisten
* Specify which UDP port number to listen to.
* Can only listen to one at a time.
*/
netulisten(port)
int port;
{
ulist.listen = port;
}
/***************************************************************************/
/* netusend
* send some data out in a UDP packet
* uses the preinitialized data in the port packet ulist.udpout
*
* returns 0 on okay send, nonzero on error
*/
netusend(machine,port,retport,buffer,n)
unsigned char *machine,*buffer;
unsigned int port,retport;
int n;
{
unsigned char *pc;
if (n > UMAXLEN)
n = UMAXLEN;
/*
* make sure that we have the right dlayer address
*/
if (!comparen(machine,ulist.udpout.i.ipdest,4)) {
pc = netdlayer(machine);
if (pc == NULL)
return(-2);
movebytes(ulist.udpout.d.dest,pc,DADDLEN);
movebytes(ulist.udpout.i.ipdest,machine,4);
movebytes(ulist.tcps.dest,machine,4);
}
ulist.udpout.u.dest = intswap(port);
ulist.udpout.u.source = intswap(retport);
ulist.tcps.tcplen = ulist.udpout.u.length = intswap(n+sizeof(UDPLAYER));
movenbytes(ulist.udpout.data,buffer,n);
/*
* put in checksum
*/
ulist.udpout.u.check = 0;
ulist.udpout.u.check = tcpcheck(&ulist.tcps,&ulist.udpout.u,n+sizeof(UDPLAYER));
/*
* iplayer for send
*/
ulist.udpout.i.tlen = intswap(n+sizeof(IPLAYER)+sizeof(UDPLAYER));
ulist.udpout.i.ident = intswap(nnipident++);
ulist.udpout.i.check = 0;
ulist.udpout.i.check = ipcheck(&ulist.udpout.i,10);
/*
* send it
*/
return(dlayersend(&ulist.udpout,
sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(UDPLAYER)+n));
}
#ifdef notneeded
/***************************************************************************/
/* neticmpsend
* Not currently used. Has not been tested since 9/87.
* Do not assume this works (TK)
*
* send out an icmp packet, probably to do a ping operation
*
* returns 0 on okay send, nonzero on error
*/
neticmpsend(machine,type,code,buffer,n)
unsigned char *machine,*buffer,type,code;
int n;
{
unsigned char *pc;
if (n > ICMPMAX)
n = ICMPMAX;
/*
* make sure that we have the right dlayer address
*
* this may be re-entrant, needs checking. Okay, as long as this compare
* is false when called from netsleep() routines!
* When called from user routines, we are okay.
*/
if (!comparen(machine,blankicmp.i.ipdest,4)) {
pc = netdlayer(machine);
if (pc == NULL)
return(-2);
movebytes(blankicmp.d.dest,pc,DADDLEN);
movebytes(blankicmp.i.ipdest,machine,4);
/* movebytes(ulist.tcps.dest,machine,4); */
}
/*
* prepare ICMP portion
*/
blankicmp.c.type = type;
blankicmp.c.code = code;
movenbytes(&blankicmp.data,buffer,n);
blankicmp.c.check = 0;
blankicmp.c.check = ipcheck(&blankicmp.c,(sizeof(ICMPLAYER)+n)>>1);
/*
* iplayer for send
*/
blankicmp.i.tlen = intswap(n+sizeof(IPLAYER)+sizeof(ICMPLAYER));
blankicmp.i.ident = intswap(nnipident++);
blankicmp.i.check = 0;
blankicmp.i.check = ipcheck(&blankicmp.i,10);
/*
* send it
*
* debug this routine before using
*/
return(dlayersend(&blankicmp,
sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(ICMPLAYER)+n));
}
#endif
/***************************************************************************/
/* neticmpturn
*
* send out an icmp packet, probably in response to a ping operation
* interchanges the source and destination addresses of the packet,
* puts in my addresses for the source and sends it
*
* does not change any of the ICMP fields, just the IP and dlayers
* returns 0 on okay send, nonzero on error
*/
neticmpturn(p,ilen)
ICMPKT *p;
int ilen;
{
unsigned char *pc;
/*
* reverse the addresses, dlayer and IP layer
*/
if (comparen(p->d.me,broadaddr,DADDLEN))
return(0);
movebytes(p->d.dest,p->d.me,DADDLEN);
#ifdef MAC
/*
* look up address in the arp cache if we are using AppleTalk
* encapsulation.
*/
if (!nnemac) {
pc = getdlayer(p->i.ipsource);
if (pc != NULL)
movebytes(p->d.dest,pc,DADDLEN);
else
return(0); /* no hope this time */
}
#endif
movebytes(p->i.ipdest,p->i.ipsource,4);
movebytes(p->d.me,nnmyaddr,DADDLEN);
movebytes(p->i.ipsource,nnipnum,4);
/*
* prepare ICMP checksum
*/
p->c.check = 0;
p->c.check = ipcheck(&p->c,ilen>>1);
/*
* iplayer for send
*/
p->i.ident = intswap(nnipident++);
p->i.check = 0;
p->i.check = ipcheck(&p->i,10);
/*
* send it
*/
return(dlayersend(p,sizeof(DLAYER)+sizeof(IPLAYER)+ilen));
}