package SSF.Net; import java.util.*; import SSF.Util.Random.*; import SSF.Net.Util.*; import SSF.OS.*; import com.renesys.raceway.DML.*; import com.renesys.raceway.SSF.*; /** Top-level modeling class for a network simulation. One Net serves * as the initializer for the entire simulation; it contains hosts, * routers (which are hosts), and links. Net also serves as the * central resource for clock granularity information and * translation among NHI, CIDR, and IP addressing schemes.

* Net may be run from the command line; use the -help flag to * learn the syntax. */ public class Net extends Entity implements Configurable { //----------------------------------------------------- CLOCK MANAGEMENT /** Static clock frequency that all simulation components should use for * reference, for consistency. One tick of the logical clock equals * 1/F seconds of simulated time. By default, frequency==10e6; this * yields microsecond simulation resolution. To avoid clock underflow, * this frequency must be set at least as high as the bit rate on the * fastest configured network interface card (NIC). */ public static long frequency = 1000000; /** Static convenience function for computing number of clock ticks * in a given number of seconds; simply returns s*frequency. For * example, if Net.frequency is 1000 (millisecond resolution), then * Net.seconds(0.25) returns 250. */ public static long seconds(double s) { return (long)(s*frequency); } //----------------------------------------------------- ADDRESS TRANSLATION /** Three-way translation table for NHI addresses, CIDR block addresses, * and IP addresses. */ public cidrBlock cidrMap; /** Return the IP address corresponding to the given global NHI address */ public String nhi_to_ip(String naddr) { return cidrMap.nhi_to_ip(naddr); } /** Return the global NHI address corresponding to the given IP address */ public String ip_to_nhi(String ipaddr) { int slash = ipaddr.indexOf("/"); int prefix_len = (slash<0?32:new Integer(ipaddr.substring(slash+1)).intValue()); return cidrMap.ip_to_nhi(IP_s.StrToInt(ipaddr),prefix_len); } /** Return the CIDR block address corresponding to the given global * NHI address. */ public String nhi_to_cidr(String naddr) { return cidrMap.nhi_to_cidr(naddr); } //---------------------------------------------------- COMMAND-LINE DRIVER /** Dump a summary of command-line syntax to standard error. */ public static void syntax() { System.err.println("Syntax: Net [-check]"); System.err.println(" [-dump] "); System.err.println(" [-relax] "); System.err.println(" [-ip a.b.c.d/m] "); System.err.println(" maxtime dmlfile1 [ dmlfile2 [...dmlfileN]]]"); System.err.println(); System.err.println(" -check: schema-check the input DML file(s) first"); System.err.println(" -dump: dump NHI/CIDR/IP address tables, but don't actually run the model"); System.err.println(" -relax: fail to enforce strict rules about CIDR/IP attributes"); System.err.println(" -ip addr: allocate IP addresses from the given block (default: 0.0.0.0/0)"); System.exit(-1); } public static Net TOP = new Net(); /** * Command line driver for the Net class.

*

 *   Syntax: Net [-check]
 *               [-dump] 
 *               [-relax] 
 *               [-ip a.b.c.d/m] 
 *               maxtime dmlfile1 [ dmlfile2 [...dmlfileN]]
 *
 *    -check: schema-check the input DML file(s) first
 *    -dump: dump NHI/CIDR/IP address tables, but don't actually run the model
 *    -relax: fail to enforce strict rules about CIDR/IP attributes
 *    -ip addr: allocate IP addresses from the given block (default: 0.0.0.0/0)
 *
*/ public static void main(String[] argv) { long totaltime = System.currentTimeMillis(); //Net N = new Net(); Net N = TOP; long runTime =0; dmlConfig netconfig = null; boolean doSchemaCheck = false; boolean doCompleteRun = true; String ipBase = "0.0.0.0/0"; // override with -ip argument int parg = 0; try { if (argv.length>1 && argv[0].indexOf("-h")!=1){ while (argv.length>parg) { if (argv[parg].equals("-relax")) { parg++; cidrBlock.strict = false; } else if (argv[parg].equals("-check")) { parg++; doSchemaCheck = true; } else if (argv[parg].equals("-dump")) { parg++; doCompleteRun = false; } else if (argv[parg].equals("-ip")) { if (argv.length==parg+1) syntax(); else ipBase = argv[parg+1]; parg += 2; } else if (argv[parg].indexOf("-")==0) syntax(); else break; } if (argv.length < parg+2) syntax(); // now argv[parg] is the logical duration, which we use later netconfig = new dmlConfig(argv[parg+1]); for (int n=parg+2; n * * REQUIRED ATTRIBUTES: none

* * OBSOLETE ATTRIBUTES:
*

* * OPTIONAL ATTRIBUTES:
*

* */ public void config(Configuration cfg) throws configException { // Tyan: long startTime = System.currentTimeMillis(); String hertz = (String)cfg.findSingle("frequency"); if (hertz == null) { System.err.println("Warning: no frequency specified for Net;"); System.err.println("\t\t Using default "+(long)(1.e9/frequency)+ "ns clock resolution"); } else { frequency = (new Long(hertz)).longValue(); System.err.println("** \t\t Using specified "+(double)(1.e9/frequency)+ "ns clock resolution"); } configRandomStreams((Configuration)cfg.findSingle("randomstream")); // Phase 0 -- check for obsolete attributes for (Enumeration elans = cfg.find("LAN"); elans.hasMoreElements();) throw new configException("Net.LAN attribute is obsolete; "+ "consult SSFNET 0.7 documentation"); // Phase I --- construct the table of routers and hosts System.err.println("\n\n--- Phase I: construct table of "+ "routers and hosts"); // Tyan: long phase1 = System.currentTimeMillis(); configHostsRouters(cfg); phase1 = System.currentTimeMillis() - phase1; // Phase II --- connect all point-to-point links System.err.println("\n\n--- Phase II: connect Point-To-Point links"); // Tyan: long phase2 = System.currentTimeMillis(); configLinks(cfg); phase2 = System.currentTimeMillis() - phase2; // Phase III --- add static routes through connected links System.err.println("\n\n--- Phase III: add static routes"); // Tyan: long phase3 = System.currentTimeMillis(); configStaticRoutes(); phase3 = System.currentTimeMillis() - phase3; // Tyan: long elapsed = System.currentTimeMillis()-startTime; System.err.println("## Net config: "+ hct+" routers and hosts"); // Tyan: String etime = (1.*phase1/1000)+""; if (etime.length()>7) etime = etime.substring(0,etime.indexOf(".")+3); System.err.println("## Elapsed time for phase 1: "+etime+" seconds"); // Tyan: etime = (1.*phase2/1000)+""; if (etime.length()>7) etime = etime.substring(0,etime.indexOf(".")+3); System.err.println("## Elapsed time for phase 2: "+etime+" seconds"); // Tyan: etime = (1.*phase3/1000)+""; if (etime.length()>7) etime = etime.substring(0,etime.indexOf(".")+3); System.err.println("## Elapsed time for phase 3: "+etime+" seconds"); // Tyan: etime = (1.*elapsed/1000)+""; if (etime.length()>7) etime = etime.substring(0,etime.indexOf(".")+3); System.err.println("## Elapsed time: "+etime+" seconds"); } //------------------------------------------------------ RANDOM DISTRIBUTIONS public static final int STREAM_PER_TIMELINE = 0x0; public static final int STREAM_PER_HOST = 0x1; public static final int STREAM_PER_PROTOCOL = 0x2; public static final int STREAM_PER_DISTRIBUTION = 0x3; protected static int sharingLevel; protected static String streamName; protected static String generatorName; public void configRandomStreams(Configuration cfg) throws configException { String replevel = null; if (cfg!=null) { generatorName = (String)cfg.findSingle("generator"); streamName = (String)cfg.findSingle("stream"); replevel = (String)cfg.findSingle("reproducibility_level"); } if (generatorName==null) generatorName = "MersenneTwister"; if (streamName==null) streamName = "DefaultStream"; if (replevel==null) replevel = "timeline"; if ("timeline".equals(replevel)) sharingLevel = STREAM_PER_TIMELINE; else if ("host".equals(replevel)) sharingLevel = STREAM_PER_HOST; else if ("protocol".equals(replevel)) sharingLevel = STREAM_PER_PROTOCOL; else if ("distribution".equals(replevel)) sharingLevel = STREAM_PER_DISTRIBUTION; else throw new configException("Bad randomstream.reproducibility_level: "+ replevel); } /** Returns a reference to a random distributrution that will be shared * by consumers depending the value of .Net.randomstream.reproducibility_level * Specification of the RandomDistribution is via a Configuration. */ public static synchronized RandomDistribution accessRandomDistribution(ProtocolSession forProtocol, Configuration distcfg, String requestName) { RandomDistribution return_dist = null; String use_stream_name = streamName; String request = requestName; // Extend the stream name with disambiguating information // if the current "sharing level" requires that distributions, // protocols, hosts, or timelines receive distinct streams. switch(sharingLevel) { // user-provided distribution instance identifier case STREAM_PER_DISTRIBUTION: use_stream_name = use_stream_name + "/" + request; // fall through to subsequent case // class name case STREAM_PER_PROTOCOL: use_stream_name = use_stream_name + "/" + forProtocol.getClass().getName(); // fall through to subsequent case // host's unique NH address case STREAM_PER_HOST: use_stream_name = use_stream_name + "/" + ((SSF.Net.Host)forProtocol.inGraph()).nhi; break; // 3 cases above must not append timeline String, bugfix 11/5/00 ato // string of the form "{Scheduler 2}" case STREAM_PER_TIMELINE: use_stream_name = use_stream_name + "/" + forProtocol.inGraph().alignment().toString(); default: break; } try { RandomStream rng = RandomStream.getStream(forProtocol.inGraph(), generatorName, use_stream_name); return_dist = RandomStream.getDistribution(distcfg,rng); } catch (RandomStream.StreamException rngex) { rngex.printStackTrace(); System.exit(-1); } return return_dist; } /** Returns a reference to a random distributrution that will be shared * by consumers depending the value of .Net.randomstream.reproducibility_level * Specification of the RandomDistribution is by a name recognized by * class method RandomStream.getDistribution(); the returned handle * is for default values of the distribution's parameters. The usage * including user-provided distribution parameters may be, from within * an instance of a ProtocolSession ('this'): *

   *  import cern.jet.random.*;
   *  RandomDistribution rnd = Net.accessRandomDistribution(this, "Uniform", "foo");
   *  ((Uniform)rnd.getImplementation()).setState(0.,1.); // go under the covers
   *  for (int x=0; x<12000; x++) next_sample = rnd.nextDouble();  // etc.
   *  
*/ public static synchronized RandomDistribution accessRandomDistribution(ProtocolSession forProtocol, String distName, String requestName) { RandomDistribution return_dist = null; String use_stream_name = streamName; String request = requestName; // Extend the stream name with disambiguating information // if the current "sharing level" requires that distributions, // protocols, hosts, or timelines receive distinct streams. switch(sharingLevel) { // user-provided distribution instance identifier case STREAM_PER_DISTRIBUTION: use_stream_name = use_stream_name + "/" + request; // fall through to subsequent case // class name case STREAM_PER_PROTOCOL: use_stream_name = use_stream_name + "/" + forProtocol.getClass().getName(); // fall through to subsequent case // host's unique NH address case STREAM_PER_HOST: use_stream_name = use_stream_name + "/" + ((SSF.Net.Host)forProtocol.inGraph()).nhi; break; // 3 cases above must not append timeline String, bugfix 11/5/00 ato // string of the form "{Scheduler 2}" case STREAM_PER_TIMELINE: use_stream_name = use_stream_name + "/" + forProtocol.inGraph().alignment().toString(); default: break; } try { RandomStream rng = RandomStream.getStream(forProtocol.inGraph(), generatorName, use_stream_name); return_dist = RandomStream.getDistribution(distName,rng); } catch (RandomStream.StreamException rngex) { rngex.printStackTrace(); System.exit(-1); } return return_dist; } /** Returns a reference to a raw random number generator output specified * by the global attribute .Net.randomstream; that will be shared * by consumers depending the value of * .Net.randomstream.reproducibility_level. * If .Net.randomstream is not provided, the default is: *
   *  generatorName = "MersenneTwister" 
   *  stream = "DefaultStream"  // root for the initial seed values, via MD5
   *  reproducibility_level  = "timeline"
   *
   * usage:
   *
   *  RandomStream rng = Net.accessRandomStream(this, "foo");
   *  for (int x=0; x<1000; x++) next_sample = rng.nextDouble();
   *  
*/ public static synchronized RandomStream accessRandomStream(ProtocolSession forProtocol, String requestName) { RandomStream rng = null; String use_stream_name = streamName; String request = requestName; // Extend the stream name with disambiguating information // if the current "sharing level" requires that // protocols, hosts, or timelines receive distinct streams. switch(sharingLevel) { // append user-provided stream instance identifier case STREAM_PER_DISTRIBUTION: use_stream_name = use_stream_name + "/" + request; // fall through to subsequent case // append class name case STREAM_PER_PROTOCOL: use_stream_name = use_stream_name + "/" + forProtocol.getClass().getName(); // fall through to subsequent case // append host's unique NH address case STREAM_PER_HOST: use_stream_name = use_stream_name + "/" + ((SSF.Net.Host)forProtocol.inGraph()).nhi; break; // 3 cases above must not append timeline String, bugfix 11/5/00 ato // append string of the form "{Scheduler 2}" case STREAM_PER_TIMELINE: use_stream_name = use_stream_name + "/" + forProtocol.inGraph().alignment().toString(); break; default: break; } try { rng = RandomStream.getStream(forProtocol.inGraph(), generatorName, use_stream_name); } catch (RandomStream.StreamException rngex) { rngex.printStackTrace(); System.exit(-1); } return rng; } //------------------------------------------------------ HOST,ROUTER CONFIG /** Configure the hosts and routers within the network. */ private void configHostsRouters(Configuration cfg) throws configException { configHostsRouters(cfg,"",null); } private void configHostsRouters(Configuration cfg, String in_nhi, String default_timeline) throws configException { //System.err.println("Configuring Net "+newnhi+"..."); String overriding_timeline = (String)cfg.findSingle("alignment"); if (overriding_timeline!=null) default_timeline = overriding_timeline; for (Enumeration enets = cfg.find("Net"); enets.hasMoreElements();) { Configuration ncfg = (Configuration)enets.nextElement(); if (null != ncfg.findSingle("randomstream")) throw new configException ("Attribute Net.randomstream may only appear at top level"); idrange netids = new idrange(); netids.config(ncfg); for (int netid=netids.minid;netid<=netids.maxid;netid++) configHostsRouters(ncfg, (("".equals(in_nhi))? (""+netid): (in_nhi+cidrBlock.NHI_SEPARATOR+netid)), default_timeline); } for (Enumeration erouters = cfg.find("router"); erouters.hasMoreElements();) { Configuration rcfg = (Configuration)erouters.nextElement(); idrange ids = new idrange(); ids.config(rcfg); for (int id = ids.minid; id<=ids.maxid; id++) config_new_router_or_host(new Router(this,in_nhi,id), rcfg,default_timeline); } for (Enumeration ehosts = cfg.find("host"); ehosts.hasMoreElements();) { Configuration hcfg = (Configuration)ehosts.nextElement(); idrange ids = new idrange(); ids.config(hcfg); for (int id = ids.minid; id<=ids.maxid; id++) config_new_router_or_host(new Host(this,in_nhi,id), hcfg,default_timeline); } } private void config_new_router_or_host(Host H, Configuration hcfg,String default_timeline) throws configException { if (default_timeline!=null) configTimeline(H,default_timeline); else H.alignTo(this); // default alignment to containing Net H.config(hcfg); // may override alignment via "alignment" attribute hosts.put(H.nhi, H); hct++; } //------------------------------------------------------ TIMELINE CONFIG /** Helper function to let Hosts, Routers, Links set their alignments * consistently from their respective config files, which share * a common optional "alignment" attribute. The values associated * with that attribute are resolved globally, so that any two * Hosts, Routers etc. that specify the same integer or string * value will get the same alignment (and will therefore execute on * the same processor). Alignments may also be specified for Net * attributes, in which case all hosts, routers, and links within the Net * get that alignment by default.

* The default is to omit all "alignment" attributes, resulting * in serial (single timeline) execution.

*/ public static void configTimeline(Entity forEntity, Configuration cfg) { if (timelineMap == null) timelineMap = new Hashtable(); try { if (cfg.findSingle("timeline")!= null) throw new configException ("The 'timeline' attribute is obsolete; use 'alignment' instead"); Enumeration timelinespec = cfg.find("alignment"); if (timelinespec.hasMoreElements()) configTimeline(forEntity,(String)timelinespec.nextElement()); } catch (configException cfgex) { cfgex.printStackTrace(); // otherwise ignore it } } private static void configTimeline(Entity forEntity, String alignspec) { if (timelineMap == null) timelineMap = new Hashtable(); Entity alignmentTarget = (Entity)timelineMap.get(alignspec); if (alignmentTarget == null) { timelineMap.put(alignspec,forEntity); forEntity.makeIndependent(); } else forEntity.alignTo(alignmentTarget); } private static Hashtable timelineMap; //------------------------------------------------------ LINK CONFIG private void configLinks(Configuration cfg) throws configException { configLinks(cfg,"",null); } private void configLinks(Configuration cfg, String in_nhi, String default_timeline) throws configException { String overriding_timeline = (String)cfg.findSingle("alignment"); if (overriding_timeline!=null) default_timeline = overriding_timeline; for (Enumeration enets = cfg.find("Net"); enets.hasMoreElements();) { Configuration ncfg = (Configuration)enets.nextElement(); idrange netids = new idrange(); netids.config(ncfg); for (int netid=netids.minid;netid<=netids.maxid;netid++) configLinks(ncfg,(in_nhi.equals("")?""+netid:in_nhi+":"+netid), default_timeline); } for (Enumeration elinks = cfg.find("link"); elinks.hasMoreElements();) config_new_link(new link(this,in_nhi), (Configuration)elinks.nextElement(), default_timeline); } private void config_new_link(link L, Configuration lcfg, String default_timeline) throws configException { if (default_timeline!=null) configTimeline(L,default_timeline); else L.alignTo(this); // default alignment to containing Net L.config(lcfg); // may override alignment via "alignment" attribute } //------------------------------------------------------ STATIC ROUTE CONFIG private void configStaticRoutes() throws configException { for (Enumeration hset= hosts.elements(); hset.hasMoreElements();) { try { Host H = (Host)hset.nextElement(); IP IPsess = (IP)H.SessionForName("ip"); RadixTreeRoutingTable RT = (RadixTreeRoutingTable)IPsess.getRoutingTable(); RT.config(H.hostConfig); // add static routes via "route" attribute for (Enumeration iset = H.interfaceNumbers.elements(); iset.hasMoreElements();) { NIC N = (NIC)iset.nextElement(); linkLayer LL = N.link_hw; if (LL == null) { if (!N.isVirtual) { System.err.println("Warning: interface "+N.ID+" of "+H+ " has not been configured"); } } else { link L = LL.onLink(); if (L!=null) RT.add(L.ip,N,0,1); } } //System.out.println(H.nhi + " -----------------------------"); //RT.print(); } catch (ProtocolException pex) { // ignore if sending host does not implement IP } } } } /*= =*/ /*= Copyright (c) 1997--2000 SSF Research Network =*/ /*= =*/ /*= SSFNet is open source software, distributed under the GNU General =*/ /*= Public License. See the file COPYING in the 'doc' subdirectory of =*/ /*= the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html =*/ /*= =*/