WIP: Add IP restriction based exclusions to ClientPeerSelection
When I saw this post: http://zzz.i2p/topics/3215-vulnerability-iprestriction-is-ignored I did some grepping and saw that TunnelPoolSettings.getIPRestrictions() was never actually called anywhere. It looks like the setting might have been added, but the code to exclude based on similar IP ranges was not? So I tried to figure out where the IPRestriction rules should be enforced to be effective and then tried to figure out how to do it. This is untested so far, but if you get a moment, I'd appreciate it if you would take a glance and tell me if I'm on the right track @zzz .
Merge request reports
Activity
requested review from @zzz
assigned to @idk
Appears to have been removed from ClientPeerSelector May 2011 when we went to slices.
ExploratoryPeerSelector has never supported ip restriction, it's marked todo
the efficient way to do this is in the select call, not the exclude call, and most of the select methods already have variants with mask args, but see below.
The situation for the various ProfileOrganizer select calls, abbreviated:
as called by CPS:
SFP(w/ slice arg) no method w/ mask arg SANFP: method w/ mask arg is private and marked that mask arg is ignored
as called by EPS:
SFP(w/o slice arg) has public method w/ mask arg, appears to work SHCP: has public method w/ mask arg, appears to work SNFP: has public method w/ mask arg, marked that mask arg is ignored
Actually I think I figured my approach might be inefficient compared to another, looping over another bunch of potential peers and comparing IP's byte-by-byte seemed like a slow way to do it. I guess ideally we would only want to do that operation on as short a list of addresses as possible, which would be after it's been filtered. So I get that part. Is what you're saying that once the versions of the filter methods that accept the mask actually use the mask to filter similar/restricted IP's what is most efficient is to use them? That's the conclusion I found myself drawing when I looked at it again this morning.
here's my quick stab at it, unreviewed, untested
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java index e84709d93..35a82a196 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java @@ -483,7 +483,7 @@ public class ProfileOrganizer { * 7: return only from group 3 *</pre> */ - public void selectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, SessionKey randomKey, Slice subTierMode) { + public void selectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, SessionKey randomKey, Slice subTierMode, int mask) { getReadLock(); try { if (subTierMode != Slice.SLICE_ALL) { @@ -492,9 +492,9 @@ public class ProfileOrganizer { subTierMode = Slice.SLICE_ALL; } if (subTierMode != Slice.SLICE_ALL) - locked_selectPeers(_fastPeers, howMany, exclude, matches, randomKey, subTierMode); + locked_selectPeers(_fastPeers, howMany, exclude, matches, randomKey, subTierMode, mask); else - locked_selectPeers(_fastPeers, howMany, exclude, matches, 2); + locked_selectPeers(_fastPeers, howMany, exclude, matches, mask); } finally { releaseReadLock(); } if (matches.size() < howMany) { if (_log.shouldLog(Log.INFO)) @@ -627,9 +627,28 @@ public class ProfileOrganizer { * be used when there is a good number of connected peers. * * @param exclude non-null, WARNING - side effect, all not-connected peers are added - * No mask parameter, to be fixed */ public void selectActiveNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches) { + selectActiveNotFailingPeers(howMany, exclude, matches, 0); + } + + /** + * Return a set of Hashes for peers that are both not failing and we're actively + * talking with. + * + * We use commSystem().isEstablished(), not profile.getIsActive(), as the + * NTCP idle time is now shorter than the 5 minute getIsActive() threshold, + * and we're using this to try and limit connections. + * + * Caution, this does NOT cascade further to non-connected peers, so it should only + * be used when there is a good number of connected peers. + * + * @param exclude non-null, WARNING - side effect, all not-connected peers are added + * @param mask 0-4 Number of bytes to match to determine if peers in the same IP range should + * not be in the same tunnel. 0 = disable check; 1 = /8; 2 = /16; 3 = /24; 4 = exact IP match + * @since 0.9.53 + */ + public void selectActiveNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask) { if (matches.size() < howMany) { Set<Hash> connected = _context.commSystem().getEstablished(); getReadLock(); @@ -638,7 +657,7 @@ public class ProfileOrganizer { if (!connected.contains(peer)) exclude.add(peer); } - locked_selectPeers(_notFailingPeers, howMany, exclude, matches, 0); + locked_selectPeers(_notFailingPeers, howMany, exclude, matches, mask); } finally { releaseReadLock(); } } } @@ -690,7 +709,6 @@ public class ProfileOrganizer { /** * @param mask ignored, should call locked_selectPeers, to be fixed - * */ private void selectAllNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask) { if (matches.size() < howMany) { @@ -1352,13 +1370,14 @@ public class ProfileOrganizer { *</pre> */ private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, - Set<Hash> matches, SessionKey randomKey, Slice subTierMode) { + Set<Hash> matches, SessionKey randomKey, Slice subTierMode, int mask) { List<Hash> all = new ArrayList<Hash>(peers.keySet()); byte[] rk = randomKey.getData(); // we use the first half of the random key here, // the second half is used in TunnelPeerSelector. long k0 = DataHelper.fromLong8(rk, 0); long k1 = DataHelper.fromLong8(rk, 8); + MaskedIPSet ipSet = new MaskedIPSet(16); // use RandomIterator to avoid shuffling the whole thing for (Iterator<Hash> iter = new RandomIterator<Hash>(all); (matches.size() < howMany) && iter.hasNext(); ) { @@ -1373,6 +1392,11 @@ public class ProfileOrganizer { if ((subTier & subTierMode.mask) != subTierMode.val) continue; boolean ok = isSelectable(peer); + if (ok) { + ok = mask <= 0 || notRestricted(peer, ipSet, mask); + if ((!ok) && _log.shouldLog(Log.WARN)) + _log.warn("IP restriction prevents " + peer + " from joining " + matches); + } if (ok) matches.add(peer); else diff --git a/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java index cb8ffdef8..1b0889e5e 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java @@ -57,6 +57,7 @@ class ClientPeerSelector extends TunnelPeerSelector { !ctx.commSystem().haveInboundCapacity(95); boolean hiddenInbound = hidden && isInbound; boolean hiddenOutbound = hidden && !isInbound; + int ipRestriction = settings.getIPRestriction(); if (shouldSelectExplicit(settings)) return selectExplicit(settings, length); @@ -73,11 +74,11 @@ class ClientPeerSelector extends TunnelPeerSelector { if (hiddenInbound) { // SANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(exclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches, ipRestriction); } if (matches.isEmpty()) { // ANFP does not fall back to non-connected - ctx.profileOrganizer().selectFastPeers(length, exclude, matches, 0); + ctx.profileOrganizer().selectFastPeers(length, exclude, matches, ipRestriction); } matches.remove(ctx.routerHash()); rv = new ArrayList<Hash>(matches); @@ -113,12 +114,12 @@ class ClientPeerSelector extends TunnelPeerSelector { log.info("CPS SANFP closest IB exclude " + lastHopExclude.size()); // SANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(lastHopExclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches, ipRestriction); if (matches.isEmpty()) { if (log.shouldInfo()) log.info("CPS SFP closest IB exclude " + lastHopExclude.size()); // ANFP does not fall back to non-connected - ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0); + ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0, ipRestriction); } } else if (hiddenOutbound) { // OBEP @@ -177,19 +178,19 @@ class ClientPeerSelector extends TunnelPeerSelector { log.info("CPS SANFP OBEP exclude " + lastHopExclude.size()); // SANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(lastHopExclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches, ipRestriction); if (matches.isEmpty()) { // ANFP does not fall back to non-connected if (log.shouldInfo()) log.info("CPS SFP OBEP exclude " + lastHopExclude.size()); - ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0); + ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0, ipRestriction); } } else { - ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0); + ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0, ipRestriction); } } else { // TODO exclude IPv6-only at OBEP? Caught in checkTunnel() below - ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0); + ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0, ipRestriction); } matches.remove(ctx.routerHash()); @@ -199,7 +200,7 @@ class ClientPeerSelector extends TunnelPeerSelector { if (length > 2) { // middle hop(s) // group 2 or 3 - ctx.profileOrganizer().selectFastPeers(length - 2, exclude, matches, randomKey, SLICE_2_3); + ctx.profileOrganizer().selectFastPeers(length - 2, exclude, matches, randomKey, SLICE_2_3, ipRestriction); matches.remove(ctx.routerHash()); if (matches.size() > 1) { // order the middle peers for tunnels >= 4 hops @@ -225,7 +226,7 @@ class ClientPeerSelector extends TunnelPeerSelector { } } // TODO exclude IPv6-only at IBGW? Caught in checkTunnel() below - ctx.profileOrganizer().selectFastPeers(1, exclude, matches, randomKey, length == 2 ? SLICE_2_3 : SLICE_1); + ctx.profileOrganizer().selectFastPeers(1, exclude, matches, randomKey, length == 2 ? SLICE_2_3 : SLICE_1, ipRestriction); matches.remove(ctx.routerHash()); rv.addAll(matches); } diff --git a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java index 866a1708b..2f8b657b8 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java @@ -70,6 +70,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { boolean hiddenInbound = hidden && isInbound; boolean hiddenOutbound = hidden && !isInbound; boolean lowOutbound = nonzero && !isInbound && !ctx.commSystem().haveHighOutboundCapacity(); + int ipRestriction = settings.getIPRestriction(); // closest-hop restrictions @@ -99,21 +100,21 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { log.info("EPS SANFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size()); // SANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(closestExclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, closest); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, closest, ipRestriction); if (closest.isEmpty()) { // ANFP does not fall back to non-connected if (log.shouldLog(Log.INFO)) log.info("EPS SFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size()); - ctx.profileOrganizer().selectFastPeers(1, closestExclude, closest); + ctx.profileOrganizer().selectFastPeers(1, closestExclude, closest, ipRestriction); } } else if (exploreHighCap) { if (log.shouldLog(Log.INFO)) log.info("EPS SHCP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size()); - ctx.profileOrganizer().selectHighCapacityPeers(1, closestExclude, closest); + ctx.profileOrganizer().selectHighCapacityPeers(1, closestExclude, closest, ipRestriction); } else { if (log.shouldLog(Log.INFO)) log.info("EPS SNFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size()); - ctx.profileOrganizer().selectNotFailingPeers(1, closestExclude, closest, false); + ctx.profileOrganizer().selectNotFailingPeers(1, closestExclude, closest, false, ipRestriction); } if (!closest.isEmpty()) { closestHop = closest.iterator().next(); @@ -155,12 +156,12 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { log.info("EPS SANFP furthest OB exclude " + exclude.size()); // ANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(exclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, furthest); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, furthest, ipRestriction); if (furthest.isEmpty()) { // ANFP does not fall back to non-connected if (log.shouldLog(Log.INFO)) log.info("EPS SFP furthest OB exclude " + exclude.size()); - ctx.profileOrganizer().selectFastPeers(1, exclude, furthest); + ctx.profileOrganizer().selectFastPeers(1, exclude, furthest, ipRestriction); } if (!furthest.isEmpty()) { furthestHop = furthest.iterator().next(); @@ -179,13 +180,10 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { HashSet<Hash> matches = new HashSet<Hash>(length); if (length > 0) { - // - // We don't honor IP Restriction here, to be fixed - // if (exploreHighCap) { if (log.shouldLog(Log.INFO)) log.info("EPS SHCP " + length + (isInbound ? " IB" : " OB") + " exclude " + exclude.size()); - ctx.profileOrganizer().selectHighCapacityPeers(length, exclude, matches); + ctx.profileOrganizer().selectHighCapacityPeers(length, exclude, matches, ipRestriction); } else { // As of 0.9.23, we include a max of 2 not failing peers, // to improve build success on 3-hop tunnels. @@ -194,7 +192,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { ctx.profileOrganizer().selectHighCapacityPeers(length - 2, exclude, matches); if (log.shouldLog(Log.INFO)) log.info("EPS SNFP " + length + (isInbound ? " IB" : " OB") + " exclude " + exclude.size()); - ctx.profileOrganizer().selectNotFailingPeers(length, exclude, matches, false); + ctx.profileOrganizer().selectNotFailingPeers(length, exclude, matches, false, ipRestriction); } matches.remove(ctx.routerHash()); }
Here's the next problem. OP (here) misunderstands the intent of the IP restriction feature.
It's not to prevent tunnel peers "too close" to you. It's to prevent tunnel peers "too close" to each other, i.e. possibly colluding.
I don't see any code in ProfileOrganizer to do the too-close check to your own router. Maybe it's elsewhere or maybe I missed it.
And this reveals why the feature was removed when we went to fast tier slicing. With slicing, we make a selectXXX() call for each slice. Nowhere do we create a single MaskedIPSet for the whole tunnel to check across all peers. So it's a little harder (and not addressed in my diff above)
If there's some reason to exclude peers too close to you, that's a separate discussion.
If there's some reason to exclude peers too close to you, that's a separate discussion.
Not that I know of, I was just wrong.
And this reveals why the feature was removed when we went to fast tier slicing. With slicing, we make a selectXXX() call for each slice. Nowhere do we create a single MaskedIPSet for the whole tunnel to check across all peers. So it's a little harder (and not addressed in my diff above)
So without a single MaskedIPSet for the tunnel, what is IP restriction actually restricting here in your patch? I see a MaskedIPSet per call to locked_selectPeers, which gets called by SFP and SANFP, but if the MaskedIPSet they're using is just for the slice(s) they're being used on, then what actually gets restricted is the set of matches locked_selectPeers picks from the set of peers it's passed, right? Peers and matches go in, matches come out, up to howMany. So could each call to a selectXXX on the same set of matches could introduce a peer to the matches that violates the IP restriction?
right, as I said, was not addressed in the diff above.
Here's a new version, where a single MaskedIPSet is created in CPS and passed to all the select w/ slice calls. Still unreviewed, untested.
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java index e84709d93..fc6ae7a4c 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java @@ -482,8 +482,12 @@ public class ProfileOrganizer { * 6: return only from group 2 * 7: return only from group 3 *</pre> + * @param mask 0-4 + * @param ipSet in/out param, use for multiple calls, may be null only if mask is 0 + * @since 0.9.53 added mask and ipSet params */ - public void selectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, SessionKey randomKey, Slice subTierMode) { + public void selectFastPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, SessionKey randomKey, + Slice subTierMode, int mask, MaskedIPSet ipSet) { getReadLock(); try { if (subTierMode != Slice.SLICE_ALL) { @@ -492,9 +496,9 @@ public class ProfileOrganizer { subTierMode = Slice.SLICE_ALL; } if (subTierMode != Slice.SLICE_ALL) - locked_selectPeers(_fastPeers, howMany, exclude, matches, randomKey, subTierMode); + locked_selectPeers(_fastPeers, howMany, exclude, matches, randomKey, subTierMode, mask, ipSet); else - locked_selectPeers(_fastPeers, howMany, exclude, matches, 2); + locked_selectPeers(_fastPeers, howMany, exclude, matches, mask); } finally { releaseReadLock(); } if (matches.size() < howMany) { if (_log.shouldLog(Log.INFO)) @@ -627,9 +631,28 @@ public class ProfileOrganizer { * be used when there is a good number of connected peers. * * @param exclude non-null, WARNING - side effect, all not-connected peers are added - * No mask parameter, to be fixed */ public void selectActiveNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches) { + selectActiveNotFailingPeers(howMany, exclude, matches, 0); + } + + /** + * Return a set of Hashes for peers that are both not failing and we're actively + * talking with. + * + * We use commSystem().isEstablished(), not profile.getIsActive(), as the + * NTCP idle time is now shorter than the 5 minute getIsActive() threshold, + * and we're using this to try and limit connections. + * + * Caution, this does NOT cascade further to non-connected peers, so it should only + * be used when there is a good number of connected peers. + * + * @param exclude non-null, WARNING - side effect, all not-connected peers are added + * @param mask 0-4 Number of bytes to match to determine if peers in the same IP range should + * not be in the same tunnel. 0 = disable check; 1 = /8; 2 = /16; 3 = /24; 4 = exact IP match + * @since 0.9.53 + */ + public void selectActiveNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, int mask) { if (matches.size() < howMany) { Set<Hash> connected = _context.commSystem().getEstablished(); getReadLock(); @@ -638,7 +661,7 @@ public class ProfileOrganizer { if (!connected.contains(peer)) exclude.add(peer); } - locked_selectPeers(_notFailingPeers, howMany, exclude, matches, 0); + locked_selectPeers(_notFailingPeers, howMany, exclude, matches, mask); } finally { releaseReadLock(); } } } @@ -690,7 +713,6 @@ public class ProfileOrganizer { /** * @param mask ignored, should call locked_selectPeers, to be fixed - * */ private void selectAllNotFailingPeers(int howMany, Set<Hash> exclude, Set<Hash> matches, boolean onlyNotFailing, int mask) { if (matches.size() < howMany) { @@ -1352,7 +1374,8 @@ public class ProfileOrganizer { *</pre> */ private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, - Set<Hash> matches, SessionKey randomKey, Slice subTierMode) { + Set<Hash> matches, SessionKey randomKey, Slice subTierMode, + int mask, MaskedIPSet ipSet) { List<Hash> all = new ArrayList<Hash>(peers.keySet()); byte[] rk = randomKey.getData(); // we use the first half of the random key here, @@ -1373,6 +1396,11 @@ public class ProfileOrganizer { if ((subTier & subTierMode.mask) != subTierMode.val) continue; boolean ok = isSelectable(peer); + if (ok) { + ok = mask <= 0 || notRestricted(peer, ipSet, mask); + if ((!ok) && _log.shouldLog(Log.WARN)) + _log.warn("IP restriction prevents " + peer + " from joining " + matches); + } if (ok) matches.add(peer); else diff --git a/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java index cb8ffdef8..2bc6450d0 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java @@ -13,6 +13,7 @@ import net.i2p.router.TunnelInfo; import net.i2p.router.TunnelManagerFacade; import net.i2p.router.TunnelPoolSettings; import static net.i2p.router.peermanager.ProfileOrganizer.Slice.*; +import net.i2p.router.util.MaskedIPSet; /** * Pick peers randomly out of the fast pool, and put them into tunnels @@ -57,6 +58,8 @@ class ClientPeerSelector extends TunnelPeerSelector { !ctx.commSystem().haveInboundCapacity(95); boolean hiddenInbound = hidden && isInbound; boolean hiddenOutbound = hidden && !isInbound; + int ipRestriction = ctx.getBooleanProperty("i2np.allowLocal") ? 0 : settings.getIPRestriction(); + MaskedIPSet ipSet = ipRestriction > 0 ? new MaskedIPSet(16) : null; if (shouldSelectExplicit(settings)) return selectExplicit(settings, length); @@ -73,11 +76,11 @@ class ClientPeerSelector extends TunnelPeerSelector { if (hiddenInbound) { // SANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(exclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches, ipRestriction); } if (matches.isEmpty()) { // ANFP does not fall back to non-connected - ctx.profileOrganizer().selectFastPeers(length, exclude, matches, 0); + ctx.profileOrganizer().selectFastPeers(length, exclude, matches, ipRestriction); } matches.remove(ctx.routerHash()); rv = new ArrayList<Hash>(matches); @@ -113,12 +116,12 @@ class ClientPeerSelector extends TunnelPeerSelector { log.info("CPS SANFP closest IB exclude " + lastHopExclude.size()); // SANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(lastHopExclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches, ipRestriction); if (matches.isEmpty()) { if (log.shouldInfo()) log.info("CPS SFP closest IB exclude " + lastHopExclude.size()); // ANFP does not fall back to non-connected - ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0); + ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0, ipRestriction, ipSet); } } else if (hiddenOutbound) { // OBEP @@ -177,19 +180,19 @@ class ClientPeerSelector extends TunnelPeerSelector { log.info("CPS SANFP OBEP exclude " + lastHopExclude.size()); // SANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(lastHopExclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches, ipRestriction); if (matches.isEmpty()) { // ANFP does not fall back to non-connected if (log.shouldInfo()) log.info("CPS SFP OBEP exclude " + lastHopExclude.size()); - ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0); + ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0, ipRestriction, ipSet); } } else { - ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0); + ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0, ipRestriction, ipSet); } } else { // TODO exclude IPv6-only at OBEP? Caught in checkTunnel() below - ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0); + ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0, ipRestriction, ipSet); } matches.remove(ctx.routerHash()); @@ -199,7 +202,7 @@ class ClientPeerSelector extends TunnelPeerSelector { if (length > 2) { // middle hop(s) // group 2 or 3 - ctx.profileOrganizer().selectFastPeers(length - 2, exclude, matches, randomKey, SLICE_2_3); + ctx.profileOrganizer().selectFastPeers(length - 2, exclude, matches, randomKey, SLICE_2_3, ipRestriction, ipSet); matches.remove(ctx.routerHash()); if (matches.size() > 1) { // order the middle peers for tunnels >= 4 hops @@ -225,7 +228,7 @@ class ClientPeerSelector extends TunnelPeerSelector { } } // TODO exclude IPv6-only at IBGW? Caught in checkTunnel() below - ctx.profileOrganizer().selectFastPeers(1, exclude, matches, randomKey, length == 2 ? SLICE_2_3 : SLICE_1); + ctx.profileOrganizer().selectFastPeers(1, exclude, matches, randomKey, length == 2 ? SLICE_2_3 : SLICE_1, ipRestriction, ipSet); matches.remove(ctx.routerHash()); rv.addAll(matches); } diff --git a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java index 866a1708b..8236c3b80 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java @@ -70,6 +70,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { boolean hiddenInbound = hidden && isInbound; boolean hiddenOutbound = hidden && !isInbound; boolean lowOutbound = nonzero && !isInbound && !ctx.commSystem().haveHighOutboundCapacity(); + int ipRestriction = ctx.getBooleanProperty("i2np.allowLocal") ? 0 : settings.getIPRestriction(); // closest-hop restrictions @@ -99,21 +100,21 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { log.info("EPS SANFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size()); // SANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(closestExclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, closest); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, closest, ipRestriction); if (closest.isEmpty()) { // ANFP does not fall back to non-connected if (log.shouldLog(Log.INFO)) log.info("EPS SFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size()); - ctx.profileOrganizer().selectFastPeers(1, closestExclude, closest); + ctx.profileOrganizer().selectFastPeers(1, closestExclude, closest, ipRestriction); } } else if (exploreHighCap) { if (log.shouldLog(Log.INFO)) log.info("EPS SHCP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size()); - ctx.profileOrganizer().selectHighCapacityPeers(1, closestExclude, closest); + ctx.profileOrganizer().selectHighCapacityPeers(1, closestExclude, closest, ipRestriction); } else { if (log.shouldLog(Log.INFO)) log.info("EPS SNFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size()); - ctx.profileOrganizer().selectNotFailingPeers(1, closestExclude, closest, false); + ctx.profileOrganizer().selectNotFailingPeers(1, closestExclude, closest, false, ipRestriction); } if (!closest.isEmpty()) { closestHop = closest.iterator().next(); @@ -155,12 +156,12 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { log.info("EPS SANFP furthest OB exclude " + exclude.size()); // ANFP adds all not-connected to exclude, so make a copy Set<Hash> SANFPExclude = new HashSet<Hash>(exclude); - ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, furthest); + ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, furthest, ipRestriction); if (furthest.isEmpty()) { // ANFP does not fall back to non-connected if (log.shouldLog(Log.INFO)) log.info("EPS SFP furthest OB exclude " + exclude.size()); - ctx.profileOrganizer().selectFastPeers(1, exclude, furthest); + ctx.profileOrganizer().selectFastPeers(1, exclude, furthest, ipRestriction); } if (!furthest.isEmpty()) { furthestHop = furthest.iterator().next(); @@ -179,13 +180,10 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { HashSet<Hash> matches = new HashSet<Hash>(length); if (length > 0) { - // - // We don't honor IP Restriction here, to be fixed - // if (exploreHighCap) { if (log.shouldLog(Log.INFO)) log.info("EPS SHCP " + length + (isInbound ? " IB" : " OB") + " exclude " + exclude.size()); - ctx.profileOrganizer().selectHighCapacityPeers(length, exclude, matches); + ctx.profileOrganizer().selectHighCapacityPeers(length, exclude, matches, ipRestriction); } else { // As of 0.9.23, we include a max of 2 not failing peers, // to improve build success on 3-hop tunnels. @@ -194,7 +192,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { ctx.profileOrganizer().selectHighCapacityPeers(length - 2, exclude, matches); if (log.shouldLog(Log.INFO)) log.info("EPS SNFP " + length + (isInbound ? " IB" : " OB") + " exclude " + exclude.size()); - ctx.profileOrganizer().selectNotFailingPeers(length, exclude, matches, false); + ctx.profileOrganizer().selectNotFailingPeers(length, exclude, matches, false, ipRestriction); } matches.remove(ctx.routerHash()); }
added 1 commit
- d3af2af3 - Tunnels: Restore support for IP restriction in client tunnels
Test results: fairly rare for the check to trigger with ip restriction = 2, maybe one a day. Hardcoding the default to 1 for testing gets a few more, seems to be working:
ProfileOrganizer: IP restriction prevents [Hash: CjURF3q~wUFm9zCwy-xCq2QpdGYKMJasLmCXxvViawQ=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: FqepmPcUjGwQGyiD5u8BKzqkspFhv8VJq8B~6~nKG0M=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: -N6Mmj9JPG6ZIJVAbOItk-Op4g9PbCu-f8AE5ZTsTQ4=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: XEyQDS6EgaCigJMasd0SqbCLJEJQsDEUuvfbbrSYh1A=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: XEyQDS6EgaCigJMasd0SqbCLJEJQsDEUuvfbbrSYh1A=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: Qgjl6sQr4RdInWiosf2yZ6CLCLiHixWjRRTyAZ3Z~Zg=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: q76tT08I5FxMPOtSuQQ7y5Jr81ghjE3X9OQdrTfrptg=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: DIYQw3o9eJp2cnr8S9l26WUvZFp0lLY6JCSCXwwyN1Y=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: Sf1kmIumpCxXUBB-FYMkVo5Zx3tymxIfdz38ks0B2Eg=] from joining [[Hash: c~IbETyUo317j4U1a0bcr9-avdWKt3yNFjt9KnVQw~Y=], [Hash: oChQ4-0nRZvnX~sGjWWtN~KAbjNXWNtc7i-IByAZeEg=]] ProfileOrganizer: IP restriction prevents [Hash: 2GZASJQHd~N31fuc~A8UWsXG2Ll-0ruCwMS0Yx80UZg=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: oChQ4-0nRZvnX~sGjWWtN~KAbjNXWNtc7i-IByAZeEg=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: m98szjhyz7So2tJY5JB5Ylfo45WssMUY7y3g9zvvVIY=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: oChQ4-0nRZvnX~sGjWWtN~KAbjNXWNtc7i-IByAZeEg=] from joining [] ProfileOrganizer: IP restriction prevents [Hash: nH6a2UPl~vA2-K3Zvo2OPhDpkAPy13~hQM1iD-NKtF8=] from joining [[Hash: oChQ4-0nRZvnX~sGjWWtN~KAbjNXWNtc7i-IByAZeEg=]] ProfileOrganizer: IP restriction prevents [Hash: vwv7v9C6tYD0rQSXZmd8rFUoVHjaVpPwquMaRAbXKcY=] from joining [[Hash: -N6Mmj9JPG6ZIJVAbOItk-Op4g9PbCu-f8AE5ZTsTQ4=], [Hash: jN3PWONt5deKDEqfAyWxAbH2IedGkiNKDIAZLYc9bi0=]] ProfileOrganizer: IP restriction prevents [Hash: dXLYZM1rbl~glQ2Qin8Pw0Mi9deaKSNJDUUp6etD6Tg=] from joining [[Hash: 7jCT0SkzRRLrzxeuy~gpQ15f80WUJ3oS4UvQYDZBon4=]] ProfileOrganizer: IP restriction prevents [Hash: 3WmRnEbBOCJTKV5OlQswwWS8K3TwYrrMUr3cNNSCn48=] from joining [[Hash: 7jCT0SkzRRLrzxeuy~gpQ15f80WUJ3oS4UvQYDZBon4=]] ProfileOrganizer: IP restriction prevents [Hash: wvbryD53EYvK0ZaPNa~ShoVacDP2twEqTaTBmGSGorY=] from joining [[Hash: q76tT08I5FxMPOtSuQQ7y5Jr81ghjE3X9OQdrTfrptg=], [Hash: 2GZASJQHd~N31fuc~A8UWsXG2Ll-0ruCwMS0Yx80UZg=]] ProfileOrganizer: IP restriction prevents [Hash: 7jCT0SkzRRLrzxeuy~gpQ15f80WUJ3oS4UvQYDZBon4=] from joining [[Hash: hKDqxeTYHxEL-riYcvpy3vcdjom0L5-8Eb9~~oAbOAE=], [Hash: 3WmRnEbBOCJTKV5OlQswwWS8K3TwYrrMUr3cNNSCn48=]]
I did go through several of the log entries, checked the RI for each hash, and verified that there was an IP overlap between the restricted router and one of the others.
The ones with "joining []" are client tunnels where we're only picking one peer for a slice, so there's nothing in matches.
Edited by zzz@idk looking for an ACK from you when you're done with your testing and review