Search in sources :

Example 11 with RouterInfo

use of net.i2p.data.router.RouterInfo in project i2p.i2p by i2p.

the class EstablishmentManager method handleCompletelyEstablished.

/**
 * ok, fully received, add it to the established cons and queue up a
 * netDb store to them
 */
private void handleCompletelyEstablished(InboundEstablishState state) {
    if (state.isComplete())
        return;
    RouterIdentity remote = state.getConfirmedIdentity();
    PeerState peer = new PeerState(_context, _transport, state.getSentIP(), state.getSentPort(), remote.calculateHash(), true);
    peer.setCurrentCipherKey(state.getCipherKey());
    peer.setCurrentMACKey(state.getMACKey());
    peer.setWeRelayToThemAs(state.getSentRelayTag());
    // Lookup the peer's MTU from the netdb, since it isn't included in the protocol setup (yet)
    // TODO if we don't have RI then we will get it shortly, but too late.
    // Perhaps netdb should notify transport when it gets a new RI...
    RouterInfo info = _context.netDb().lookupRouterInfoLocally(remote.calculateHash());
    if (info != null) {
        RouterAddress addr = _transport.getTargetAddress(info);
        if (addr != null) {
            String smtu = addr.getOption(UDPAddress.PROP_MTU);
            if (smtu != null) {
                try {
                    boolean isIPv6 = state.getSentIP().length == 16;
                    int mtu = MTU.rectify(isIPv6, Integer.parseInt(smtu));
                    peer.setHisMTU(mtu);
                } catch (NumberFormatException nfe) {
                }
            }
        }
    }
    if (_log.shouldLog(Log.DEBUG))
        _log.debug("Handle completely established (inbound): " + state + " - " + peer.getRemotePeer());
    // if (true) // for now, only support direct
    // peer.setRemoteRequiresIntroduction(false);
    _transport.addRemotePeerState(peer);
    boolean isIPv6 = state.getSentIP().length == 16;
    _transport.inboundConnectionReceived(isIPv6);
    _transport.setIP(remote.calculateHash(), state.getSentIP());
    _context.statManager().addRateData("udp.inboundEstablishTime", state.getLifetime());
    sendInboundComplete(peer);
    OutNetMessage msg;
    while ((msg = state.getNextQueuedMessage()) != null) {
        if (_context.clock().now() - Router.CLOCK_FUDGE_FACTOR > msg.getExpiration()) {
            msg.timestamp("took too long but established...");
            _transport.failed(msg, "Took too long to establish, but it was established");
        } else {
            msg.timestamp("session fully established and sent");
            _transport.send(msg);
        }
    }
    state.complete();
}
Also used : OutNetMessage(net.i2p.router.OutNetMessage) RouterIdentity(net.i2p.data.router.RouterIdentity) RouterInfo(net.i2p.data.router.RouterInfo) RouterAddress(net.i2p.data.router.RouterAddress)

Example 12 with RouterInfo

use of net.i2p.data.router.RouterInfo in project i2p.i2p by i2p.

the class EstablishmentManager method establish.

/**
 *  @param queueIfMaxExceeded true normally, false if called from locked_admit so we don't loop
 *  @since 0.9.2
 */
private void establish(OutNetMessage msg, boolean queueIfMaxExceeded) {
    RouterInfo toRouterInfo = msg.getTarget();
    RouterAddress ra = _transport.getTargetAddress(toRouterInfo);
    if (ra == null) {
        _transport.failed(msg, "Remote peer has no address, cannot establish");
        return;
    }
    RouterIdentity toIdentity = toRouterInfo.getIdentity();
    Hash toHash = toIdentity.calculateHash();
    if (toRouterInfo.getNetworkId() != _networkID) {
        _context.banlist().banlistRouter(toHash);
        _transport.markUnreachable(toHash);
        _transport.failed(msg, "Remote peer is on the wrong network, cannot establish");
        return;
    }
    UDPAddress addr = new UDPAddress(ra);
    RemoteHostId maybeTo = null;
    InetAddress remAddr = addr.getHostAddress();
    int port = addr.getPort();
    // claimed address (which we won't be using if indirect)
    if (remAddr != null && port > 0 && port <= 65535) {
        maybeTo = new RemoteHostId(remAddr.getAddress(), port);
        if ((!_transport.isValid(maybeTo.getIP())) || (Arrays.equals(maybeTo.getIP(), _transport.getExternalIP()) && !_transport.allowLocal())) {
            _transport.failed(msg, "Remote peer's IP isn't valid");
            _transport.markUnreachable(toHash);
            // _context.banlist().banlistRouter(msg.getTarget().getIdentity().calculateHash(), "Invalid SSU address", UDPTransport.STYLE);
            _context.statManager().addRateData("udp.establishBadIP", 1);
            return;
        }
        InboundEstablishState inState = _inboundStates.get(maybeTo);
        if (inState != null) {
            // we have an inbound establishment in progress, queue it there instead
            synchronized (inState) {
                switch(inState.getState()) {
                    case IB_STATE_UNKNOWN:
                    case IB_STATE_REQUEST_RECEIVED:
                    case IB_STATE_CREATED_SENT:
                    case IB_STATE_CONFIRMED_PARTIALLY:
                    case IB_STATE_CONFIRMED_COMPLETELY:
                        // queue it
                        inState.addMessage(msg);
                        if (_log.shouldLog(Log.WARN))
                            _log.debug("OB msg queued to IES");
                        break;
                    case IB_STATE_COMPLETE:
                        // race, send it out (but don't call _transport.send() again and risk a loop)
                        _transport.sendIfEstablished(msg);
                        break;
                    case IB_STATE_FAILED:
                        // race, failed
                        _transport.failed(msg, "OB msg failed during IB establish");
                        break;
                }
            }
            return;
        }
    }
    RemoteHostId to;
    boolean isIndirect = addr.getIntroducerCount() > 0 || maybeTo == null;
    if (isIndirect) {
        to = new RemoteHostId(toHash);
    } else {
        to = maybeTo;
    }
    OutboundEstablishState state = null;
    int deferred = 0;
    boolean rejected = false;
    int queueCount = 0;
    state = _outboundStates.get(to);
    if (state == null) {
        state = _outboundByHash.get(toHash);
        if (state != null && _log.shouldLog(Log.INFO))
            _log.info("Found by hash: " + state);
    }
    if (state == null) {
        if (queueIfMaxExceeded && _outboundStates.size() >= getMaxConcurrentEstablish()) {
            if (_queuedOutbound.size() >= MAX_QUEUED_OUTBOUND && !_queuedOutbound.containsKey(to)) {
                rejected = true;
            } else {
                List<OutNetMessage> newQueued = new ArrayList<OutNetMessage>(MAX_QUEUED_PER_PEER);
                List<OutNetMessage> queued = _queuedOutbound.putIfAbsent(to, newQueued);
                if (queued == null) {
                    queued = newQueued;
                    if (_log.shouldLog(Log.WARN))
                        _log.warn("Queueing outbound establish to " + to + ", increase " + PROP_MAX_CONCURRENT_ESTABLISH);
                }
                // There are still races possible but this should prevent AIOOBE and NPE
                synchronized (queued) {
                    queueCount = queued.size();
                    if (queueCount < MAX_QUEUED_PER_PEER) {
                        queued.add(msg);
                        // increment for the stat below
                        queueCount++;
                    } else {
                        rejected = true;
                    }
                    deferred = _queuedOutbound.size();
                }
            }
        } else {
            // must have a valid session key
            byte[] keyBytes = addr.getIntroKey();
            if (keyBytes == null) {
                _transport.markUnreachable(toHash);
                _transport.failed(msg, "Peer has no key, cannot establish");
                return;
            }
            SessionKey sessionKey;
            try {
                sessionKey = new SessionKey(keyBytes);
            } catch (IllegalArgumentException iae) {
                _transport.markUnreachable(toHash);
                _transport.failed(msg, "Peer has bad key, cannot establish");
                return;
            }
            boolean allowExtendedOptions = VersionComparator.comp(toRouterInfo.getVersion(), VERSION_ALLOW_EXTENDED_OPTIONS) >= 0 && !_context.getBooleanProperty(PROP_DISABLE_EXT_OPTS);
            // w/o ext options, it's always 'requested', no need to set
            // don't ask if they are indirect
            boolean requestIntroduction = allowExtendedOptions && !isIndirect && _transport.introducersMaybeRequired();
            state = new OutboundEstablishState(_context, maybeTo, to, toIdentity, allowExtendedOptions, requestIntroduction, sessionKey, addr, _transport.getDHFactory());
            OutboundEstablishState oldState = _outboundStates.putIfAbsent(to, state);
            boolean isNew = oldState == null;
            if (isNew) {
                if (isIndirect && maybeTo != null)
                    _outboundByClaimedAddress.put(maybeTo, state);
                if (_log.shouldLog(Log.DEBUG))
                    _log.debug("Adding new " + state);
            } else {
                // whoops, somebody beat us to it, throw out the state we just created
                state = oldState;
            }
        }
    }
    if (state != null) {
        state.addMessage(msg);
        List<OutNetMessage> queued = _queuedOutbound.remove(to);
        if (queued != null) {
            // see comments above
            synchronized (queued) {
                for (OutNetMessage m : queued) {
                    state.addMessage(m);
                }
            }
        }
    }
    if (rejected) {
        if (_log.shouldLog(Log.WARN))
            _log.warn("Too many pending, rejecting outbound establish to " + to);
        _transport.failed(msg, "Too many pending outbound connections");
        _context.statManager().addRateData("udp.establishRejected", deferred);
        return;
    }
    if (queueCount >= MAX_QUEUED_PER_PEER) {
        _transport.failed(msg, "Too many pending messages for the given peer");
        _context.statManager().addRateData("udp.establishOverflow", queueCount, deferred);
        return;
    }
    if (deferred > 0)
        msg.timestamp("too many deferred establishers");
    else if (state != null)
        msg.timestamp("establish state already waiting");
    notifyActivity();
}
Also used : RouterInfo(net.i2p.data.router.RouterInfo) RouterIdentity(net.i2p.data.router.RouterIdentity) ArrayList(java.util.ArrayList) RouterAddress(net.i2p.data.router.RouterAddress) Hash(net.i2p.data.Hash) OutNetMessage(net.i2p.router.OutNetMessage) SessionKey(net.i2p.data.SessionKey) InetAddress(java.net.InetAddress)

Example 13 with RouterInfo

use of net.i2p.data.router.RouterInfo in project i2p.i2p by i2p.

the class IntroductionManager method pickInbound.

/**
 * Grab a bunch of peers who are willing to be introducers for us that
 * are locally known (duh) and have published their own SSU address (duh^2).
 * The picked peers have their info tacked on to the ssuOptions parameter for
 * use in the SSU RouterAddress.
 *
 * Try to use "good" peers (i.e. reachable, active)
 *
 * Also, ping all idle peers that were introducers in the last 2 hours,
 * to keep the connection up, since the netDb can have quite stale information,
 * and we want to keep our introducers valid.
 *
 * @param current current router address, may be null
 * @param ssuOptions out parameter, options are added
 * @return number of introducers added
 */
public int pickInbound(RouterAddress current, Properties ssuOptions, int howMany) {
    int start = _context.random().nextInt();
    if (_log.shouldLog(Log.DEBUG))
        _log.debug("Picking inbound out of " + _inbound.size());
    if (_inbound.isEmpty())
        return 0;
    List<PeerState> peers = new ArrayList<PeerState>(_inbound);
    int sz = peers.size();
    start = start % sz;
    int found = 0;
    long now = _context.clock().now();
    // 15 min
    long inactivityCutoff = now - (UDPTransport.EXPIRE_TIMEOUT / 2);
    // if not too many to choose from, be less picky
    if (sz <= howMany + 2)
        inactivityCutoff -= UDPTransport.EXPIRE_TIMEOUT / 4;
    List<Introducer> introducers = new ArrayList<Introducer>(howMany);
    for (int i = 0; i < sz && found < howMany; i++) {
        PeerState cur = peers.get((start + i) % sz);
        RouterInfo ri = _context.netDb().lookupRouterInfoLocally(cur.getRemotePeer());
        if (ri == null) {
            if (_log.shouldLog(Log.INFO))
                _log.info("Picked peer has no local routerInfo: " + cur);
            continue;
        }
        // FIXME we can include all his addresses including IPv6 even if we don't support IPv6 (isValid() is false)
        // but requires RelayRequest support, see below
        RouterAddress ra = _transport.getTargetAddress(ri);
        if (ra == null) {
            if (_log.shouldLog(Log.INFO))
                _log.info("Picked peer has no SSU address: " + ri);
            continue;
        }
        if (/* _context.profileOrganizer().isFailing(cur.getRemotePeer()) || */
        _context.banlist().isBanlisted(cur.getRemotePeer()) || _transport.wasUnreachable(cur.getRemotePeer())) {
            if (_log.shouldLog(Log.INFO))
                _log.info("Peer is failing, shistlisted or was unreachable: " + cur);
            continue;
        }
        // FIXED, was ||, is this OK now?
        if (cur.getLastReceiveTime() < inactivityCutoff && cur.getLastSendTime() < inactivityCutoff) {
            if (_log.shouldLog(Log.INFO))
                _log.info("Peer is idle too long: " + cur);
            continue;
        }
        // FIXME we can include all his addresses including IPv6 even if we don't support IPv6 (isValid() is false)
        // but requires RelayRequest support, see below
        byte[] ip = cur.getRemoteIP();
        int port = cur.getRemotePort();
        if (!isValid(ip, port))
            continue;
        if (_log.shouldLog(Log.INFO))
            _log.info("Picking introducer: " + cur);
        cur.setIntroducerTime();
        UDPAddress ura = new UDPAddress(ra);
        byte[] ikey = ura.getIntroKey();
        if (ikey == null)
            continue;
        introducers.add(new Introducer(ip, port, ikey, cur.getTheyRelayToUsAs()));
        found++;
    }
    // we sort them so a change in order only won't happen, and won't cause a republish
    Collections.sort(introducers);
    String exp = Long.toString((now + INTRODUCER_EXPIRATION) / 1000);
    for (int i = 0; i < found; i++) {
        Introducer in = introducers.get(i);
        ssuOptions.setProperty(UDPAddress.PROP_INTRO_HOST_PREFIX + i, in.sip);
        ssuOptions.setProperty(UDPAddress.PROP_INTRO_PORT_PREFIX + i, in.sport);
        ssuOptions.setProperty(UDPAddress.PROP_INTRO_KEY_PREFIX + i, in.skey);
        ssuOptions.setProperty(UDPAddress.PROP_INTRO_TAG_PREFIX + i, in.stag);
        String sexp = exp;
        // and reuse if still recent enough, so deepEquals() won't fail in UDPT.rEA
        if (current != null) {
            for (int j = 0; j < UDPTransport.PUBLIC_RELAY_COUNT; j++) {
                if (in.sip.equals(current.getOption(UDPAddress.PROP_INTRO_HOST_PREFIX + j)) && in.sport.equals(current.getOption(UDPAddress.PROP_INTRO_PORT_PREFIX + j)) && in.skey.equals(current.getOption(UDPAddress.PROP_INTRO_KEY_PREFIX + j)) && in.stag.equals(current.getOption(UDPAddress.PROP_INTRO_TAG_PREFIX + j))) {
                    // found old one
                    String oexp = current.getOption(UDPAddress.PROP_INTRO_EXP_PREFIX + j);
                    if (oexp != null) {
                        try {
                            long oex = Long.parseLong(oexp) * 1000;
                            if (oex > now + UDPTransport.INTRODUCER_EXPIRATION_MARGIN) {
                                // still good, use old expiration time
                                sexp = oexp;
                            }
                        } catch (NumberFormatException nfe) {
                        }
                    }
                    break;
                }
            }
        }
        ssuOptions.setProperty(UDPAddress.PROP_INTRO_EXP_PREFIX + i, sexp);
    }
    // FIXME failsafe if found == 0, relax inactivityCutoff and try again?
    pingIntroducers();
    return found;
}
Also used : RouterInfo(net.i2p.data.router.RouterInfo) ArrayList(java.util.ArrayList) RouterAddress(net.i2p.data.router.RouterAddress)

Example 14 with RouterInfo

use of net.i2p.data.router.RouterInfo in project i2p.i2p by i2p.

the class NTCPTransport method bid.

public TransportBid bid(RouterInfo toAddress, long dataSize) {
    if (!isAlive())
        return null;
    if (dataSize > NTCPConnection.MAX_MSG_SIZE) {
        // let SSU deal with it
        _context.statManager().addRateData("ntcp.noBidTooLargeI2NP", dataSize);
        return null;
    }
    Hash peer = toAddress.getIdentity().calculateHash();
    if (_context.banlist().isBanlisted(peer, STYLE)) {
        // we aren't banlisted in general (since we are trying to get a bid), but we have
        // recently banlisted the peer on the NTCP transport, so don't try it
        _context.statManager().addRateData("ntcp.attemptBanlistedPeer", 1);
        return null;
    } else if (isUnreachable(peer)) {
        _context.statManager().addRateData("ntcp.attemptUnreachablePeer", 1);
        return null;
    }
    boolean established = isEstablished(toAddress.getIdentity());
    if (established) {
        // _log.debug("fast bid when trying to send to " + peer + " as its already established");
        return _fastBid;
    }
    RouterAddress addr = getTargetAddress(toAddress);
    if (addr == null) {
        markUnreachable(peer);
        return null;
    }
    // Check for supported sig type
    SigType type = toAddress.getIdentity().getSigType();
    if (type == null || !type.isAvailable()) {
        markUnreachable(peer);
        return null;
    }
    // Can we connect to them if we are not DSA?
    RouterInfo us = _context.router().getRouterInfo();
    if (us != null) {
        RouterIdentity id = us.getIdentity();
        if (id.getSigType() != SigType.DSA_SHA1) {
            String v = toAddress.getVersion();
            if (VersionComparator.comp(v, MIN_SIGTYPE_VERSION) < 0) {
                markUnreachable(peer);
                return null;
            }
        }
    }
    if (!allowConnection()) {
        // _log.warn("no bid when trying to send to " + peer + ", max connection limit reached");
        return _transientFail;
    }
    // _log.debug("slow bid when trying to send to " + peer);
    if (haveCapacity()) {
        if (addr.getCost() > DEFAULT_COST)
            return _slowCostBid;
        else
            return _slowBid;
    } else {
        if (addr.getCost() > DEFAULT_COST)
            return _nearCapacityCostBid;
        else
            return _nearCapacityBid;
    }
}
Also used : RouterInfo(net.i2p.data.router.RouterInfo) RouterIdentity(net.i2p.data.router.RouterIdentity) RouterAddress(net.i2p.data.router.RouterAddress) Hash(net.i2p.data.Hash) SigType(net.i2p.crypto.SigType)

Example 15 with RouterInfo

use of net.i2p.data.router.RouterInfo in project i2p.i2p by i2p.

the class BuildHandler method handleReq.

/**
 * If we are dropping lots of requests before even trying to handle them,
 * I suppose you could call us "overloaded"
 */
/**
 ** unused, see handleReq() below
 *    private final static int MAX_PROACTIVE_DROPS = 240;
 *
 *    private int countProactiveDrops() {
 *        int dropped = 0;
 *        dropped += countEvents("tunnel.dropLoadProactive", 60*1000);
 *        dropped += countEvents("tunnel.dropLoad", 60*1000);
 *        dropped += countEvents("tunnel.dropLoadBacklog", 60*1000);
 *        dropped += countEvents("tunnel.dropLoadDelay", 60*1000);
 *        return dropped;
 *    }
 *
 *    private int countEvents(String stat, long period) {
 *        RateStat rs = _context.statManager().getRate(stat);
 *        if (rs != null) {
 *            Rate r = rs.getRate(period);
 *            if (r != null)
 *                return (int)r.getCurrentEventCount();
 *        }
 *        return 0;
 *    }
 ***
 */
/**
 *  Actually process the request and send the reply.
 *
 *  Todo: Replies are not subject to RED for bandwidth reasons,
 *  and the bandwidth is not credited to any tunnel.
 *  If we did credit the reply to the tunnel, it would
 *  prevent the classification of the tunnel as 'inactive' on tunnels.jsp.
 */
private void handleReq(RouterInfo nextPeerInfo, BuildMessageState state, BuildRequestRecord req, Hash nextPeer) {
    long ourId = req.readReceiveTunnelId();
    long nextId = req.readNextTunnelId();
    boolean isInGW = req.readIsInboundGateway();
    boolean isOutEnd = req.readIsOutboundEndpoint();
    Hash from = state.fromHash;
    if (from == null && state.from != null)
        from = state.from.calculateHash();
    if (isInGW && isOutEnd) {
        _context.statManager().addRateData("tunnel.rejectHostile", 1);
        _log.error("Dropping build request, IBGW+OBEP: " + req);
        if (from != null)
            _context.commSystem().mayDisconnect(from);
        return;
    }
    if (ourId <= 0 || ourId > TunnelId.MAX_ID_VALUE || nextId <= 0 || nextId > TunnelId.MAX_ID_VALUE) {
        _context.statManager().addRateData("tunnel.rejectHostile", 1);
        if (_log.shouldWarn())
            _log.warn("Dropping build request, bad tunnel ID: " + req);
        if (from != null)
            _context.commSystem().mayDisconnect(from);
        return;
    }
    // Loop checks
    if ((!isOutEnd) && _context.routerHash().equals(nextPeer)) {
        _context.statManager().addRateData("tunnel.rejectHostile", 1);
        // old i2pd
        if (_log.shouldWarn())
            _log.warn("Dropping build request, we are the next hop: " + req);
        if (from != null)
            _context.commSystem().mayDisconnect(from);
        return;
    }
    if (!isInGW) {
        // but if not, something is seriously wrong here.
        if (from == null || _context.routerHash().equals(from)) {
            _context.statManager().addRateData("tunnel.rejectHostile", 1);
            if (_log.shouldWarn())
                _log.warn("Dropping build request, we are the previous hop: " + req);
            return;
        }
    }
    if ((!isOutEnd) && (!isInGW)) {
        // A-B-C-A is not preventable
        if (nextPeer.equals(from)) {
            // i2pd does this
            _context.statManager().addRateData("tunnel.rejectHostile", 1);
            if (_log.shouldLog(Log.WARN))
                _log.warn("Dropping build request with the same previous and next hop: " + req);
            _context.commSystem().mayDisconnect(from);
            return;
        }
    }
    // time is in hours, rounded down.
    // tunnel-alt-creation.html specifies that this is enforced +/- 1 hour but it was not.
    // As of 0.9.16, allow + 5 minutes to - 65 minutes.
    long time = req.readRequestTime();
    long now = (_context.clock().now() / (60l * 60l * 1000l)) * (60 * 60 * 1000);
    long timeDiff = now - time;
    if (timeDiff > MAX_REQUEST_AGE) {
        _context.statManager().addRateData("tunnel.rejectTooOld", 1);
        if (_log.shouldLog(Log.WARN))
            _log.warn("Dropping build request too old... replay attack? " + DataHelper.formatDuration(timeDiff) + ": " + req);
        if (from != null)
            _context.commSystem().mayDisconnect(from);
        return;
    }
    if (timeDiff < 0 - MAX_REQUEST_FUTURE) {
        _context.statManager().addRateData("tunnel.rejectFuture", 1);
        if (_log.shouldLog(Log.WARN))
            _log.warn("Dropping build request too far in future " + DataHelper.formatDuration(0 - timeDiff) + ": " + req);
        if (from != null)
            _context.commSystem().mayDisconnect(from);
        return;
    }
    int response;
    if (_context.router().isHidden()) {
        _context.throttle().setTunnelStatus(_x("Rejecting tunnels: Hidden mode"));
        response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
    } else {
        response = _context.throttle().acceptTunnelRequest();
    }
    // This only checked OUR tunnels, so the log message was wrong.
    // Now checked by TunnelDispatcher.joinXXX()
    // and returned as success value, checked below.
    // if (_context.tunnelManager().getTunnelInfo(new TunnelId(ourId)) != null) {
    // if (_log.shouldLog(Log.ERROR))
    // _log.error("Already participating in a tunnel with the given Id (" + ourId + "), so gotta reject");
    // if (response == 0)
    // response = TunnelHistory.TUNNEL_REJECT_PROBABALISTIC_REJECT;
    // }
    // if ( (response == 0) && (_context.random().nextInt(50) <= 1) )
    // response = TunnelHistory.TUNNEL_REJECT_PROBABALISTIC_REJECT;
    long recvDelay = _context.clock().now() - state.recvTime;
    if (response == 0) {
        // unused
        // int proactiveDrops = countProactiveDrops();
        float pDrop = ((float) recvDelay) / (float) (BuildRequestor.REQUEST_TIMEOUT * 3);
        pDrop = (float) Math.pow(pDrop, 16);
        if (_context.random().nextFloat() < pDrop) {
            // || (proactiveDrops > MAX_PROACTIVE_DROPS) ) ) {
            _context.statManager().addRateData("tunnel.rejectOverloaded", recvDelay);
            _context.throttle().setTunnelStatus(_x("Rejecting tunnels: Request overload"));
            // if (true || (proactiveDrops < MAX_PROACTIVE_DROPS*2))
            response = TunnelHistory.TUNNEL_REJECT_TRANSIENT_OVERLOAD;
        // else
        // response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
        } else {
            _context.statManager().addRateData("tunnel.acceptLoad", recvDelay);
        }
    }
    /*
         * Being a IBGW or OBEP generally leads to more connections, so if we are
         * approaching our connection limit (i.e. !haveCapacity()),
         * reject this request.
         *
         * Don't do this for class N or O, under the assumption that they are already talking
         * to most of the routers, so there's no reason to reject. This may drive them
         * to their conn. limits, but it's hopefully a temporary solution to the
         * tunnel build congestion. As the net grows this will have to be revisited.
         */
    RouterInfo ri = _context.router().getRouterInfo();
    if (response == 0) {
        if (ri == null) {
            // ?? We should always have a RI
            response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
        } else {
            char bw = ri.getBandwidthTier().charAt(0);
            if (bw != 'O' && bw != 'N' && bw != 'P' && bw != 'X' && ((isInGW && !_context.commSystem().haveInboundCapacity(87)) || (isOutEnd && !_context.commSystem().haveOutboundCapacity(87)))) {
                _context.statManager().addRateData("tunnel.rejectConnLimits", 1);
                _context.throttle().setTunnelStatus(_x("Rejecting tunnels: Connection limit"));
                response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
            }
        }
    }
    // We may need another counter above for requests.
    if (response == 0 && !isInGW) {
        if (from != null && _throttler.shouldThrottle(from)) {
            if (_log.shouldLog(Log.WARN))
                _log.warn("Rejecting tunnel (hop throttle), previous hop: " + from + ": " + req);
            // no setTunnelStatus() indication
            _context.statManager().addRateData("tunnel.rejectHopThrottle", 1);
            response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
        }
    }
    if (response == 0 && (!isOutEnd) && _throttler.shouldThrottle(nextPeer)) {
        if (_log.shouldLog(Log.WARN))
            _log.warn("Rejecting tunnel (hop throttle), next hop: " + req);
        _context.statManager().addRateData("tunnel.rejectHopThrottle", 1);
        // no setTunnelStatus() indication
        response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
    }
    HopConfig cfg = null;
    if (response == 0) {
        cfg = new HopConfig();
        cfg.setCreation(_context.clock().now());
        cfg.setExpiration(_context.clock().now() + 10 * 60 * 1000);
        cfg.setIVKey(req.readIVKey());
        cfg.setLayerKey(req.readLayerKey());
        if (isInGW) {
        // default
        // cfg.setReceiveFrom(null);
        } else {
            if (from != null) {
                cfg.setReceiveFrom(from);
            } else {
                // b0rk
                return;
            }
        }
        cfg.setReceiveTunnelId(DataHelper.toLong(4, ourId));
        if (isOutEnd) {
        // default
        // cfg.setSendTo(null);
        // cfg.setSendTunnelId(null);
        } else {
            cfg.setSendTo(nextPeer);
            cfg.setSendTunnelId(DataHelper.toLong(4, nextId));
        }
        // now "actually" join
        boolean success;
        if (isOutEnd)
            success = _context.tunnelDispatcher().joinOutboundEndpoint(cfg);
        else if (isInGW)
            success = _context.tunnelDispatcher().joinInboundGateway(cfg);
        else
            success = _context.tunnelDispatcher().joinParticipant(cfg);
        if (success) {
            if (_log.shouldLog(Log.DEBUG))
                _log.debug("Joining: " + req);
        } else {
            // Dup Tunnel ID. This can definitely happen (birthday paradox).
            // Probability in 11 minutes (per hop type):
            // 0.1% for 2900 tunnels; 1% for 9300 tunnels
            response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
            _context.statManager().addRateData("tunnel.rejectDupID", 1);
            if (_log.shouldLog(Log.WARN))
                _log.warn("DUP ID failure: " + req);
        }
    }
    if (response != 0) {
        _context.statManager().addRateData("tunnel.reject." + response, 1);
        _context.messageHistory().tunnelRejected(from, new TunnelId(ourId), nextPeer, // (isOutEnd ? "outbound endpoint" : isInGW ? "inbound gw" : "participant"));
        Integer.toString(response));
        if (from != null)
            _context.commSystem().mayDisconnect(from);
        // 81% = between 75% control measures in Transports and 87% rejection above
        if ((!_context.routerHash().equals(nextPeer)) && (!_context.commSystem().haveOutboundCapacity(81)) && (!_context.commSystem().isEstablished(nextPeer))) {
            _context.statManager().addRateData("tunnel.dropConnLimits", 1);
            if (_log.shouldLog(Log.WARN))
                _log.warn("Not sending rejection due to conn limits: " + req);
            return;
        }
    } else if (isInGW && from != null) {
        // we're the start of the tunnel, no use staying connected
        _context.commSystem().mayDisconnect(from);
    }
    if (_log.shouldLog(Log.DEBUG))
        _log.debug("Responding to " + state.msg.getUniqueId() + " after " + recvDelay + " with " + response + " from " + (from != null ? from : "tunnel") + ": " + req);
    EncryptedBuildRecord reply = BuildResponseRecord.create(_context, response, req.readReplyKey(), req.readReplyIV(), state.msg.getUniqueId());
    int records = state.msg.getRecordCount();
    int ourSlot = -1;
    for (int j = 0; j < records; j++) {
        if (state.msg.getRecord(j) == null) {
            ourSlot = j;
            state.msg.setRecord(j, reply);
            // + ": " + Base64.encode(reply));
            break;
        }
    }
    if (_log.shouldLog(Log.DEBUG))
        _log.debug("Read slot " + ourSlot + " containing: " + req + " accepted? " + response + " recvDelay " + recvDelay + " replyMessage " + req.readReplyMessageId());
    // now actually send the response
    long expires = _context.clock().now() + NEXT_HOP_SEND_TIMEOUT;
    if (!isOutEnd) {
        state.msg.setUniqueId(req.readReplyMessageId());
        state.msg.setMessageExpiration(expires);
        OutNetMessage msg = new OutNetMessage(_context, state.msg, expires, PRIORITY, nextPeerInfo);
        if (response == 0)
            msg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg));
        _context.outNetMessagePool().add(msg);
    } else {
        // We are the OBEP.
        // send it to the reply tunnel on the reply peer within a new TunnelBuildReplyMessage
        // (enough layers jrandom?)
        TunnelBuildReplyMessage replyMsg;
        if (records == TunnelBuildMessage.MAX_RECORD_COUNT)
            replyMsg = new TunnelBuildReplyMessage(_context);
        else
            replyMsg = new VariableTunnelBuildReplyMessage(_context, records);
        for (int i = 0; i < records; i++) replyMsg.setRecord(i, state.msg.getRecord(i));
        replyMsg.setUniqueId(req.readReplyMessageId());
        replyMsg.setMessageExpiration(expires);
        TunnelGatewayMessage m = new TunnelGatewayMessage(_context);
        m.setMessage(replyMsg);
        m.setMessageExpiration(expires);
        m.setTunnelId(new TunnelId(nextId));
        if (_context.routerHash().equals(nextPeer)) {
            // ok, we are the gateway, so inject it
            if (_log.shouldLog(Log.DEBUG))
                _log.debug("We are the reply gateway for " + nextId + " when replying to replyMessage " + req);
            _context.tunnelDispatcher().dispatch(m);
        } else {
            // ok, the gateway is some other peer, shove 'er across
            OutNetMessage outMsg = new OutNetMessage(_context, m, expires, PRIORITY, nextPeerInfo);
            if (response == 0)
                outMsg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg));
            _context.outNetMessagePool().add(outMsg);
        }
    }
}
Also used : EncryptedBuildRecord(net.i2p.data.i2np.EncryptedBuildRecord) TunnelGatewayMessage(net.i2p.data.i2np.TunnelGatewayMessage) RouterInfo(net.i2p.data.router.RouterInfo) HopConfig(net.i2p.router.tunnel.HopConfig) Hash(net.i2p.data.Hash) TunnelId(net.i2p.data.TunnelId) VariableTunnelBuildReplyMessage(net.i2p.data.i2np.VariableTunnelBuildReplyMessage) TunnelBuildReplyMessage(net.i2p.data.i2np.TunnelBuildReplyMessage) VariableTunnelBuildReplyMessage(net.i2p.data.i2np.VariableTunnelBuildReplyMessage) OutNetMessage(net.i2p.router.OutNetMessage)

Aggregations

RouterInfo (net.i2p.data.router.RouterInfo)95 Hash (net.i2p.data.Hash)45 ArrayList (java.util.ArrayList)18 RouterAddress (net.i2p.data.router.RouterAddress)17 DataFormatException (net.i2p.data.DataFormatException)11 IOException (java.io.IOException)10 RouterIdentity (net.i2p.data.router.RouterIdentity)10 DatabaseEntry (net.i2p.data.DatabaseEntry)9 OutNetMessage (net.i2p.router.OutNetMessage)9 File (java.io.File)8 DatabaseStoreMessage (net.i2p.data.i2np.DatabaseStoreMessage)8 Properties (java.util.Properties)7 LeaseSet (net.i2p.data.LeaseSet)6 BigInteger (java.math.BigInteger)5 Date (java.util.Date)5 SigType (net.i2p.crypto.SigType)5 TunnelId (net.i2p.data.TunnelId)5 TunnelInfo (net.i2p.router.TunnelInfo)5 FileOutputStream (java.io.FileOutputStream)4 HashMap (java.util.HashMap)4