package net.i2p.router.transport.udp;
import java.net.InetAddress;
import java.net.Inet6Address;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
import net.i2p.util.SystemVersion;
/**
* Get the MTU for the network interface of an address.
* Not available until Java 6 / Android API 9.
*
* Public only for command line test.
* Not for external use, not a public API.
*
* @since 0.9.2. public since 0.9.27
*/
public class MTU {
private static final boolean hasMTU = SystemVersion.isJava6();
private MTU() {};
/**
* The MTU for the socket interface, if available.
* Not available for Java 5.
*
* Note that we don't return the value for the default interface if
* we can't find the address. Finding the default interface is hard,
* altough we could perhaps just look for the first non-loopback address.
* But the MTU of the default route probably isn't relevant.
*
* @param ia null ok
* @return 0 if Java 5, or if not bound to an address;
* limited to range MIN_MTU to LARGE_MTU.
*/
public static int getMTU(InetAddress ia) {
if (ia == null || !hasMTU)
return 0;
Enumeration<NetworkInterface> ifcs;
try {
ifcs = NetworkInterface.getNetworkInterfaces();
} catch (SocketException se) {
return 0;
} catch (java.lang.Error e) {
// Windows, possibly when IPv6 only...
// https://bugs.openjdk.java.net/browse/JDK-8046500
// java.lang.Error: IP Helper Library GetIfTable function failed
// at java.net.NetworkInterface.getAll(Native Method)
// at java.net.NetworkInterface.getNetworkInterfaces(Unknown Source)
// at net.i2p.util.Addresses.getAddresses ...
return 0;
}
if (ifcs != null) {
while (ifcs.hasMoreElements()) {
NetworkInterface ifc = ifcs.nextElement();
for(Enumeration<InetAddress> addrs = ifc.getInetAddresses(); addrs.hasMoreElements();) {
InetAddress addr = addrs.nextElement();
if (ia.equals(addr)) {
try {
// testing
//return ifc.getMTU();
boolean isIPv6 = addr instanceof Inet6Address;
int mtu = ifc.getMTU();
if ((isIPv6 && mtu < PeerState.MIN_IPV6_MTU) ||
(!isIPv6 && mtu < PeerState.MIN_MTU)) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(MTU.class);
log.logAlways(Log.WARN, "Unusually low MTU " + mtu + " for interface " + ia +
", consider disabling");
}
return rectify(isIPv6, mtu);
} catch (SocketException se) {
// ignore
} catch (Throwable t) {
// NoSuchMethodException or NoSuchMethodError if we somehow got the
// version detection wrong or the JVM doesn't support it
return 0;
}
}
}
}
}
return 0;
}
/**
* @return min of PeerState.MIN_MTU, max of PeerState.LARGE_MTU,
* rectified so rv % 16 == 12 (IPv4)
* or rv % 16 == 0 (IPv6)
*/
public static int rectify(boolean isIPv6, int mtu) {
int rv = mtu;
int mod = rv % 16;
if (isIPv6) {
rv -= mod;
return Math.max(PeerState.MIN_IPV6_MTU, Math.min(PeerState.MAX_IPV6_MTU, rv));
}
if (mod > 12)
rv -= mod - 12;
else if (mod < 12)
rv -= mod + 4;
return Math.max(PeerState.MIN_MTU, Math.min(PeerState.LARGE_MTU, rv));
}
public static void main(String args[]) {
/****
System.out.println("Cmd line interfaces:");
for (int i = 0; i < args.length; i++) {
try {
InetAddress test = InetAddress.getByName(args[i]);
System.out.println("MTU of " + args[i] + " is " + getMTU(test));
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("All interfaces:");
****/
try {
Enumeration<NetworkInterface> ifcs = NetworkInterface.getNetworkInterfaces();
if (ifcs != null) {
while (ifcs.hasMoreElements()) {
NetworkInterface ifc = ifcs.nextElement();
for(Enumeration<InetAddress> addrs = ifc.getInetAddresses(); addrs.hasMoreElements();) {
InetAddress addr = addrs.nextElement();
System.out.println("I2P MTU for " + addr.getHostAddress() + " is " + getMTU(addr));
}
}
}
} catch (SocketException se) {
System.out.println("no interfaces");
}
}
}