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:
parent
f5d934dfd8
commit
29c5a2335c
@ -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):
|
@ -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())
|
@ -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()
|
Loading…
x
Reference in New Issue
Block a user