/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.net.ipv4.tcp; import java.io.IOException; import java.net.BindException; import java.net.DatagramSocketImplFactory; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketImplFactory; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import org.apache.log4j.Logger; import org.jnode.driver.net.NetworkException; import org.jnode.net.SocketBuffer; import org.jnode.net.ipv4.IPv4Address; import org.jnode.net.ipv4.IPv4Constants; import org.jnode.net.ipv4.IPv4Header; import org.jnode.net.ipv4.IPv4Protocol; import org.jnode.net.ipv4.IPv4Service; import org.jnode.vm.objects.Statistics; /** * @author epr */ public class TCPProtocol implements IPv4Protocol, IPv4Constants, TCPConstants { private static final boolean DEBUG = false; /** * The IP service I'm a part of */ private final IPv4Service ipService; /** The ICMP service */ // private final ICMPUtils icmp; /** * My statistics */ private final TCPStatistics stat = new TCPStatistics(); /** * The SocketImpl factory for TCP */ private final TCPSocketImplFactory socketImplFactory; /** * My control blocks */ private final TCPControlBlockList controlBlocks; /** * The timer */ private final TCPTimer timer; /** * My logger */ private static final Logger log = Logger.getLogger(TCPProtocol.class); /** * Initialize a new instance * * @param ipService */ public TCPProtocol(IPv4Service ipService) throws NetworkException { this.ipService = ipService; // this.icmp = new ICMPUtils(ipService); this.controlBlocks = new TCPControlBlockList(this); this.timer = new TCPTimer(controlBlocks); try { socketImplFactory = new TCPSocketImplFactory(this); try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { public Object run() throws IOException { Socket.setSocketImplFactory(socketImplFactory); ServerSocket.setSocketFactory(socketImplFactory); return null; } }); } catch (SecurityException ex) { log.error("No permission for set socket factory.", ex); } catch (PrivilegedActionException ex) { throw new NetworkException(ex.getException()); } } catch (IOException ex) { throw new NetworkException(ex); } timer.start(); } /** * @see org.jnode.net.TransportLayer#getDatagramSocketImplFactory() */ public DatagramSocketImplFactory getDatagramSocketImplFactory() throws SocketException { throw new SocketException("TCP is socket based"); } /** * @see org.jnode.net.TransportLayer#getName() */ public String getName() { return "tcp"; } /** * @see org.jnode.net.TransportLayer#getProtocolID() */ public int getProtocolID() { return IPPROTO_TCP; } /** * @see org.jnode.net.TransportLayer#getSocketImplFactory() */ public SocketImplFactory getSocketImplFactory() throws SocketException { return socketImplFactory; } /** * @see org.jnode.net.TransportLayer#getStatistics() */ public Statistics getStatistics() { return stat; } /** * @see org.jnode.net.TransportLayer#receive(org.jnode.net.SocketBuffer) */ public void receive(SocketBuffer skbuf) throws SocketException { // Increment stats stat.ipackets.inc(); // Get the IP header final IPv4Header ipHdr = (IPv4Header) skbuf.getNetworkLayerHeader(); // Read the TCP header final TCPHeader hdr = new TCPHeader(skbuf); // Set the TCP header in the buffer-field skbuf.setTransportLayerHeader(hdr); // Remove the TCP header from the head of the buffer skbuf.pull(hdr.getLength()); // Trim the buffer up to the length in the TCP header skbuf.trim(hdr.getDataLength()); if (!hdr.isChecksumOk()) { if (DEBUG) { if (log.isDebugEnabled()) { log.debug("Receive: badsum: " + hdr); } } stat.badsum.inc(); } else { if (DEBUG) { if (log.isDebugEnabled()) { log.debug("Receive: " + hdr); } } // Find the corresponding control block final TCPControlBlock cb = (TCPControlBlock) controlBlocks.lookup(ipHdr.getSource(), hdr.getSrcPort(), ipHdr.getDestination(), hdr.getDstPort(), true); if (cb == null) { final boolean ack = hdr.isFlagAcknowledgeSet(); final boolean rst = hdr.isFlagResetSet(); stat.noport.inc(); // Port unreachable if (ack && rst) { // the source is also unreachable log.debug("Dropping segment due to: connection refused as the source is also unreachable"); } else { processPortUnreachable(ipHdr, hdr); } } else { // Let the cb handle the receive cb.receive(hdr, skbuf); } } } /** * @see org.jnode.net.ipv4.IPv4Protocol#receiveError(org.jnode.net.SocketBuffer) */ public void receiveError(SocketBuffer skbuf) throws SocketException { // TODO Auto-generated method stub } /** * Process a segment whose destination port is unreachable * * @param hdr */ private void processPortUnreachable(IPv4Header ipHdr, TCPHeader hdr) throws SocketException { final TCPHeader replyHdr = new TCPHeader(hdr.getDstPort(), hdr.getSrcPort(), 0, 0, hdr.getSequenceNr() + 1, 0, 0); replyHdr.setFlags(TCPF_ACK | TCPF_RST); final IPv4Header replyIpHdr = new IPv4Header(ipHdr); replyIpHdr.swapAddresses(); send(replyIpHdr, replyHdr, new SocketBuffer()); } /** * Create a binding for a local address * * @param lAddr * @param lPort */ public TCPControlBlock bind(IPv4Address lAddr, int lPort) throws BindException { return (TCPControlBlock) controlBlocks.bind(lAddr, lPort); } /** * Send an TCP packet * * @param skbuf */ protected void send(IPv4Header ipHdr, TCPHeader tcpHdr, SocketBuffer skbuf) throws SocketException { if (DEBUG) { if (log.isDebugEnabled()) { log.debug("send(ipHdr, " + tcpHdr + ')'); } } skbuf.setTransportLayerHeader(tcpHdr); tcpHdr.prefixTo(skbuf); ipHdr.setDataLength(skbuf.getSize()); ipService.transmit(ipHdr, skbuf); stat.opackets.inc(); } /** * Get the current time counter */ protected long getTimeCounter() { return timer.getCounter(); } }