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
This commit is contained in:
kjellander@webrtc.org 2012-06-01 08:42:17 +00:00
parent f5d934dfd8
commit 29c5a2335c
3 changed files with 54 additions and 45 deletions

View File

@ -7,7 +7,7 @@
# in the file PATENTS. All contributing project authors may # in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree. # 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): class ConnectionConfig(object):

View File

@ -16,12 +16,12 @@ import socket
import sys import sys
import config import config
import network_simulator import network_emulator
_DEFAULT_LOG_LEVEL = logging.INFO _DEFAULT_LOG_LEVEL = logging.INFO
# Default port range to apply network constraints on. # Default port range to apply network constraints on.
_DEFAULT_PORT_RANGE = (30000, 65535) _DEFAULT_PORT_RANGE = (32768, 65535)
_PRESETS = [ _PRESETS = [
config.ConnectionConfig(1, 'Generic, Bad', 95, 95, 250, 2, 100), config.ConnectionConfig(1, 'Generic, Bad', 95, 95, 250, 2, 100),
@ -133,7 +133,7 @@ def _set_logger(verbose):
def _main(): def _main():
"""Checks arguments, permissions and runs a network simulation.""" """Checks arguments, permissions and runs a network emulation."""
if os.name != 'posix': if os.name != 'posix':
print >> sys.stderr, 'This script is only supported on Linux and Mac.' print >> sys.stderr, 'This script is only supported on Linux and Mac.'
return 1 return 1
@ -154,11 +154,11 @@ def _main():
if options.queue: if options.queue:
connection_config.queue_slots = options.queue connection_config.queue_slots = options.queue
simulator = network_simulator.NetworkSimulator(connection_config, emulator = network_emulator.NetworkEmulator(connection_config,
options.port_range) options.port_range)
try: try:
simulator.check_permissions() emulator.check_permissions()
except network_simulator.NetworkSimulatorError as e: except network_emulator.NetworkEmulatorError as e:
logging.error('Error: %s\n\nCause: %s', e.msg, e.error) logging.error('Error: %s\n\nCause: %s', e.msg, e.error)
return -1 return -1
@ -167,28 +167,32 @@ def _main():
else: else:
external_ip = options.target_ip external_ip = options.target_ip
logging.info('Simulating traffic to/from IP: %s', external_ip) logging.info('Constraining traffic to/from IP: %s', external_ip)
simulator.simulate(external_ip) try:
logging.info('Started network simulation with the following configuration:\n' emulator.emulate(external_ip)
' Receive bandwidth: %s kbps (%s kB/s)\n' logging.info('Started network emulation with the following configuration:\n'
' Send bandwidth : %s kbps (%s kB/s)\n' ' Receive bandwidth: %s kbps (%s kB/s)\n'
' Delay : %s ms\n' ' Send bandwidth : %s kbps (%s kB/s)\n'
' Packet loss : %s %%\n' ' Delay : %s ms\n'
' Queue slots : %s', ' Packet loss : %s %%\n'
connection_config.receive_bw_kbps, ' Queue slots : %s',
connection_config.receive_bw_kbps/8, connection_config.receive_bw_kbps,
connection_config.send_bw_kbps, connection_config.receive_bw_kbps/8,
connection_config.send_bw_kbps/8, connection_config.send_bw_kbps,
connection_config.delay_ms, connection_config.send_bw_kbps/8,
connection_config.packet_loss_percent, connection_config.delay_ms,
connection_config.queue_slots) connection_config.packet_loss_percent,
logging.info('Affected traffic: IP traffic on ports %s-%s', connection_config.queue_slots)
options.port_range[0], options.port_range[1]) logging.info('Affected traffic: IP traffic on ports %s-%s',
raw_input('Press Enter to abort Network simulation...') options.port_range[0], options.port_range[1])
logging.info('Flushing all Dummynet rules...') raw_input('Press Enter to abort Network Emulation...')
simulator.cleanup() logging.info('Flushing all Dummynet rules...')
logging.info('Completed Network Simulation.') emulator.cleanup()
return 0 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__': if __name__ == '__main__':
sys.exit(_main()) sys.exit(_main())

View File

@ -12,10 +12,11 @@
import logging import logging
import os import os
import subprocess import subprocess
import sys
class NetworkSimulatorError(BaseException): class NetworkEmulatorError(BaseException):
"""Exception raised for errors in the network simulator. """Exception raised for errors in the network emulator.
Attributes: Attributes:
msg: User defined error message. msg: User defined error message.
@ -35,15 +36,15 @@ class NetworkSimulatorError(BaseException):
self.error = error self.error = error
class NetworkSimulator(object): class NetworkEmulator(object):
"""A network simulator that can constrain the network using Dummynet.""" """A network emulator that can constrain the network using Dummynet."""
def __init__(self, connection_config, port_range): def __init__(self, connection_config, port_range):
"""Constructor. """Constructor.
Args: Args:
connection_config: A config.ConnectionConfig object containing the 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. port_range: Tuple containing two integers defining the port range.
""" """
self._pipe_counter = 0 self._pipe_counter = 0
@ -51,8 +52,8 @@ class NetworkSimulator(object):
self._port_range = port_range self._port_range = port_range
self._connection_config = connection_config self._connection_config = connection_config
def simulate(self, target_ip): def emulate(self, target_ip):
"""Starts a network simulation by setting up Dummynet rules. """Starts a network emulation by setting up Dummynet rules.
Args: Args:
target_ip: The IP address of the interface that shall be that have the 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) self._connection_config.queue_slots)
logging.debug('Created send pipe: %s', send_pipe_id) 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', incoming_rule_id = self._create_dummynet_rule(receive_pipe_id, 'any',
target_ip, self._port_range) target_ip, self._port_range)
logging.debug('Created incoming rule: %s', incoming_rule_id) 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. """Checks if permissions are available to run Dummynet commands.
Raises: Raises:
NetworkSimulatorError: If permissions to run Dummynet commands are not NetworkEmulatorError: If permissions to run Dummynet commands are not
available. available.
""" """
if os.geteuid() != 0: if os.geteuid() != 0:
@ -93,17 +94,17 @@ class NetworkSimulator(object):
'root or have password-less sudo access to this command.')) 'root or have password-less sudo access to this command.'))
def cleanup(self): 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 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'], self._run_shell_command(['sudo', 'ipfw', '-f', 'flush'],
'Failed to flush Dummynet rules!') 'Failed to flush Dummynet rules!')
def _create_dummynet_rule(self, pipe_id, from_address, to_address, def _create_dummynet_rule(self, pipe_id, from_address, to_address,
port_range): port_range):
"""Creates a network simulation rule and returns its ID. """Creates a network emulation rule and returns its ID.
Args: Args:
pipe_id: integer ID of the pipe. pipe_id: integer ID of the pipe.
@ -144,7 +145,11 @@ class NetworkSimulator(object):
'delay', '%sms' % delay_ms, 'delay', '%sms' % delay_ms,
'plr', (packet_loss_percent/100.0), 'plr', (packet_loss_percent/100.0),
'queue', queue_slots] '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 return self._pipe_counter
def _run_shell_command(self, command, msg=None): def _run_shell_command(self, command, msg=None):
@ -158,7 +163,7 @@ class NetworkSimulator(object):
The standard output from running the command. The standard output from running the command.
Raises: Raises:
NetworkSimulatorError: If command fails. Message is set by the msg NetworkEmulatorError: If command fails. Message is set by the msg
parameter. parameter.
""" """
cmd_list = [str(x) for x in command] cmd_list = [str(x) for x in command]
@ -169,5 +174,5 @@ class NetworkSimulator(object):
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
output, error = process.communicate() output, error = process.communicate()
if process.returncode != 0: if process.returncode != 0:
raise NetworkSimulatorError(msg, cmd, process.returncode, output, error) raise NetworkEmulatorError(msg, cmd, process.returncode, output, error)
return output.strip() return output.strip()