package net.i2p.router; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.HashSet; import java.util.Properties; import java.util.Scanner; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; import net.i2p.data.router.RouterInfo; import net.i2p.router.Router; /** * Fire up multiple routers in the same VM, all with their own RouterContext * (and all that entails). In addition, this creates a root I2PAppContext for * any objects not booted through one of the RouterContexts. Each of these * contexts are configured through a simple properties file (where the name=value * contained in them are used for the context's getProperty(name)). <p> * * <b>Usage:</b><pre> * MultiRouter numberRouters * </pre> * * Each routerContext specified is used to boot up a single router. It is HIGHLY * recommended that those context files contain a few base env properties: <ul> * <li>loggerFilenameOverride=rN/logs/log-router-#.txt</li> * <li>router.configLocation=rN/router.config</li> * </ul> * (where "rN" is an instance number, such as r0 or r9). * Additionally, two other properties might be useful:<ul> * <li>i2p.vmCommSystem=true</li> * <li>i2p.encryption=off</li> * </ul> * The first line tells the router to use an in-VM comm system for sending * messages back and forth between routers (see net.i2p.transport.VMCommSystem), * and the second tells the router to stub out ElGamal, AES, and DSA code, reducing * the CPU load (but obviously making the router incapable of talking to things * that need the encryption enabled). To run a client app through a router that * has i2p.encryption=off, you should also add that line to the client's JVM * (for example, <code>java -Di2p.encryption=off -jar lib/i2ptunnel.jar</code>).<p> * * To make the router console work, either run from a directory containing * lib/, webapps/, docs/, etc., or point i2p.dir.base to a directory containing the * above. * * The multirouter waits until all of the routers are shut down (which none will * do on their own, so as before, you'll want to kill the proc or ^C it). */ public class MultiRouter { private static final int BASE_PORT = 5000; private static int nbrRouters; private static PrintStream _out; private static ArrayList<Router> _routers = new ArrayList<Router>(8); private static I2PAppContext _defaultContext; public static void main(String args[]) { if ( (args == null) || (args.length < 1) ) { usage(); return; } Scanner scan = new Scanner(args[0]); if (!scan.hasNextInt()) { usage(); scan.close(); return; } nbrRouters = scan.nextInt(); if (nbrRouters < 0) { usage(); scan.close(); return; } scan.close(); _out = System.out; buildClientProps(0); _defaultContext = new I2PAppContext(buildRouterProps(0)); _defaultContext.clock().setOffset(0); _out.println("RouterConsole for Router 0 is listening on: 127.0.0.1:" + (BASE_PORT-1)); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { _out.println("Shutting down in a few moments.."); for(Router r : _routers) { r.shutdown(0); } try { Thread.sleep(1500); } catch (InterruptedException ie) {} Runtime.getRuntime().halt(0); } }); for (int i = 0; i < nbrRouters; i++) { Router router = new Router(buildRouterProps(i)); router.setKillVMOnEnd(false); _routers.add(router); _out.println("Router " + i + " was created"); try { Thread.sleep(100); } catch (InterruptedException ie) {} } for (int i = 0; i < nbrRouters; i++) { final Router r = _routers.get(i); long offset = r.getContext().random().nextLong(Router.CLOCK_FUDGE_FACTOR/2); if (r.getContext().random().nextBoolean()) offset = 0 - offset; r.getContext().clock().setOffset(offset, true); /* Start the routers in separate threads since it takes some time. */ (new Thread() { public void run() { r.runRouter(); } }).start(); try { Thread.sleep(100); } catch (InterruptedException ie) {} _out.println("Router " + i + " was started with time offset " + offset); } _out.println("All routers have been started"); /* Wait for routers to start services and generate keys * before doing the internal reseed. */ int waitForRouters = (nbrRouters/10)*1000; _out.println("Waiting " + waitForRouters/1000 + " seconds for routers to start" + "before doing the internal reseed"); try { Thread.sleep(waitForRouters); } catch (InterruptedException ie) {} internalReseed(); waitForCompletion(); } private static void internalReseed() { HashSet<RouterInfo> riSet = new HashSet<RouterInfo>(); for(Router r : _routers) { riSet.addAll(r.getContext().netDb().getRouters()); } for(Router r : _routers) { for(RouterInfo ri : riSet){ r.getContext().netDb().publish(ri); } } _out.println(riSet.size() + " RouterInfos were reseeded"); } private static Properties buildRouterProps(int id) { Properties props = getRouterProps(id); File f = new File(props.getProperty("router.configLocation")); if (!f.exists()) { f.getParentFile().mkdirs(); try { DataHelper.storeProps(props, f); } catch (IOException e) { e.printStackTrace(); } } return props; } private static Properties getRouterProps(int id) { Properties props = new Properties(); props.setProperty("router.profileDir", "/peerProfiles"); props.setProperty("router.sessionKeys.location", "/sessionKeys.dat"); props.setProperty("router.info.location", "/router.info"); props.setProperty("router.keys.location", "/router.keys"); props.setProperty("router.networkDatabase.dbDir", "/netDb"); props.setProperty("router.tunnelPoolFile", "/tunnelPool.dat"); props.setProperty("router.keyBackupDir", "/keyBackup"); props.setProperty("router.clientConfigFile", getBaseDir(id) + "/clients.config"); props.setProperty("router.configLocation", getBaseDir(id) + "/router.config"); props.setProperty("router.pingFile", getBaseDir(id) + "/router.ping"); props.setProperty("router.rejectStartupTime", "0"); props.setProperty("router.reseedDisable", "true"); props.setProperty("i2p.dir.app", getBaseDir(id)); /* If MultiRouter is not run from a dir containing lib/, webapps/, docs/, etc. * point i2p.dir.base to a directory containing the above. */ //props.setProperty("i2p.dir.base", getBaseDir(id)); props.setProperty("i2p.dir.config", getBaseDir(id)); props.setProperty("i2p.dir.log", getBaseDir(id)); props.setProperty("i2p.dir.router", getBaseDir(id)); props.setProperty("i2p.dir.pid", getBaseDir(id)); //props.setProperty("i2p.vmCommSystem", "true"); props.setProperty("i2np.ntcp.hostname", "127.0.0.1"); props.setProperty("i2np.udp.host", "127.0.0.1"); props.setProperty("i2np.ntcp.port", BASE_PORT + id + ""); props.setProperty("i2np.udp.port", BASE_PORT + id + ""); props.setProperty("i2np.allowLocal", "true"); props.setProperty("i2np.udp.internalPort", BASE_PORT + id + ""); props.setProperty("i2cp.port", Integer.toString((BASE_PORT + nbrRouters + id))); return props; } private static Properties buildClientProps(int id) { Properties rProps = getRouterProps(id); Properties props = getClientProps(); File f = new File(rProps.getProperty("router.clientConfigFile")); if (!f.exists()) { f.getParentFile().mkdirs(); try { DataHelper.storeProps(props, f); } catch (IOException e) { e.printStackTrace(); } } return props; } private static Properties getClientProps() { Properties props = new Properties(); props.setProperty("clientApp.0.args", (BASE_PORT-1) + " 127.0.0.1 ./webapps"); props.setProperty("clientApp.0.main", "net.i2p.router.web.RouterConsoleRunner"); props.setProperty("clientApp.0.name", "webconsole"); props.setProperty("clientApp.0.onBoot", "true"); props.setProperty("clientApp.1.args", "i2ptunnel.config"); props.setProperty("clientApp.1.main", "net.i2p.i2ptunnel.TunnelControllerGroup"); props.setProperty("clientApp.1.name", "tunnels"); props.setProperty("clientApp.1.delay", "6"); return props; } private static String getBaseDir(int id) { File f = new File("."); return f.getAbsoluteFile().getParentFile().toString() + "/multirouter/"+ Integer.toString(id); } private static void waitForCompletion() { while (true) { int alive = 0; for (int i = 0; i < _routers.size(); i++) { Router r = _routers.get(i); if (!r.isAlive()) { _out.println("Router " + i + " is dead"); } else { alive++; } } if (alive > 0) { try { Thread.sleep(30*1000); } catch (InterruptedException ie) {} } else { break; } } _out.println("All routers shut down"); } private static void usage() { System.err.println("Usage: MultiRouter nbr_routers"); System.err.println(" Where nbr_routers > 0"); } }