Changeset 44770b7


Ignore:
Timestamp:
Sep 10, 2005 4:30:36 AM (15 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
727d76d
Parents:
b5d571c
git-author:
jrandom <jrandom> (09/10/05 04:30:36)
git-committer:
zzz <zzz@…> (09/10/05 04:30:36)
Message:

2005-09-09 jrandom

  • Added preliminary support for NAT hole punching through SSU introducers
  • Honor peer test results from peers that we have an SSU session with if those sessions are idle for 3 minutes or more.
Files:
1 added
1 deleted
13 edited

Legend:

Unmodified
Added
Removed
  • history.txt

    rb5d571c r44770b7  
    1 $Id: history.txt,v 1.241 2005/09/07 17:32:06 jrandom Exp $
     1$Id: history.txt,v 1.242 2005/09/09 20:13:50 cervantes Exp $
     2
     32005-09-09  jrandom
     4    * Added preliminary support for NAT hole punching through SSU introducers
     5    * Honor peer test results from peers that we have an SSU session with if
     6      those sessions are idle for 3 minutes or more.
    27
    382005-09-09  cervantes
    4         * New build due to change in build number :P (thanks ugha!)
     9    * New build due to change in build number :P (thanks ugha!)
    510
    6112005-09-07  BarkerJr
  • router/doc/udp.html

    rb5d571c r44770b7  
    1 <code>$Id: udp.html,v 1.15 2005/08/03 13:58:13 jrandom Exp $</code>
     1<code>$Id: udp.html,v 1.16 2005/08/17 15:05:02 jrandom Exp $</code>
    22
    33<h1>Secure Semireliable UDP (SSU)</h1>
     
    252252        <li>4 byte relay tag</li>
    253253        <li>1 byte IP address size</li>
    254         <li>that many byte representation of Bob's IP address</li>
    255         <li>1 byte IP address size</li>
    256254        <li>that many byte representation of Alice's IP address</li>
    257255        <li>2 byte port number (of Alice)</li>
    258256        <li>1 byte challenge size</li>
    259257        <li>that many bytes to be relayed to Charlie in the intro</li>
     258        <li>Alice's intro key (so Bob can reply with Charlie's info)</li>
     259        <li>4 byte nonce of alice's relay request</li>
    260260        <li>N bytes, currently uninterpreted</li>
    261261        </ul></td></tr>
     
    268268 |      relay tag    |size| that many    |
    269269 +----+----+----+----+----+         +----|
    270  | bytes making up Bob's IP address |size|
    271  +----+----+----+----+----+----+----+----+
    272  | that many bytes making up Alice's IP  |
    273  +----+----+----+----+----+----+----+----+
    274  | Port (A)|size| that many challenge    |
    275  +----+----+----+                        |
    276  | bytes to be delivered to Charlie      |
    277  +----+----+----+----+----+----+----+----+
     270 | bytes for Alice's IP address     |port
     271 +----+----+----+----+----+----+----+----+
     272  (A) |size| that many challenge bytes   |
     273 +----+----+                             |
     274 | to be delivered to Charlie            |
     275 +----+----+----+----+----+----+----+----+
     276 | Alice's intro key                     |
     277 |                                       |
     278 |                                       |
     279 |                                       |
     280 +----+----+----+----+----+----+----+----+
     281 |       nonce       |                   |
     282 +----+----+----+----+                   |
    278283 | arbitrary amount of uninterpreted data|
    279284 +----+----+----+----+----+----+----+----+
     
    292297        <li>that many byte representation of Alice's IP address</li>
    293298        <li>2 byte port number</li>
     299        <li>4 byte nonce sent by Alice</li>
    294300        <li>N bytes, currently uninterpreted</li>
    295301        </ul></td></tr>
     
    308314 | Alice's IP address          | Port (A)|
    309315 +----+----+----+----+----+----+----+----+
     316 |       nonce       |                   |
     317 +----+----+----+----+                   |
    310318 | arbitrary amount of uninterpreted data|
    311319 +----+----+----+----+----+----+----+----+
     
    333341 |size| that many bytes making up        |
    334342 +----+                        +----+----+
    335  | Charlie's IP address        | Port (C)|
     343 | Alice's IP address          | Port (A)|
    336344 +----+----+----+----+----+----+----+----+
    337345 |size| that many bytes of challenge     |
     
    564572replay prevention - higher layers should take that into account.</p>
    565573
     574<h2><a name="introduction">Introduction</a></h2>
     575
     576<p>Indirect session establishment by means of a third party introduction
     577is necessary for efficient NAT traversal.  Charlie, a router behind a
     578NAT or firewall which does not allow unsolicited inbound UDP packets,
     579first contacts a few peers, choosing some to serve as introducers.  Each
     580of these peers (Bob, Bill, Betty, etc) provide Charlie with an introduction
     581tag - a 4 byte random number - which he then makes available to the public
     582as methods of contacting him.  Alice, a router who has Charlie's published
     583contact methods, first sends a RelayRequest packet to one or more of the
     584introducers, asking each to introduce her to Charlie (offering the
     585introduction tag to identify Charlie).  Bob then forwards a RelayIntro
     586packet to Charlie including Alice's public IP and port number, then sends
     587Alice back a RelayResponse packet containing Charlie's public IP and port
     588number.  When Charlie receives the RelayIntro packet, he sends off a small
     589random packet to Alice's IP and port (poking a hole in his NAT/firewall),
     590and when Alice receive's Bob's RelayResponse packet, she begins a new
     591full direction session establishment with the specified IP and port.</p>
     592
     593<!--
     594  should Bob wait for Charlie to ack the RelayIntro packet to avoid
     595  situations where that packet is lost yet Alice gets Charlie's IP with
     596  Charlie not yet punching a hole in his NAT for her to get through? 
     597  Perhaps Alice should send to multiple Bobs at once, hoping that at
     598  least one of them gets through
     599-->
     600
    566601<h2><a name="peerTesting">Peer testing</a></h2>
    567602
  • router/java/src/net/i2p/router/RouterVersion.java

    rb5d571c r44770b7  
    1616 */
    1717public class RouterVersion {
    18     public final static String ID = "$Revision: 1.228 $ $Date: 2005/09/07 17:31:13 $";
     18    public final static String ID = "$Revision: 1.229 $ $Date: 2005/09/09 20:13:49 $";
    1919    public final static String VERSION = "0.6.0.5";
    20     public final static long BUILD = 3;
     20    public final static long BUILD = 4;
    2121    public static void main(String args[]) {
    2222        System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
  • router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java

    rb5d571c r44770b7  
    22
    33import java.net.InetAddress;
     4import java.net.UnknownHostException;
    45import java.util.ArrayList;
    56import java.util.HashMap;
     
    910
    1011import net.i2p.crypto.DHSessionKeyBuilder;
     12import net.i2p.data.Base64;
    1113import net.i2p.data.RouterAddress;
    1214import net.i2p.data.RouterIdentity;
     
    1416import net.i2p.data.Signature;
    1517import net.i2p.data.i2np.DatabaseStoreMessage;
     18import net.i2p.router.CommSystemFacade;
    1619import net.i2p.router.OutNetMessage;
    1720import net.i2p.router.RouterContext;
    1821import net.i2p.util.I2PThread;
    1922import net.i2p.util.Log;
     23import net.i2p.util.SimpleTimer;
    2024
    2125/**
     
    3640    /** map of RemoteHostId to List of OutNetMessage for messages exceeding capacity */
    3741    private Map _queuedOutbound;
     42    /** map of nonce (Long) to OutboundEstablishState */
     43    private Map _liveIntroductions;
    3844    private boolean _alive;
    3945    private Object _activityLock;
     
    5157        _outboundStates = new HashMap(32);
    5258        _queuedOutbound = new HashMap(32);
     59        _liveIntroductions = new HashMap(32);
    5360        _activityLock = new Object();
    5461        _context.statManager().createRateStat("udp.inboundEstablishTime", "How long it takes for a new inbound session to be established", "udp", new long[] { 60*60*1000, 24*60*60*1000 });
     
    5663        _context.statManager().createRateStat("udp.inboundEstablishFailedState", "What state a failed inbound establishment request fails in", "udp", new long[] { 60*60*1000, 24*60*60*1000 });
    5764        _context.statManager().createRateStat("udp.outboundEstablishFailedState", "What state a failed outbound establishment request fails in", "udp", new long[] { 60*60*1000, 24*60*60*1000 });
     65        _context.statManager().createRateStat("udp.sendIntroRelayRequest", "How often we send a relay request to reach a peer", "udp", new long[] { 60*60*1000, 24*60*60*1000 });
     66        _context.statManager().createRateStat("udp.sendIntroRelayTimeout", "How often a relay request times out before getting a response (due to the target or intro peer being offline)", "udp", new long[] { 60*60*1000, 24*60*60*1000 });
     67        _context.statManager().createRateStat("udp.receiveIntroRelayResponse", "How long it took to receive a relay response", "udp", new long[] { 60*60*1000, 24*60*60*1000 });
    5868    }
    5969   
     
    135145                    state = new OutboundEstablishState(_context, remAddr, port,
    136146                                                       msg.getTarget().getIdentity(),
    137                                                        new SessionKey(addr.getIntroKey()));
     147                                                       new SessionKey(addr.getIntroKey()), addr);
    138148                    _outboundStates.put(to, state);
    139149                }
     
    156166     */
    157167    void receiveSessionRequest(RemoteHostId from, UDPPacketReader reader) {
     168        boolean isNew = false;
    158169        InboundEstablishState state = null;
    159170        synchronized (_inboundStates) {
     
    161172            if (state == null) {
    162173                state = new InboundEstablishState(_context, from.getIP(), from.getPort(), _transport.getLocalPort());
     174                isNew = true;
    163175                _inboundStates.put(from, state);
    164176            }
    165177        }
    166178        state.receiveSessionRequest(reader.getSessionRequestReader());
     179        if (isNew) {
     180            if (!_transport.introducersRequired()) {
     181                long tag = _context.random().nextLong(MAX_TAG_VALUE);
     182                state.setSentRelayTag(tag);
     183                if (_log.shouldLog(Log.INFO))
     184                    _log.info("Received session request from " + from + ", sending relay tag " + tag);
     185            } else {
     186                if (_log.shouldLog(Log.INFO))
     187                    _log.info("Received session request, but our status is " + _transport.getReachabilityStatus());
     188            }
     189        }
    167190       
    168191        if (_log.shouldLog(Log.DEBUG))
     
    266289            OutboundEstablishState qstate = new OutboundEstablishState(_context, remAddr, port,
    267290                                               msg.getTarget().getIdentity(),
    268                                                new SessionKey(addr.getIntroKey()));
     291                                               new SessionKey(addr.getIntroKey()), addr);
    269292            _outboundStates.put(to, qstate);
    270293
     
    305328        peer.setRemoteAddress(state.getSentIP(), state.getSentPort());
    306329        peer.setRemotePeer(remote.calculateHash());
    307         if (true) // for now, only support direct
    308             peer.setRemoteRequiresIntroduction(false);
     330        peer.setWeRelayToThemAs(state.getSentRelayTag());
    309331        peer.setTheyRelayToUsAs(0);
    310         peer.setWeRelayToThemAs(state.getSentRelayTag());
     332        //if (true) // for now, only support direct
     333        //    peer.setRemoteRequiresIntroduction(false);
    311334       
    312335        _transport.addRemotePeerState(peer);
     
    335358        peer.setRemoteAddress(state.getSentIP(), state.getSentPort());
    336359        peer.setRemotePeer(remote.calculateHash());
    337         if (true) // for now, only support direct
    338             peer.setRemoteRequiresIntroduction(false);
    339360        peer.setTheyRelayToUsAs(state.getReceivedRelayTag());
    340361        peer.setWeRelayToThemAs(0);
     
    365386    }
    366387   
     388    public static final long MAX_TAG_VALUE = 0xFFFFFFFFl;
     389   
    367390    private void sendCreated(InboundEstablishState state) {
    368391        long now = _context.clock().now();
    369         if (true) // for now, don't offer to relay
     392        if (!_transport.introducersRequired()) {
     393            // offer to relay
     394            // (perhaps we should check our bw usage and/or how many peers we are
     395            //  already offering introducing?)
     396            state.setSentRelayTag(_context.random().nextLong(MAX_TAG_VALUE));
     397        } else {
     398            // don't offer to relay
    370399            state.setSentRelayTag(0);
     400        }
    371401       
    372402        if (_log.shouldLog(Log.DEBUG))
     
    391421    private void sendRequest(OutboundEstablishState state) {
    392422        long now = _context.clock().now();
    393         state.prepareSessionRequest();
    394423        if (_log.shouldLog(Log.DEBUG))
    395424            _log.debug("Send request to: " + state.getRemoteHostId().toString());
    396425        _transport.send(_builder.buildSessionRequestPacket(state));
    397426        state.requestSent();
     427    }
     428   
     429    private static final long MAX_NONCE = 0xFFFFFFFFl;
     430    /** if we don't get a relayResponse in 3 seconds, try again with another intro peer */
     431    private static final int INTRO_ATTEMPT_TIMEOUT = 3*1000;
     432   
     433    private void handlePendingIntro(OutboundEstablishState state) {
     434        long nonce = _context.random().nextLong(MAX_NONCE);
     435        while (true) {
     436            synchronized (_liveIntroductions) {
     437                OutboundEstablishState old = (OutboundEstablishState)_liveIntroductions.put(new Long(nonce), state);
     438                if (old != null) {
     439                    nonce = _context.random().nextLong(MAX_NONCE);
     440                } else {
     441                    break;
     442                }
     443            }
     444        }
     445        SimpleTimer.getInstance().addEvent(new FailIntroduction(state, nonce), INTRO_ATTEMPT_TIMEOUT);
     446        state.setIntroNonce(nonce);
     447        _context.statManager().addRateData("udp.sendIntroRelayRequest", 1, 0);
     448        _transport.send(_builder.buildRelayRequest(state, _transport.getIntroKey()));
     449        if (_log.shouldLog(Log.DEBUG))
     450            _log.debug("Send intro for " + state.getRemoteHostId().toString() + " with our intro key as " + _transport.getIntroKey().toBase64());
     451        state.introSent();
     452    }
     453    private class FailIntroduction implements SimpleTimer.TimedEvent {
     454        private long _nonce;
     455        private OutboundEstablishState _state;
     456        public FailIntroduction(OutboundEstablishState state, long nonce) {
     457            _nonce = nonce;
     458            _state = state;
     459        }
     460        public void timeReached() {
     461            OutboundEstablishState removed = null;
     462            synchronized (_liveIntroductions) {
     463                removed = (OutboundEstablishState)_liveIntroductions.remove(new Long(_nonce));
     464                if (removed != _state) {
     465                    // another one with the same nonce in a very brief time...
     466                    _liveIntroductions.put(new Long(_nonce), removed);
     467                    removed = null;
     468                }
     469            }
     470            if (removed != null) {
     471                _context.statManager().addRateData("udp.sendIntroRelayTimeout", 1, 0);
     472                notifyActivity();
     473            }
     474        }
     475    }
     476   
     477    public void receiveRelayResponse(RemoteHostId bob, UDPPacketReader reader) {
     478        long nonce = reader.getRelayResponseReader().readNonce();
     479        OutboundEstablishState state = null;
     480        synchronized (_liveIntroductions) {
     481            state = (OutboundEstablishState)_liveIntroductions.remove(new Long(nonce));
     482        }
     483        if (state == null)
     484            return; // already established
     485       
     486        int sz = reader.getRelayResponseReader().readCharlieIPSize();
     487        byte ip[] = new byte[sz];
     488        reader.getRelayResponseReader().readCharlieIP(ip, 0);
     489        InetAddress addr = null;
     490        try {
     491            addr = InetAddress.getByAddress(ip);
     492        } catch (UnknownHostException uhe) {
     493            if (_log.shouldLog(Log.WARN))
     494                _log.warn("Introducer for " + state + " (" + bob + ") sent us an invalid IP for our targer: " + Base64.encode(ip), uhe);
     495            // these two cause this peer to requeue for a new intro peer
     496            state.introductionFailed();
     497            notifyActivity();
     498            return;
     499        }
     500        _context.statManager().addRateData("udp.receiveIntroRelayResponse", state.getLifetime(), 0);
     501        int port = reader.getRelayResponseReader().readCharliePort();
     502        state.introduced(addr, ip, port);
     503        notifyActivity();
    398504    }
    399505   
     
    595701                            err = "Took too long to establish remote connection (request sent)";
    596702                            break;
     703                        case OutboundEstablishState.STATE_PENDING_INTRO:
     704                            err = "Took too long to establish remote connection (intro failed)";
     705                            break;
    597706                        case OutboundEstablishState.STATE_UNKNOWN: // fallthrough
    598707                        default:
     
    626735                    case OutboundEstablishState.STATE_CONFIRMED_COMPLETELY:
    627736                        handleCompletelyEstablished(outboundState);
     737                        break;
     738                    case OutboundEstablishState.STATE_PENDING_INTRO:
     739                        handlePendingIntro(outboundState);
    628740                        break;
    629741                    default:
  • router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java

    rb5d571c r44770b7  
    5757    private List _queuedMessages;
    5858    private int _currentState;
     59    private long _introductionNonce;
     60    // intro
     61    private UDPAddress _remoteAddress;
    5962   
    6063    /** nothin sent yet */
     
    6871    /** we have received a data packet */
    6972    public static final int STATE_CONFIRMED_COMPLETELY = 4;
     73    /** we need to have someone introduce us to the peer, but haven't received a RelayResponse yet */
     74    public static final int STATE_PENDING_INTRO = 5;
    7075   
    7176    public OutboundEstablishState(RouterContext ctx, InetAddress remoteHost, int remotePort,
    72                                   RouterIdentity remotePeer, SessionKey introKey) {
     77                                  RouterIdentity remotePeer, SessionKey introKey, UDPAddress addr) {
    7378        _context = ctx;
    7479        _log = ctx.logManager().getLog(OutboundEstablishState.class);
    75         _bobIP = remoteHost.getAddress();
     80        _bobIP = (remoteHost != null ? remoteHost.getAddress() : null);
    7681        _bobPort = remotePort;
    7782        _remoteHostId = new RemoteHostId(_bobIP, _bobPort);
     
    8287        _currentState = STATE_UNKNOWN;
    8388        _establishBegin = ctx.clock().now();
     89        _remoteAddress = addr;
     90        _introductionNonce = -1;
     91        prepareSessionRequest();
     92        if ( (addr != null) && (addr.getIntroducerCount() > 0) ) {
     93            if (_log.shouldLog(Log.DEBUG))
     94                _log.debug("new outbound establish to " + remotePeer.calculateHash().toBase64() + ", with address: " + addr);
     95            _currentState = STATE_PENDING_INTRO;
     96        }
    8497    }
    8598   
    8699    public synchronized int getState() { return _currentState; }
    87100
     101    public UDPAddress getRemoteAddress() { return _remoteAddress; }
     102    public void setIntroNonce(long nonce) { _introductionNonce = nonce; }
     103    public long getIntroNonce() { return _introductionNonce; }
     104   
    88105    public void addMessage(OutNetMessage msg) {
    89106        synchronized (_queuedMessages) {
     
    102119    public SessionKey getIntroKey() { return _introKey; }
    103120   
    104     public synchronized void prepareSessionRequest() {
     121    private void prepareSessionRequest() {
    105122        _keyBuilder = new DHSessionKeyBuilder();
    106123        byte X[] = _keyBuilder.getMyPublicValue().toByteArray();
     
    343360            _currentState = STATE_REQUEST_SENT;
    344361    }
     362    public synchronized void introSent() {
     363        _lastSend = _context.clock().now();
     364        _nextSend = _lastSend + 5*1000;
     365        if (_currentState == STATE_UNKNOWN)
     366            _currentState = STATE_PENDING_INTRO;
     367    }
     368    public synchronized void introductionFailed() {
     369        _nextSend = _context.clock().now();
     370        // keep the state as STATE_PENDING_INTRO, so next time the EstablishmentManager asks us
     371        // whats up, it'll try a new random intro peer
     372    }
     373   
     374    public synchronized void introduced(InetAddress bob, byte bobIP[], int bobPort) {
     375        if (_currentState != STATE_PENDING_INTRO)
     376            return; // we've already successfully been introduced, so don't overwrite old settings
     377        _nextSend = _context.clock().now() + 500; // wait briefly for the hole punching
     378        if (_currentState == STATE_PENDING_INTRO) {
     379            // STATE_UNKNOWN will probe the EstablishmentManager to send a new
     380            // session request to this newly known address
     381            _currentState = STATE_UNKNOWN;
     382        }
     383        _bobIP = bobIP;
     384        _bobPort = bobPort;
     385        _remoteHostId = new RemoteHostId(bobIP, bobPort);
     386    }
    345387   
    346388    /** how long have we been trying to establish this session? */
  • router/java/src/net/i2p/router/transport/udp/PacketBuilder.java

    rb5d571c r44770b7  
    629629    }
    630630   
     631    /**
     632     * full flag info for a relay request message.  this can be fixed,
     633     * since we never rekey on relay request, and don't need any extended options
     634     */
     635    private static final byte PEER_RELAY_REQUEST_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_REQUEST << 4);
     636
     637    // specify these if we know what our external receive ip/port is and if its different
     638    // from what bob is going to think
     639    private byte[] getOurExplicitIP() { return null; }
     640    private int getOurExplicitPort() { return 0; }
     641   
     642    public UDPPacket buildRelayRequest(OutboundEstablishState state, SessionKey ourIntroKey) {
     643        UDPAddress addr = state.getRemoteAddress();
     644        int index = _context.random().nextInt(UDPAddress.MAX_INTRODUCERS) % addr.getIntroducerCount();
     645        InetAddress iaddr = addr.getIntroducerHost(index);
     646        int iport = addr.getIntroducerPort(index);
     647        byte ikey[] = addr.getIntroducerKey(index);
     648        long tag = addr.getIntroducerTag(index);
     649        return buildRelayRequest(iaddr, iport, ikey, tag, ourIntroKey, state.getIntroNonce(), true);
     650    }
     651   
     652    public UDPPacket buildRelayRequest(InetAddress introHost, int introPort, byte introKey[], long introTag, SessionKey ourIntroKey, long introNonce, boolean encrypt) {
     653        UDPPacket packet = UDPPacket.acquire(_context);
     654        byte data[] = packet.getPacket().getData();
     655        Arrays.fill(data, 0, data.length, (byte)0x0);
     656        int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
     657       
     658        byte ourIP[] = getOurExplicitIP();
     659        int ourPort = getOurExplicitPort();
     660       
     661        // header
     662        data[off] = PEER_RELAY_REQUEST_FLAG_BYTE;
     663        off++;
     664        long now = _context.clock().now() / 1000;
     665        DataHelper.toLong(data, off, 4, now);
     666        if (_log.shouldLog(Log.INFO))
     667            _log.info("Sending intro relay request to " + introHost + ":" + introPort); // + " regarding " + state.getRemoteIdentity().calculateHash().toBase64());
     668        off += 4;
     669       
     670        // now for the body
     671        DataHelper.toLong(data, off, 4, introTag);
     672        off += 4;
     673        if (ourIP != null) {
     674            DataHelper.toLong(data, off, 1, ourIP.length);
     675            off++;
     676            System.arraycopy(ourIP, 0, data, off, ourIP.length);
     677            off += ourIP.length;
     678        } else {
     679            DataHelper.toLong(data, off, 1, 0);
     680            off++;
     681        }
     682       
     683        DataHelper.toLong(data, off, 2, ourPort);
     684        off += 2;
     685       
     686        // challenge...
     687        DataHelper.toLong(data, off, 1, 0);
     688        off++;
     689        off += 0; // *cough*
     690       
     691        System.arraycopy(ourIntroKey.getData(), 0, data, off, SessionKey.KEYSIZE_BYTES);
     692        off += SessionKey.KEYSIZE_BYTES;
     693       
     694        if (_log.shouldLog(Log.WARN))
     695            _log.warn("wrote alice intro key: " + Base64.encode(data, off-SessionKey.KEYSIZE_BYTES, SessionKey.KEYSIZE_BYTES)
     696                      + " with nonce " + introNonce + " size=" + (off+4 + (16 - (off+4)%16))
     697                      + " and data: " + Base64.encode(data, 0, off));
     698       
     699        DataHelper.toLong(data, off, 4, introNonce);
     700        off += 4;
     701       
     702        // we can pad here if we want, maybe randomized?
     703       
     704        // pad up so we're on the encryption boundary
     705        if ( (off % 16) != 0)
     706            off += 16 - (off % 16);
     707        packet.getPacket().setLength(off);
     708        if (encrypt)
     709            authenticate(packet, new SessionKey(introKey), new SessionKey(introKey));
     710        setTo(packet, introHost, introPort);
     711        return packet;
     712    }
     713
     714    /**
     715     * full flag info for a relay intro message.  this can be fixed,
     716     * since we never rekey on relay request, and don't need any extended options
     717     */
     718    private static final byte PEER_RELAY_INTRO_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_INTRO << 4);
     719   
     720    public UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request) {
     721        UDPPacket packet = UDPPacket.acquire(_context);
     722        byte data[] = packet.getPacket().getData();
     723        Arrays.fill(data, 0, data.length, (byte)0x0);
     724        int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
     725       
     726        // header
     727        data[off] = PEER_RELAY_INTRO_FLAG_BYTE;
     728        off++;
     729        long now = _context.clock().now() / 1000;
     730        DataHelper.toLong(data, off, 4, now);
     731        if (_log.shouldLog(Log.INFO))
     732            _log.info("Sending intro to " + charlie + " for " + alice);
     733        off += 4;
     734       
     735        // now for the body
     736        byte ip[] = alice.getIP();
     737        DataHelper.toLong(data, off, 1, ip.length);
     738        off++;
     739        System.arraycopy(ip, 0, data, off, ip.length);
     740        off += ip.length;
     741        DataHelper.toLong(data, off, 2, alice.getPort());
     742        off += 2;
     743       
     744        int sz = request.readChallengeSize();
     745        DataHelper.toLong(data, off, 1, sz);
     746        off++;
     747        if (sz > 0) {
     748            request.readChallengeSize(data, off);
     749            off += sz;
     750        }
     751       
     752        // we can pad here if we want, maybe randomized?
     753       
     754        // pad up so we're on the encryption boundary
     755        if ( (off % 16) != 0)
     756            off += 16 - (off % 16);
     757        packet.getPacket().setLength(off);
     758        authenticate(packet, charlie.getCurrentCipherKey(), charlie.getCurrentMACKey());
     759        setTo(packet, charlie.getRemoteIPAddress(), charlie.getRemotePort());
     760        return packet;
     761    }
     762
     763    /**
     764     * full flag info for a relay response message.  this can be fixed,
     765     * since we never rekey on relay response, and don't need any extended options
     766     */
     767    private static final byte PEER_RELAY_RESPONSE_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE << 4);
     768   
     769    public UDPPacket buildRelayResponse(RemoteHostId alice, PeerState charlie, long nonce, SessionKey aliceIntroKey) {
     770        InetAddress aliceAddr = null;
     771        try {
     772            aliceAddr = InetAddress.getByAddress(alice.getIP());
     773        } catch (UnknownHostException uhe) {
     774            return null;
     775        }
     776       
     777        UDPPacket packet = UDPPacket.acquire(_context);
     778        byte data[] = packet.getPacket().getData();
     779        Arrays.fill(data, 0, data.length, (byte)0x0);
     780        int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
     781       
     782        // header
     783        data[off] = PEER_RELAY_RESPONSE_FLAG_BYTE;
     784        off++;
     785        long now = _context.clock().now() / 1000;
     786        DataHelper.toLong(data, off, 4, now);
     787        off += 4;
     788       
     789        if (_log.shouldLog(Log.INFO))
     790            _log.info("Sending relay response to " + alice + " for " + charlie + " with alice's intro key " + aliceIntroKey.toBase64());
     791
     792        // now for the body
     793        byte charlieIP[] = charlie.getRemoteIP();
     794        DataHelper.toLong(data, off, 1, charlieIP.length);
     795        off++;
     796        System.arraycopy(charlieIP, 0, data, off, charlieIP.length);
     797        off += charlieIP.length;
     798        DataHelper.toLong(data, off, 2, charlie.getRemotePort());
     799        off += 2;
     800       
     801        byte aliceIP[] = alice.getIP();
     802        DataHelper.toLong(data, off, 1, aliceIP.length);
     803        off++;
     804        System.arraycopy(aliceIP, 0, data, off, aliceIP.length);
     805        off += aliceIP.length;
     806        DataHelper.toLong(data, off, 2, alice.getPort());
     807        off += 2;
     808       
     809        DataHelper.toLong(data, off, 4, nonce);
     810        off += 4;
     811       
     812        // we can pad here if we want, maybe randomized?
     813       
     814        // pad up so we're on the encryption boundary
     815        if ( (off % 16) != 0)
     816            off += 16 - (off % 16);
     817        packet.getPacket().setLength(off);
     818        authenticate(packet, aliceIntroKey, aliceIntroKey);
     819        setTo(packet, aliceAddr, alice.getPort());
     820        return packet;
     821    }
     822   
     823    public UDPPacket buildHolePunch(UDPPacketReader reader) {
     824        UDPPacket packet = UDPPacket.acquire(_context);
     825        byte data[] = packet.getPacket().getData();
     826        Arrays.fill(data, 0, data.length, (byte)0x0);
     827        int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
     828       
     829        int ipSize = reader.getRelayIntroReader().readIPSize();
     830        byte ip[] = new byte[ipSize];
     831        reader.getRelayIntroReader().readIP(ip, 0);
     832        int port = reader.getRelayIntroReader().readPort();
     833       
     834        InetAddress to = null;
     835        try {
     836            to = InetAddress.getByAddress(ip);
     837        } catch (UnknownHostException uhe) {
     838            if (_log.shouldLog(Log.WARN))
     839                _log.warn("IP for alice to hole punch to is invalid", uhe);
     840            return null;
     841        }
     842       
     843        if (_log.shouldLog(Log.INFO))
     844            _log.info("Sending relay hole punch to " + to + ":" + port);
     845
     846        // the packet is empty and does not need to be authenticated, since
     847        // its just for hole punching
     848        packet.getPacket().setLength(0);
     849        setTo(packet, to, port);
     850        return packet;
     851    }
     852   
    631853    private void setTo(UDPPacket packet, InetAddress ip, int port) {
    632854        packet.getPacket().setAddress(ip);
  • router/java/src/net/i2p/router/transport/udp/PacketHandler.java

    rb5d571c r44770b7  
    3131    private InboundMessageFragments _inbound;
    3232    private PeerTestManager _testManager;
     33    private IntroductionManager _introManager;
    3334    private boolean _keepReading;
    3435    private List _handlers;
     
    3940   
    4041   
    41     public PacketHandler(RouterContext ctx, UDPTransport transport, UDPEndpoint endpoint, EstablishmentManager establisher, InboundMessageFragments inbound, PeerTestManager testManager) {
     42    public PacketHandler(RouterContext ctx, UDPTransport transport, UDPEndpoint endpoint, EstablishmentManager establisher, InboundMessageFragments inbound, PeerTestManager testManager, IntroductionManager introManager) {
    4243        _context = ctx;
    4344        _log = ctx.logManager().getLog(PacketHandler.class);
     
    4748        _inbound = inbound;
    4849        _testManager = testManager;
     50        _introManager = introManager;
    4951        _handlers = new ArrayList(NUM_HANDLERS);
    5052        for (int i = 0; i < NUM_HANDLERS; i++) {
     
    194196                        // (after an outbound establishment process, there wouldn't
    195197                        //  be any stray packets)
    196                         if (_log.shouldLog(Log.INFO))
    197                             _log.info("Validation with existing con failed, but validation as reestablish/stray passed");
     198                        if (_log.shouldLog(Log.DEBUG))
     199                            _log.debug("Validation with existing con failed, but validation as reestablish/stray passed");
    198200                        packet.decrypt(_transport.getIntroKey());
    199201                    } else {
     
    236238                return;
    237239            } else {
    238                 if (_log.shouldLog(Log.INFO))
    239                     _log.info("Valid introduction packet received: " + packet);
     240                if (_log.shouldLog(Log.DEBUG))
     241                    _log.debug("Valid introduction packet received: " + packet);
    240242            }
    241243
     
    393395                    if (outState != null)
    394396                        state = _establisher.receiveData(outState);
    395                     if (_log.shouldLog(Log.INFO))
    396                         _log.info("Received new DATA packet from " + state + ": " + packet);
     397                    if (_log.shouldLog(Log.DEBUG))
     398                        _log.debug("Received new DATA packet from " + state + ": " + packet);
    397399                    _inbound.receiveData(state, reader.getDataReader());
    398400                    break;
    399401                case UDPPacket.PAYLOAD_TYPE_TEST:
    400402                    _state = 51;
     403                    if (_log.shouldLog(Log.DEBUG))
     404                        _log.debug("Received test packet: " + reader + " from " + from);
     405                    _testManager.receiveTest(from, reader);
     406                    break;
     407                case UDPPacket.PAYLOAD_TYPE_RELAY_REQUEST:
    401408                    if (_log.shouldLog(Log.INFO))
    402                         _log.info("Received test packet: " + reader + " from " + from);
    403                     _testManager.receiveTest(from, reader);
     409                        _log.info("Received relay request packet: " + reader + " from " + from);
     410                    _introManager.receiveRelayRequest(from, reader);
     411                    break;
     412                case UDPPacket.PAYLOAD_TYPE_RELAY_INTRO:
     413                    if (_log.shouldLog(Log.INFO))
     414                        _log.info("Received relay intro packet: " + reader + " from " + from);
     415                    _introManager.receiveRelayIntro(from, reader);
     416                    break;
     417                case UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE:
     418                    if (_log.shouldLog(Log.INFO))
     419                        _log.info("Received relay response packet: " + reader + " from " + from);
     420                    _establisher.receiveRelayResponse(from, reader);
    404421                    break;
    405422                default:
  • router/java/src/net/i2p/router/transport/udp/PeerTestManager.java

    rb5d571c r44770b7  
    126126    }
    127127   
     128    /**
     129     * If we have sent a packet to charlie within the last 3 minutes, ignore any test
     130     * results we get from them, as our NAT will have poked a hole anyway
     131     *
     132     */
     133    private static final long CHARLIE_RECENT_PERIOD = 3*60*1000;
    128134
    129135    /**
     
    153159        } else {
    154160            PeerState charlieSession = _transport.getPeerState(from);
    155             if (charlieSession != null) {
     161            long recentBegin = _context.clock().now() - CHARLIE_RECENT_PERIOD;
     162            if ( (charlieSession != null) &&
     163                 (charlieSession.getLastACKSend() > recentBegin) &&
     164                 (charlieSession.getLastSendTime() > recentBegin) ) {
    156165                if (_log.shouldLog(Log.WARN))
    157166                    _log.warn("Bob chose a charlie we already have a session to, cancelling the test and rerunning (bob: "
  • router/java/src/net/i2p/router/transport/udp/RemoteHostId.java

    rb5d571c r44770b7  
    3737    }
    3838   
    39     public String toString() {
     39    public String toString() { return toString(true); }
     40    public String toString(boolean includePort) {
    4041        StringBuffer buf = new StringBuffer(_ip.length + 5);
    41         for (int i = 0; i < _ip.length; i++)
    42             buf.append(_ip[i]&0xFF).append('.');
    43         buf.append(_port);
     42        for (int i = 0; i < _ip.length; i++) {
     43            buf.append(_ip[i]&0xFF);
     44            if (i + 1 < _ip.length)
     45                buf.append('.');
     46        }
     47        if (includePort)
     48            buf.append(':').append(_port);
    4449        return buf.toString();
    4550    }
     51    public String toHostString() { return toString(false); }
    4652}
  • router/java/src/net/i2p/router/transport/udp/UDPAddress.java

    rb5d571c r44770b7  
    77import net.i2p.data.Base64;
    88import net.i2p.data.RouterAddress;
     9import net.i2p.data.SessionKey;
    910
    1011/**
     
    1617    private int _port;
    1718    private byte[] _introKey;
     19    private String _introHosts[];
     20    private InetAddress _introAddresses[];
     21    private int _introPorts[];
     22    private byte[] _introKeys[];
     23    private long _introTags[];
    1824   
    1925    public static final String PROP_PORT = "port";
     
    2430    public static final char CAPACITY_TESTING = 'B';
    2531    public static final char CAPACITY_INTRODUCER = 'C';
     32   
     33    public static final String PROP_INTRO_HOST_PREFIX = "ihost";
     34    public static final String PROP_INTRO_PORT_PREFIX = "iport";
     35    public static final String PROP_INTRO_KEY_PREFIX = "ikey";
     36    public static final String PROP_INTRO_TAG_PREFIX = "itag";
     37    static final int MAX_INTRODUCERS = 3;
    2638
    2739    public UDPAddress(RouterAddress addr) {
    2840        parse(addr);
     41    }
     42   
     43    public String toString() {
     44        StringBuffer rv = new StringBuffer(64);
     45        rv.append("[SSU ");
     46        if (_host != null)
     47            rv.append("host: ").append(_host).append(' ');
     48        if (_port > 0)
     49            rv.append("port: ").append(_port).append(' ');
     50        if (_introKey != null)
     51            rv.append("key: ").append(Base64.encode(_introKey)).append(' ');
     52        if (_introHosts != null) {
     53            for (int i = 0; i < _introHosts.length; i++) {
     54                rv.append("intro[" + i + "]: ").append(_introHosts[i]);
     55                rv.append(':').append(_introPorts[i]);
     56                rv.append('/').append(Base64.encode(_introKeys[i])).append(' ');
     57            }
     58        }   
     59        return rv.toString();
    2960    }
    3061   
     
    4374        if (key != null)
    4475            _introKey = Base64.decode(key.trim());
     76       
     77        for (int i = MAX_INTRODUCERS; i >= 0; i--) {
     78            String host = opts.getProperty(PROP_INTRO_HOST_PREFIX + i);
     79            if (host == null) continue;
     80            String port = opts.getProperty(PROP_INTRO_PORT_PREFIX + i);
     81            if (port == null) continue;
     82            String k = opts.getProperty(PROP_INTRO_KEY_PREFIX + i);
     83            if (k == null) continue;
     84            byte ikey[] = Base64.decode(k);
     85            if ( (ikey == null) || (ikey.length != SessionKey.KEYSIZE_BYTES) )
     86                continue;
     87            String t = opts.getProperty(PROP_INTRO_TAG_PREFIX + i);
     88            if (t == null) continue;
     89            int p = -1;
     90            try {
     91                p = Integer.parseInt(port);
     92                if (p <= 0) continue;
     93            } catch (NumberFormatException nfe) {
     94                continue;
     95            }
     96            long tag = -1;
     97            try {
     98                tag = Long.parseLong(t);
     99                if (tag <= 0) continue;
     100            } catch (NumberFormatException nfe) {
     101                continue;
     102            }
     103            if (_introHosts == null) {
     104                _introHosts = new String[i+1];
     105                _introPorts = new int[i+1];
     106                _introAddresses = new InetAddress[i+1];
     107                _introKeys = new byte[i+1][];
     108                _introTags = new long[i+1];
     109            }
     110            _introHosts[i] = host;
     111            _introPorts[i] = p;
     112            _introKeys[i] = ikey;
     113            _introTags[i] = tag;
     114        }
    45115    }
    46116   
     
    58128    public int getPort() { return _port; }
    59129    public byte[] getIntroKey() { return _introKey; }
     130   
     131    public int getIntroducerCount() { return (_introAddresses == null ? 0 : _introAddresses.length); }
     132    public InetAddress getIntroducerHost(int i) {
     133        if (_introAddresses[i] == null) {
     134            try {
     135                _introAddresses[i] = InetAddress.getByName(_introHosts[i]);
     136            } catch (UnknownHostException uhe) {
     137                _introAddresses[i] = null;
     138            }
     139        }
     140        return _introAddresses[i];
     141    }
     142    public int getIntroducerPort(int i) { return _introPorts[i]; }
     143    public byte[] getIntroducerKey(int i) { return _introKeys[i]; }
     144    public long getIntroducerTag(int i) { return _introTags[i]; }
     145       
    60146}
  • router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java

    rb5d571c r44770b7  
    11package net.i2p.router.transport.udp;
    22
     3import java.net.InetAddress;
    34import net.i2p.I2PAppContext;
    45import net.i2p.data.Base64;
     
    2526    private DataReader _dataReader;
    2627    private PeerTestReader _peerTestReader;
     28    private RelayRequestReader _relayRequestReader;
     29    private RelayIntroReader _relayIntroReader;
     30    private RelayResponseReader _relayResponseReader;
    2731   
    2832    private static final int KEYING_MATERIAL_LENGTH = 64;
     
    3640        _dataReader = new DataReader();
    3741        _peerTestReader = new PeerTestReader();
     42        _relayRequestReader = new RelayRequestReader();
     43        _relayIntroReader = new RelayIntroReader();
     44        _relayResponseReader = new RelayResponseReader();
    3845    }
    3946   
     
    94101    public DataReader getDataReader() { return _dataReader; }
    95102    public PeerTestReader getPeerTestReader() { return _peerTestReader; }
     103    public RelayRequestReader getRelayRequestReader() { return _relayRequestReader; }
     104    public RelayIntroReader getRelayIntroReader() { return _relayIntroReader; }
     105    public RelayResponseReader getRelayResponseReader() { return _relayResponseReader; }
    96106   
    97107    public String toString() {
     
    107117            case UDPPacket.PAYLOAD_TYPE_TEST:
    108118                return "Peer test packet";
     119            case UDPPacket.PAYLOAD_TYPE_RELAY_INTRO:
     120                return "Relay intro packet";
     121            case UDPPacket.PAYLOAD_TYPE_RELAY_REQUEST:
     122                return "Relay request packet";
     123            case UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE:
     124                return "Relay response packet";
    109125            default:
    110126                return "Other packet type...";
     
    539555        }
    540556    }
     557   
     558    /** Help read the RelayRequest payload */
     559    public class RelayRequestReader {
     560        public long readTag() {
     561            long rv = DataHelper.fromLong(_message, readBodyOffset(), 4);
     562            if (_log.shouldLog(Log.WARN))
     563                _log.warn("read alice tag: " + rv);
     564            return rv;
     565        }
     566        public int readIPSize() {
     567            int offset = readBodyOffset() + 4;
     568            int rv = (int)DataHelper.fromLong(_message, offset, 1);
     569            if (_log.shouldLog(Log.WARN))
     570                _log.warn("read alice ip size: " + rv);
     571            return rv;
     572        }
     573       
     574        /** what IP Alice is reachable on */
     575        public void readIP(byte target[], int targetOffset) {
     576            int offset = readBodyOffset() + 4;
     577            int size = (int)DataHelper.fromLong(_message, offset, 1);
     578            offset++;
     579            System.arraycopy(_message, offset, target, targetOffset, size);
     580            if (_log.shouldLog(Log.WARN))
     581                _log.warn("read alice ip: " + Base64.encode(target, targetOffset, size));
     582        }
     583        public int readPort() {
     584            int offset = readBodyOffset() + 4;
     585            offset += DataHelper.fromLong(_message, offset, 1);
     586            offset++;
     587            int rv = (int)DataHelper.fromLong(_message, offset, 2);
     588            if (_log.shouldLog(Log.WARN))
     589                _log.warn("read alice port: " + rv);
     590            return rv;
     591        }
     592        public int readChallengeSize() {
     593            int offset = readBodyOffset() + 4;
     594            offset += DataHelper.fromLong(_message, offset, 1);
     595            offset++;
     596            offset += 2;
     597            int rv = (int)DataHelper.fromLong(_message, offset, 1);
     598            if (_log.shouldLog(Log.WARN))
     599                _log.warn("read challenge size: " + rv);
     600            return rv;
     601        }
     602        public void readChallengeSize(byte target[], int targetOffset) {
     603            int offset = readBodyOffset() + 4;
     604            offset += DataHelper.fromLong(_message, offset, 1);
     605            offset++;
     606            offset += 2;
     607            int sz = (int)DataHelper.fromLong(_message, offset, 1);
     608            offset++;
     609            System.arraycopy(_message, offset, target, targetOffset, sz);
     610            if (_log.shouldLog(Log.WARN))
     611                _log.warn("read challenge data: " + Base64.encode(target));
     612        }
     613        public void readAliceIntroKey(byte target[], int targetOffset) {
     614            int offset = readBodyOffset() + 4;
     615            offset += DataHelper.fromLong(_message, offset, 1);
     616            offset++;
     617            offset += 2;
     618            int sz = (int)DataHelper.fromLong(_message, offset, 1);
     619            offset++;
     620            offset += sz;
     621            System.arraycopy(_message, offset, target, targetOffset, SessionKey.KEYSIZE_BYTES);
     622            if (_log.shouldLog(Log.WARN))
     623                _log.warn("read alice intro key: " + Base64.encode(target, targetOffset, SessionKey.KEYSIZE_BYTES)
     624                          + " packet size: " + _payloadLength + " off: " + offset + " data: " + Base64.encode(_message));
     625        }
     626        public long readNonce() {
     627            int offset = readBodyOffset() + 4;
     628            offset += DataHelper.fromLong(_message, offset, 1);
     629            offset++;
     630            offset += 2;
     631            int sz = (int)DataHelper.fromLong(_message, offset, 1);
     632            offset++;
     633            offset += sz;
     634            offset += SessionKey.KEYSIZE_BYTES;
     635            long rv = DataHelper.fromLong(_message, offset, 4);
     636            if (_log.shouldLog(Log.WARN))
     637                _log.warn("read request nonce: " + rv);
     638            return rv;
     639        }
     640    }
     641   
     642    /** Help read the RelayIntro payload */
     643    public class RelayIntroReader {
     644        public int readIPSize() {
     645            int offset = readBodyOffset();
     646            return (int)DataHelper.fromLong(_message, offset, 1);
     647        }
     648       
     649        /** what IP Alice is reachable on */
     650        public void readIP(byte target[], int targetOffset) {
     651            int offset = readBodyOffset();
     652            int size = (int)DataHelper.fromLong(_message, offset, 1);
     653            offset++;
     654            System.arraycopy(_message, offset, target, targetOffset, size);
     655        }
     656        public int readPort() {
     657            int offset = readBodyOffset();
     658            offset += DataHelper.fromLong(_message, offset, 1);
     659            offset++;
     660            return (int)DataHelper.fromLong(_message, offset, 2);
     661        }
     662        public int readChallengeSize() {
     663            int offset = readBodyOffset();
     664            offset += DataHelper.fromLong(_message, offset, 1);
     665            offset++;
     666            offset += 2;
     667            return (int)DataHelper.fromLong(_message, offset, 1);
     668        }
     669        public void readChallengeSize(byte target[], int targetOffset) {
     670            int offset = readBodyOffset();
     671            offset += DataHelper.fromLong(_message, offset, 1);
     672            offset++;
     673            offset += 2;
     674            int sz = (int)DataHelper.fromLong(_message, offset, 1);
     675            offset++;
     676            System.arraycopy(_message, offset, target, targetOffset, sz);
     677        }
     678    }
     679   
     680   
     681    /** Help read the RelayResponse payload */
     682    public class RelayResponseReader {
     683        public int readCharlieIPSize() {
     684            int offset = readBodyOffset();
     685            return (int)DataHelper.fromLong(_message, offset, 1);
     686        }
     687        /** what IP charlie is reachable on */
     688        public void readCharlieIP(byte target[], int targetOffset) {
     689            int offset = readBodyOffset();
     690            int size = (int)DataHelper.fromLong(_message, offset, 1);
     691            offset++;
     692            System.arraycopy(_message, offset, target, targetOffset, size);
     693        }
     694        /** what port charlie is reachable on */
     695        public int readCharliePort() {
     696            int offset = readBodyOffset();
     697            offset += DataHelper.fromLong(_message, offset, 1);
     698            offset++;
     699            return (int)DataHelper.fromLong(_message, offset, 2);
     700        }
     701       
     702        public int readAliceIPSize() {
     703            int offset = readBodyOffset();
     704            offset += DataHelper.fromLong(_message, offset, 1);
     705            offset++;
     706            offset += 2;
     707            return (int)DataHelper.fromLong(_message, offset, 1);
     708        }
     709        public void readAliceIP(byte target[], int targetOffset) {
     710            int offset = readBodyOffset();
     711            offset += DataHelper.fromLong(_message, offset, 1);
     712            offset++;
     713            offset += 2;
     714            int sz = (int)DataHelper.fromLong(_message, offset, 1);
     715            offset++;
     716            System.arraycopy(_message, offset, target, targetOffset, sz);
     717        }
     718        public int readAlicePort() {
     719            int offset = readBodyOffset();
     720            offset += DataHelper.fromLong(_message, offset, 1);
     721            offset++;
     722            offset += 2;
     723            int sz = (int)DataHelper.fromLong(_message, offset, 1);
     724            offset++;
     725            offset += sz;
     726            return (int)DataHelper.fromLong(_message, offset, 2);
     727        }
     728        public long readNonce() {
     729            int offset = readBodyOffset();
     730            offset += DataHelper.fromLong(_message, offset, 1);
     731            offset++;
     732            offset += 2;
     733            int sz = (int)DataHelper.fromLong(_message, offset, 1);
     734            offset++;
     735            offset += sz;
     736            offset += 2;
     737            return DataHelper.fromLong(_message, offset, 4);
     738        }
     739    }
     740   
     741   
     742    public static void main(String args[]) {
     743        I2PAppContext ctx = I2PAppContext.getGlobalContext();
     744        try {
     745            PacketBuilder b = new PacketBuilder(ctx);
     746            InetAddress introHost = InetAddress.getLocalHost();
     747            int introPort = 1234;
     748            byte introKey[] = new byte[SessionKey.KEYSIZE_BYTES];
     749            ctx.random().nextBytes(introKey);
     750            long introTag = ctx.random().nextLong(0xFFFFFFFFl);
     751            long introNonce = ctx.random().nextLong(0xFFFFFFFFl);
     752            SessionKey ourIntroKey = ctx.keyGenerator().generateSessionKey();
     753            UDPPacket packet = b.buildRelayRequest(introHost, introPort, introKey, introTag, ourIntroKey, introNonce, false);
     754            UDPPacketReader r = new UDPPacketReader(ctx);
     755            r.initialize(packet);
     756            RelayRequestReader reader = r.getRelayRequestReader();
     757            System.out.println("Nonce: " + reader.readNonce() + " / " + introNonce);
     758            System.out.println("Tag  : " + reader.readTag() + " / " + introTag);
     759            byte readKey[] = new byte[SessionKey.KEYSIZE_BYTES];
     760            reader.readAliceIntroKey(readKey, 0);
     761            System.out.println("Key  : " + Base64.encode(readKey) + " / " + ourIntroKey.toBase64());
     762        } catch (Exception e) {
     763            e.printStackTrace();
     764        }
     765       
     766    }
    541767}
  • router/java/src/net/i2p/router/transport/udp/UDPReceiver.java

    rb5d571c r44770b7  
    4343        _context.statManager().createRateStat("udp.droppedInboundProbabalistically", "How many packet we drop probabalistically (to simulate failures)", "udp", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
    4444        _context.statManager().createRateStat("udp.acceptedInboundProbabalistically", "How many packet we accept probabalistically (to simulate failures)", "udp", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
     45        _context.statManager().createRateStat("udp.receiveHolePunch", "How often we receive a NAT hole punch", "udp", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
    4546    }
    4647   
     
    213214                        while (req.getPendingInboundRequested() > 0)
    214215                            req.waitForNextAllocation();
    215                     }
    216                    
    217                     int queued = receive(packet);
    218                     _context.statManager().addRateData("udp.receivePacketSize", size, queued);
     216                       
     217                        int queued = receive(packet);
     218                        _context.statManager().addRateData("udp.receivePacketSize", size, queued);
     219                    } else {
     220                        _context.statManager().addRateData("udp.receiveHolePunch", 1, 0);
     221                        // nat hole punch packets are 0 bytes
     222                        if (_log.shouldLog(Log.INFO))
     223                            _log.info("Received a 0 byte udp packet from " + packet.getPacket().getAddress() + ":" + packet.getPacket().getPort());
     224                    }
    219225                } catch (IOException ioe) {
    220226                    if (_socketChanged) {
  • router/java/src/net/i2p/router/transport/udp/UDPTransport.java

    rb5d571c r44770b7  
    4141    /** RemoteHostId to PeerState */
    4242    private Map _peersByRemoteHost;
    43     /** Relay tag (base64 String) to PeerState */
    44     private Map _peersByRelayTag;
    4543    /**
    4644     * Array of list of PeerState instances, where each list contains peers with one
     
    5856    private UDPFlooder _flooder;
    5957    private PeerTestManager _testManager;
     58    private IntroductionManager _introManager;
    6059    private ExpirePeerEvent _expireEvent;
    6160    private PeerTestEvent _testEvent;
    6261    private short _reachabilityStatus;
    6362    private long _reachabilityStatusLastUpdated;
    64    
    65     /** list of RelayPeer objects for people who will relay to us */
    66     private List _relayPeers;
    67 
     63    private long _introducersSelectedOn;
     64   
    6865    /** summary info to distribute */
    6966    private RouterAddress _externalAddress;
     
    123120        _peersByIdent = new HashMap(128);
    124121        _peersByRemoteHost = new HashMap(128);
    125         _peersByRelayTag = new HashMap(128);
    126122        _peersByCapacity = new ArrayList['Z'-'A'+1];
    127123        for (int i = 0; i < _peersByCapacity.length; i++)
     
    132128        _outboundMessages = mq;
    133129        _activeThrottle = mq;
    134         _relayPeers = new ArrayList(1);
    135130
    136131        _fastBid = new SharedBid(50);
     
    144139        _testEvent = new PeerTestEvent();
    145140        _reachabilityStatus = CommSystemFacade.STATUS_UNKNOWN;
     141        _introManager = new IntroductionManager(_context, this);
     142        _introducersSelectedOn = -1;
    146143       
    147144        _context.statManager().createRateStat("udp.droppedPeer", "How long ago did we receive from a dropped peer (duration == session lifetime", "udp", new long[] { 60*60*1000, 24*60*60*1000 });
     
    173170        if (_flooder != null)
    174171            _flooder.shutdown();
     172        _introManager.reset();
    175173       
    176174        _introKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
     
    226224       
    227225        if (_handler == null)
    228             _handler = new PacketHandler(_context, this, _endpoint, _establisher, _inboundFragments, _testManager);
     226            _handler = new PacketHandler(_context, this, _endpoint, _establisher, _inboundFragments, _testManager, _introManager);
    229227       
    230228        if (_refiller == null)
     
    366364     * offering to introduce anyone with that tag.
    367365     */
    368     public PeerState getPeerState(String relayTag) {
    369         synchronized (_peersByRelayTag) {
    370             return (PeerState)_peersByRelayTag.get(relayTag);
    371         }
     366    public PeerState getPeerState(long relayTag) {
     367        return _introManager.get(relayTag);
    372368    }
    373369   
     
    487483        }
    488484       
    489         if ( (oldPeer != null) && (_log.shouldLog(Log.WARN)) )
    490             _log.warn("Peer already connected: old=" + oldPeer + " new=" + peer, new Exception("dup"));
    491485        oldPeer = null;
    492486       
     
    514508        _expireEvent.add(peer);
    515509       
     510        _introManager.add(peer);
     511       
    516512        if (oldEstablishedOn > 0)
    517513            _context.statManager().addRateData("udp.alreadyConnected", oldEstablishedOn, 0);
     514       
     515        // if we need introducers, try to shift 'em around every 10 minutes
     516        if (introducersRequired() && (_introducersSelectedOn < _context.clock().now() - 10*60*1000))
     517            rebuildExternalAddress();
    518518        return true;
     519    }
     520   
     521    public RouterAddress getCurrentAddress() {
     522        // if we need introducers, try to shift 'em around every 10 minutes
     523        if (introducersRequired() && (_introducersSelectedOn < _context.clock().now() - 10*60*1000))
     524            rebuildExternalAddress(false);
     525        return super.getCurrentAddress();
    519526    }
    520527   
     
    563570        }
    564571       
     572        _introManager.remove(peer);
     573        // a bit overzealous - perhaps we should only rebuild the external if the peer being dropped
     574        // is one of our introducers?
     575        rebuildExternalAddress();
     576       
    565577        if (peer.getRemotePeer() != null) {
    566578            dropPeerCapacities(peer);
     
    702714        _externalListenHost = InetAddress.getByAddress(addr);
    703715    }
    704     void addRelayPeer(String host, int port, byte tag[], SessionKey relayIntroKey) {
    705         if ( (_externalListenPort > 0) && (_externalListenHost != null) )
    706             return; // no need for relay peers, as we are reachable
    707        
    708         RelayPeer peer = new RelayPeer(host, port, tag, relayIntroKey);
    709         synchronized (_relayPeers) {
    710             _relayPeers.add(peer);
    711         }
    712     }
    713716
    714717    private boolean explicitAddressSpecified() {
     
    716719    }
    717720   
    718     void rebuildExternalAddress() {
     721    void rebuildExternalAddress() { rebuildExternalAddress(true); }
     722    void rebuildExternalAddress(boolean allowRebuildRouterInfo) {
    719723        // if the external port is specified, we want to use that to bind to even
    720724        // if we don't know the external host.
     
    737741        }
    738742           
    739         Properties options = new Properties();
     743        Properties options = new Properties();
     744        boolean introducersRequired = introducersRequired();
     745        if (introducersRequired) {
     746            List peers = new ArrayList(PUBLIC_RELAY_COUNT);
     747            int found = 0;
     748            _introManager.pickInbound(peers, PUBLIC_RELAY_COUNT);
     749            if (_log.shouldLog(Log.INFO))
     750                _log.info("Introducers required, picked peers: " + peers);
     751            for (int i = 0; i < peers.size(); i++) {
     752                PeerState peer = (PeerState)peers.get(i);
     753                RouterInfo ri = _context.netDb().lookupRouterInfoLocally(peer.getRemotePeer());
     754                if (ri == null) {
     755                    if (_log.shouldLog(Log.INFO))
     756                        _log.info("Picked peer has no local routerInfo: " + peer);
     757                    continue;
     758                }
     759                RouterAddress ra = ri.getTargetAddress(STYLE);
     760                if (ra == null) {
     761                    if (_log.shouldLog(Log.INFO))
     762                        _log.info("Picked peer has no SSU address: " + ri);
     763                    continue;
     764                }
     765                UDPAddress ura = new UDPAddress(ra);
     766                options.setProperty(UDPAddress.PROP_INTRO_HOST_PREFIX + i, peer.getRemoteHostId().toHostString());
     767                options.setProperty(UDPAddress.PROP_INTRO_PORT_PREFIX + i, String.valueOf(peer.getRemotePort()));
     768                options.setProperty(UDPAddress.PROP_INTRO_KEY_PREFIX + i, Base64.encode(ura.getIntroKey()));
     769                options.setProperty(UDPAddress.PROP_INTRO_TAG_PREFIX + i, String.valueOf(peer.getTheyRelayToUsAs()));
     770                found++;
     771            }
     772            if (found > 0) {
     773                if (_log.shouldLog(Log.INFO))
     774                    _log.info("Picked peers: " + found);
     775                _introducersSelectedOn = _context.clock().now();
     776            }
     777        }
    740778        if ( (_externalListenPort > 0) && (_externalListenHost != null) ) {
    741779            options.setProperty(UDPAddress.PROP_PORT, String.valueOf(_externalListenPort));
    742780            options.setProperty(UDPAddress.PROP_HOST, _externalListenHost.getHostAddress());
    743781            // if we have explicit external addresses, they had better be reachable
    744             options.setProperty(UDPAddress.PROP_CAPACITY, ""+UDPAddress.CAPACITY_TESTING);
    745         } else {
    746             // grab 3 relays randomly
    747             synchronized (_relayPeers) {
    748                 Collections.shuffle(_relayPeers);
    749                 int numPeers = PUBLIC_RELAY_COUNT;
    750                 if (numPeers > _relayPeers.size())
    751                     numPeers = _relayPeers.size();
    752                 for (int i = 0; i < numPeers; i++) {
    753                     RelayPeer peer = (RelayPeer)_relayPeers.get(i);
    754                     options.setProperty("relay." + i + ".host", peer.getHost());
    755                     options.setProperty("relay." + i + ".port", String.valueOf(peer.getPort()));
    756                     options.setProperty("relay." + i + ".tag", Base64.encode(peer.getTag()));
    757                     options.setProperty("relay." + i + ".key", peer.getIntroKey().toBase64());
    758                 }
    759             }
    760             if (options.size() <= 0)
    761                 return;
     782            if (introducersRequired)
     783                options.setProperty(UDPAddress.PROP_CAPACITY, ""+UDPAddress.CAPACITY_TESTING);
     784            else
     785                options.setProperty(UDPAddress.PROP_CAPACITY, ""+UDPAddress.CAPACITY_TESTING + UDPAddress.CAPACITY_INTRODUCER);
    762786        }
    763787        options.setProperty(UDPAddress.PROP_INTRO_KEY, _introKey.toBase64());
     
    769793        addr.setOptions(options);
    770794       
     795        boolean wantsRebuild = false;
     796        if ( (_externalAddress == null) || !(_externalAddress.equals(addr)) )
     797            wantsRebuild = true;
    771798        _externalAddress = addr;
    772799        if (_log.shouldLog(Log.INFO))
    773800            _log.info("Address rebuilt: " + addr);
    774801        replaceAddress(addr);
     802        if (allowRebuildRouterInfo)
     803            _context.router().rebuildRouterInfo();
     804    }
     805   
     806    public static final String PROP_FORCE_INTRODUCERS = "i2np.udp.forceIntroducers";
     807    public boolean introducersRequired() {
     808        String forceIntroducers = _context.getProperty(PROP_FORCE_INTRODUCERS);
     809        if ( (forceIntroducers != null) && (Boolean.valueOf(forceIntroducers).booleanValue()) )
     810            return true;
     811        switch (getReachabilityStatus()) {
     812            case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
     813            case CommSystemFacade.STATUS_DIFFERENT:
     814                return true;
     815            default:
     816                return false;
     817        }
    775818    }
    776819   
     
    870913           
    871914            String name = peer.getRemotePeer().toBase64().substring(0,6);
    872             buf.append("<td valign=\"top\" nowrap><code>");
     915            buf.append("<td valign=\"top\" nowrap=\"nowrap\"><code>");
    873916            buf.append("<a href=\"netdb.jsp#");
    874917            buf.append(name);
     
    898941            buf.append(port);
    899942            buf.append("</a>");
     943            if (peer.getWeRelayToThemAs() > 0)
     944                buf.append("&gt;");
     945            else
     946                buf.append("&nbsp;");
     947            if (peer.getTheyRelayToUsAs() > 0)
     948                buf.append("&lt;");
     949            else
     950                buf.append("&nbsp;");
     951           
    900952            boolean appended = false;
    901953            if (_activeThrottle.isChoked(peer.getRemotePeer())) {
     
    11321184        }
    11331185    }
    1134     public short getReachabilityStatus() { return _reachabilityStatus; }
     1186    private static final String PROP_REACHABILITY_STATUS_OVERRIDE = "i2np.udp.status";
     1187    public short getReachabilityStatus() {
     1188        String override = _context.getProperty(PROP_REACHABILITY_STATUS_OVERRIDE);
     1189        if (override == null)
     1190            return _reachabilityStatus;
     1191           
     1192        if ("ok".equals(override))
     1193            return CommSystemFacade.STATUS_OK;
     1194        else if ("err-reject".equals(override))
     1195            return CommSystemFacade.STATUS_REJECT_UNSOLICITED;
     1196        else if ("err-different".equals(override))
     1197            return CommSystemFacade.STATUS_DIFFERENT;
     1198       
     1199        return _reachabilityStatus;
     1200    }
    11351201    public void recheckReachability() {
    11361202        _testEvent.runTest();
Note: See TracChangeset for help on using the changeset viewer.