forked from I2P_Developers/i2p.i2p
NetDB:
- Just before midnight, flood to new location too so lookups don't fail after keyspace rotation (ticket #510) - Refactor RoutingKeyGenerator and UpdateRoutingKeyModifierJob in support of the above - Display next key on LS debug page
This commit is contained in:
@ -195,6 +195,8 @@ public class NetDbRenderer {
|
|||||||
buf.append("</b></p><p><b>Published (RAP) Leasesets: ").append(netdb.getKnownLeaseSets());
|
buf.append("</b></p><p><b>Published (RAP) Leasesets: ").append(netdb.getKnownLeaseSets());
|
||||||
buf.append("</b></p><p><b>Mod Data: \"").append(DataHelper.getUTF8(_context.routingKeyGenerator().getModData()))
|
buf.append("</b></p><p><b>Mod Data: \"").append(DataHelper.getUTF8(_context.routingKeyGenerator().getModData()))
|
||||||
.append("\" Last Changed: ").append(new Date(_context.routingKeyGenerator().getLastChanged()));
|
.append("\" Last Changed: ").append(new Date(_context.routingKeyGenerator().getLastChanged()));
|
||||||
|
buf.append("</b></p><p><b>Next Mod Data: \"").append(DataHelper.getUTF8(_context.routingKeyGenerator().getNextModData()))
|
||||||
|
.append("\" Change in: ").append(DataHelper.formatDuration(_context.routingKeyGenerator().getTimeTillMidnight()));
|
||||||
int ff = _context.peerManager().getPeersByCapability(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL).size();
|
int ff = _context.peerManager().getPeersByCapability(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL).size();
|
||||||
buf.append("</b></p><p><b>Known Floodfills: ").append(ff);
|
buf.append("</b></p><p><b>Known Floodfills: ").append(ff);
|
||||||
buf.append("</b></p><p><b>Currently Floodfill? ");
|
buf.append("</b></p><p><b>Currently Floodfill? ");
|
||||||
|
@ -107,15 +107,17 @@ public abstract class DatabaseEntry extends DataStructureImpl {
|
|||||||
*/
|
*/
|
||||||
public Hash getRoutingKey() {
|
public Hash getRoutingKey() {
|
||||||
RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance();
|
RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance();
|
||||||
if ((gen.getModData() == null) || (_routingKeyGenMod == null)
|
byte[] mod = gen.getModData();
|
||||||
|| (!DataHelper.eq(gen.getModData(), _routingKeyGenMod))) {
|
if (!DataHelper.eq(mod, _routingKeyGenMod)) {
|
||||||
_currentRoutingKey = gen.getRoutingKey(getHash());
|
_currentRoutingKey = gen.getRoutingKey(getHash());
|
||||||
_routingKeyGenMod = gen.getModData();
|
_routingKeyGenMod = mod;
|
||||||
}
|
}
|
||||||
return _currentRoutingKey;
|
return _currentRoutingKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated unused
|
||||||
|
*/
|
||||||
public void setRoutingKey(Hash key) {
|
public void setRoutingKey(Hash key) {
|
||||||
_currentRoutingKey = key;
|
_currentRoutingKey = key;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import java.util.TimeZone;
|
|||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.crypto.SHA256Generator;
|
import net.i2p.crypto.SHA256Generator;
|
||||||
|
import net.i2p.util.HexDump;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,6 +38,8 @@ import net.i2p.util.Log;
|
|||||||
* Also - the method generateDateBasedModData() should be called after midnight GMT
|
* Also - the method generateDateBasedModData() should be called after midnight GMT
|
||||||
* once per day to generate the correct routing keys!
|
* once per day to generate the correct routing keys!
|
||||||
*
|
*
|
||||||
|
* Warning - API subject to change. Not for use outside the router.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public class RoutingKeyGenerator {
|
public class RoutingKeyGenerator {
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
@ -54,6 +57,8 @@ public class RoutingKeyGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private volatile byte _currentModData[];
|
private volatile byte _currentModData[];
|
||||||
|
private volatile byte _nextModData[];
|
||||||
|
private volatile long _nextMidnight;
|
||||||
private volatile long _lastChanged;
|
private volatile long _lastChanged;
|
||||||
|
|
||||||
private final static Calendar _cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
|
private final static Calendar _cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||||
@ -61,14 +66,74 @@ public class RoutingKeyGenerator {
|
|||||||
private static final int LENGTH = FORMAT.length();
|
private static final int LENGTH = FORMAT.length();
|
||||||
private final static SimpleDateFormat _fmt = new SimpleDateFormat(FORMAT);
|
private final static SimpleDateFormat _fmt = new SimpleDateFormat(FORMAT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current (today's) mod data.
|
||||||
|
* Warning - not a copy, do not corrupt.
|
||||||
|
*
|
||||||
|
* @return non-null, 8 bytes
|
||||||
|
*/
|
||||||
public byte[] getModData() {
|
public byte[] getModData() {
|
||||||
return _currentModData;
|
return _currentModData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tomorrow's mod data.
|
||||||
|
* Warning - not a copy, do not corrupt.
|
||||||
|
* For debugging use only.
|
||||||
|
*
|
||||||
|
* @return non-null, 8 bytes
|
||||||
|
* @since 0.9.10
|
||||||
|
*/
|
||||||
|
public byte[] getNextModData() {
|
||||||
|
return _nextModData;
|
||||||
|
}
|
||||||
|
|
||||||
public long getLastChanged() {
|
public long getLastChanged() {
|
||||||
return _lastChanged;
|
return _lastChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long until midnight (ms)
|
||||||
|
*
|
||||||
|
* @return could be slightly negative
|
||||||
|
* @since 0.9.10 moved from UpdateRoutingKeyModifierJob
|
||||||
|
*/
|
||||||
|
public long getTimeTillMidnight() {
|
||||||
|
return _nextMidnight - _context.clock().now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set _cal to midnight for the time given.
|
||||||
|
* Caller must synch.
|
||||||
|
* @since 0.9.10
|
||||||
|
*/
|
||||||
|
private void setCalToPreviousMidnight(long now) {
|
||||||
|
_cal.setTime(new Date(now));
|
||||||
|
_cal.set(Calendar.YEAR, _cal.get(Calendar.YEAR)); // gcj <= 4.0 workaround
|
||||||
|
_cal.set(Calendar.DAY_OF_YEAR, _cal.get(Calendar.DAY_OF_YEAR)); // gcj <= 4.0 workaround
|
||||||
|
_cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
_cal.set(Calendar.MINUTE, 0);
|
||||||
|
_cal.set(Calendar.SECOND, 0);
|
||||||
|
_cal.set(Calendar.MILLISECOND, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate mod data from _cal.
|
||||||
|
* Caller must synch.
|
||||||
|
* @since 0.9.10
|
||||||
|
*/
|
||||||
|
private byte[] generateModDataFromCal() {
|
||||||
|
Date today = _cal.getTime();
|
||||||
|
|
||||||
|
String modVal = _fmt.format(today);
|
||||||
|
if (modVal.length() != LENGTH)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
byte[] mod = new byte[LENGTH];
|
||||||
|
for (int i = 0; i < LENGTH; i++)
|
||||||
|
mod[i] = (byte)(modVal.charAt(i) & 0xFF);
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current modifier data with some bytes derived from the current
|
* Update the current modifier data with some bytes derived from the current
|
||||||
* date (yyyyMMdd in GMT)
|
* date (yyyyMMdd in GMT)
|
||||||
@ -77,34 +142,26 @@ public class RoutingKeyGenerator {
|
|||||||
*/
|
*/
|
||||||
public synchronized boolean generateDateBasedModData() {
|
public synchronized boolean generateDateBasedModData() {
|
||||||
long now = _context.clock().now();
|
long now = _context.clock().now();
|
||||||
_cal.setTime(new Date(now));
|
setCalToPreviousMidnight(now);
|
||||||
_cal.set(Calendar.YEAR, _cal.get(Calendar.YEAR)); // gcj <= 4.0 workaround
|
byte[] mod = generateModDataFromCal();
|
||||||
_cal.set(Calendar.DAY_OF_YEAR, _cal.get(Calendar.DAY_OF_YEAR)); // gcj <= 4.0 workaround
|
|
||||||
_cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
||||||
_cal.set(Calendar.MINUTE, 0);
|
|
||||||
_cal.set(Calendar.SECOND, 0);
|
|
||||||
_cal.set(Calendar.MILLISECOND, 0);
|
|
||||||
Date today = _cal.getTime();
|
|
||||||
|
|
||||||
String modVal = _fmt.format(today);
|
|
||||||
if (modVal.length() != LENGTH)
|
|
||||||
throw new IllegalStateException();
|
|
||||||
byte[] mod = new byte[LENGTH];
|
|
||||||
for (int i = 0; i < LENGTH; i++)
|
|
||||||
mod[i] = (byte)(modVal.charAt(i) & 0xFF);
|
|
||||||
boolean changed = !DataHelper.eq(_currentModData, mod);
|
boolean changed = !DataHelper.eq(_currentModData, mod);
|
||||||
if (changed) {
|
if (changed) {
|
||||||
|
// add a day and store next midnight and mod data for convenience
|
||||||
|
_cal.add(Calendar.DATE, 1);
|
||||||
|
_nextMidnight = _cal.getTime().getTime();
|
||||||
|
byte[] next = generateModDataFromCal();
|
||||||
_currentModData = mod;
|
_currentModData = mod;
|
||||||
|
_nextModData = next;
|
||||||
_lastChanged = now;
|
_lastChanged = now;
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Routing modifier generated: " + modVal);
|
_log.info("Routing modifier generated: " + HexDump.dump(mod));
|
||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a modified (yet consistent) hash from the origKey by generating the
|
* Generate a modified (yet consistent) hash from the origKey by generating the
|
||||||
* SHA256 of the targetKey with the current modData appended to it, *then*
|
* SHA256 of the targetKey with the current modData appended to it
|
||||||
*
|
*
|
||||||
* This makes Sybil's job a lot harder, as she needs to essentially take over the
|
* This makes Sybil's job a lot harder, as she needs to essentially take over the
|
||||||
* whole keyspace.
|
* whole keyspace.
|
||||||
@ -112,10 +169,29 @@ public class RoutingKeyGenerator {
|
|||||||
* @throws IllegalArgumentException if origKey is null
|
* @throws IllegalArgumentException if origKey is null
|
||||||
*/
|
*/
|
||||||
public Hash getRoutingKey(Hash origKey) {
|
public Hash getRoutingKey(Hash origKey) {
|
||||||
|
return getKey(origKey, _currentModData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the routing key using tomorrow's modData, not today's
|
||||||
|
*
|
||||||
|
* @since 0.9.10
|
||||||
|
*/
|
||||||
|
public Hash getNextRoutingKey(Hash origKey) {
|
||||||
|
return getKey(origKey, _nextModData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a modified (yet consistent) hash from the origKey by generating the
|
||||||
|
* SHA256 of the targetKey with the specified modData appended to it
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if origKey is null
|
||||||
|
*/
|
||||||
|
private static Hash getKey(Hash origKey, byte[] modData) {
|
||||||
if (origKey == null) throw new IllegalArgumentException("Original key is null");
|
if (origKey == null) throw new IllegalArgumentException("Original key is null");
|
||||||
byte modVal[] = new byte[Hash.HASH_LENGTH + LENGTH];
|
byte modVal[] = new byte[Hash.HASH_LENGTH + LENGTH];
|
||||||
System.arraycopy(origKey.getData(), 0, modVal, 0, Hash.HASH_LENGTH);
|
System.arraycopy(origKey.getData(), 0, modVal, 0, Hash.HASH_LENGTH);
|
||||||
System.arraycopy(_currentModData, 0, modVal, Hash.HASH_LENGTH, LENGTH);
|
System.arraycopy(modData, 0, modVal, Hash.HASH_LENGTH, LENGTH);
|
||||||
return SHA256Generator.getInstance().calculateHash(modVal);
|
return SHA256Generator.getInstance().calculateHash(modVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
2013-12-14 zzz
|
||||||
|
* NetDB:
|
||||||
|
- Just before midnight, flood to new location too so lookups
|
||||||
|
don't fail after keyspace rotation (ticket #510)
|
||||||
|
- Refactor RoutingKeyGenerator and UpdateRoutingKeyModifierJob
|
||||||
|
in support of the above
|
||||||
|
|
||||||
2013-12-13 zzz
|
2013-12-13 zzz
|
||||||
* i2ptunnel: Show destination for persistent client key only if available;
|
* i2ptunnel: Show destination for persistent client key only if available;
|
||||||
show b32 for the key as well
|
show b32 for the key as well
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 2;
|
public final static long BUILD = 3;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -43,6 +43,9 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
|
|||||||
|
|
||||||
private static final int FLOOD_PRIORITY = OutNetMessage.PRIORITY_NETDB_FLOOD;
|
private static final int FLOOD_PRIORITY = OutNetMessage.PRIORITY_NETDB_FLOOD;
|
||||||
private static final int FLOOD_TIMEOUT = 30*1000;
|
private static final int FLOOD_TIMEOUT = 30*1000;
|
||||||
|
private static final long NEXT_RKEY_RI_ADVANCE_TIME = 45*60*1000;
|
||||||
|
private static final long NEXT_RKEY_LS_ADVANCE_TIME = 10*60*1000;
|
||||||
|
private static final int NEXT_FLOOD_QTY = 2;
|
||||||
|
|
||||||
public FloodfillNetworkDatabaseFacade(RouterContext context) {
|
public FloodfillNetworkDatabaseFacade(RouterContext context) {
|
||||||
super(context);
|
super(context);
|
||||||
@ -197,6 +200,23 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
|
|||||||
Hash rkey = _context.routingKeyGenerator().getRoutingKey(key);
|
Hash rkey = _context.routingKeyGenerator().getRoutingKey(key);
|
||||||
FloodfillPeerSelector sel = (FloodfillPeerSelector)getPeerSelector();
|
FloodfillPeerSelector sel = (FloodfillPeerSelector)getPeerSelector();
|
||||||
List<Hash> peers = sel.selectFloodfillParticipants(rkey, MAX_TO_FLOOD, getKBuckets());
|
List<Hash> peers = sel.selectFloodfillParticipants(rkey, MAX_TO_FLOOD, getKBuckets());
|
||||||
|
long until = _context.routingKeyGenerator().getTimeTillMidnight();
|
||||||
|
if (until < NEXT_RKEY_LS_ADVANCE_TIME ||
|
||||||
|
(ds.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO && until < NEXT_RKEY_RI_ADVANCE_TIME)) {
|
||||||
|
// to avoid lookup failures after midnight, also flood to some closest to the
|
||||||
|
// next routing key for a period of time before midnight.
|
||||||
|
Hash nkey = _context.routingKeyGenerator().getNextRoutingKey(key);
|
||||||
|
List<Hash> nextPeers = sel.selectFloodfillParticipants(nkey, NEXT_FLOOD_QTY, getKBuckets());
|
||||||
|
int i = 0;
|
||||||
|
for (Hash h : nextPeers) {
|
||||||
|
if (!peers.contains(h)) {
|
||||||
|
peers.add(h);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i > 0 && _log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Flooding the entry for " + key + " to " + i + " more, just before midnight");
|
||||||
|
}
|
||||||
int flooded = 0;
|
int flooded = 0;
|
||||||
for (int i = 0; i < peers.size(); i++) {
|
for (int i = 0; i < peers.size(); i++) {
|
||||||
Hash peer = peers.get(i);
|
Hash peer = peers.get(i);
|
||||||
|
@ -8,11 +8,7 @@ package net.i2p.router.tasks;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Calendar;
|
import net.i2p.data.RoutingKeyGenerator;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
import net.i2p.router.JobImpl;
|
import net.i2p.router.JobImpl;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
@ -26,7 +22,6 @@ import net.i2p.util.Log;
|
|||||||
*/
|
*/
|
||||||
public class UpdateRoutingKeyModifierJob extends JobImpl {
|
public class UpdateRoutingKeyModifierJob extends JobImpl {
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
private final Calendar _cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
|
||||||
// Run every 15 minutes in case of time zone change, clock skew, etc.
|
// Run every 15 minutes in case of time zone change, clock skew, etc.
|
||||||
private static final long MAX_DELAY_FAILSAFE = 15*60*1000;
|
private static final long MAX_DELAY_FAILSAFE = 15*60*1000;
|
||||||
|
|
||||||
@ -38,29 +33,11 @@ public class UpdateRoutingKeyModifierJob extends JobImpl {
|
|||||||
public String getName() { return "Update Routing Key Modifier"; }
|
public String getName() { return "Update Routing Key Modifier"; }
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
|
RoutingKeyGenerator gen = getContext().routingKeyGenerator();
|
||||||
// make sure we requeue quickly if just before midnight
|
// make sure we requeue quickly if just before midnight
|
||||||
long delay = Math.min(MAX_DELAY_FAILSAFE, getTimeTillMidnight());
|
long delay = Math.max(5, Math.min(MAX_DELAY_FAILSAFE, gen.getTimeTillMidnight()));
|
||||||
// TODO tell netdb if mod data changed?
|
// TODO tell netdb if mod data changed?
|
||||||
getContext().routingKeyGenerator().generateDateBasedModData();
|
gen.generateDateBasedModData();
|
||||||
requeue(delay);
|
requeue(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getTimeTillMidnight() {
|
|
||||||
long now = getContext().clock().now();
|
|
||||||
_cal.setTime(new Date(now));
|
|
||||||
_cal.set(Calendar.YEAR, _cal.get(Calendar.YEAR)); // gcj <= 4.0 workaround
|
|
||||||
_cal.set(Calendar.DAY_OF_YEAR, _cal.get(Calendar.DAY_OF_YEAR)); // gcj <= 4.0 workaround
|
|
||||||
_cal.add(Calendar.DATE, 1);
|
|
||||||
_cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
||||||
_cal.set(Calendar.MINUTE, 0);
|
|
||||||
_cal.set(Calendar.SECOND, 0);
|
|
||||||
_cal.set(Calendar.MILLISECOND, 0);
|
|
||||||
long then = _cal.getTime().getTime();
|
|
||||||
long howLong = then - now;
|
|
||||||
if (howLong < 0) // hi kaffe
|
|
||||||
howLong = 24*60*60*1000l + howLong;
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Time till midnight: " + howLong + "ms");
|
|
||||||
return howLong;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user