Changeset b6112727


Ignore:
Timestamp:
Aug 22, 2012 5:43:09 PM (7 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
54b367b
Parents:
1d41c2fd
Message:
  • SSU:
    • Fail establishment immediately on SessionCreated? validation fail
    • Defer outbound DH generation until required
    • Validate address/port in RelayIntro? messages
    • Throttle hole punches
    • More cleanups
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • history.txt

    r1d41c2fd rb6112727  
     12012-08-22 zzz
     2 * NetDB: Add hash collision detection
     3 * SimpleTimer2: Synchronization improvements (ticket #653)
     4 * SSU:
     5   - Fail establishment immediately on SessionCreated
     6     validation fail
     7   - Defer outbound DH generation until required
     8   - Validate address/port in RelayIntro messages
     9   - Throttle hole punches
     10   - Workaround for Android ICS bug
     11   - More cleanups
     12
    1132012-08-21 zzz
    214 * NetDB: Decrease stat publish probability
  • router/java/src/net/i2p/router/RouterVersion.java

    r1d41c2fd rb6112727  
    1919    public final static String ID = "Monotone";
    2020    public final static String VERSION = CoreVersion.VERSION;
    21     public final static long BUILD = 14;
     21    public final static long BUILD = 15;
    2222
    2323    /** for example "-test" */
  • router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java

    r1d41c2fd rb6112727  
    341341                    state = new OutboundEstablishState(_context, maybeTo, to,
    342342                                                       toIdentity,
    343                                                        sessionKey, addr, _transport.getDHBuilder());
     343                                                       sessionKey, addr, _transport.getDHFactory());
    344344                    OutboundEstablishState oldState = _outboundStates.putIfAbsent(to, state);
    345345                    boolean isNew = oldState == null;
     
    396396   
    397397    /**
     398     * Should we allow another inbound establishment?
     399     * Used to throttle outbound hole punches.
     400     * @since 0.9.2
     401     */
     402    public boolean shouldAllowInboundEstablishment() {
     403        return _inboundStates.size() < getMaxInboundEstablishers();
     404    }
     405   
     406    /**
    398407     * Got a SessionRequest (initiates an inbound establishment)
    399408     *
     
    406415        }
    407416       
    408         int maxInbound = getMaxInboundEstablishers();
    409        
    410417        boolean isNew = false;
    411418
     
    414421                // TODO this is insufficient to prevent DoSing, especially if
    415422                // IP spoofing is used. For further study.
    416                 if (_inboundStates.size() >= maxInbound) {
     423                if (!shouldAllowInboundEstablishment()) {
    417424                    if (_log.shouldLog(Log.WARN))
    418425                        _log.warn("Dropping inbound establish, increase " + PROP_MAX_CONCURRENT_ESTABLISH);
     
    862869        }
    863870        _context.statManager().addRateData("udp.sendIntroRelayRequest", 1, 0);
    864         UDPPacket requests[] = _builder.buildRelayRequest(_transport, state, _transport.getIntroKey());
    865         for (int i = 0; i < requests.length; i++) {
    866             if (requests[i] != null)
    867                 _transport.send(requests[i]);
     871        List<UDPPacket> requests = _builder.buildRelayRequest(_transport, state, _transport.getIntroKey());
     872        if (requests.isEmpty()) {
     873            // FIXME need a failed OB state
     874            if (_log.shouldLog(Log.WARN))
     875                _log.warn("No valid introducers! " + state);
     876            // set failed state, remove nonce, and return
     877        }
     878        for (UDPPacket req : requests) {
     879            _transport.send(req);
    868880        }
    869881        if (_log.shouldLog(Log.DEBUG))
     
    896908                _log.warn("Introducer for " + state + " (" + bob + ") sent us an invalid address for our target: " + Addresses.toString(ip, port), uhe);
    897909            // these two cause this peer to requeue for a new intro peer
    898             state.introductionFailed();
    899             notifyActivity();
     910            // FIXME no it doesn't, we send to all at once
     911            //state.introductionFailed();
     912            //notifyActivity();
    900913            return;
    901914        }
     
    937950        if (!valid) {
    938951            // validate clears fields on failure
    939             // TODO - send destroy? shitlist?
     952            // sendDestroy(state) won't work as we haven't sent the confirmed...
    940953            if (_log.shouldLog(Log.WARN))
    941954                _log.warn("SessionCreated validate failed: " + state);
     
    10191032                    iter.remove();
    10201033                    inboundState = cur;
    1021                     if (_log.shouldLog(Log.DEBUG))
    1022                         _log.debug("Removing completely confirmed inbound state");
     1034                    //if (_log.shouldLog(Log.DEBUG))
     1035                    //    _log.debug("Removing completely confirmed inbound state");
    10231036                    break;
    10241037                } else if (cur.getLifetime() > MAX_IB_ESTABLISH_TIME) {
     
    10271040                    inboundState = cur;
    10281041                    //_context.statManager().addRateData("udp.inboundEstablishFailedState", cur.getState(), cur.getLifetime());
    1029                     if (_log.shouldLog(Log.DEBUG))
    1030                         _log.debug("Removing expired inbound state");
     1042                    //if (_log.shouldLog(Log.DEBUG))
     1043                    //    _log.debug("Removing expired inbound state");
    10311044                    expired = true;
    10321045                    break;
     
    11341147            for (Iterator<OutboundEstablishState> iter = _outboundStates.values().iterator(); iter.hasNext(); ) {
    11351148                OutboundEstablishState cur = iter.next();
    1136                 if (cur.getState() == OB_STATE_CONFIRMED_COMPLETELY) {
    1137                     // completely received
     1149                OutboundEstablishState.OutboundState state = cur.getState();
     1150                if (state == OB_STATE_CONFIRMED_COMPLETELY ||
     1151                    state == OB_STATE_VALIDATION_FAILED) {
    11381152                    iter.remove();
    11391153                    outboundState = cur;
    1140                     if (_log.shouldLog(Log.DEBUG))
    1141                         _log.debug("Removing confirmed outbound: " + cur);
    11421154                    break;
    11431155                } else if (cur.getLifetime() >= MAX_OB_ESTABLISH_TIME) {
     
    11461158                    outboundState = cur;
    11471159                    //_context.statManager().addRateData("udp.outboundEstablishFailedState", cur.getState(), cur.getLifetime());
    1148                     if (_log.shouldLog(Log.DEBUG))
    1149                         _log.debug("Removing expired outbound: " + cur);
     1160                    //if (_log.shouldLog(Log.DEBUG))
     1161                    //    _log.debug("Removing expired outbound: " + cur);
    11501162                    break;
    11511163                } else {
     
    12341246                            handlePendingIntro(outboundState);
    12351247                        break;
     1248
     1249                    case OB_STATE_VALIDATION_FAILED:
     1250                        processExpired(outboundState);
     1251                        break;
    12361252                }
    12371253            }
  • router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java

    r1d41c2fd rb6112727  
    179179    public int getSentPort() { return _alicePort; }
    180180   
    181     public synchronized byte[] getBobIP() { return _bobIP; }
    182    
    183181    public synchronized byte[] getSentY() {
    184182        if (_sentY == null)
     
    329327    /**
    330328     * Who is Alice (null if forged/unknown)
     329     *
     330     * Note that this isn't really confirmed - see below.
    331331     */
    332332    public synchronized RouterIdentity getConfirmedIdentity() {
     
    342342     * identity signs: Alice's IP + Alice's port + Bob's IP + Bob's port
    343343     * + Alice's new relay key + Alice's signed on time
    344      */
    345     private synchronized void verifyIdentity() {
     344     *
     345     * Note that the protocol does not include a signature of the RouterIdentity,
     346     * which could be a problem?
     347     *
     348     * Caller must synch on this.
     349     */
     350    private void verifyIdentity() {
    346351        int identSize = 0;
    347352        for (int i = 0; i < _receivedIdentity.length; i++)
     
    386391            boolean ok = _context.dsa().verifySignature(sig, signed, peer.getSigningPublicKey());
    387392            if (ok) {
     393                // todo partial spoof detection - get peer.calculateHash(),
     394                // lookup in netdb locally, if not equal, fail?
    388395                _receivedConfirmedIdentity = peer;
    389396            } else {
     
    409416        buf.append("IES ");
    410417        buf.append(Addresses.toString(_aliceIP, _alicePort));
    411         if (_receivedX != null)
    412             buf.append(" ReceivedX: ").append(Base64.encode(_receivedX, 0, 4));
    413         if (_sentY != null)
    414             buf.append(" SentY: ").append(Base64.encode(_sentY, 0, 4));
     418        //if (_receivedX != null)
     419        //    buf.append(" ReceivedX: ").append(Base64.encode(_receivedX, 0, 4));
     420        //if (_sentY != null)
     421        //    buf.append(" SentY: ").append(Base64.encode(_sentY, 0, 4));
    415422        //buf.append(" Bob: ").append(Addresses.toString(_bobIP, _bobPort));
    416423        buf.append(" RelayTag: ").append(_sentRelayTag);
  • router/java/src/net/i2p/router/transport/udp/IntroductionManager.java

    r1d41c2fd rb6112727  
    11package net.i2p.router.transport.udp;
    22
     3import java.net.InetAddress;
     4import java.net.UnknownHostException;
    35import java.util.ArrayList;
     6import java.util.HashSet;
    47import java.util.List;
    58import java.util.Map;
     
    1922
    2023/**
    21  *
     24 *  Keep track of inbound and outbound introductions.
    2225 */
    2326class IntroductionManager {
     
    3033    /** list of peers (PeerState) who have given us introduction tags */
    3134    private final Set<PeerState> _inbound;
     35    private final Set<InetAddress> _recentHolePunches;
     36    private long _lastHolePunchClean;
    3237
    3338    /**
     
    4247     */
    4348    private static final int MAX_OUTBOUND = 100;
     49
     50    /** Max one per target in this time */
     51    private static final long PUNCH_CLEAN_TIME = 5*1000;
     52    /** Max for all targets per PUNCH_CLEAN_TIME */
     53    private static final int MAX_PUNCHES = 8;
    4454
    4555    public IntroductionManager(RouterContext ctx, UDPTransport transport) {
     
    5060        _outbound = new ConcurrentHashMap(MAX_OUTBOUND);
    5161        _inbound = new ConcurrentHashSet(MAX_INBOUND);
     62        _recentHolePunches = new HashSet(16);
    5263        ctx.statManager().createRateStat("udp.receiveRelayIntro", "How often we get a relayed request for us to talk to someone?", "udp", UDPTransport.RATES);
    5364        ctx.statManager().createRateStat("udp.receiveRelayRequest", "How often we receive a good request to relay to someone else?", "udp", UDPTransport.RATES);
     
    204215        if (_context.router().isHidden())
    205216            return;
     217        _context.statManager().addRateData("udp.receiveRelayIntro", 1, 0);
     218
     219        if (!_transport.allowConnection()) {
     220            if (_log.shouldLog(Log.WARN))
     221                _log.warn("Dropping RelayIntro, over conn limit");
     222            return;
     223        }
     224       
     225        int ipSize = reader.getRelayIntroReader().readIPSize();
     226        byte ip[] = new byte[ipSize];
     227        reader.getRelayIntroReader().readIP(ip, 0);
     228        int port = reader.getRelayIntroReader().readPort();
    206229        if (_log.shouldLog(Log.INFO))
    207             _log.info("Receive relay intro from " + bob);
    208         _context.statManager().addRateData("udp.receiveRelayIntro", 1, 0);
    209 
    210         if (!_transport.allowConnection())
    211             return;
    212 
    213         // TODO throttle
    214         // TODO IB req limits
    215         // TODO check if already have a session or in progress state.
    216 
    217         _transport.send(_builder.buildHolePunch(reader));
     230            _log.info("Receive relay intro from " + bob + " for " + Addresses.toString(ip, port));
     231       
     232        InetAddress to = null;
     233        try {
     234            if (!_transport.isValid(ip))
     235                throw new UnknownHostException("non-public IP");
     236            if (port <= 0 || port > 65535)
     237                throw new UnknownHostException("bad port " + port);
     238            to = InetAddress.getByAddress(ip);
     239        } catch (UnknownHostException uhe) {
     240            // shitlist Bob?
     241            if (_log.shouldLog(Log.WARN))
     242                _log.warn("IP for alice to hole punch to is invalid", uhe);
     243            return;
     244        }
     245       
     246        RemoteHostId alice = new RemoteHostId(ip, port);
     247        if (_transport.getPeerState(alice) != null) {
     248            if (_log.shouldLog(Log.INFO))
     249                _log.info("Ignoring RelayIntro, already have a session to " + to);
     250            return;
     251        }
     252        EstablishmentManager establisher = _transport.getEstablisher();
     253        if (establisher != null) {
     254            if (establisher.getInboundState(alice) != null) {
     255                // This check may be common, as Alice sends RelayRequests to
     256                // several introducers at once.
     257                if (_log.shouldLog(Log.INFO))
     258                    _log.info("Ignoring RelayIntro, establishment in progress to " + to);
     259                return;
     260            }
     261            if (!establisher.shouldAllowInboundEstablishment()) {
     262                if (_log.shouldLog(Log.WARN))
     263                    _log.warn("Dropping RelayIntro, too many establishments in progress - for " + to);
     264                return;
     265            }
     266        }
     267
     268        // basic throttle, don't bother saving per-peer send times
     269        // we throttle on IP only, ignoring port
     270        boolean tooMany = false;
     271        boolean already = false;
     272        synchronized (_recentHolePunches) {
     273            long now = _context.clock().now();
     274            if (now > _lastHolePunchClean + PUNCH_CLEAN_TIME) {
     275                _recentHolePunches.clear();
     276                _lastHolePunchClean = now;
     277                _recentHolePunches.add(to);
     278            } else {
     279                tooMany = _recentHolePunches.size() >= MAX_PUNCHES;
     280                if (!tooMany)
     281                    already = !_recentHolePunches.add(to);
     282            }
     283        }
     284        if (tooMany) {
     285            if (_log.shouldLog(Log.WARN))
     286                _log.warn("Dropping - too many - RelayIntro for " + to);
     287            return;
     288        }
     289        if (already) {
     290            // This check will trigger a lot, as Alice sends RelayRequests to
     291            // several introducers at once.
     292            if (_log.shouldLog(Log.INFO))
     293                _log.info("Ignoring dup RelayIntro for " + to);
     294            return;
     295        }
     296
     297        _transport.send(_builder.buildHolePunch(to, port));
    218298    }
    219299   
  • router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java

    r1d41c2fd rb6112727  
    2727    private final Log _log;
    2828    // SessionRequest message
    29     private final byte _sentX[];
     29    private byte _sentX[];
    3030    private byte _bobIP[];
    3131    private int _bobPort;
    32     private final DHSessionKeyBuilder _keyBuilder;
     32    private final DHSessionKeyBuilder.Factory _keyFactory;
     33    private DHSessionKeyBuilder _keyBuilder;
    3334    // SessionCreated message
    3435    private byte _receivedY[];
     
    8384        OB_STATE_PENDING_INTRO,
    8485        /** RelayResponse received */
    85         OB_STATE_INTRODUCED
     86        OB_STATE_INTRODUCED,
     87        /** SessionConfirmed failed validation */
     88        OB_STATE_VALIDATION_FAILED
    8689    }
    8790   
     
    100103                                  RemoteHostId remoteHostId,
    101104                                  RouterIdentity remotePeer, SessionKey introKey, UDPAddress addr,
    102                                   DHSessionKeyBuilder dh) {
     105                                  DHSessionKeyBuilder.Factory dh) {
    103106        _context = ctx;
    104107        _log = ctx.logManager().getLog(OutboundEstablishState.class);
     
    118121        _remoteAddress = addr;
    119122        _introductionNonce = -1;
    120         _keyBuilder = dh;
    121         _sentX = new byte[UDPPacketReader.SessionRequestReader.X_LENGTH];
    122         prepareSessionRequest();
     123        _keyFactory = dh;
    123124        if (addr.getIntroducerCount() > 0) {
    124125            if (_log.shouldLog(Log.DEBUG))
     
    166167    public SessionKey getIntroKey() { return _introKey; }
    167168   
    168     /** called from constructor, no need to synch */
     169    /** caller must synch - only call once */
    169170    private void prepareSessionRequest() {
     171        _keyBuilder = _keyFactory.getBuilder();
     172        _sentX = new byte[UDPPacketReader.SessionRequestReader.X_LENGTH];
    170173        byte X[] = _keyBuilder.getMyPublicValue().toByteArray();
    171174        if (X.length == 257)
     
    177180    }
    178181
    179     public byte[] getSentX() { return _sentX; }
     182    public synchronized byte[] getSentX() {
     183        // We defer keygen until now so that it gets done in the Establisher loop,
     184        // and so that we don't waste entropy on failed introductions
     185        if (_sentX == null)
     186            prepareSessionRequest();
     187        return _sentX;
     188    }
    180189
    181190    /**
     
    192201
    193202    public synchronized void receiveSessionCreated(UDPPacketReader.SessionCreatedReader reader) {
     203        if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
     204            if (_log.shouldLog(Log.WARN))
     205                _log.warn("Session created already failed");
     206            return;
     207        }
    194208        if (_receivedY != null) {
    195209            if (_log.shouldLog(Log.DEBUG))
     
    237251     */
    238252    public synchronized boolean validateSessionCreated() {
     253        if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
     254            if (_log.shouldLog(Log.WARN))
     255                _log.warn("Session created already failed");
     256            return false;
     257        }
    239258        if (_receivedSignature != null) {
    240259            if (_log.shouldLog(Log.WARN))
     
    266285    }
    267286   
     287    /**
     288     *  The SessionCreated validation failed
     289     */
    268290    public synchronized void fail() {
    269291        _receivedY = null;
     
    274296        _receivedIV = null;
    275297        _receivedSignature = null;
    276 
    277         if ( (_currentState == OutboundState.OB_STATE_UNKNOWN) ||
    278              (_currentState == OutboundState.OB_STATE_CREATED_RECEIVED) )
    279             _currentState = OutboundState.OB_STATE_REQUEST_SENT;
     298        // sure, there's a chance the packet was corrupted, but in practice
     299        // this means that Bob doesn't know his external port, so give up.
     300        _currentState = OutboundState.OB_STATE_VALIDATION_FAILED;
    280301
    281302        _nextSend = _context.clock().now();
     
    288309    private void generateSessionKey() throws DHSessionKeyBuilder.InvalidPublicParameterException {
    289310        if (_sessionKey != null) return;
     311        if (_keyBuilder == null)
     312            throw new DHSessionKeyBuilder.InvalidPublicParameterException("Illegal state - never generated a key builder");
    290313        _keyBuilder.setPeerPublicValue(_receivedY);
    291314        _sessionKey = _keyBuilder.getSessionKey();
  • router/java/src/net/i2p/router/transport/udp/PacketBuilder.java

    r1d41c2fd rb6112727  
    33import java.net.InetAddress;
    44import java.net.UnknownHostException;
     5import java.util.ArrayList;
    56import java.util.Arrays;
    67import java.util.Collections;
     
    660661       
    661662        // now for the body
    662         System.arraycopy(state.getSentX(), 0, data, off, state.getSentX().length);
    663         off += state.getSentX().length;
    664         DataHelper.toLong(data, off, 1, state.getSentIP().length);
     663        byte[] x = state.getSentX();
     664        System.arraycopy(x, 0, data, off, x.length);
     665        off += x.length;
     666        DataHelper.toLong(data, off, 1, toIP.length);
    665667        off += 1;
    666         System.arraycopy(toIP, 0, data, off, state.getSentIP().length);
     668        System.arraycopy(toIP, 0, data, off, toIP.length);
    667669        off += toIP.length;
    668         DataHelper.toLong(data, off, 2, state.getSentPort());
     670        int port = state.getSentPort();
     671        DataHelper.toLong(data, off, 2, port);
    669672        off += 2;
    670673       
     
    676679        packet.getPacket().setLength(off);
    677680        authenticate(packet, state.getIntroKey(), state.getIntroKey());
    678         setTo(packet, to, state.getSentPort());
     681        setTo(packet, to, port);
    679682        packet.setMessageType(TYPE_SREQ);
    680683        return packet;
     
    10511054    private int getOurExplicitPort() { return 0; }
    10521055   
    1053     /** build intro packets for each of the published introducers */
    1054     @SuppressWarnings("static-access")
    1055     public UDPPacket[] buildRelayRequest(UDPTransport transport, OutboundEstablishState state, SessionKey ourIntroKey) {
     1056    /**
     1057     *  build intro packets for each of the published introducers
     1058     *  @return empty list on failure
     1059     */
     1060    public List<UDPPacket> buildRelayRequest(UDPTransport transport, OutboundEstablishState state, SessionKey ourIntroKey) {
    10561061        UDPAddress addr = state.getRemoteAddress();
    10571062        int count = addr.getIntroducerCount();
    1058         if (count <= 0)
    1059             return new UDPPacket[0];
    1060         UDPPacket rv[] = new UDPPacket[count];
     1063        List<UDPPacket> rv = new ArrayList(count);
    10611064        for (int i = 0; i < count; i++) {
    10621065            InetAddress iaddr = addr.getIntroducerHost(i);
     
    10701073                continue;
    10711074            }
     1075            // TODO implement some sort of introducer shitlist
    10721076            if (transport.isValid(iaddr.getAddress()))
    1073                 rv[i] = buildRelayRequest(iaddr, iport, ikey, tag, ourIntroKey, state.getIntroNonce(), true);
     1077                rv.add(buildRelayRequest(iaddr, iport, ikey, tag, ourIntroKey, state.getIntroNonce(), true));
    10741078        }
    10751079        return rv;
     
    12281232   
    12291233    /**
    1230      *  Sends an empty unauthenticated packet for hole punching
    1231      */
    1232     public UDPPacket buildHolePunch(UDPPacketReader reader) {
     1234     *  Sends an empty unauthenticated packet for hole punching.
     1235     *  Parameters must be validated previously.
     1236     */
     1237    public UDPPacket buildHolePunch(InetAddress to, int port) {
    12331238        UDPPacket packet = UDPPacket.acquire(_context, false);
    1234         byte data[] = packet.getPacket().getData();
    1235         Arrays.fill(data, 0, data.length, (byte)0x0);
    1236        
    1237         int ipSize = reader.getRelayIntroReader().readIPSize();
    1238         byte ip[] = new byte[ipSize];
    1239         reader.getRelayIntroReader().readIP(ip, 0);
    1240         int port = reader.getRelayIntroReader().readPort();
    1241        
    1242         InetAddress to = null;
    1243         try {
    1244             to = InetAddress.getByAddress(ip);
    1245         } catch (UnknownHostException uhe) {
    1246             if (_log.shouldLog(Log.WARN))
    1247                 _log.warn("IP for alice to hole punch to is invalid", uhe);
    1248             packet.release();
    1249             return null;
    1250         }
    1251        
    12521239        if (_log.shouldLog(Log.INFO))
    12531240            _log.info("Sending relay hole punch to " + to + ":" + port);
  • router/java/src/net/i2p/router/transport/udp/UDPTransport.java

    r1d41c2fd rb6112727  
    717717    }
    718718   
     719    /**
     720     *  For IntroductionManager
     721     *  @return may be null if not started
     722     *  @since 0.9.2
     723     */
     724    EstablishmentManager getEstablisher() {
     725        return _establisher;
     726    }
    719727    /**
    720728     * Intercept RouterInfo entries received directly from a peer to inject them into
     
    16831691   
    16841692    /**
     1693     *  @return a new DHSessionKeyBuilder
    16851694     *  @since 0.9
    16861695     */
    16871696    DHSessionKeyBuilder getDHBuilder() {
    16881697        return _dhFactory.getBuilder();
     1698    }
     1699   
     1700    /**
     1701     *  @return the factory
     1702     *  @since 0.9.2
     1703     */
     1704    DHSessionKeyBuilder.Factory getDHFactory() {
     1705        return _dhFactory;
    16891706    }
    16901707
Note: See TracChangeset for help on using the changeset viewer.