forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.next' (head e89194f845a2a74dbf00f0f9e3c25a8f96ec36e4)
to branch 'i2p.i2p' (head 9cb2795f19efeae08b9ecffa5137fd944de120c7)
This commit is contained in:
@@ -18,7 +18,6 @@
|
|||||||
<!-- Depend on classes instead of jars where available -->
|
<!-- Depend on classes instead of jars where available -->
|
||||||
<classpath>
|
<classpath>
|
||||||
<pathelement location="../../../core/java/build/obj" />
|
<pathelement location="../../../core/java/build/obj" />
|
||||||
<pathelement location="../../../router/java/build/obj" />
|
|
||||||
<pathelement location="../../ministreaming/java/build/obj" />
|
<pathelement location="../../ministreaming/java/build/obj" />
|
||||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
||||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||||
@@ -32,7 +31,7 @@
|
|||||||
srcdir="./src"
|
srcdir="./src"
|
||||||
debug="true" deprecation="on" source="1.5" target="1.5"
|
debug="true" deprecation="on" source="1.5" target="1.5"
|
||||||
destdir="./build/obj"
|
destdir="./build/obj"
|
||||||
classpath="../../../core/java/build/i2p.jar:../../../router/java/build/router.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../ministreaming/java/build/mstreaming.jar" />
|
classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../ministreaming/java/build/mstreaming.jar" />
|
||||||
</target>
|
</target>
|
||||||
<target name="jar" depends="builddep, compile">
|
<target name="jar" depends="builddep, compile">
|
||||||
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/*Servlet.class">
|
<jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/*Servlet.class">
|
||||||
|
44
apps/i2psnark/java/src/org/klomp/snark/BWLimits.java
Normal file
44
apps/i2psnark/java/src/org/klomp/snark/BWLimits.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
package org.klomp.snark;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.client.I2PSessionException;
|
||||||
|
import net.i2p.client.I2PClient;
|
||||||
|
import net.i2p.client.I2PSession;
|
||||||
|
import net.i2p.client.I2PSimpleClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect via I2CP and ask the router the bandwidth limits.
|
||||||
|
*
|
||||||
|
* The call is blocking and returns null on failure.
|
||||||
|
* Timeout is set to 5 seconds in I2PSimpleSession but it should be much faster.
|
||||||
|
*
|
||||||
|
* @author zzz
|
||||||
|
*/
|
||||||
|
class BWLimits {
|
||||||
|
|
||||||
|
public static int[] getBWLimits(String host, int port) {
|
||||||
|
int[] rv = null;
|
||||||
|
try {
|
||||||
|
I2PClient client = new I2PSimpleClient();
|
||||||
|
Properties opts = new Properties();
|
||||||
|
opts.put(I2PClient.PROP_TCP_HOST, host);
|
||||||
|
opts.put(I2PClient.PROP_TCP_PORT, "" + port);
|
||||||
|
I2PSession session = client.createSession(null, opts);
|
||||||
|
session.connect();
|
||||||
|
rv = session.bandwidthLimits();
|
||||||
|
session.destroySession();
|
||||||
|
} catch (I2PSessionException ise) {}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) {
|
||||||
|
System.out.println(Arrays.toString(getBWLimits("127.0.0.1", 7654)));
|
||||||
|
}
|
||||||
|
}
|
@@ -36,7 +36,6 @@ import java.util.Timer;
|
|||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.router.client.ClientManagerFacadeImpl;
|
|
||||||
import net.i2p.client.streaming.I2PServerSocket;
|
import net.i2p.client.streaming.I2PServerSocket;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.util.I2PThread;
|
import net.i2p.util.I2PThread;
|
||||||
@@ -261,9 +260,9 @@ public class Snark
|
|||||||
public Snark(I2PAppContext ctx, Properties opts, String torrent,
|
public Snark(I2PAppContext ctx, Properties opts, String torrent,
|
||||||
StorageListener slistener, boolean start, String rootDir) {
|
StorageListener slistener, boolean start, String rootDir) {
|
||||||
this(new I2PSnarkUtil(ctx), torrent, null, -1, slistener, null, null, null, null, false, rootDir);
|
this(new I2PSnarkUtil(ctx), torrent, null, -1, slistener, null, null, null, null, false, rootDir);
|
||||||
String host = opts.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_HOST);
|
String host = opts.getProperty("i2cp.hostname");
|
||||||
int port = 0;
|
int port = 0;
|
||||||
String s = opts.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_PORT);
|
String s = opts.getProperty("i2cp.port");
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
try {
|
try {
|
||||||
port = Integer.parseInt(s);
|
port = Integer.parseInt(s);
|
||||||
|
@@ -18,7 +18,6 @@ import java.util.TreeMap;
|
|||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.router.RouterContext;
|
|
||||||
import net.i2p.util.I2PAppThread;
|
import net.i2p.util.I2PAppThread;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
@@ -147,22 +146,21 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
_config.setProperty(PROP_EEP_PORT, "4444");
|
_config.setProperty(PROP_EEP_PORT, "4444");
|
||||||
if (!_config.containsKey(PROP_UPLOADERS_TOTAL))
|
if (!_config.containsKey(PROP_UPLOADERS_TOTAL))
|
||||||
_config.setProperty(PROP_UPLOADERS_TOTAL, "" + Snark.MAX_TOTAL_UPLOADERS);
|
_config.setProperty(PROP_UPLOADERS_TOTAL, "" + Snark.MAX_TOTAL_UPLOADERS);
|
||||||
if (!_config.containsKey(PROP_UPBW_MAX)) {
|
|
||||||
try {
|
|
||||||
if (_context instanceof RouterContext)
|
|
||||||
_config.setProperty(PROP_UPBW_MAX, "" + (((RouterContext)_context).bandwidthLimiter().getOutboundKBytesPerSecond() / 2));
|
|
||||||
else
|
|
||||||
_config.setProperty(PROP_UPBW_MAX, "" + DEFAULT_MAX_UP_BW);
|
|
||||||
} catch (NoClassDefFoundError ncdfe) {
|
|
||||||
_config.setProperty(PROP_UPBW_MAX, "" + DEFAULT_MAX_UP_BW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!_config.containsKey(PROP_DIR))
|
if (!_config.containsKey(PROP_DIR))
|
||||||
_config.setProperty(PROP_DIR, "i2psnark");
|
_config.setProperty(PROP_DIR, "i2psnark");
|
||||||
if (!_config.containsKey(PROP_AUTO_START))
|
if (!_config.containsKey(PROP_AUTO_START))
|
||||||
_config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START);
|
_config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START);
|
||||||
updateConfig();
|
updateConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** call from DirMonitor since loadConfig() is called before router I2CP is up */
|
||||||
|
private void getBWLimit() {
|
||||||
|
if (!_config.containsKey(PROP_UPBW_MAX)) {
|
||||||
|
int[] limits = BWLimits.getBWLimits(_util.getI2CPHost(), _util.getI2CPPort());
|
||||||
|
if (limits != null && limits[1] > 0)
|
||||||
|
_util.setMaxUpBW(limits[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateConfig() {
|
private void updateConfig() {
|
||||||
String i2cpHost = _config.getProperty(PROP_I2CP_HOST);
|
String i2cpHost = _config.getProperty(PROP_I2CP_HOST);
|
||||||
@@ -619,6 +617,9 @@ public class SnarkManager implements Snark.CompleteListener {
|
|||||||
_messages.remove(0);
|
_messages.remove(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// here because we need to delay until I2CP is up
|
||||||
|
// although the user will see the default until then
|
||||||
|
getBWLimit();
|
||||||
while (true) {
|
while (true) {
|
||||||
File dir = getDataDir();
|
File dir = getDataDir();
|
||||||
_log.debug("Directory Monitor loop over " + dir.getAbsolutePath());
|
_log.debug("Directory Monitor loop over " + dir.getAbsolutePath());
|
||||||
|
25
core/java/src/net/i2p/client/BWLimitsMessageHandler.java
Normal file
25
core/java/src/net/i2p/client/BWLimitsMessageHandler.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package net.i2p.client;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.data.i2cp.I2CPMessage;
|
||||||
|
import net.i2p.data.i2cp.BandwidthLimitsMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle I2CP BW replies from the router
|
||||||
|
*/
|
||||||
|
class BWLimitsMessageHandler extends HandlerImpl {
|
||||||
|
public BWLimitsMessageHandler(I2PAppContext ctx) {
|
||||||
|
super(ctx, BandwidthLimitsMessage.MESSAGE_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
|
||||||
|
_log.debug("Handle message " + message);
|
||||||
|
BandwidthLimitsMessage msg = (BandwidthLimitsMessage) message;
|
||||||
|
((I2PSimpleSession)session).bwReceived(msg.getLimits());
|
||||||
|
}
|
||||||
|
}
|
@@ -143,6 +143,11 @@ public interface I2PSession {
|
|||||||
*/
|
*/
|
||||||
public Destination lookupDest(Hash h) throws I2PSessionException;
|
public Destination lookupDest(Hash h) throws I2PSessionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current bandwidth limits
|
||||||
|
*/
|
||||||
|
public int[] bandwidthLimits() throws I2PSessionException;
|
||||||
|
|
||||||
/** See I2PSessionMuxedImpl for details */
|
/** See I2PSessionMuxedImpl for details */
|
||||||
public void addSessionListener(I2PSessionListener lsnr, int proto, int port);
|
public void addSessionListener(I2PSessionListener lsnr, int proto, int port);
|
||||||
/** See I2PSessionMuxedImpl for details */
|
/** See I2PSessionMuxedImpl for details */
|
||||||
|
@@ -656,6 +656,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int[] bandwidthLimits() throws I2PSessionException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateActivity() {
|
protected void updateActivity() {
|
||||||
_lastActivity = _context.clock().now();
|
_lastActivity = _context.clock().now();
|
||||||
if (_isReduced) {
|
if (_isReduced) {
|
||||||
|
@@ -17,25 +17,32 @@ import net.i2p.I2PAppContext;
|
|||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.i2cp.BandwidthLimitsMessage;
|
||||||
import net.i2p.data.i2cp.DestLookupMessage;
|
import net.i2p.data.i2cp.DestLookupMessage;
|
||||||
import net.i2p.data.i2cp.DestReplyMessage;
|
import net.i2p.data.i2cp.DestReplyMessage;
|
||||||
|
import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
|
||||||
import net.i2p.data.i2cp.I2CPMessageReader;
|
import net.i2p.data.i2cp.I2CPMessageReader;
|
||||||
import net.i2p.util.I2PThread;
|
import net.i2p.util.I2PThread;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new session for doing naming queries only. Do not create a Destination.
|
* Create a new session for doing naming and bandwidth queries only. Do not create a Destination.
|
||||||
* Don't create a producer. Do not send/receive messages to other Destinations.
|
* Don't create a producer. Do not send/receive messages to other Destinations.
|
||||||
* Cannot handle multiple simultaneous queries atm.
|
* Cannot handle multiple simultaneous queries atm.
|
||||||
* Could be expanded to ask the router other things.
|
* Could be expanded to ask the router other things.
|
||||||
|
*
|
||||||
|
* @author zzz
|
||||||
*/
|
*/
|
||||||
class I2PSimpleSession extends I2PSessionImpl2 {
|
class I2PSimpleSession extends I2PSessionImpl2 {
|
||||||
private boolean _destReceived;
|
private boolean _destReceived;
|
||||||
private Object _destReceivedLock;
|
private Object _destReceivedLock;
|
||||||
private Destination _destination;
|
private Destination _destination;
|
||||||
|
private boolean _bwReceived;
|
||||||
|
private Object _bwReceivedLock;
|
||||||
|
private int[] _bwLimits;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new session for doing naming queries only. Do not create a destination.
|
* Create a new session for doing naming and bandwidth queries only. Do not create a destination.
|
||||||
*
|
*
|
||||||
* @throws I2PSessionException if there is a problem
|
* @throws I2PSessionException if there is a problem
|
||||||
*/
|
*/
|
||||||
@@ -94,6 +101,14 @@ class I2PSimpleSession extends I2PSessionImpl2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bwReceived(int[] i) {
|
||||||
|
_bwReceived = true;
|
||||||
|
_bwLimits = i;
|
||||||
|
synchronized (_bwReceivedLock) {
|
||||||
|
_bwReceivedLock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Destination lookupDest(Hash h) throws I2PSessionException {
|
public Destination lookupDest(Hash h) throws I2PSessionException {
|
||||||
if (_closed)
|
if (_closed)
|
||||||
return null;
|
return null;
|
||||||
@@ -110,14 +125,31 @@ class I2PSimpleSession extends I2PSessionImpl2 {
|
|||||||
return _destination;
|
return _destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int[] bandwidthLimits() throws I2PSessionException {
|
||||||
|
if (_closed)
|
||||||
|
return null;
|
||||||
|
_bwReceivedLock = new Object();
|
||||||
|
sendMessage(new GetBandwidthLimitsMessage());
|
||||||
|
for (int i = 0; i < 5 && !_bwReceived; i++) {
|
||||||
|
try {
|
||||||
|
synchronized (_bwReceivedLock) {
|
||||||
|
_bwReceivedLock.wait(1000);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
_bwReceived = false;
|
||||||
|
return _bwLimits;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only map message handlers that we will use
|
* Only map message handlers that we will use
|
||||||
*/
|
*/
|
||||||
class SimpleMessageHandlerMap extends I2PClientMessageHandlerMap {
|
class SimpleMessageHandlerMap extends I2PClientMessageHandlerMap {
|
||||||
public SimpleMessageHandlerMap(I2PAppContext context) {
|
public SimpleMessageHandlerMap(I2PAppContext context) {
|
||||||
int highest = DestReplyMessage.MESSAGE_TYPE;
|
int highest = Math.max(DestReplyMessage.MESSAGE_TYPE, BandwidthLimitsMessage.MESSAGE_TYPE);
|
||||||
_handlers = new I2CPMessageHandler[highest+1];
|
_handlers = new I2CPMessageHandler[highest+1];
|
||||||
_handlers[DestReplyMessage.MESSAGE_TYPE] = new DestReplyMessageHandler(context);
|
_handlers[DestReplyMessage.MESSAGE_TYPE] = new DestReplyMessageHandler(context);
|
||||||
|
_handlers[BandwidthLimitsMessage.MESSAGE_TYPE] = new BWLimitsMessageHandler(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
101
core/java/src/net/i2p/data/i2cp/BandwidthLimitsMessage.java
Normal file
101
core/java/src/net/i2p/data/i2cp/BandwidthLimitsMessage.java
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package net.i2p.data.i2cp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* public domain
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the other side the limits
|
||||||
|
*
|
||||||
|
* @author zzz
|
||||||
|
*/
|
||||||
|
public class BandwidthLimitsMessage extends I2CPMessageImpl {
|
||||||
|
private final static Log _log = new Log(BandwidthLimitsMessage.class);
|
||||||
|
public final static int MESSAGE_TYPE = 23;
|
||||||
|
private static final int LIMITS = 16;
|
||||||
|
private int[] data;
|
||||||
|
|
||||||
|
public BandwidthLimitsMessage() {
|
||||||
|
super();
|
||||||
|
data = new int[LIMITS];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let's define it this way.
|
||||||
|
* Leave some extra. This is only local and rarely sent so we don't care about waste.
|
||||||
|
*
|
||||||
|
* 0) Client inbound limit (KBps)
|
||||||
|
* 1) Client outbound limit (KBps)
|
||||||
|
* 2) Router inbound limit (KBps)
|
||||||
|
* 3) Router inbound burst limit (KBps)
|
||||||
|
* 4) Router outbound limit (KBps)
|
||||||
|
* 5) Router outbound burst limit (KBps)
|
||||||
|
* 6) Router burst time (seconds)
|
||||||
|
* 7-15) undefined
|
||||||
|
*/
|
||||||
|
public BandwidthLimitsMessage(int in, int out) {
|
||||||
|
this();
|
||||||
|
data[0] = in;
|
||||||
|
data[1] = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getLimits() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < LIMITS; i++) {
|
||||||
|
data[i] = (int) DataHelper.readLong(in, 4);
|
||||||
|
}
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("Unable to load the message data", dfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream(64);
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < LIMITS; i++) {
|
||||||
|
DataHelper.writeLong(os, 4, data[i]);
|
||||||
|
}
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("Error writing out the message data", dfe);
|
||||||
|
}
|
||||||
|
return os.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return MESSAGE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if ((object != null) && (object instanceof BandwidthLimitsMessage)) {
|
||||||
|
BandwidthLimitsMessage msg = (BandwidthLimitsMessage) object;
|
||||||
|
return DataHelper.eq(data, msg.getLimits());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("[BandwidthLimitsMessage");
|
||||||
|
buf.append("\n\tIn: ").append(data[0]);
|
||||||
|
buf.append("\n\tOut: ").append(data[1]);
|
||||||
|
buf.append("]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,56 @@
|
|||||||
|
package net.i2p.data.i2cp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* public domain
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the router tells us the current bw limits
|
||||||
|
*
|
||||||
|
* @author zzz
|
||||||
|
*/
|
||||||
|
public class GetBandwidthLimitsMessage extends I2CPMessageImpl {
|
||||||
|
private final static Log _log = new Log(GetBandwidthLimitsMessage.class);
|
||||||
|
public final static int MESSAGE_TYPE = 8;
|
||||||
|
|
||||||
|
public GetBandwidthLimitsMessage() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||||
|
byte rv[] = new byte[0];
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return MESSAGE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if ((object != null) && (object instanceof GetBandwidthLimitsMessage)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("[GetBandwidthLimitsMessage]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
@@ -94,6 +94,10 @@ public class I2CPMessageHandler {
|
|||||||
return new DestLookupMessage();
|
return new DestLookupMessage();
|
||||||
case DestReplyMessage.MESSAGE_TYPE:
|
case DestReplyMessage.MESSAGE_TYPE:
|
||||||
return new DestReplyMessage();
|
return new DestReplyMessage();
|
||||||
|
case GetBandwidthLimitsMessage.MESSAGE_TYPE:
|
||||||
|
return new GetBandwidthLimitsMessage();
|
||||||
|
case BandwidthLimitsMessage.MESSAGE_TYPE:
|
||||||
|
return new BandwidthLimitsMessage();
|
||||||
default:
|
default:
|
||||||
throw new I2CPMessageException("The type " + type + " is an unknown I2CP message");
|
throw new I2CPMessageException("The type " + type + " is an unknown I2CP message");
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ package net.i2p.router;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
@@ -44,6 +45,7 @@ class DummyTunnelManagerFacade implements TunnelManagerFacade {
|
|||||||
public void setInboundSettings(Hash client, TunnelPoolSettings settings) {}
|
public void setInboundSettings(Hash client, TunnelPoolSettings settings) {}
|
||||||
public void setOutboundSettings(Hash client, TunnelPoolSettings settings) {}
|
public void setOutboundSettings(Hash client, TunnelPoolSettings settings) {}
|
||||||
public int getInboundBuildQueueSize() { return 0; }
|
public int getInboundBuildQueueSize() { return 0; }
|
||||||
|
public Set<Hash> selectPeersInTooManyTunnels() { return null; }
|
||||||
|
|
||||||
public void renderStatusHTML(Writer out) throws IOException {}
|
public void renderStatusHTML(Writer out) throws IOException {}
|
||||||
public void restart() {}
|
public void restart() {}
|
||||||
|
@@ -10,6 +10,7 @@ package net.i2p.router;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
@@ -62,6 +63,9 @@ public interface TunnelManagerFacade extends Service {
|
|||||||
/** count how many inbound tunnel requests we have received but not yet processed */
|
/** count how many inbound tunnel requests we have received but not yet processed */
|
||||||
public int getInboundBuildQueueSize();
|
public int getInboundBuildQueueSize();
|
||||||
|
|
||||||
|
/** @return Set of peers that should not be allowed to be in another tunnel */
|
||||||
|
public Set<Hash> selectPeersInTooManyTunnels();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the client connected (or updated their settings), so make sure we have
|
* the client connected (or updated their settings), so make sure we have
|
||||||
* the tunnels for them, and whenever necessary, ask them to authorize
|
* the tunnels for them, and whenever necessary, ask them to authorize
|
||||||
|
@@ -11,10 +11,12 @@ package net.i2p.router.client;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import net.i2p.data.Payload;
|
import net.i2p.data.Payload;
|
||||||
|
import net.i2p.data.i2cp.BandwidthLimitsMessage;
|
||||||
import net.i2p.data.i2cp.CreateLeaseSetMessage;
|
import net.i2p.data.i2cp.CreateLeaseSetMessage;
|
||||||
import net.i2p.data.i2cp.CreateSessionMessage;
|
import net.i2p.data.i2cp.CreateSessionMessage;
|
||||||
import net.i2p.data.i2cp.DestLookupMessage;
|
import net.i2p.data.i2cp.DestLookupMessage;
|
||||||
import net.i2p.data.i2cp.DestroySessionMessage;
|
import net.i2p.data.i2cp.DestroySessionMessage;
|
||||||
|
import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
|
||||||
import net.i2p.data.i2cp.GetDateMessage;
|
import net.i2p.data.i2cp.GetDateMessage;
|
||||||
import net.i2p.data.i2cp.I2CPMessage;
|
import net.i2p.data.i2cp.I2CPMessage;
|
||||||
import net.i2p.data.i2cp.I2CPMessageException;
|
import net.i2p.data.i2cp.I2CPMessageException;
|
||||||
@@ -93,6 +95,9 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
case ReconfigureSessionMessage.MESSAGE_TYPE:
|
case ReconfigureSessionMessage.MESSAGE_TYPE:
|
||||||
handleReconfigureSession(reader, (ReconfigureSessionMessage)message);
|
handleReconfigureSession(reader, (ReconfigureSessionMessage)message);
|
||||||
break;
|
break;
|
||||||
|
case GetBandwidthLimitsMessage.MESSAGE_TYPE:
|
||||||
|
handleGetBWLimits(reader, (GetBandwidthLimitsMessage)message);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("Unhandled I2CP type received: " + message.getType());
|
_log.error("Unhandled I2CP type received: " + message.getType());
|
||||||
@@ -274,6 +279,24 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divide router limit by 1.75 for overhead.
|
||||||
|
* This could someday give a different answer to each client.
|
||||||
|
* But it's not enforced anywhere.
|
||||||
|
*/
|
||||||
|
private void handleGetBWLimits(I2CPMessageReader reader, GetBandwidthLimitsMessage message) {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Got BW Limits request");
|
||||||
|
int in = _context.bandwidthLimiter().getInboundKBytesPerSecond() * 4 / 7;
|
||||||
|
int out = _context.bandwidthLimiter().getOutboundKBytesPerSecond() * 4 / 7;
|
||||||
|
BandwidthLimitsMessage msg = new BandwidthLimitsMessage(in, out);
|
||||||
|
try {
|
||||||
|
_runner.doSend(msg);
|
||||||
|
} catch (I2CPMessageException ime) {
|
||||||
|
_log.error("Error writing out the session status message", ime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
|
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
|
||||||
private final static int MAX_SESSION_ID = 32767;
|
private final static int MAX_SESSION_ID = 32767;
|
||||||
|
|
||||||
|
@@ -176,6 +176,7 @@ public abstract class TunnelPeerSelector {
|
|||||||
|
|
||||||
Set peers = new HashSet(1);
|
Set peers = new HashSet(1);
|
||||||
peers.addAll(ctx.profileOrganizer().selectPeersRecentlyRejecting());
|
peers.addAll(ctx.profileOrganizer().selectPeersRecentlyRejecting());
|
||||||
|
peers.addAll(ctx.tunnelManager().selectPeersInTooManyTunnels());
|
||||||
// if (false && filterUnreachable(ctx, isInbound, isExploratory)) {
|
// if (false && filterUnreachable(ctx, isInbound, isExploratory)) {
|
||||||
if (filterUnreachable(ctx, isInbound, isExploratory)) {
|
if (filterUnreachable(ctx, isInbound, isExploratory)) {
|
||||||
List caps = ctx.peerManager().getPeersByCapability(Router.CAPABILITY_UNREACHABLE);
|
List caps = ctx.peerManager().getPeersByCapability(Router.CAPABILITY_UNREACHABLE);
|
||||||
|
@@ -28,7 +28,7 @@ public class TunnelPool {
|
|||||||
private RouterContext _context;
|
private RouterContext _context;
|
||||||
private Log _log;
|
private Log _log;
|
||||||
private TunnelPoolSettings _settings;
|
private TunnelPoolSettings _settings;
|
||||||
private ArrayList _tunnels;
|
private ArrayList<TunnelInfo> _tunnels;
|
||||||
private TunnelPeerSelector _peerSelector;
|
private TunnelPeerSelector _peerSelector;
|
||||||
private TunnelPoolManager _manager;
|
private TunnelPoolManager _manager;
|
||||||
private boolean _alive;
|
private boolean _alive;
|
||||||
@@ -227,7 +227,7 @@ public class TunnelPool {
|
|||||||
*
|
*
|
||||||
* @return list of TunnelInfo objects
|
* @return list of TunnelInfo objects
|
||||||
*/
|
*/
|
||||||
public List listTunnels() {
|
public List<TunnelInfo> listTunnels() {
|
||||||
synchronized (_tunnels) {
|
synchronized (_tunnels) {
|
||||||
return new ArrayList(_tunnels);
|
return new ArrayList(_tunnels);
|
||||||
}
|
}
|
||||||
|
@@ -6,9 +6,12 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
@@ -506,6 +509,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
|||||||
out.write("</table>\n");
|
out.write("</table>\n");
|
||||||
out.write("Inactive participating tunnels: " + inactive + "<br />\n");
|
out.write("Inactive participating tunnels: " + inactive + "<br />\n");
|
||||||
out.write("Lifetime bandwidth usage: " + DataHelper.formatSize(processed*1024) + "B<br />\n");
|
out.write("Lifetime bandwidth usage: " + DataHelper.formatSize(processed*1024) + "B<br />\n");
|
||||||
|
renderPeers(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TunnelComparator implements Comparator {
|
class TunnelComparator implements Comparator {
|
||||||
@@ -579,6 +583,135 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
|||||||
DataHelper.formatSize(processedOut*1024) + "B out<br />");
|
DataHelper.formatSize(processedOut*1024) + "B out<br />");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void renderPeers(Writer out) throws IOException {
|
||||||
|
// count up the peers in the local pools
|
||||||
|
HashCounter lc = new HashCounter();
|
||||||
|
int tunnelCount = countTunnelsPerPeer(lc);
|
||||||
|
|
||||||
|
// count up the peers in the participating tunnels
|
||||||
|
HashCounter pc = new HashCounter();
|
||||||
|
int partCount = countParticipatingPerPeer(pc);
|
||||||
|
|
||||||
|
Set<Hash> peers = new HashSet(lc.hashes());
|
||||||
|
peers.addAll(pc.hashes());
|
||||||
|
List<Hash> peerList = new ArrayList(peers);
|
||||||
|
Collections.sort(peerList, new HashComparator());
|
||||||
|
|
||||||
|
out.write("<h2><a name=\"peers\">Tunnel Counts By Peer</a>:</h2>\n");
|
||||||
|
out.write("<table border=\"1\"><tr><td><b>Peer</b></td><td><b>Expl. + Client</b></td><td><b>% of total</b></td><td><b>Part. from + to</b></td><td><b>% of total</b></td></tr>\n");
|
||||||
|
for (Hash h : peerList) {
|
||||||
|
out.write("<tr><td>");
|
||||||
|
out.write(netDbLink(h));
|
||||||
|
out.write("<td align=\"right\">" + lc.count(h));
|
||||||
|
out.write("<td align=\"right\">");
|
||||||
|
if (tunnelCount > 0)
|
||||||
|
out.write("" + (lc.count(h) * 100 / tunnelCount));
|
||||||
|
else
|
||||||
|
out.write('0');
|
||||||
|
out.write("<td align=\"right\">" + pc.count(h));
|
||||||
|
out.write("<td align=\"right\">");
|
||||||
|
if (partCount > 0)
|
||||||
|
out.write("" + (pc.count(h) * 100 / partCount));
|
||||||
|
else
|
||||||
|
out.write('0');
|
||||||
|
out.write('\n');
|
||||||
|
}
|
||||||
|
out.write("<tr><td>Tunnels<td align=\"right\">" + tunnelCount);
|
||||||
|
out.write("<td> <td align=\"right\">" + partCount);
|
||||||
|
out.write("<td> </table>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return total number of non-fallback expl. + client tunnels */
|
||||||
|
private int countTunnelsPerPeer(HashCounter lc) {
|
||||||
|
List<TunnelPool> pools = new ArrayList();
|
||||||
|
listPools(pools);
|
||||||
|
int tunnelCount = 0;
|
||||||
|
for (TunnelPool tp : pools) {
|
||||||
|
for (TunnelInfo info : tp.listTunnels()) {
|
||||||
|
if (info.getLength() > 1) {
|
||||||
|
tunnelCount++;
|
||||||
|
for (int j = 0; j < info.getLength(); j++) {
|
||||||
|
Hash peer = info.getPeer(j);
|
||||||
|
if (!_context.routerHash().equals(peer))
|
||||||
|
lc.increment(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tunnelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int DEFAULT_MAX_PCT_TUNNELS = 33;
|
||||||
|
/**
|
||||||
|
* For reliability reasons, don't allow a peer in more than x% of
|
||||||
|
* client and exploratory tunnels.
|
||||||
|
*
|
||||||
|
* This also will prevent a single huge-capacity (or malicious) peer from
|
||||||
|
* taking all the tunnels in the network (although it would be nice to limit
|
||||||
|
* the % of total network tunnels to 10% or so, but that appears to be
|
||||||
|
* too low to set as a default here... much lower than 33% will push client
|
||||||
|
* tunnels out of the fast tier into high cap or beyond...)
|
||||||
|
*
|
||||||
|
* Possible improvement - restrict based on count per IP, or IP block,
|
||||||
|
* to slightly increase costs of collusion
|
||||||
|
*
|
||||||
|
* @return Set of peers that should not be allowed in another tunnel
|
||||||
|
*/
|
||||||
|
public Set<Hash> selectPeersInTooManyTunnels() {
|
||||||
|
HashCounter lc = new HashCounter();
|
||||||
|
int tunnelCount = countTunnelsPerPeer(lc);
|
||||||
|
Set<Hash> rv = new HashSet();
|
||||||
|
if (tunnelCount >= 4 && _context.router().getUptime() > 10*60*1000) {
|
||||||
|
int max = _context.getProperty("router.maxTunnelPercentage", DEFAULT_MAX_PCT_TUNNELS);
|
||||||
|
for (Hash h : lc.hashes()) {
|
||||||
|
if (lc.count(h) > 0 && (lc.count(h) + 1) * 100 / (tunnelCount + 1) > max)
|
||||||
|
rv.add(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return total number of part. tunnels */
|
||||||
|
private int countParticipatingPerPeer(HashCounter pc) {
|
||||||
|
List<HopConfig> participating = _context.tunnelDispatcher().listParticipatingTunnels();
|
||||||
|
for (HopConfig cfg : participating) {
|
||||||
|
Hash from = cfg.getReceiveFrom();
|
||||||
|
if (from != null)
|
||||||
|
pc.increment(from);
|
||||||
|
Hash to = cfg.getSendTo();
|
||||||
|
if (to != null)
|
||||||
|
pc.increment(to);
|
||||||
|
}
|
||||||
|
return participating.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
class HashComparator implements Comparator {
|
||||||
|
public int compare(Object l, Object r) {
|
||||||
|
return ((Hash)l).toBase64().compareTo(((Hash)r).toBase64());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HashCounter {
|
||||||
|
private ConcurrentHashMap<Hash, Integer> _map;
|
||||||
|
public HashCounter() {
|
||||||
|
_map = new ConcurrentHashMap();
|
||||||
|
}
|
||||||
|
public void increment(Hash h) {
|
||||||
|
Integer i = _map.putIfAbsent(h, Integer.valueOf(1));
|
||||||
|
if (i != null)
|
||||||
|
_map.put(h, Integer.valueOf(i.intValue() + 1));
|
||||||
|
}
|
||||||
|
public int count(Hash h) {
|
||||||
|
Integer i = _map.get(h);
|
||||||
|
if (i != null)
|
||||||
|
return i.intValue();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public Set<Hash> hashes() {
|
||||||
|
return _map.keySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String getCapacity(Hash peer) {
|
private String getCapacity(Hash peer) {
|
||||||
RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer);
|
RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer);
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
|
Reference in New Issue
Block a user