Search in sources :

Example 11 with TunnelInfo

use of net.i2p.router.TunnelInfo in project i2p.i2p by i2p.

the class StoreJob method sendStoreThroughClient.

/**
 * Send a leaseset store message out the client tunnel,
 * with the reply to come back through a client tunnel.
 * Stores are garlic encrypted to hide the identity from the OBEP.
 *
 * This makes it harder for an exploratory OBEP or IBGW to correlate it
 * with one or more destinations. Since we are publishing the leaseset,
 * it's easy to find out that an IB tunnel belongs to this dest, and
 * it isn't much harder to do the same for an OB tunnel.
 *
 * As a side benefit, client tunnels should be faster and more reliable than
 * exploratory tunnels.
 *
 * @param msg must contain a leaseset
 * @since 0.7.10
 */
private void sendStoreThroughClient(DatabaseStoreMessage msg, RouterInfo peer, long expiration) {
    long token = 1 + getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
    Hash client = msg.getKey();
    Hash to = peer.getIdentity().getHash();
    TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundTunnel(client, to);
    if (replyTunnel == null) {
        if (_log.shouldLog(Log.WARN))
            _log.warn("No reply inbound tunnels available!");
        fail();
        return;
    }
    TunnelId replyTunnelId = replyTunnel.getReceiveTunnelId(0);
    msg.setReplyToken(token);
    msg.setReplyTunnel(replyTunnelId);
    msg.setReplyGateway(replyTunnel.getPeer(0));
    if (_log.shouldLog(Log.DEBUG))
        _log.debug(getJobId() + ": send(dbStore) w/ token expected " + token);
    TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel(client, to);
    if (outTunnel != null) {
        I2NPMessage sent;
        boolean shouldEncrypt = supportsEncryption(peer);
        if (shouldEncrypt) {
            // garlic encrypt
            MessageWrapper.WrappedMessage wm = MessageWrapper.wrap(getContext(), msg, client, peer);
            if (wm == null) {
                if (_log.shouldLog(Log.WARN))
                    _log.warn("Fail garlic encrypting from: " + client);
                fail();
                return;
            }
            sent = wm.getMessage();
            _state.addPending(to, wm);
        } else {
            _state.addPending(to);
            // now that almost all floodfills are at 0.7.10,
            // just refuse to store unencrypted to older ones.
            _state.replyTimeout(to);
            getContext().jobQueue().addJob(new WaitJob(getContext()));
            return;
        }
        SendSuccessJob onReply = new SendSuccessJob(getContext(), peer, outTunnel, sent.getMessageSize());
        FailedJob onFail = new FailedJob(getContext(), peer, getContext().clock().now());
        StoreMessageSelector selector = new StoreMessageSelector(getContext(), getJobId(), peer, token, expiration);
        if (_log.shouldLog(Log.DEBUG)) {
            if (shouldEncrypt)
                _log.debug("sending encrypted store to " + peer.getIdentity().getHash() + " through " + outTunnel + ": " + sent);
            else
                _log.debug("sending store to " + peer.getIdentity().getHash() + " through " + outTunnel + ": " + sent);
        // _log.debug("Expiration is " + new Date(sent.getMessageExpiration()));
        }
        getContext().messageRegistry().registerPending(selector, onReply, onFail);
        getContext().tunnelDispatcher().dispatchOutbound(sent, outTunnel.getSendTunnelId(0), null, to);
    } else {
        if (_log.shouldLog(Log.WARN))
            _log.warn("No outbound tunnels to send a dbStore out - delaying...");
        // continueSending() above did an addPending() so remove it here.
        // This means we will skip the peer next time, can't be helped for now
        // without modding StoreState
        _state.replyTimeout(to);
        Job waiter = new WaitJob(getContext());
        waiter.getTiming().setStartAfter(getContext().clock().now() + 3 * 1000);
        getContext().jobQueue().addJob(waiter);
    // fail();
    }
}
Also used : TunnelInfo(net.i2p.router.TunnelInfo) Hash(net.i2p.data.Hash) TunnelId(net.i2p.data.TunnelId) I2NPMessage(net.i2p.data.i2np.I2NPMessage) ReplyJob(net.i2p.router.ReplyJob) Job(net.i2p.router.Job)

Example 12 with TunnelInfo

use of net.i2p.router.TunnelInfo in project i2p.i2p by i2p.

the class TunnelPool method selectTunnel.

private TunnelInfo selectTunnel(boolean allowRecurseOnFail) {
    boolean avoidZeroHop = getSettings().getLength() > 0 && getSettings().getLength() + getSettings().getLengthVariance() > 0;
    long period = curPeriod();
    synchronized (_tunnels) {
        if (_lastSelectionPeriod == period) {
            if ((_lastSelected != null) && (_lastSelected.getExpiration() > period) && (_tunnels.contains(_lastSelected)))
                return _lastSelected;
        }
        _lastSelectionPeriod = period;
        _lastSelected = null;
        if (_tunnels.isEmpty()) {
            if (_log.shouldLog(Log.WARN))
                _log.warn(toString() + ": No tunnels to select from");
        } else {
            Collections.shuffle(_tunnels, _context.random());
            // if there are nonzero hop tunnels and the zero hop tunnels are fallbacks,
            // avoid the zero hop tunnels
            TunnelInfo backloggedTunnel = null;
            if (avoidZeroHop) {
                for (int i = 0; i < _tunnels.size(); i++) {
                    TunnelInfo info = _tunnels.get(i);
                    if ((info.getLength() > 1) && (info.getExpiration() > _context.clock().now())) {
                        // avoid outbound tunnels where the 1st hop is backlogged
                        if (_settings.isInbound() || !_context.commSystem().isBacklogged(info.getPeer(1))) {
                            _lastSelected = info;
                            return info;
                        } else {
                            backloggedTunnel = info;
                        }
                    }
                }
                // return a random backlogged tunnel
                if (backloggedTunnel != null) {
                    if (_log.shouldLog(Log.WARN))
                        _log.warn(toString() + ": All tunnels are backlogged");
                    return backloggedTunnel;
                }
            }
            // randomly
            for (int i = 0; i < _tunnels.size(); i++) {
                TunnelInfo info = _tunnels.get(i);
                if (info.getExpiration() > _context.clock().now()) {
                    // avoid outbound tunnels where the 1st hop is backlogged
                    if (_settings.isInbound() || info.getLength() <= 1 || !_context.commSystem().isBacklogged(info.getPeer(1))) {
                        // _log.debug("Selecting tunnel: " + info + " - " + _tunnels);
                        _lastSelected = info;
                        return info;
                    } else {
                        backloggedTunnel = info;
                    }
                }
            }
            // return a random backlogged tunnel
            if (backloggedTunnel != null)
                return backloggedTunnel;
            if (_log.shouldLog(Log.WARN))
                _log.warn(toString() + ": after " + _tunnels.size() + " tries, no unexpired ones were found: " + _tunnels);
        }
    }
    if (_alive && _settings.getAllowZeroHop())
        buildFallback();
    if (allowRecurseOnFail)
        return selectTunnel(false);
    else
        return null;
}
Also used : TunnelInfo(net.i2p.router.TunnelInfo)

Example 13 with TunnelInfo

use of net.i2p.router.TunnelInfo in project i2p.i2p by i2p.

the class TunnelPool method selectTunnel.

/**
 * Return the tunnel from the pool that is XOR-closet to the target.
 * By using this instead of the random selectTunnel(),
 * we force some locality in OBEP-IBGW connections to minimize
 * those connections network-wide.
 *
 * Does not check for backlogged next peer.
 * Does not return an expired tunnel.
 *
 * @return null on failure
 * @since 0.8.10
 */
TunnelInfo selectTunnel(Hash closestTo) {
    boolean avoidZeroHop = getSettings().getLength() > 0 && getSettings().getLength() + getSettings().getLengthVariance() > 0;
    TunnelInfo rv = null;
    synchronized (_tunnels) {
        if (!_tunnels.isEmpty()) {
            if (_tunnels.size() > 1)
                Collections.sort(_tunnels, new TunnelInfoComparator(closestTo, avoidZeroHop));
            for (TunnelInfo info : _tunnels) {
                if (info.getExpiration() > _context.clock().now()) {
                    rv = info;
                    break;
                }
            }
        }
    }
    if (rv != null) {
        _context.statManager().addRateData("tunnel.matchLease", closestTo.equals(rv.getFarEnd()) ? 1 : 0);
    } else {
        if (_log.shouldLog(Log.WARN))
            _log.warn(toString() + ": No tunnels to select from");
    }
    return rv;
}
Also used : TunnelInfo(net.i2p.router.TunnelInfo)

Example 14 with TunnelInfo

use of net.i2p.router.TunnelInfo in project i2p.i2p by i2p.

the class TunnelPool method countHowManyToBuild.

/**
 * Gather the data to see how many tunnels to build, and then actually compute that value (delegated to
 * the countHowManyToBuild function below)
 */
int countHowManyToBuild() {
    if (!isAlive()) {
        return 0;
    }
    int wanted = getAdjustedTotalQuantity();
    boolean allowZeroHop = ((getSettings().getLength() + getSettings().getLengthVariance()) <= 0);
    /**
     * This algorithm builds based on the previous average length of time it takes
     * to build a tunnel. This average is kept in the _buildRateName stat.
     * It is a separate stat for each type of pool, since in and out building use different methods,
     * as do exploratory and client pools,
     * and each pool can have separate length and length variance settings.
     * We add one minute to the stat for safety (two for exploratory tunnels).
     *
     * We linearly increase the number of builds per expiring tunnel from
     * 1 to PANIC_FACTOR as the time-to-expire gets shorter.
     *
     * The stat will be 0 for first 10m of uptime so we will use the older, conservative algorithm
     * below instead. This algorithm will take about 30m of uptime to settle down.
     * Or, if we are building more than 33% of the time something is seriously wrong,
     * we also use the conservative algorithm instead
     */
    final String rateName = buildRateName();
    // Compute the average time it takes us to build a single tunnel of this type.
    int avg = 0;
    RateStat rs = _context.statManager().getRate(rateName);
    if (rs == null) {
        // Create the RateStat here rather than at the top because
        // the user could change the length settings while running
        _context.statManager().createRequiredRateStat(rateName, "Tunnel Build Frequency", "Tunnels", new long[] { TUNNEL_LIFETIME });
        rs = _context.statManager().getRate(rateName);
    }
    if (rs != null) {
        Rate r = rs.getRate(TUNNEL_LIFETIME);
        if (r != null)
            avg = (int) (TUNNEL_LIFETIME * r.getAverageValue() / wanted);
    }
    if (avg > 0 && avg < TUNNEL_LIFETIME / 3) {
        // if we're taking less than 200s per tunnel to build
        // how many builds to kick off when time gets short
        final int PANIC_FACTOR = 4;
        // one minute safety factor
        avg += 60 * 1000;
        if (_settings.isExploratory())
            // two minute safety factor
            avg += 60 * 1000;
        long now = _context.clock().now();
        int expireSoon = 0;
        int expireLater = 0;
        int[] expireTime;
        int fallback = 0;
        synchronized (_tunnels) {
            expireTime = new int[_tunnels.size()];
            for (int i = 0; i < _tunnels.size(); i++) {
                TunnelInfo info = _tunnels.get(i);
                if (allowZeroHop || (info.getLength() > 1)) {
                    int timeToExpire = (int) (info.getExpiration() - now);
                    if (timeToExpire > 0 && timeToExpire < avg) {
                        expireTime[expireSoon++] = timeToExpire;
                    } else {
                        expireLater++;
                    }
                } else if (info.getExpiration() - now > avg) {
                    fallback++;
                }
            }
        }
        int inProgress;
        synchronized (_inProgress) {
            inProgress = _inProgress.size();
        }
        int remainingWanted = (wanted - expireLater) - inProgress;
        if (allowZeroHop)
            remainingWanted -= fallback;
        int rv = 0;
        int latesttime = 0;
        if (remainingWanted > 0) {
            if (remainingWanted > expireSoon) {
                // for tunnels completely missing
                rv = PANIC_FACTOR * (remainingWanted - expireSoon);
                remainingWanted = expireSoon;
            }
            // the other ones are extras
            for (int i = 0; i < remainingWanted; i++) {
                int latestidx = 0;
                // given the small size of the array this is efficient enough
                for (int j = 0; j < expireSoon; j++) {
                    if (expireTime[j] > latesttime) {
                        latesttime = expireTime[j];
                        latestidx = j;
                    }
                }
                expireTime[latestidx] = 0;
                if (latesttime > avg / 2)
                    rv += 1;
                else
                    rv += 2 + ((PANIC_FACTOR - 2) * (((avg / 2) - latesttime) / (avg / 2)));
            }
        }
        if (rv > 0 && _log.shouldLog(Log.DEBUG))
            _log.debug("New Count: rv: " + rv + " allow? " + allowZeroHop + " avg " + avg + " latesttime " + latesttime + " soon " + expireSoon + " later " + expireLater + " std " + wanted + " inProgress " + inProgress + " fallback " + fallback + " for " + toString());
        _context.statManager().addRateData(rateName, rv + inProgress, 0);
        return rv;
    }
    // fixed, conservative algorithm - starts building 3 1/2 - 6m before expiration
    // (210 or 270s) + (0..90s random)
    // + _settings.getRebuildPeriod() + _expireSkew;
    long expireAfter = _context.clock().now() + _expireSkew;
    int expire30s = 0;
    int expire90s = 0;
    int expire150s = 0;
    int expire210s = 0;
    int expire270s = 0;
    int expireLater = 0;
    int fallback = 0;
    synchronized (_tunnels) {
        for (int i = 0; i < _tunnels.size(); i++) {
            TunnelInfo info = _tunnels.get(i);
            if (allowZeroHop || (info.getLength() > 1)) {
                long timeToExpire = info.getExpiration() - expireAfter;
                if (timeToExpire <= 0) {
                // consider it unusable
                } else if (timeToExpire <= 30 * 1000) {
                    expire30s++;
                } else if (timeToExpire <= 90 * 1000) {
                    expire90s++;
                } else if (timeToExpire <= 150 * 1000) {
                    expire150s++;
                } else if (timeToExpire <= 210 * 1000) {
                    expire210s++;
                } else if (timeToExpire <= 270 * 1000) {
                    expire270s++;
                } else {
                    expireLater++;
                }
            } else if (info.getExpiration() > expireAfter) {
                fallback++;
            }
        }
    }
    int inProgress = 0;
    synchronized (_inProgress) {
        inProgress = _inProgress.size();
        for (int i = 0; i < inProgress; i++) {
            PooledTunnelCreatorConfig cfg = _inProgress.get(i);
            if (cfg.getLength() <= 1)
                fallback++;
        }
    }
    int rv = countHowManyToBuild(allowZeroHop, expire30s, expire90s, expire150s, expire210s, expire270s, expireLater, wanted, inProgress, fallback);
    _context.statManager().addRateData(rateName, (rv > 0 || inProgress > 0) ? 1 : 0, 0);
    return rv;
}
Also used : RateStat(net.i2p.stat.RateStat) Rate(net.i2p.stat.Rate) TunnelInfo(net.i2p.router.TunnelInfo)

Example 15 with TunnelInfo

use of net.i2p.router.TunnelInfo in project i2p.i2p by i2p.

the class TunnelPool method locked_buildNewLeaseSet.

/**
 * Build a leaseSet with the required tunnels that aren't about to expire.
 * Caller must synchronize on _tunnels.
 *
 * @return null on failure
 */
protected LeaseSet locked_buildNewLeaseSet() {
    if (!_alive)
        return null;
    int wanted = Math.min(_settings.getQuantity(), LeaseSet.MAX_LEASES);
    if (_tunnels.size() < wanted) {
        if (_log.shouldLog(Log.WARN))
            _log.warn(toString() + ": Not enough tunnels (" + _tunnels.size() + ", wanted " + wanted + ")");
        // see comment below
        if (_tunnels.isEmpty())
            return null;
    }
    // + _settings.getRebuildPeriod();
    long expireAfter = _context.clock().now();
    TunnelInfo zeroHopTunnel = null;
    Lease zeroHopLease = null;
    TreeSet<Lease> leases = new TreeSet<Lease>(new LeaseComparator());
    for (int i = 0; i < _tunnels.size(); i++) {
        TunnelInfo tunnel = _tunnels.get(i);
        if (tunnel.getExpiration() <= expireAfter)
            // expires too soon, skip it
            continue;
        if (tunnel.getLength() <= 1) {
            // Keep only the one that expires the latest.
            if (zeroHopTunnel != null) {
                if (zeroHopTunnel.getExpiration() > tunnel.getExpiration())
                    continue;
                if (zeroHopLease != null)
                    leases.remove(zeroHopLease);
            }
            zeroHopTunnel = tunnel;
        }
        TunnelId inId = tunnel.getReceiveTunnelId(0);
        Hash gw = tunnel.getPeer(0);
        if ((inId == null) || (gw == null)) {
            _log.error(toString() + ": broken? tunnel has no inbound gateway/tunnelId? " + tunnel);
            continue;
        }
        Lease lease = new Lease();
        // bugfix
        // ExpireJob reduces the expiration, which causes a 2nd leaseset with the same lease
        // to have an earlier expiration, so it isn't stored.
        // Get the "real" expiration from the gateway hop config,
        // HopConfig expirations are the same as the "real" expiration and don't change
        // see configureNewTunnel()
        lease.setEndDate(new Date(((TunnelCreatorConfig) tunnel).getConfig(0).getExpiration()));
        lease.setTunnelId(inId);
        lease.setGateway(gw);
        leases.add(lease);
        // remember in case we want to remove it for a later-expiring zero-hopper
        if (tunnel.getLength() <= 1)
            zeroHopLease = lease;
    }
    // Do we want a config option for this, or are there times when we shouldn't do this?
    if (leases.size() < wanted) {
        if (_log.shouldLog(Log.WARN))
            _log.warn(toString() + ": Not enough leases (" + leases.size() + ", wanted " + wanted + ")");
        if (leases.isEmpty())
            return null;
    }
    LeaseSet ls = new LeaseSet();
    Iterator<Lease> iter = leases.iterator();
    int count = Math.min(leases.size(), wanted);
    for (int i = 0; i < count; i++) ls.addLease(iter.next());
    if (_log.shouldLog(Log.INFO))
        _log.info(toString() + ": built new leaseSet: " + ls);
    return ls;
}
Also used : LeaseSet(net.i2p.data.LeaseSet) Lease(net.i2p.data.Lease) TreeSet(java.util.TreeSet) TunnelInfo(net.i2p.router.TunnelInfo) Hash(net.i2p.data.Hash) TunnelId(net.i2p.data.TunnelId) Date(java.util.Date)

Aggregations

TunnelInfo (net.i2p.router.TunnelInfo)30 Hash (net.i2p.data.Hash)15 TunnelId (net.i2p.data.TunnelId)8 RouterInfo (net.i2p.data.router.RouterInfo)6 ArrayList (java.util.ArrayList)4 DatabaseLookupMessage (net.i2p.data.i2np.DatabaseLookupMessage)4 DatabaseStoreMessage (net.i2p.data.i2np.DatabaseStoreMessage)4 I2NPMessage (net.i2p.data.i2np.I2NPMessage)4 TunnelManagerFacade (net.i2p.router.TunnelManagerFacade)4 Job (net.i2p.router.Job)3 TunnelPoolSettings (net.i2p.router.TunnelPoolSettings)3 HashSet (java.util.HashSet)2 OutNetMessage (net.i2p.router.OutNetMessage)2 ReplyJob (net.i2p.router.ReplyJob)2 Date (java.util.Date)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Map (java.util.Map)1 Set (java.util.Set)1 TreeSet (java.util.TreeSet)1