www.pudn.com > ejip.zip > TcpIp.java
/*
* Copyright (c) Martin Schoeberl, martin@jopdesign.com
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Martin Schoeberl
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
package ejip;
/*
* Changelog:
* 2002-03-16 works with ethernet
* 2002-10-21 use Packet buffer, 4 bytes in one word
*
*
*/
import util.*;
/**
* A minimalistic TCP/IP stack (with ICMP).
*
* It's enough to handel a HTTP request (and nothing more)!
*/
public class TcpIp {
private static final int PROT_ICMP = 1;
private static final int PROT_TCP = 6;
static final int FL_URG = 0x20;
static final int FL_ACK = 0x10;
static final int FL_PSH = 0x8;
static final int FL_RST = 0x4;
static final int FL_SYN = 0x2;
static final int FL_FIN = 0x1;
static int ip_id, tcb_port; // ip id, tcp port
static int tcb_st; // state
static final int ST_LISTEN = 0;
static final int ST_ESTAB = 2;
static final int ST_FW1 = 3;
static final int ST_FW2 = 4;
static final int MTU = 1500-8;
static final int WINDOW = 2680;
/**
* calc ip header check sum.
* assume (32 bit) word boundries. rest of buffer is 0.
* off offset in buffer (in words)
* cnt length in bytes
*/
// static int chkSum(int[] buf, int off, int cnt) {
// for OEBB
public static int chkSum(int[] buf, int off, int cnt) {
int i;
int sum = 0;
cnt = (cnt+3)>>2; // word count
while (cnt != 0) {
i = buf[off];
sum += i & 0xffff;
sum += i>>>16;
++off;
--cnt;
}
while ((sum>>16) != 0) sum = (sum & 0xffff) + (sum >> 16);
sum = (~sum) & 0xffff;
return sum;
}
public static void init() {
tcb_st = ST_LISTEN; // select();
ip_id = 0x12340000;
Html.init();
}
/**
* process one ip packet.
* change buffer and set length to get a packet sent back.
* called from Eth.process().
*/
public static void receive(Packet p) {
int i, j;
int ret = 0;
int[] buf = p.buf;
int len;
/*
Dbg.wr('\n');
Dbg.wr('I');
for (i=0; i<(p.len+3)>>2; ++i) {
Dbg.hexVal(p.buf[i]);
}
*/
i = buf[0];
len = i & 0xffff; // len from IP header
// NO options are assumed in ICMP/TCP/IP...
// => copy it options present
if (len > p.len || (i>>>24!=0x45)) {
p.len = 0; // packet to short or ip options => drop it
return;
} else {
p.len = len; // correct for to long packets
}
// TODO check version, header checksum
// TODO fragmentation
// TODO unique id for sent packet
int prot = (buf[2]>>16) & 0xff; // protocol
if (prot==PROT_ICMP) {
doICMP(p);
} else if (prot==PROT_TCP) {
doTCP(p);
} else if (prot==Udp.PROTOCOL) {
Udp.process(p);
} else {
p.len = 0;
}
len = p.len;
if (len != 0) {
buf[0] = 0x45000000 + len; // ip length (header without options)
buf[1] = ip_id; // identification, no fragmentation
ip_id += 0x10000; // increment id (upper part of word)
buf[2] = (0x20<<24) + (prot<<16); // ttl, protocol, clear checksum
i = buf[3]; // swap ip addresses
buf[3] = buf[4];
buf[4] = i;
buf[2] |= chkSum(buf, 0, 20);
/*
Dbg.wr('\n');
Dbg.wr('i');
for (i=0; i<(p.len+3)>>2; ++i) {
Dbg.hexVal(p.buf[i]);
}
Dbg.wr('\n');
*/
} else {
p.len = 0; // no response
}
return;
}
/**
* the famous ping.
*/
private static void doICMP(Packet p) {
Dbg.wr('P');
if (p.buf[5]>>>16 == 0x0800) {
// TODO check checksum
p.buf[5] = 0; // echo replay plus clear checksu,
p.buf[5] = chkSum(p.buf, 5, p.len-20); // echo replay (0x0000) plus checksum
} else {
p.len = 0;
}
}
// TODO:!!!!!! do a real state machine,
// end is wrong (sending ack in fw1 !!!) makes remote site crazy
static void doTCP(Packet p) {
int i;
int datlen;
int[] buf = p.buf;
int rcvcnt, sndcnt;
int fl;
Dbg.wr('T');
// Find the payload
i = buf[8]>>>16;
int flags = i & 0xff;
int hlen = i>>>12;
datlen = p.len - 20 - (hlen<<2);
// "TCB"
// In a full tcp implementation we would keep track of this per connection.
// This implementation only handles one connection at a time.
// As a result, very little of this state is actually used after
// the reply packet has been sent.
// if (datlen < 0) return 0;
// If it's not http, just drop it
i = buf[5];
if ((i & 0xffff) != 80) {
p.len = 0;
return;
}
// Get source port
tcb_port = i>>>16;
rcvcnt = buf[6]; // sequence number
sndcnt = buf[7]; // acknowledge number
// sndcnt has to be incremented for SYN!!!
fl = FL_ACK;
p.len = 40;
// Figure out what kind of packet this is, and respond
if ((flags & FL_SYN) != 0) {
// SYN
sndcnt = -1; // start with -1 for SYN
rcvcnt++;
fl |= FL_SYN;
// tcb_st = ST_ESTAB;
} else if (datlen > 0) {
// incoming data
rcvcnt += datlen;
// TODO get url
if (sndcnt==0) {
p.len += Html.setText(buf, 5+hlen, datlen, 10);
// Send reply packet
// if (len > MTU) len = MTU; // TODO MTU should be taken from tcp options
// Read next segment of data into buffer
} else {
fl |= FL_FIN;
// tcb_st = ST_FW1;
}
fl |= FL_PSH;
} else if ((flags & FL_FIN) != 0) {
// FIN
rcvcnt++;
// Don't bother with FIN-WAIT-2, TIME-WAIT, or CLOSED; they just cause trouble
// tcb_st = ST_LISTEN;
} else if ((flags & FL_ACK) != 0) {
// ack with no data
if (sndcnt > 0) {
// calculate no of bytes left to send
// i = len2send - sndnxt
i = 0;
if (i == 0) {
// EOF; send FIN
fl |= FL_FIN;
// tcb_st = ST_FW1;
} else if (i > 0) {
// not EOF; send next segment
// len += i;
fl |= FL_PSH;
} else { // ***** this is never used! thats bad
// ack of FIN; no reply
p.len = 0;
return;
}
} else {
p.len = 0;
return; // No reply packet
}
} else {
p.len = 0;
return; // drop it
}
// Fill in TCP header
buf[5] = (80<<16) + tcb_port;
buf[6] = sndcnt;
buf[7] = rcvcnt;
buf[8] = 0x50000000 + (fl<<16) + WINDOW; // hlen = 20, no options
buf[9] = 0; // clear checksum field
buf[2] = (PROT_TCP<<16) + p.len - 20; // set protocol and tcp length in iph checksum for tcp checksum
buf[9] = chkSum(buf, 2, p.len-8)<<16;
}
private static final int IO_STATUS = 1;
private static final int IO_UART = 2;
private static final int IO_UART2 = 3;
private static final int MSK_UA_TDRE = 1;
private static final int MSK_UA2_TDRE = 4;
static void xxx(int c) {
while ((com.jopdesign.sys.Native.rd(IO_STATUS)&MSK_UA_TDRE)==0) ;
com.jopdesign.sys.Native.wr(c, IO_UART);
}
}