protected void removeRequest(PeerNATTraversal request, int outcome) { synchronized (initiators) { LinkedList requests = (LinkedList) initiators.get(request.getInitiator()); if (requests != null) { requests.remove(request); } pending_requests.remove(request); if (active_requests.remove(request)) { usage_average.addValue(request.getTimeUsed()); if (outcome == OUTCOME_SUCCESS) { success_count++; } else { InetSocketAddress target = request.getTarget(); negative_result_bloom.add(target.toString().getBytes()); if (outcome == OUTCOME_FAILED_NO_REND) { failed_no_rendezvous++; } } } } }
public class PeerNATTraverser implements NATTraversalHandler { private static final LogIDs LOGID = LogIDs.PEER; private static final int OUTCOME_SUCCESS = 0; private static final int OUTCOME_FAILED_NO_REND = 1; private static final int OUTCOME_FAILED_OTHER = 2; private static PeerNATTraverser singleton; public static void initialise(AzureusCore core) { singleton = new PeerNATTraverser(core); } public static PeerNATTraverser getSingleton() { return (singleton); } private static int MAX_ACTIVE_REQUESTS; static { COConfigurationManager.addAndFireParameterListener( "peer.nat.traversal.request.conc.max", new ParameterListener() { public void parameterChanged(String name) { MAX_ACTIVE_REQUESTS = COConfigurationManager.getIntParameter(name); } }); } private static final int TIMER_PERIOD = 10 * 1000; private static final int USAGE_PERIOD = TIMER_PERIOD; private static final int USAGE_DURATION_SECS = 60; private static final int MAX_USAGE_PER_MIN = MAX_ACTIVE_REQUESTS * 5 * 1000; private static final int STATS_TICK_COUNT = 120 * 1000 / TIMER_PERIOD; private NATTraverser nat_traverser; private Map initiators = new HashMap(); private LinkedList pending_requests = new LinkedList(); private List active_requests = new ArrayList(); private Average usage_average = Average.getInstance(USAGE_PERIOD, USAGE_DURATION_SECS); private int attempted_count = 0; private int success_count = 0; private int failed_no_rendezvous = 0; private int failed_negative_bloom = 0; private BloomFilter negative_result_bloom = BloomFilterFactory.createAddOnly(BLOOM_SIZE); private static final int BLOOM_SIZE = MAX_ACTIVE_REQUESTS * 1024; private static final int BLOOM_REBUILD_PERIOD = 5 * 60 * 1000; private static final int BLOOM_REBUILD_TICKS = BLOOM_REBUILD_PERIOD / TIMER_PERIOD; private PeerNATTraverser(AzureusCore core) { nat_traverser = core.getNATTraverser(); nat_traverser.registerHandler(this); SimpleTimer.addPeriodicEvent( "PeerNAT:stats", TIMER_PERIOD, new TimerEventPerformer() { private int ticks; public void perform(TimerEvent event) { ticks++; List to_run = null; synchronized (initiators) { if (ticks % BLOOM_REBUILD_TICKS == 0) { int size = negative_result_bloom.getEntryCount(); if (Logger.isEnabled()) { if (size > 0) { Logger.log( new LogEvent(LOGID, "PeerNATTraverser: negative bloom size = " + size)); } } negative_result_bloom = BloomFilterFactory.createAddOnly(BLOOM_SIZE); } if (ticks % STATS_TICK_COUNT == 0) { String msg = "NAT traversal stats: active=" + active_requests.size() + ",pending=" + pending_requests.size() + ",attempted=" + attempted_count + ",no rendezvous=" + failed_no_rendezvous + ",negative bloom=" + failed_negative_bloom + ",successful=" + success_count; // System.out.println( msg ); if (Logger.isEnabled()) { Logger.log(new LogEvent(LOGID, msg)); } } int used = 0; for (int i = 0; i < active_requests.size(); i++) { used += ((PeerNATTraversal) active_requests.get(i)).getTimeUsed(); } usage_average.addValue(used); int usage = (int) usage_average.getAverage(); if (usage > MAX_USAGE_PER_MIN) { return; } // System.out.println( "usage = " + usage ); while (true) { if (pending_requests.size() == 0 || active_requests.size() >= MAX_ACTIVE_REQUESTS) { break; } // TODO: prioritisation based on initiator connections etc? PeerNATTraversal traversal = (PeerNATTraversal) pending_requests.removeFirst(); active_requests.add(traversal); if (to_run == null) { to_run = new ArrayList(); } to_run.add(traversal); attempted_count++; } } if (to_run != null) { for (int i = 0; i < to_run.size(); i++) { PeerNATTraversal traversal = (PeerNATTraversal) to_run.get(i); boolean bad = false; synchronized (initiators) { if (negative_result_bloom.contains(traversal.getTarget().toString().getBytes())) { bad = true; failed_negative_bloom++; } } if (bad) { removeRequest(traversal, OUTCOME_FAILED_OTHER); traversal.getAdapter().failed(); } else { traversal.run(); } } } } }); } public int getType() { return (NATTraverser.TRAVERSE_REASON_PEER_DATA); } public String getName() { return ("Peer Traversal"); } public void register(PeerNATInitiator initiator) { synchronized (initiators) { if (initiators.put(initiator, new LinkedList()) != null) { Debug.out("initiator already present"); } } } public void unregister(PeerNATInitiator initiator) { List to_cancel; synchronized (initiators) { LinkedList requests = (LinkedList) initiators.remove(initiator); if (requests == null) { Debug.out("initiator not present"); return; } else { to_cancel = requests; } } Iterator it = to_cancel.iterator(); while (it.hasNext()) { PeerNATTraversal traversal = (PeerNATTraversal) it.next(); traversal.cancel(); } } public void create( PeerNATInitiator initiator, InetSocketAddress target, PeerNATTraversalAdapter adapter) { boolean bad = false; synchronized (initiators) { if (negative_result_bloom.contains(target.toString().getBytes())) { bad = true; failed_negative_bloom++; } else { LinkedList requests = (LinkedList) initiators.get(initiator); if (requests == null) { // we get here when download stopped at same time // Debug.out( "initiator not found" ); bad = true; } else { PeerNATTraversal traversal = new PeerNATTraversal(initiator, target, adapter); requests.addLast(traversal); pending_requests.addLast(traversal); if (Logger.isEnabled()) { Logger.log( new LogEvent( LOGID, "created NAT traversal for " + initiator.getDisplayName() + "/" + target)); } } } } if (bad) { adapter.failed(); } } public List getTraversals(PeerNATInitiator initiator) { List result = new ArrayList(); synchronized (initiators) { LinkedList requests = (LinkedList) initiators.get(initiator); if (requests != null) { Iterator it = requests.iterator(); while (it.hasNext()) { PeerNATTraversal x = (PeerNATTraversal) it.next(); result.add(x.getTarget()); } } } return (result); } protected void removeRequest(PeerNATTraversal request, int outcome) { synchronized (initiators) { LinkedList requests = (LinkedList) initiators.get(request.getInitiator()); if (requests != null) { requests.remove(request); } pending_requests.remove(request); if (active_requests.remove(request)) { usage_average.addValue(request.getTimeUsed()); if (outcome == OUTCOME_SUCCESS) { success_count++; } else { InetSocketAddress target = request.getTarget(); negative_result_bloom.add(target.toString().getBytes()); if (outcome == OUTCOME_FAILED_NO_REND) { failed_no_rendezvous++; } } } } } public Map process(InetSocketAddress originator, Map data) { // System.out.println( "PeerNAT: received traversal from " + originator ); return (null); } protected class PeerNATTraversal implements NATTraversalObserver { private PeerNATInitiator initiator; private InetSocketAddress target; private PeerNATTraversalAdapter adapter; private NATTraversal traversal; private boolean cancelled; private long time; protected PeerNATTraversal( PeerNATInitiator _initiator, InetSocketAddress _target, PeerNATTraversalAdapter _adapter) { initiator = _initiator; target = _target; adapter = _adapter; } protected PeerNATInitiator getInitiator() { return (initiator); } protected InetSocketAddress getTarget() { return (target); } protected PeerNATTraversalAdapter getAdapter() { return (adapter); } protected long getTimeUsed() { long now = SystemTime.getCurrentTime(); long elapsed = now - time; time = now; if (elapsed < 0) { elapsed = 0; } else { // sanity check elapsed = Math.min(elapsed, TIMER_PERIOD); } return (elapsed); } protected void run() { synchronized (this) { if (!cancelled) { time = SystemTime.getCurrentTime(); traversal = nat_traverser.attemptTraversal(PeerNATTraverser.this, target, null, false, this); } } } public void succeeded(InetSocketAddress rendezvous, InetSocketAddress target, Map reply) { removeRequest(this, OUTCOME_SUCCESS); if (Logger.isEnabled()) { Logger.log( new LogEvent( LOGID, "NAT traversal for " + initiator.getDisplayName() + "/" + target + " succeeded")); } adapter.success(target); } public void failed(int reason) { removeRequest( this, reason == NATTraversalObserver.FT_NO_RENDEZVOUS ? OUTCOME_FAILED_NO_REND : OUTCOME_FAILED_OTHER); adapter.failed(); } public void failed(Throwable cause) { removeRequest(this, OUTCOME_FAILED_OTHER); adapter.failed(); } public void disabled() { removeRequest(this, OUTCOME_FAILED_OTHER); adapter.failed(); } protected void cancel() { NATTraversal active_traversal; synchronized (this) { cancelled = true; active_traversal = traversal; } if (active_traversal == null) { removeRequest(this, OUTCOME_FAILED_OTHER); } else { active_traversal.cancel(); } adapter.failed(); } } }