From 29c5a2335c73bc66c47ec8f2b41d64b9cdc1af1d Mon Sep 17 00:00:00 2001
From: "kjellander@webrtc.org"
 <kjellander@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>
Date: Fri, 1 Jun 2012 08:42:17 +0000
Subject: [PATCH] Renamed to Network Emulator and improved error handling.
 Changed default start port of the port-range to 32768.

BUG=None
TEST=Tested locally.

Review URL: https://webrtc-codereview.appspot.com/627004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@2338 4adac7df-926f-26a2-2b94-8c16560cd09d
---
 .../config.py                                 |  2 +-
 .../emulate.py}                               | 62 ++++++++++---------
 .../network_emulator.py}                      | 35 ++++++-----
 3 files changed, 54 insertions(+), 45 deletions(-)
 rename tools/{network_simulator => network_emulator}/config.py (96%)
 rename tools/{network_simulator/simulate.py => network_emulator/emulate.py} (80%)
 rename tools/{network_simulator/network_simulator.py => network_emulator/network_emulator.py} (83%)

diff --git a/tools/network_simulator/config.py b/tools/network_emulator/config.py
similarity index 96%
rename from tools/network_simulator/config.py
rename to tools/network_emulator/config.py
index ac450f6b9..60fa485db 100644
--- a/tools/network_simulator/config.py
+++ b/tools/network_emulator/config.py
@@ -7,7 +7,7 @@
 #  in the file PATENTS.  All contributing project authors may
 #  be found in the AUTHORS file in the root of the source tree.
 
-"""Configuration class for network simulation."""
+"""Configuration class for network emulation."""
 
 
 class ConnectionConfig(object):
diff --git a/tools/network_simulator/simulate.py b/tools/network_emulator/emulate.py
similarity index 80%
rename from tools/network_simulator/simulate.py
rename to tools/network_emulator/emulate.py
index b1b3e67a6..e25670574 100755
--- a/tools/network_simulator/simulate.py
+++ b/tools/network_emulator/emulate.py
@@ -16,12 +16,12 @@ import socket
 import sys
 
 import config
-import network_simulator
+import network_emulator
 
 _DEFAULT_LOG_LEVEL = logging.INFO
 
 # Default port range to apply network constraints on.
-_DEFAULT_PORT_RANGE = (30000, 65535)
+_DEFAULT_PORT_RANGE = (32768, 65535)
 
 _PRESETS = [
     config.ConnectionConfig(1, 'Generic, Bad', 95, 95, 250, 2, 100),
@@ -133,7 +133,7 @@ def _set_logger(verbose):
 
 
 def _main():
-  """Checks arguments, permissions and runs a network simulation."""
+  """Checks arguments, permissions and runs a network emulation."""
   if os.name != 'posix':
     print >> sys.stderr, 'This script is only supported on Linux and Mac.'
     return 1
@@ -154,11 +154,11 @@ def _main():
   if options.queue:
     connection_config.queue_slots = options.queue
 
-  simulator = network_simulator.NetworkSimulator(connection_config,
-                                                 options.port_range)
+  emulator = network_emulator.NetworkEmulator(connection_config,
+                                              options.port_range)
   try:
-    simulator.check_permissions()
-  except network_simulator.NetworkSimulatorError as e:
+    emulator.check_permissions()
+  except network_emulator.NetworkEmulatorError as e:
     logging.error('Error: %s\n\nCause: %s', e.msg, e.error)
     return -1
 
@@ -167,28 +167,32 @@ def _main():
   else:
     external_ip = options.target_ip
 
-  logging.info('Simulating traffic to/from IP: %s', external_ip)
-  simulator.simulate(external_ip)
-  logging.info('Started network simulation with the following configuration:\n'
-               '  Receive bandwidth: %s kbps (%s kB/s)\n'
-               '  Send bandwidth   : %s kbps (%s kB/s)\n'
-               '  Delay            : %s ms\n'
-               '  Packet loss      : %s %%\n'
-               '  Queue slots      : %s',
-               connection_config.receive_bw_kbps,
-               connection_config.receive_bw_kbps/8,
-               connection_config.send_bw_kbps,
-               connection_config.send_bw_kbps/8,
-               connection_config.delay_ms,
-               connection_config.packet_loss_percent,
-               connection_config.queue_slots)
-  logging.info('Affected traffic: IP traffic on ports %s-%s',
-               options.port_range[0], options.port_range[1])
-  raw_input('Press Enter to abort Network simulation...')
-  logging.info('Flushing all Dummynet rules...')
-  simulator.cleanup()
-  logging.info('Completed Network Simulation.')
-  return 0
+  logging.info('Constraining traffic to/from IP: %s', external_ip)
+  try:
+    emulator.emulate(external_ip)
+    logging.info('Started network emulation with the following configuration:\n'
+                 '  Receive bandwidth: %s kbps (%s kB/s)\n'
+                 '  Send bandwidth   : %s kbps (%s kB/s)\n'
+                 '  Delay            : %s ms\n'
+                 '  Packet loss      : %s %%\n'
+                 '  Queue slots      : %s',
+                 connection_config.receive_bw_kbps,
+                 connection_config.receive_bw_kbps/8,
+                 connection_config.send_bw_kbps,
+                 connection_config.send_bw_kbps/8,
+                 connection_config.delay_ms,
+                 connection_config.packet_loss_percent,
+                 connection_config.queue_slots)
+    logging.info('Affected traffic: IP traffic on ports %s-%s',
+                 options.port_range[0], options.port_range[1])
+    raw_input('Press Enter to abort Network Emulation...')
+    logging.info('Flushing all Dummynet rules...')
+    emulator.cleanup()
+    logging.info('Completed Network Emulation.')
+    return 0
+  except network_emulator.NetworkEmulatorError as e:
+    logging.error('Error: %s\n\nCause: %s', e.msg, e.error)
+    return -2
 
 if __name__ == '__main__':
   sys.exit(_main())
diff --git a/tools/network_simulator/network_simulator.py b/tools/network_emulator/network_emulator.py
similarity index 83%
rename from tools/network_simulator/network_simulator.py
rename to tools/network_emulator/network_emulator.py
index 3518999c2..2876939ea 100644
--- a/tools/network_simulator/network_simulator.py
+++ b/tools/network_emulator/network_emulator.py
@@ -12,10 +12,11 @@
 import logging
 import os
 import subprocess
+import sys
 
 
-class NetworkSimulatorError(BaseException):
-  """Exception raised for errors in the network simulator.
+class NetworkEmulatorError(BaseException):
+  """Exception raised for errors in the network emulator.
 
   Attributes:
     msg: User defined error message.
@@ -35,15 +36,15 @@ class NetworkSimulatorError(BaseException):
     self.error = error
 
 
-class NetworkSimulator(object):
-  """A network simulator that can constrain the network using Dummynet."""
+class NetworkEmulator(object):
+  """A network emulator that can constrain the network using Dummynet."""
 
   def __init__(self, connection_config, port_range):
     """Constructor.
 
     Args:
         connection_config: A config.ConnectionConfig object containing the
-            characteristics for the connection to be simulated.
+            characteristics for the connection to be emulation.
         port_range: Tuple containing two integers defining the port range.
     """
     self._pipe_counter = 0
@@ -51,8 +52,8 @@ class NetworkSimulator(object):
     self._port_range = port_range
     self._connection_config = connection_config
 
-  def simulate(self, target_ip):
-    """Starts a network simulation by setting up Dummynet rules.
+  def emulate(self, target_ip):
+    """Starts a network emulation by setting up Dummynet rules.
 
     Args:
         target_ip: The IP address of the interface that shall be that have the
@@ -71,7 +72,7 @@ class NetworkSimulator(object):
         self._connection_config.queue_slots)
     logging.debug('Created send pipe: %s', send_pipe_id)
 
-    # Adding the rules will start the simulation.
+    # Adding the rules will start the emulation.
     incoming_rule_id = self._create_dummynet_rule(receive_pipe_id, 'any',
                                                   target_ip, self._port_range)
     logging.debug('Created incoming rule: %s', incoming_rule_id)
@@ -83,7 +84,7 @@ class NetworkSimulator(object):
     """Checks if permissions are available to run Dummynet commands.
 
     Raises:
-      NetworkSimulatorError: If permissions to run Dummynet commands are not
+      NetworkEmulatorError: If permissions to run Dummynet commands are not
       available.
     """
     if os.geteuid() != 0:
@@ -93,17 +94,17 @@ class NetworkSimulator(object):
                'root or have password-less sudo access to this command.'))
 
   def cleanup(self):
-    """Stops the network simulation by flushing all Dummynet rules.
+    """Stops the network emulation by flushing all Dummynet rules.
 
     Notice that this will flush any rules that may have been created previously
-    before starting the simulation.
+    before starting the emulation.
     """
     self._run_shell_command(['sudo', 'ipfw', '-f', 'flush'],
                             'Failed to flush Dummynet rules!')
 
   def _create_dummynet_rule(self, pipe_id, from_address, to_address,
                             port_range):
-    """Creates a network simulation rule and returns its ID.
+    """Creates a network emulation rule and returns its ID.
 
     Args:
         pipe_id: integer ID of the pipe.
@@ -144,7 +145,11 @@ class NetworkSimulator(object):
            'delay', '%sms' % delay_ms,
            'plr', (packet_loss_percent/100.0),
            'queue', queue_slots]
-    self._run_shell_command(cmd, 'Failed to create Dummynet pipe')
+    error_message = 'Failed to create Dummynet pipe. '
+    if sys.platform.startswith('linux'):
+      error_message += ('Make sure you have loaded the ipfw_mod.ko module to '
+                        'your kernel (sudo insmod /path/to/ipfw_mod.ko)')
+    self._run_shell_command(cmd, error_message)
     return self._pipe_counter
 
   def _run_shell_command(self, command, msg=None):
@@ -158,7 +163,7 @@ class NetworkSimulator(object):
       The standard output from running the command.
 
     Raises:
-      NetworkSimulatorError: If command fails. Message is set by the msg
+      NetworkEmulatorError: If command fails. Message is set by the msg
         parameter.
     """
     cmd_list = [str(x) for x in command]
@@ -169,5 +174,5 @@ class NetworkSimulator(object):
                                stderr=subprocess.PIPE)
     output, error = process.communicate()
     if process.returncode != 0:
-      raise NetworkSimulatorError(msg, cmd, process.returncode, output, error)
+      raise NetworkEmulatorError(msg, cmd, process.returncode, output, error)
     return output.strip()