📄 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.Dbg;/*** 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 check sum.* assume (32 bit) word boundries. rest of buffer is 0.* off offset in buffer (in words)* cnt length in bytes*/ 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(); }/*** return IP id in upper 16 bit.*/ public static int getId() { ip_id += 0x10000; return ip_id; }/*** process one ip packet.* change buffer and set length to get a packet sent back.* called from Net.run().*/ public static void receive(Packet p) { int i, j; int ret = 0; int[] buf = p.buf; int len; i = buf[0]; len = i & 0xffff; // len from IP header// NO options are assumed in ICMP/TCP/IP...// => copy if options present if (len > p.len || (i>>>24!=0x45)) { p.setStatus(Packet.FREE); // packet to short or ip options => drop it return; } else { p.len = len; // correct for to long packets } // TODO fragmentation if (chkSum(buf, 0, 20)!=0) { p.setStatus(Packet.FREE);Dbg.wr("wrong IP checksum "); return; } int prot = (buf[2]>>16) & 0xff; // protocol if (prot==PROT_ICMP) { doICMP(p); doIp(p, prot); } else if (prot==PROT_TCP) { doTCP(p); doIp(p, prot); } else if (prot==Udp.PROTOCOL) { Udp.process(p); // Udp generates the reply } else { p.setStatus(Packet.FREE); // mark packet free } }/*** very simple generation of IP header.* just swap source and destination.*/ private static void doIp(Packet p, int prot) { int[] buf = p.buf; int len = p.len; int i; if (len == 0) { p.setStatus(Packet.FREE); // mark packet free } else { buf[0] = 0x45000000 + len; // ip length (header without options) buf[1] = getId(); // identification, no fragmentation 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); // a VERY dummy arp/routing! // should this be in the cs8900 ?// p.llh[0] = p.llh[3];// p.llh[1] = p.llh[4];// p.llh[2] = p.llh[5]; p.llh[6] = 0x0800; p.setStatus(Packet.SND); // mark packet ready to send } }/*** the famous ping.*/ private static void doICMP(Packet p) { int type_code = p.buf[5]>>>16;Dbg.wr('P');Dbg.hexVal(type_code); if (type_code == 0x0800) { // TODO check received ICMP 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 - sndnxti = 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; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -