3 Simulation Customization

Rob Jansen edited this page Nov 28, 2015 · 17 revisions

Shadow configuration

Shadow uses a standard XML format to accept configuration options from users, and uses GLib's XML parser to parse the simple structure. Examples of a Shadow configuration file, shadow.config.xml, are distributed with the release. The following describes valid XML elements and attributes that Shadow will accept from a shadow.config.xml file and that can be used to customize a simulation.

The kill element

<kill time="INTEGER" />

Required attribute: time

The time attribute represents the number of virtual seconds to simulate, after which the experiment will be killed and resources released.

The plugin element

<plugin id="STRING" path="STRING" />

Required attributes: id, path

The plugin element represents a library plug-in that Shadow should dynamically load. The id attribute identifies this plugin and must be a string that is unique among all id attributes for any element in the XML file.

The path attribute holds the system path to the plug-in *.so library. If path begins with ~/, the path will be considered relative to the current user's home directory.

The topology element

<topology path="STRING" />

Required attributes:
Optional attributes: path
Optional child element: <![CDATA[TEXT]]>

The topology element must hold either a collection of vertices and edges that represent a network topology as the TEXT in a <![CDATA[TEXT]]> style element, or a path to a file holding such data. The TEXT data should be in graphml format, and hold an undirected, complete, connected graph with certain attributes specified on the vertices and links. For more information on how to structure this data, see the Topology Format.

The node element

<node id="STRING" iphint="STRING" geocodehint="STRING" typehint="STRING" quantity="INTEGER" bandwidthdown="INTEGER" bandwidthup="INTEGER" interfacebuffer="INTEGER" socketrecvbuffer="INTEGER" socketsendbuffer="INTEGER" loglevel="STRING" heartbeatloglevel="STRING" heartbeatloginfo="STRING" heartbeatfrequency="INTEGER" cpufrequency="INTEGER" logpcap="STRING" pcapdir="STRING">
  <application ... />
  ...
</node>

Required attributes: id
Optional attributes: iphint, geocodehint, typehint, quantity, bandwidthdown, bandwidthup, interfacebuffer, socketrecvbuffer, socketsendbuffer, loglevel, heartbeatloglevel, heartbeatloginfo, heartbeatfrequency, cpufrequency, logpcap, pcapdir
Required child element: <application>

The node element represents a node or virtual host in the simulation. The id attribute identifies this node and must be a string that is unique among all id attributes for any element in the XML file. id will also be used as the network hostname of this node.

The iphint, geocodehint, and typehint attributes provide hints to Shadow's name and routing system on where to attach this host to the topology. These hints will be matched based on the values of the ip, geocode, and type of the vertices as specified in the topology file.

The quantity attribute specifies the number of hosts of this type to start. If quantity is greater than 1, each host's hostname will be prefixed with a counter. For example, a node with an id of host and quantity=2 would produce nodes with hostnames 1.host and 2.host.

bandwidthdown and bandwidthup optionally specify the downstream and upstream bandwidth capacities for this node, and override any default bandwidth values set in the cluster element corresponding to the cluster attribute. If not given, the default bandwidth values from the assigned cluster element are used.

interfacebuffer controls the size of the interface receive buffer that accepts packets from the network. socketrecvbuffer and socketsendbuffer control the initial size of the socket buffers that hold packets to and from the application. Note that these sizes may be adjusted by auto-tuning, in order fill the channel capacity as defined by the bandwidth-delay product between two nodes. These values can instead be set globally for all nodes with the Shadow command line options --interface-buffer, --socket-recv-buffer, and --socket-send-buffer (see shadow --help-network for more info).

loglevel and heartbeatloglevel are node-specific overrides for the simulator default log levels (the defaults are adjustable with shadow arguments --log-level and --heartbeat-log-level). Valid strings include 'error', 'critical', 'warning', 'message', 'info', and 'debug'. heartbeatloginfo is a node-specific override for the type of information that will get logged for this node during the heartbeat. Valid values are 'node', 'socket', and 'ram'. heartbeatfrequency is a node-specific override for the default number of seconds between which heartbeat messages are logged (the default is adjustable with shadow argument --heartbeat-frequency). Each heartbeat message contains useful statistics about the node.

cpufrequency is the speed of this node's virtual CPU in kilohertz. Along with the CPU processing requirements of the plug-in application, this determines how often events for this node are delayed during simulation.

logpcap is a case insenstive boolean string (e.g. "true") that specifies that Shadow should log all network input and output for this node in PCAP format (for viewing in e.g. wireshark). pcapdir is the directory to which the logs should be saved for this node.

Nodes must have at least one child <application> (see below), and may have more than one.

The application element

<application plugin="STRING" starttime="INTEGER" stoptime="INTEGER" arguments="STRING" />

Required attributes: plugin, starttime, arguments
Optional attributes: stoptime
Required parent element: <node>

The application element represents an application the node will run. The plugin attribute should be set to the id of the plugin element that represents the plug-in that should be used to launch this application at starttime virtual seconds from the beginning of the simulation. The application will be stopped at stoptime virtual seconds if given.

The arguments attribute should be set to a string holding the required plug-in arguments. This string will be passed to the plug-in in an argv-style array, similar to how arguments are passed to the main function in a C program. Please see the plug-in documentation for usage and format of the argument string.

Network configuration

Shadow uses a graphml format to represent a network topology. The python module networkx can be used to manipulate such a format. Shadow is distributed with some graphml topology files that were generated using data from a variety of sources, including Net Index, and CAIDA. Because the topology is represented in a standard graphml format, it is easy to swap out specific measurements of latency, jitter, packetloss, etc, if desired.

For more information on modeling topologies, check out some recent work on Tor network modeling.

Vertices

All vertices must have the following explicit attributes (in addition to the default id attribute): type. type is currently one of client, relay, or server, and is used to help determine where to attach virtual hosts to the topology.

In addition, all point of interest (poi) vertices must have the following attributes: ip, geocode, bandwidthup, bandwidthdown, packetloss. The asn attribute is optional.

Points of Interest are special vertices that represent a collection of Internet routers that are very close to each other in terms of network distance. These vertices also represent end-points in the network where virtual hosts may be attached. Shadow does this attachment using the typehint, iphint, and geocodehint attributes to the node element as specified in a Shadow config file. Hosts are always attached to the closest match to the best known location following the hinted restrictions.

Edges

All edges must have the following explicit attributes (in addition to the default source and target attributes): latency, jitter, packetloss. These are used when computing paths and routing packets between virtual hosts.

Routing

If the topology is a complete graph, Shadow uses the single link between each vertex as the path. Otherwise, a routing path is approximated using Dijkstra's shortest path algorithm.

Example

The following is an example of a properly-formed graphml file for Shadow:

<?xml version="1.0" encoding="utf-8"?><graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
  <key attr.name="packetloss" attr.type="double" for="edge" id="d9" />
  <key attr.name="jitter" attr.type="double" for="edge" id="d8" />
  <key attr.name="latency" attr.type="double" for="edge" id="d7" />
  <key attr.name="asn" attr.type="int" for="node" id="d6" />
  <key attr.name="type" attr.type="string" for="node" id="d5" />
  <key attr.name="bandwidthup" attr.type="int" for="node" id="d4" />
  <key attr.name="bandwidthdown" attr.type="int" for="node" id="d3" />
  <key attr.name="geocode" attr.type="string" for="node" id="d2" />
  <key attr.name="ip" attr.type="string" for="node" id="d1" />
  <key attr.name="packetloss" attr.type="double" for="node" id="d0" />
  <graph edgedefault="undirected">
    <node id="poi-1">
      <data key="d0">0.0</data>
      <data key="d1">0.0.0.0</data>
      <data key="d2">US</data>
      <data key="d3">10240</data>
      <data key="d4">10240</data>
      <data key="d5">net</data>
      <data key="d6">0</data>
    </node>
    <edge source="poi-1" target="poi-1">
      <data key="d7">50.0</data>
      <data key="d8">0.0</data>
      <data key="d9">0.0</data>
    </edge>
  </graph>
</graphml>

Generating new topologies

TODO

Traffic generator configuration

This section covers the traffic generator, tgen, which is distributed and built as part of Shadow (tgen is capable of running outside of Shadow as well). Tgen is a C application that models traffic behaviors using an action-dependency graph represented using the standard graphml.xml format. Each tgen node takes a graphml-formatted file as a parameter, and then begins transferring data to/from other nodes by following a path through the action graph.

Action-dependency graph format

Graph vertices represent actions, and graph edges represent dependencies. Each vertex may contain vertex attributes which specify action parameters. Tgen will walk the directed graph path starting at a start vertex, and execute each action along that path. The actions control the behavior at each stage.

Actions (vertices)

The following are valid actions and parameters (all parameters are currently stored as strings in graphml):

start: The start action is required for all tgen graph files, and only one start action is allowed per tgen instance. Acceptable attributes are:

  • serverport (required):
    the local port that will be opened to listen for other tgen connections
  • time (optional):
    the time (see format below) that the tgen node should delay before starting a walk through the action graph
  • socksproxy (optional):
    a peer (ip:port, e.g., 127.0.0.1:9051) to use as a proxy server through which all connections to other tgen peers will be made
  • timeout (optional):
    the default time (see format below) since the transfer started after which we give up on stalled transfers, used for all incoming server-side transfers and all client transfers that do not explicitly specify a timeout attribute. If this is not set or set to 0 and not overridden by the transfer, then an internally defined timeout is used instead (currently 60 seconds).
  • stallout (optional):
    the default time (see format below) since bytes were last sent/received for this transfer after which we give up on stalled transfers, used for all incoming server-side transfers and all client transfers that do not explicitly specify a stallout attribute. If this is not set or set to 0 and not overridden by the transfer, then an internally defined stallout is used instead (currently 15 seconds).
  • heartbeat (optional):
    the time period (see format below) between which heartbeat status messages are logged at 'message' level. The default of 1 second is used if heartbeat is 0 or is not set.
  • loglevel (optional):
    the level above which tgen log messages will be filtered and not shown or logged. Valid values in increasing order are: 'error', 'critical', 'message', 'info', and 'debug'. The default value if loglevel is not set is 'message'.
  • peers (special):
    a list of peers (ip1:port1,ip2:port21, e.g., 192.168.1.100:8888,192.168.1.101:8888) to use for transfers that do not explicitly specify a peer. The peers attribute is optional, only if all transfers specify a peers attribute.

transfer: Transfer actions are optional. Acceptable attributes are:

  • type (required):
    type of transfer: "get" to download or "put" to upload
  • protocol (required):
    protocol to use for this transfer (only "tcp" is supported)
  • size (required):
    amount of data to transfer (see format below)
  • timeout (optional):
    the time (see format below) since the transfer started after which we consider this a stalled transfer and give up on it. If specified, this overrides the default timeout attribute of the start element for this specific transfer. If this is set to 0, then an internally defined timeout is used instead (currently 60 seconds).
  • stallout (optional):
    the time (see format below) since bytes were last sent/received for this transfer after which we consider this a stalled transfer and give up on it. If specified, this overrides the default stallout attribute of the start element for this specific transfer. If this is set to 0, then an internally defined stallout is used instead (currently 15 seconds).
  • peers (special):
    a list of peers (ip1:port1,ip2:port21, e.g., 192.168.1.100:8888,192.168.1.101:8888) to use for this transfer. The peers attribute is optional, only if a peers attribute is specified in the start action. A peer will be selected at random from this list, or at random from the start action list if this attribute is not specified for a transfer.

pause: Pause actions are optional. Acceptable attributes are:

  • time (optional):
    the time (see format below) that the tgen node should pause before resuming the walk through the action graph.

If no time is given, then this action will pause a walk until the vertex has been visited by all incoming edges. More specifically, a pause action without a time is 'activated' when it is first visiting by an incoming edge, and then begins counting visitation by all other incoming edges. Once the 'activated' vertex is visited by all incoming edges, it 'deactivates' and then the walk continues by following each outgoing edge.

The pause action acts as a 'barrier'; an example usage would be to start multiple transfers in parallel and wait for them all to finish with a 'pause' before starting the next action.

end: End actions are optional. The parameters represent termination conditions: if any of the conditions are met upon arriving at an end action vertex, the tgen node will stop and shutdown. Acceptable attributes are:

  • time (optional):
    the time (see format below) since the node started
  • count (optional):
    the number of transfer completed by this node
  • size (optional):
    the total amount of data (see format below) transferred (read+write) by this node

Attribute value formats:

  • size: e.g., "5", or "5 suffix" where suffix is case in-sensitive and one of: kb, mb, gb, tb, kib, mib, gib, tib
  • time: e.g., "60" (defaults to seconds), or "60 suffix" where suffix is case in-sensitive and one of:
    nanosecond, nanoseconds, nsec, nsecs, ns,
    microsecond, microseconds, usec, usecs, us,
    millisecond, milliseconds, msec, msecs, ms,
    second, seconds, sec, secs, s,
    minute, minutes, min, mins, m,
    hour, hours, hr, hrs, h

Dependencies (edges)

Edges direct tgen from one action to the next. tgen will perform the above actions by starting at the start vertex and following the edges through the graph. By default, tgen will follow all outgoing edges from a vertex in parallel, thereby creating multiple concurrent execution paths in the graph.

Each edge may contain a 'weight' attribute with a floating point 'double' value, e.g., weight="1.0". While tgen will follow all outgoing edges of a vertex for which no weight is assigned, tgen will choose and follow one and only one edge from the set of weighted outgoing edges of a vertex. A single execution path can be maintained by assigning weights to all outgoing edges of a vertex.

A weighted choice is used to select which weighted outgoing edge of a vertex to follow, based on the sum of weights of all weighted outgoing edges. Therefore, if all weighted outgoing edges have the same weight, the choice will essentially be a uniform random choice.

Be warned that edge weights must be used carefully, especially when combined with the synchronize action. A synchronize action expects that all incoming edges will visit it, which may not be the case if weighted edges were used at some point in a path leading to the synchronize action.

Customizing generator behaviors

Given the above actions and parameters, simple python scripts can be used to generate behavior models using the networkx python module.

This script would generate a tgen configuration file for a client that:

  • downloads a single 320 KiB file from a server in the server pool;
  • pauses for a time selected uniformly at random between 1 and 60 seconds;
  • repeats.
import networkx as nx

servers="server1:8888,server2:8888"

G = nx.DiGraph()

G.add_node("start", serverport="8888", peers=servers)
G.add_node("transfer", type="get", protocol="tcp", size="320 KiB")
G.add_node("pause", time="1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60")

G.add_edge("start", "transfer")
G.add_edge("transfer", "pause")
G.add_edge("pause", "start")

nx.write_graphml(G, "tgen.web.graphml.xml")

And this script would generate a bulk client that repeatedly downloads a single 5 MiB file from a server in the server pool.

import networkx as nx

servers="server1:8888,server2:8888"

G = nx.DiGraph()

G.add_node("start", serverport="8888", peers=servers)
G.add_node("transfer", type="get", protocol="tcp", size="5 MiB")

G.add_edge("start", "transfer")
G.add_edge("transfer", "start")

nx.write_graphml(G, "tgen.bulk.graphml.xml")

The scripts are simple, but capable of generating complex behavior profiles.