ipv4sender.java

来自「纯java操作系统jnode,安装简单和操作简单的个人使用的Java操作系统」· Java 代码 · 共 308 行

JAVA
308
字号
/*
 * $Id: IPv4Sender.java,v 1.1 2004/01/02 08:57:08 epr Exp $
 */
package org.jnode.net.ipv4.layer;

import java.net.NoRouteToHostException;

import org.jnode.driver.ApiNotFoundException;
import org.jnode.driver.Device;
import org.jnode.driver.net.NetDeviceAPI;
import org.jnode.driver.net.NetworkException;
import org.jnode.net.HardwareAddress;
import org.jnode.net.NoSuchProtocolException;
import org.jnode.net.SocketBuffer;
import org.jnode.net.arp.ARPNetworkLayer;
import org.jnode.net.ethernet.EthernetConstants;
import org.jnode.net.ipv4.IPv4Address;
import org.jnode.net.ipv4.IPv4Constants;
import org.jnode.net.ipv4.IPv4Header;
import org.jnode.net.ipv4.IPv4ProtocolAddressInfo;
import org.jnode.net.ipv4.IPv4Route;
import org.jnode.net.ipv4.IPv4RoutingTable;
import org.jnode.net.util.NetUtils;
import org.jnode.util.TimeoutException;

/**
 * @author epr
 */
public class IPv4Sender implements IPv4Constants, EthernetConstants {
	
	/** The routing table */
	private final IPv4RoutingTable rt;
	/** The ARP service */
	private ARPNetworkLayer arp;
	/** Timeout for arp requests */
	private long arpTimeout = 5000;
	/** Last identification number */
	private int lastId = 1;
	/** My statistics */
	private final IPv4Statistics stat;
	
	/**
	 * Create a new instance
	 * @param ipNetworkLayer
	 */
	public IPv4Sender(IPv4NetworkLayer ipNetworkLayer) {
		this.rt = ipNetworkLayer.getRoutingTable();
		this.stat = (IPv4Statistics)ipNetworkLayer.getStatistics();
	}
	
	/**
	 * Transmit an IP packet.
	 * The given buffer must contain all packet data AND the header(s)
	 * of any IP sub-protocols, before this method is called.
	 * 
	 * The following fields of the IP header must be set:
	 * tos, ttl, protocol, dstAddress.
	 * <p/>
	 * All other header fields are set, unless they have been set before.
	 * <p/>
	 * The following fields are always set (also when set before):
	 * version, hdrlength, identification, fragmentOffset, checksum
	 * <p/>
	 * If the device attribute of the skbuf has been set, the packet will
	 * be send to this device, otherwise a suitable route will be searched
	 * for in the routing table.
	 * 
	 * @param hdr
	 * @param skbuf
	 * @throws NoRouteToHostException No suitable route for this packet was found
	 * @throws NetworkException The packet could not be transmitted.
	 */
	public void transmit(IPv4Header hdr, SocketBuffer skbuf)
	throws NoRouteToHostException, NetworkException {
		
		// Set the network layer header
		skbuf.setNetworkLayerHeader(hdr);
		
		// The destination address must have been set, check it
		if (hdr.getDestination() == null) {
			throw new NetworkException("The destination address must have been set");
		}
		//Syslog.debug("IP.transmit");
		stat.opackets.inc();

		// The device we will use to transmit the packet 
		final Device dev;
		final NetDeviceAPI api;
		// The hardware address we will be sending to
		final HardwareAddress hwDstAddr;
		
		// Has the destination device been given?		
		if (skbuf.getDevice() == null) {
			// The device has not been send, figure out the route ourselves.

			// First lets try to find a route
			final IPv4Route route;
			route = findRoute(hdr, skbuf);
			route.incUseCount();

			// Get the device
			dev = route.getDevice();
			api = route.getDeviceAPI();
		
			// Get my source address if not already set
			if (hdr.getSource() == null) {
				hdr.setSource(getSourceAddress(route, hdr, skbuf));
			}
		
			// Get the hardware address for this device
			hwDstAddr = findDstHWAddress(route, hdr, skbuf);
		} else {
			// The devive has been given, use it
			dev = skbuf.getDevice();
			try {
				api = (NetDeviceAPI)dev.getAPI(NetDeviceAPI.class);
			} catch (ApiNotFoundException ex) {
				throw new NetworkException("Device is not a network device", ex);
			}			
			// The source address must have been set, check it
			if (hdr.getSource() == null) {
				throw new NetworkException("The source address must have been set");
			}
			// Find the HW destination address
			hwDstAddr = findDstHWAddress(hdr.getDestination(), dev, hdr, skbuf);
		}		
		
		// Set the datalength (if not set)
		if (hdr.getDataLength() == 0) {
			hdr.setDataLength(skbuf.getSize());
		}
		
		// Set the identification number, if not set before
		if (hdr.getIdentification() == 0) {
			hdr.setIdentification(getNextID());
		}
		
		// Should we fragment?
		final int mtu = api.getMTU();
		
		if (hdr.getTotalLength() <= mtu) {
			// We can send the complete packet
			hdr.setMoreFragments(false);
			hdr.setFragmentOffset(0);
			sendPacket(api, hwDstAddr, hdr, skbuf);
		} else if (hdr.isDontFragment()) {
			// This packet cannot be send of this device
			throw new NetworkException("Packet is too large, mtu=" + mtu);
		} else {
			// Fragment the packet and send the fragments
			fragmentPacket(api, hwDstAddr, hdr, skbuf, mtu);
		}
	}
	
	/**
	 * Search for a route for the given buffer
	 * @param skbuf
	 * @return
	 * @throws NoRouteToHostException
	 */
	private IPv4Route findRoute(IPv4Header hdr, SocketBuffer skbuf) 
	throws NoRouteToHostException {
		final IPv4Address destination = hdr.getDestination();
		return rt.search(destination);
	}

	/**
	 * Gets the source address to use for a given route.
	 * @param route
	 * @param hdr
	 * @param skbuf
	 * @return
	 */	
	private IPv4Address getSourceAddress(IPv4Route route, IPv4Header hdr, SocketBuffer skbuf) 
	throws NetworkException {
		final Object addrInfo = route.getDeviceAPI().getProtocolAddressInfo(ETH_P_IP);
		if (addrInfo == null) {
			throw new NetworkException("Source IP address not configured for device " + route.getDevice().getId());
		}
		if (!(addrInfo instanceof IPv4ProtocolAddressInfo)) {
			throw new NetworkException("Source IP address not valid class for device " + route.getDevice().getId());
		}
		return (IPv4Address)((IPv4ProtocolAddressInfo)addrInfo).getDefaultAddress();
	}
	
	/**
	 * Find the hardware address for the destination address of the given route.
	 * @param route
	 * @return
	 */
	private HardwareAddress findDstHWAddress(IPv4Route route, IPv4Header hdr, SocketBuffer skbuf) 
	throws NetworkException {
		final ARPNetworkLayer arp = getARP();
		final IPv4Address dstAddr;
		if (hdr.getDestination().isBroadcast()) {
			return null;
		} else if (route.isGateway()) {
			dstAddr = route.getGateway();
		} else {
			dstAddr = hdr.getDestination();
		}
		try {
			return arp.getHardwareAddress(dstAddr, hdr.getSource(), route.getDevice(), arpTimeout);
		} catch (TimeoutException ex) {
			throw new NetworkException("Cannot find hardware address of " + dstAddr, ex);
		}
	}
	
	/**
	 * Find the hardware address for the destination address of the given route.
	 * @param destination
	 * @param device
	 * @param hdr
	 * @param skbuf
	 * @return HardwareAddress
	 */
	private HardwareAddress findDstHWAddress(IPv4Address destination, Device device, IPv4Header hdr, SocketBuffer skbuf) 
	throws NetworkException {
		final ARPNetworkLayer arp = getARP();
		if (destination.isBroadcast()) {
			return null;
		} else {
			try {
				return arp.getHardwareAddress(destination, hdr.getSource(), device, arpTimeout);
			} catch (TimeoutException ex) {
				throw new NetworkException("Cannot find hardware address of " + destination, ex);
			}
		}
	}
	
	/**
	 * Insert the IP header into the buffer and send it to the device.
	 * @param api
	 * @param hdr
	 * @param skbuf
	 * @throws NetworkException
	 */
	private void sendPacket(NetDeviceAPI api, HardwareAddress dstHwAddr, IPv4Header hdr, SocketBuffer skbuf)
	throws NetworkException {
		//Syslog.debug("Sending IP packet to " + dstHwAddr);
		skbuf.setProtocolID(ETH_P_IP);
		hdr.prefixTo(skbuf);
		api.transmit(skbuf, dstHwAddr);
	}
	
	/**
	 * Fragment the packet and send the to the device
	 * @param api
	 * @param hdr
	 * @param skbuf
	 * @throws NetworkException
	 */
	private void fragmentPacket(NetDeviceAPI api, HardwareAddress dstHwAddr, IPv4Header hdr, SocketBuffer skbuf, int mtu)
	throws NetworkException {
		if ((hdr.getLength() + IP_MIN_FRAG_SIZE) > mtu) {
			throw new NetworkException("MTU is too small for IP, mtu=" + mtu); 
		}
		
		// The complete packet
		final byte[] packet = skbuf.toByteArray();
		int length = packet.length;
		int offset = 0;
		// Size of a single fragment
		final int maxFragSize = (mtu - hdr.getLength()) & ~IP_MIN_FRAG_SIZE;
		
		// Now create the fragmented packets and send them
		while (length > 0) {
			final int fragLen = Math.min(maxFragSize, length);
			final SocketBuffer fBuf = new SocketBuffer(packet, offset, fragLen);
			hdr.setFragmentOffset(offset);
			hdr.setMoreFragments((length - fragLen) > 0);
			hdr.setDataLength(fragLen);
			sendPacket(api, dstHwAddr, hdr, fBuf);
			offset += fragLen;
			length -= fragLen;
		}
	}
	
	/**
	 * Gets the ARP service
	 * @return
	 */
	private ARPNetworkLayer getARP() 
	throws NetworkException {
		if (arp == null) {
			try {
				arp = (ARPNetworkLayer)NetUtils.getNLM().getNetworkLayer(EthernetConstants.ETH_P_ARP);
				//arp = (ARPService)InitialNaming.lookup(ARPService.NAME);
			} catch (NoSuchProtocolException ex) {
				throw new NetworkException("Cannot find ARP layer", ex);
			}
		}
		return arp;
	}
	
	/**
	 * Gets a unique identification number
	 * @return
	 */
	private synchronized int getNextID() {
		lastId++;
		if (lastId > 0xFFFF) {
			lastId = 0;
		}
		return lastId;
	}
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?