package ejip123; import ejip123.util.Dbg; /** A packet buffer. Contains a fixed length array for data and some control information. */ public class Packet{ /** The packet is free to use. */ public final static int FREE = 0; /** Allocated and either under interpretation or under construction. */ public final static int ALLOC = 1; // the following states are used while the packets float from one layer to the other /** A packet of a connectionless protocol ready to be sent by a link layer. */ public final static int DGRAM_RDY = 2; /** Received packet ready to be processed by the network stack. */ public final static int RCV = 3; /** Used to identify a connection-orientated protocol packet in the IP layer. This is changed to CON_RDY in the IP layer. */ public final static int CON_PREP = 4; /** A packet of a connection-orientated protocol ready to be sent. This will change to CON_ONFLY after sending. */ public final static int CON_RDY = 5; /** A sent connection-oriented packet. Needs to be freed by the corresponding protocol, not by a link layer. */ public final static int CON_ONFLY = 6; /** A packet that is used outside the scope of the core network stack (e.g. {@link UdpHandler}). */ public final static int APP = 7; //private final Object this = new Object(); /** Current status of the packet. */ private int status; /** Packet length in bytes. */ private int len; /** source/destination interface. */ private LinkLayer linkLayer; /** network layer protocol. e.g. ARP, IP. As defined by ethernet/802.3 */ private int prot; /** Buffer for the ip datagram. */ public final int[] buf; public Packet(int max){ buf = new int[max>>2]; linkLayer = null; } public void setStatus(int v){ synchronized(this){ status = v; } } /** Frees a packet. */ public void free(){ synchronized(this){ len = 0; status = FREE; linkLayer = null; } } /** Frees a packet, if it is currently set to {@link Packet#APP}. */ public boolean freeIfApp(){ synchronized(this){ if(status == APP){ // Dbg.wr("freed packerl\n"); len = 0; status = FREE; linkLayer = null; return true; } else return false; } } /** Changes the status of the packet. The status is changed iff it equals <code>test</code> only. @param test The status the packet has to be before calling. @param set The status the packet should become. @return True, if the status was changed, true otherwise. */ public boolean testSetStatus(int test, int set){ synchronized(this){ if(status == test){ status = set; return true; } } return false; } public boolean isConPrep(){ return status == CON_PREP; } /** Writes a CharSequence into the packet at a given offset. @param off offset in the packet in bytes @param s source of characters. @return returns the number of bytes written into the packet buffer. */ public int setData(int off, CharSequence s){ int cnt; synchronized(this){ cnt = Math.min(s.length(), PacketPool.PACKET_SIZE() - off); if(cnt <= 0) return 0; int first = (4 - off)&0x3; int last = (off + cnt)&0x3; int mid = cnt - first - last; int b = 0; int w = off>>2; if(first != 0){ int t = buf[w]>>>(first<<3); for(int j = (off&0x3); j < 4; ++j){ t <<= 8; t += s.charAt(b); b++; } buf[w] = t; w++; } int k = 0; for(int i = 0; i < mid; i += 4){ for(int j = 0; j < 4; ++j){ k <<= 8; if(b < cnt){ k += s.charAt(b); b++; } } buf[w + (i>>>2)] = k; } if(last != 0){ int msk; // = 0xffffffff>>>(last<<3) if(last == 1) msk = 0xffffff; else if(last == 2) msk = 0xffff; else if(last == 3) msk = 0xff; else msk = 0; w = (off + cnt)>>2; int t = 0; for(int j = 0; j < 4; ++j){ t <<= 8; if(j < last){ t += s.charAt(b); b++; } } t += (buf[w]&msk);//>>>((4-last)<<3); buf[w] = t; } int overshoot = off + cnt - len; // number of bytes written over the current length of the packet if(overshoot > 0) len += overshoot; } return cnt; } /** Appends a CharSequence at the end of the packet. @param s The source of characters. @return The count of bytes written. */ public int appendData(CharSequence s){ return setData(len, s); } /** Copy packet data into a StringBuffer. Ensures that only data inside the current length boundary gets copied. @param off offset in the packet in 32-bit words. @param s StringBuffer destination. @return the number of bytes copied. */ public int getData(int off, StringBuffer s){ // DONE: capacity check to ensure allocation free appending int slen = Math.min(len - (off<<2), s.capacity()); s.setLength(0); for(int i = (off<<2); i < slen + (off<<2); i++){ s.append((char)((buf[i>>2]>>>(24 - ((i&3)<<3)))&0xff)); } return slen; } /** Make a deep copy from Packet p. Used just for ARP requests with a TCP packet as the TCP packet is kept in the connection. */ public void copy(Packet p){ synchronized(this){ len = p.len(); linkLayer = p.linkLayer(); prot = p.prot(); for(int i = 0; i < PacketPool.PACKET_SIZE(); ++i) buf[i] = p.buf[i]; } } public void print(int length){ if(length <= 0){ length = len; } Dbg.wr('\n'); if(length == 0){ Dbg.wr("empty packet.\n"); return; } Dbg.wr("dumping packet content inside the first "); Dbg.intVal(length); Dbg.wr('/'); Dbg.intVal(len); Dbg.wr("bytes\n"); int max = length + 14; for(int i = 0, iip = 8; i < max; i++, iip = (iip <= 0) ? 24 : iip - 8){ int rem = i&0xf; // rem = i % 16 if(rem == 0){ Dbg.wr('\n'); if(i < 0x100) Dbg.wr('0'); if(i < 0x1000) Dbg.wr('0'); Dbg.hexVal(i); Dbg.wr(' '); } if(rem == 8) Dbg.wr(' '); if(i < 14){ Dbg.hexVal(0); } else{ Dbg.hexVal((buf[(i - 14)>>>2]>>>iip)&0xff); } } Dbg.lf(); } public int status(){ return status; } public int len(){ return len; } public void setLen(int len){ this.len = len; } public void addToLen(int amount){ len += amount; } public LinkLayer linkLayer(){ return linkLayer; } public void setLinkLayer(LinkLayer linkLayer){ this.linkLayer = linkLayer; } public int prot(){ return prot; } public void setProt(int prot){ this.prot = prot; } public void setRdy(){ status = (status == CON_PREP) ? Packet.CON_RDY : Packet.DGRAM_RDY; } /** Sets one octet of the packet. @param c The octet to be written into the packet. @param pos The offset in bytes of octet. */ public void setByte(int c, int pos){ if(pos > PacketPool.PACKET_SIZE()) return; int posw = pos>>2; int shift = 24 - ((pos&3)<<3); int msk = ~(0xff<<shift); synchronized(this){ int tmp = buf[posw]&msk; tmp |= (c<<shift); buf[posw] = tmp; } } /** Pads the last word with zeros. */ public void padWithZeros(){ int filled = len&3; synchronized(this){ if(filled == 1) buf[len>>2] &= 0xff000000; else if(filled == 2) buf[len>>2] &= 0xffff0000; else if(filled == 3) buf[len>>2] &= 0xffffff00; } } }