def modify_rpl_debug(contiki_dir): """ This function checks and modifies, if necessary, the DEBUG constant in [CONTIKI]/core/net/ipv6/uip-ds6-route.c in order to enable mote relationships logging. This is required for the script.js used in Cooja simulations to catch relationships evolution and to plot the sensor network in an animated GIF. :param contiki_dir: Contiki's directory :return: None """ pattern = '#define DEBUG DEBUG_NONE' for module, descr in zip([('ipv6', 'uip-ds6-route.c'), ('rpl', 'rpl.c')], ["IPv6", "RPL"]): filepath = join(contiki_dir, 'core', 'net', *module) changed = False with open(filepath) as f: source = f.read() buffer = [] for line in source.split('\n'): if pattern in line: buffer.append(line.replace(pattern, '#define DEBUG DEBUG_ANNOTATE')) changed = True else: buffer.append(line) with open(filepath, 'w') as f: f.write('\n'.join(buffer)) logger.debug((" > {} debug modified" if changed else " > {} debug already up-to-date").format(descr))
def __run(name, **kwargs): """ Run an experiment. :param name: experiment name :param path: expanded path of the experiment (dynamically filled in through 'command' decorator with 'expand' """ path = kwargs['path'] check_structure(path, remove=True) with hide(*HIDDEN_ALL): for sim in ["without", "with"]: sim_path = join(path, "{}-malicious".format(sim)) data, results = join(sim_path, 'data'), join(sim_path, 'results') # the Makefile is at experiment's root ('path') with lcd(sim_path): logger.debug(" > Running simulation {} the malicious mote...".format(sim)) local("make run", capture=True) # simulations are in their respective folders ('sim_path') remove_files(sim_path, 'COOJA.log', 'COOJA.testlog') # once the execution is over, gather the screenshots into a single GIF and keep the first and # the last screenshots ; move these to the results folder with lcd(data): local('convert -delay 10 -loop 0 network*.png wsn-{}-malicious.gif'.format(sim)) network_images = {int(fn.split('.')[0].split('_')[-1]): fn for fn in listdir(data) if fn.startswith('network_')} move_files(data, results, 'wsn-{}-malicious.gif'.format(sim)) net_start_old = network_images[min(network_images.keys())] net_start, ext = splitext(net_start_old) net_start_new = 'wsn-{}-malicious_start{}'.format(sim, ext) net_end_old = network_images[max(network_images.keys())] net_end, ext = splitext(net_end_old) net_end_new = 'wsn-{}-malicious_end{}'.format(sim, ext) move_files(data, results, (net_start_old, net_start_new), (net_end_old, net_end_new)) remove_files(data, *network_images.values()) parsing_chain(sim_path)
def modify_rpl_debug(contiki_dir): """ This function checks and modifies, if necessary, the DEBUG constant in [CONTIKI]/core/net/ipv6/uip-ds6-route.c in order to enable mote relationships logging. This is required for the script.js used in Cooja simulations to catch relationships evolution and to plot the sensor network in an animated GIF. :param contiki_dir: Contiki's directory :return: None """ pattern = '#define DEBUG DEBUG_NONE' for module, descr in zip([('ipv6', 'uip-ds6-route.c'), ('rpl', 'rpl.c')], ["IPv6", "RPL"]): filepath = join(contiki_dir, 'core', 'net', *module) changed = False with open(filepath) as f: source = f.read() buffer = [] for line in source.split('\n'): if pattern in line: buffer.append( line.replace(pattern, '#define DEBUG DEBUG_ANNOTATE')) changed = True else: buffer.append(line) with open(filepath, 'w') as f: f.write('\n'.join(buffer)) logger.debug((" > {} debug modified" if changed else " > {} debug already up-to-date").format(descr))
def modify_cooja(cooja_dir): """ This function inserts a block in the IF statement for parsing Cooja's input arguments. It searches for the IF statement pattern containing the '-nogui' case and inserts an IF block for a new '-hidden' case (aimed to run a simulation with the GUI hidden). :param cooja_dir: Cooja's directory :return: None """ pattern = 'if (args.length > 0 && args[0].startsWith("-nogui="))' cooja_file = join(cooja_dir, 'java', 'org', 'contikios', 'cooja', 'Cooja.java') changed = False with open(cooja_file) as f: source = f.read() buffer = [] for line in source.split('\n'): if pattern in line: with open('src/Cooja.java.snippet') as f: line = line.replace( pattern, '{} else {}'.format(f.read().strip(), pattern)) changed = True buffer.append(line) with open(cooja_file, 'w') as f: f.write('\n'.join(buffer)) logger.debug(" > Cooja.java modified" if changed else " > Cooja.java already up-to-date")
def update_cooja_build(cooja_dir): """ This function adds a line for the 'visualizer_screenshot' plugin in the 'clean' and 'jar' sections of Cooja's build.xml. :param cooja_dir: Cooja's directory :return: None """ cooja_build = join(cooja_dir, 'build.xml') with open(cooja_build) as f: source = f.read() buffer, tmp, is_in_jar_block, is_in_clean_block = [], [], False, False for line in source.split('\n'): if '<target name="clean" depends="init">' in line: is_in_clean_block = True elif '<target name="jar" depends="jar_cooja">' in line: is_in_jar_block = True if (is_in_clean_block or is_in_jar_block) and '"apps/visualizer_screenshot"' in line: return # if in 'clean' block, collect '<delete dir=...' lines in a buffer, add the required line then re-append buffer if is_in_clean_block and '</target>' in line: while buffer[-1].strip().startswith('<delete dir='): tmp.append(buffer.pop()) buffer.append(' <ant antfile="build.xml" dir="apps/visualizer_screenshot"' ' target="clean" inheritAll="false"/>') while len(tmp) > 0: buffer.append(tmp.pop()) # if in 'jar' block, just put the required line at the end of the block elif is_in_jar_block and '</target>' in line: buffer.append(' <ant antfile="build.xml" dir="apps/visualizer_screenshot"' ' target="jar" inheritAll="false"/>') buffer.append(line) with open(cooja_build, 'w') as f: f.write('\n'.join(buffer)) logger.debug(" > Cooja's build.xml modified")
def update(silent=False, **kwargs): """ Update Contiki-OS and RPL Attacks Framework. """ for folder, repository in zip([CONTIKI_FOLDER, FRAMEWORK_FOLDER], ["Contiki-OS", "RPL Attacks Framework"]): with hide(*HIDDEN_ALL): with lcd(folder): uptodate = "branch is up-to-date" in local( 'git checkout master', capture=True).strip().split('\n')[-1] if not uptodate: logger.warn( "You are about to loose any custom change made to {} ;" .format(repository)) if silent or std_input( "Proceed anyway ? (yes|no) [default: no] ", 'yellow') == 'yes': local('git submodule update --init') local('git fetch --all') local('git reset --hard origin/master') local('git pull') logger.debug(" > {} {}".format(repository, ["updated", "already up-to-date"][uptodate])) if repository == "Contiki-OS" and not uptodate: setup(silent=True)
def wrapper(cmd, *args, **kwargs): out = f(cmd + ' 2>&1 /dev/null', *args, **kwargs) if out.return_code != 0: logger.critical( "Command '{}' returned error code {} with the following error:\n" .format(cmd, out.return_code) + '\n' + out) raise Exception("Command '{}' failed.".format(cmd)) logger.debug(out)
def __set_info(self, status, result=None, expires=True): logger.debug(' > Process {} is over.'.format(self)) self.tasklist[self].update({ 'status': status, 'result': result or self.tasklist[self]['result'] }) if expires: self.tasklist[self]['expires'] = datetime.now() + timedelta( seconds=TASK_EXPIRATION)
def clean(name, ask=True, **kwargs): """ Remove an experiment. :param name: experiment name (or absolute path to experiment) :param ask: ask confirmation """ console = kwargs.get('console') if console is None or not any([i['name'] == name and i['status'] == 'PENDING' for i in console.tasklist.values()]): logger.debug(" > Cleaning folder...") with hide(*HIDDEN_ALL): local("rm -rf {}".format(kwargs['path']))
def write_template(path, env, name, **kwargs): """ This function fills in a markdown template and copy it to its destination. :param path: folder where the template is to be copied :param env: template environment :param name: template's key in the templates dictionary :param kwargs: parameters associated to this template """ logger.debug(" > Setting template file: {}".format(name)) template = env.get_template(name).render(**kwargs) with open(join(path, name), "w") as f: f.write(template)
def write_template(path, env, name, **kwargs): """ This function fills in a template and copy it to its destination. :param path: folder where the template is to be copied :param env: template environment :param name: template's key in the templates dictionary :param kwargs: parameters associated to this template """ logger.debug(" > Setting template file: {}".format(name)) template = env.get_template(name).render(**kwargs) with open(join(path, name), "w") as f: f.write(template)
def apply_debug_flags(contiki, new_old_pair='NONE', logger=None): """ This function replaces debug flags in ContikiRPL files. :param contiki_rpl: path to ContikiRPL custom library :param new_old_pair: the new value to be set for the debug flag """ for filename in DEBUG_FILES: old_pattern = r'^#define DEBUG DEBUG_([A-Z]+)$' logger.debug( " > Replacing in {} where regex {} with new value: {}".format( filename, old_pattern, new_old_pair)) replace_in_file(join(contiki, filename), (old_pattern, new_old_pair), logger=logger)
def clean(name, ask=True, **kwargs): """ Remove an experiment. :param name: experiment name (or absolute path to experiment) :param ask: ask confirmation """ console = kwargs.get('console') if console is None or not any([ i['name'] == name and i['status'] == 'PENDING' for i in console.tasklist.values() ]): logger.debug(" > Cleaning folder...") with hide(*HIDDEN_ALL): local("rm -rf {}".format(kwargs['path']))
def register_new_path_in_profile(): """ This function appends PATH adaptation to user's .profile for support of the last version of msp430-gcc. :return: None """ msp430_path_adapted = False with open(expanduser('~/.profile')) as f: for line in f.readlines(): if 'export PATH=/usr/local/msp430/bin:$PATH' in line: msp430_path_adapted = True if not msp430_path_adapted: with open(expanduser('~/.profile'), 'a') as f: f.write("\n\n# msp430-gcc (GCC) 4.6.3\n# export PATH=/usr/bin/msp430-gcc/bin:$PATH\n" "# msp430-gcc (GCC) 4.7.0\nexport PATH=/usr/local/msp430/bin:$PATH") logger.debug(" > PATH adapted for msp430-gcc (GCC) 4.7.0 support")
def register_new_path_in_profile(): """ This function appends PATH adaptation to user's .profile for support of the last version of msp430-gcc. :return: None """ msp430_path_adapted = False with open(expanduser('~/.profile')) as f: for line in f.readlines(): if 'export PATH=/usr/local/msp430/bin:$PATH' in line: msp430_path_adapted = True if not msp430_path_adapted: with open(expanduser('~/.profile'), 'a') as f: f.write( "\n\n# msp430-gcc (GCC) 4.6.3\n# export PATH=/usr/bin/msp430-gcc/bin:$PATH\n" "# msp430-gcc (GCC) 4.7.0\nexport PATH=/usr/local/msp430/bin:$PATH" ) logger.debug(" > PATH adapted for msp430-gcc (GCC) 4.7.0 support")
def __run(name, **kwargs): """ Run an experiment. :param name: experiment name :param path: expanded path of the experiment (dynamically filled in through 'command' decorator with 'expand' """ path = kwargs['path'] check_structure(path, remove=True) with hide(*HIDDEN_ALL): for sim in ["without", "with"]: sim_path = join(path, "{}-malicious".format(sim)) data, results = join(sim_path, 'data'), join(sim_path, 'results') # the Makefile is at experiment's root ('path') with lcd(sim_path): logger.debug( " > Running simulation {} the malicious mote...".format( sim)) local("make run", capture=True) # simulations are in their respective folders ('sim_path') remove_files(sim_path, 'COOJA.log', 'COOJA.testlog') # once the execution is over, gather the screenshots into a single GIF and keep the first and # the last screenshots ; move these to the results folder with lcd(data): local( 'convert -delay 10 -loop 0 network*.png wsn-{}-malicious.gif' .format(sim)) network_images = { int(fn.split('.')[0].split('_')[-1]): fn for fn in listdir(data) if fn.startswith('network_') } move_files(data, results, 'wsn-{}-malicious.gif'.format(sim)) net_start_old = network_images[min(network_images.keys())] net_start, ext = splitext(net_start_old) net_start_new = 'wsn-{}-malicious_start{}'.format(sim, ext) net_end_old = network_images[max(network_images.keys())] net_end, ext = splitext(net_end_old) net_end_new = 'wsn-{}-malicious_end{}'.format(sim, ext) move_files(data, results, (net_start_old, net_start_new), (net_end_old, net_end_new)) remove_files(data, *network_images.values()) parsing_chain(sim_path)
def update_cooja_build(cooja_dir): """ This function adds a line for the 'visualizer_screenshot' plugin in the 'clean' and 'jar' sections of Cooja's build.xml. :param cooja_dir: Cooja's directory :return: None """ cooja_build = join(cooja_dir, 'build.xml') with open(cooja_build) as f: source = f.read() buffer, tmp, is_in_jar_block, is_in_clean_block = [], [], False, False for line in source.split('\n'): if '<target name="clean" depends="init">' in line: is_in_clean_block = True elif '<target name="jar" depends="jar_cooja">' in line: is_in_jar_block = True if (is_in_clean_block or is_in_jar_block) and '"apps/visualizer_screenshot"' in line: return # if in 'clean' block, collect '<delete dir=...' lines in a buffer, add the required line then re-append buffer if is_in_clean_block and '</target>' in line: while buffer[-1].strip().startswith('<delete dir='): tmp.append(buffer.pop()) buffer.append( ' <ant antfile="build.xml" dir="apps/visualizer_screenshot"' ' target="clean" inheritAll="false"/>') is_in_clean_block = False while len(tmp) > 0: buffer.append(tmp.pop()) # if in 'jar' block, just put the required line at the end of the block elif is_in_jar_block and '</target>' in line: buffer.append( ' <ant antfile="build.xml" dir="apps/visualizer_screenshot"' ' target="jar" inheritAll="false"/>') is_in_jar_block = False buffer.append(line) with open(cooja_build, 'w') as f: f.write('\n'.join(buffer)) logger.debug(" > Cooja's build.xml modified")
def modify_cooja(cooja_dir): """ This function inserts a block in the IF statement for parsing Cooja's input arguments. It searches for the IF statement pattern containing the '-nogui' case and inserts an IF block for a new '-hidden' case (aimed to run a simulation with the GUI hidden). :param cooja_dir: Cooja's directory :return: None """ pattern = 'if (args.length > 0 && args[0].startsWith("-nogui="))' cooja_file = join(cooja_dir, 'java', 'org', 'contikios', 'cooja', 'Cooja.java') with open(cooja_file) as f: source = f.read() buffer = [] for line in source.split('\n'): if pattern in line: with open('src/Cooja.java.snippet') as f: line = line.replace(pattern, '{} else {}'.format(f.read().strip(), pattern)) buffer.append(line) with open(cooja_file, 'w') as f: f.write('\n'.join(buffer)) logger.debug(" > Cooja.java modified")
def update_cooja_user_properties(): """ This function updates ~/.cooja.user.properties to include 'visualizer_screenshot' plugin :return: None """ cooja_user_properties = join(expanduser('~'), '.cooja.user.properties') # if it does not exist, create it from 'src' folder's template if not exists(cooja_user_properties): with open('src/cooja-user-properties') as tf: with open(cooja_user_properties, 'w') as nf: nf.write(tf.read()) logger.debug(" > Cooja user properties created") return # if it exists, append plugin's reference inside with open(cooja_user_properties) as f: source = f.read() buffer, plugin_appended = [], False for line in source.split('\n'): if line.startswith('DEFAULT_PROJECTDIRS='): if '[APPS_DIR]/visualizer_screenshot' in line: return else: plugin_appended = True line += ';[APPS_DIR]/visualizer_screenshot' buffer.append(line) # if the line with the correct constant name was not found, create it from 'src' folder's template if not plugin_appended: with open('src/cooja-user-properties') as f: for line in f.readlines(): if 'DEFAULT_PROJECTDIRS=' in line: buffer.append(line) break # then write the modified .cooja.user.properties with open(cooja_user_properties, 'w') as f: f.write('\n'.join(buffer)) logger.debug(" > Cooja user properties modified")
def __remake(name, build=False, **kwargs): """ Remake the malicious mote of an experiment. (meaning that it lets all simulation's files unchanged except ./motes/malicious.[target]) :param name: experiment name :param path: expanded path of the experiment (dynamically filled in through 'command' decorator with 'expand' """ path = kwargs['path'] logger.debug(" > Retrieving parameters...") params = read_config(path) ext_lib = params.get("ext_lib") if ext_lib and not exists(ext_lib): logger.error("External library does not exist !") logger.critical("Make aborted.") return False logger.debug(" > Recompiling malicious mote...") # remove former compiled malicious mote and prepare the template templates = get_path(path, 'templates', create=True) get_path(templates, 'motes', create=True) copy_files((TEMPLATES_FOLDER, 'experiment'), templates, ('motes/{}.c'.format(params["mtype_malicious"]), 'motes/malicious.c')) # recreate malicious C file from template and clean the temporary template replacements = render_templates(path, only_malicious=True, **params) # then clean the temporary folder with templates remove_folder(templates) # now recompile with settings(hide(*HIDDEN_ALL), warn_only=True): with_malicious = join(path, 'with-malicious', 'motes') without_malicious = join(path, 'without-malicious', 'motes') contiki = join(with_malicious, 'contiki') contiki_rpl = join(contiki, 'core', 'net', 'rpl') with lcd(with_malicious): malicious = 'malicious.{}'.format(params["malicious_target"]) croot, csensor = 'root.{}'.format(params["target"]), 'sensor.{}'.format(params["target"]) # handle the malicious mote recompilation copy_folder(CONTIKI_FOLDER, with_malicious, includes=get_contiki_includes(params["malicious_target"])) if ext_lib is not None: remove_folder(contiki_rpl) copy_folder(ext_lib, contiki_rpl) apply_replacements(contiki_rpl, replacements) logger.debug(" > Making '{}'...".format(malicious)) stderr(local)("make malicious{} CONTIKI={}" .format(['', '.upload'][build], contiki), capture=True) if build: build = get_path(path, 'build', create=True) move_files(with_malicious, build, 'tmpimage.ihex') copy_files(with_malicious, build, malicious) move_files(with_malicious, without_malicious, malicious) local('make clean') remove_files(with_malicious, 'malicious.c') move_files(without_malicious, with_malicious, malicious) copy_files(without_malicious, with_malicious, croot, csensor) remove_folder(contiki)
def render_templates(path, only_malicious=False, **params): """ This function is aimed to adapt and render the base templates dictionary with provided parameters. :param path: experiment folder path :param only_malicious: flag to indicate if all the templates have to be deployed or only malicious' one :param params: dictionary with all the parameters for the experiment :return: eventual replacements to be made in ContikiRPL files """ templates = deepcopy(TEMPLATES) env = Environment(loader=FileSystemLoader(join(path, 'templates'))) # fill in the different templates with input parameters constants, replacements = get_constants_and_replacements(params["blocks"]) templates["motes/malicious.c"]["constants"] = "\n".join( ["#define {} {}".format(*c) for c in constants.items()]) if only_malicious: template_malicious = "motes/malicious.c" write_template(join(path, "with-malicious"), env, template_malicious, **templates[template_malicious]) return replacements # generate the list of motes (first one is the root, last one is the malicious mote) motes = params['motes'] or eval(params["wsn_gen_algo"])(defaults=DEFAULTS, **params) # fill in simulation file templates templates["report.md"] = deepcopy(params) templates["motes/Makefile"]["target"] = params["target"] # important note: timeout is milliseconds in the simulation script templates["script.js"]["timeout"] = 1000 * params["duration"] # important note: sampling period is relative to the measured time in the simulation, which is in microseconds ; # the '10 * ' thus means that we take 100 measures regardless the duration of the simulation templates["script.js"][ "sampling_period"] = templates["script.js"]["timeout"] * 10 templates["simulation.csc"][ "title"] = params["title"] + ' (with the malicious mote)' templates["simulation.csc"]["goal"] = params["goal"] templates["simulation.csc"]["notes"] = params["notes"] templates["simulation.csc"]["interference_range"] = params["int_range"] templates["simulation.csc"]["transmitting_range"] = params["tx_range"] templates["simulation.csc"]["target"] = params["target"] templates["simulation.csc"]["target_capitalized"] = params[ "target"].capitalize() templates["simulation.csc"]["malicious_target"] = params[ "malicious_target"] templates["simulation.csc"]["malicious_target_capitalized"] = params[ "malicious_target"].capitalize() logger.debug(" > Motes: {}".format(motes)) templates["simulation.csc"]["motes"] = motes for mote_type in templates["simulation.csc"]["mote_types"]: mote_type["target"] = params["target"] if mote_type[ "name"] != "malicious" else params["malicious_target"] # write to folder: render the templates for the simulation with the malicious mote for name, kwargs in templates.items(): write_template(join(path, 'with-malicious'), env, name, **kwargs) # now, adapt the title and mote source template del templates["motes/Makefile"] del templates["motes/root.c"] del templates["motes/sensor.c"] del templates["motes/malicious.c"] templates["simulation.csc"][ "title"] = params["title"] + ' (without the malicious mote)' templates["simulation.csc"]["motes"] = motes[:-1] del templates["simulation.csc"]["mote_types"][-1] # render the templates for the simulation without the malicious mote for name, kwargs in templates.items(): write_template(join(path, 'without-malicious'), env, name, **kwargs) return replacements
def __set_info(self, status, result=None, expires=True): if status != 'PENDING': logger.debug(' > Process {} is over.'.format(self)) self.tasklist[self].update({'status': status, 'result': result or self.tasklist[self]['result']}) if expires: self.tasklist[self]['expires'] = datetime.now() + timedelta(seconds=TASK_EXPIRATION)
def __make(name, ask=True, **kwargs): """ Make a new experiment. :param name: experiment name (or path to the experiment, if expanded in the 'command' decorator) :param ask: ask confirmation :param path: expanded path of the experiment (dynamically filled in through 'command' decorator with 'expand' :param kwargs: simulation keyword arguments (see the documentation for more information) """ global reuse_bin_path set_logging(kwargs.get('loglevel')) path = kwargs['path'] logger.debug(" > Validating parameters...") params = validated_parameters(kwargs) ext_lib = params.get("ext_lib") if ext_lib and not exists(ext_lib): logger.error("External library does not exist !") logger.critical("Make aborded.") return False logger.debug(" > Creating simulation...") # create experiment's directories check_structure(path, create=True, remove=True) templates = get_path(path, 'templates', create=True) get_path(templates, 'motes', create=True) # select the right malicious mote template and duplicate the simulation file copy_files( (TEMPLATES_FOLDER, 'experiment'), templates, ('motes/{}.c'.format(params["mtype_root"]), 'motes/root.c'), ('motes/{}.c'.format(params["mtype_sensor"]), 'motes/sensor.c'), ('motes/{}.c'.format(params["mtype_malicious"]), 'motes/malicious.c'), 'motes/Makefile', 'Makefile', 'simulation.csc', 'script.js') # create experiment's files from templates then clean the templates folder replacements = render_templates(path, **params) remove_folder(templates) # now, write the config file without the list of motes del params['motes'] write_config(path, params) # now compile with settings(hide(*HIDDEN_ALL), warn_only=True): with_malicious = join(path, 'with-malicious', 'motes') without_malicious = join(path, 'without-malicious', 'motes') contiki = join(with_malicious, split(CONTIKI_FOLDER)[-1]) contiki_rpl = join(contiki, 'core', 'net', 'rpl') # copy a reduced version of Contiki where the debug flags can be set for RPL files set in DEBUG_FILES copy_folder(CONTIKI_FOLDER, with_malicious, includes=get_contiki_includes(params["target"], params["malicious_target"])) apply_debug_flags(contiki_rpl, debug=['NONE', 'PRINT'][params["debug"]]) with lcd(with_malicious): # first, compile root and sensor mote types croot, csensor = 'root.{}'.format( params["target"]), 'sensor.{}'.format(params["target"]) if reuse_bin_path is None or reuse_bin_path == with_malicious: logger.debug(" > Making '{}'...".format(croot)) stderr(local)("make root CONTIKI={}".format(contiki), capture=True) logger.debug(" > Making '{}'...".format(csensor)) stderr(local)("make sensor CONTIKI={}".format(contiki), capture=True) # here, files are moved ; otherwise, 'make clean' would also remove *.z1 move_files(with_malicious, without_malicious, croot, csensor) # after compiling, clean artifacts local('make clean') remove_files(with_malicious, 'root.c', 'sensor.c') else: copy_files(reuse_bin_path, without_malicious, croot, csensor) # second, handle the malicious mote compilation malicious = 'malicious.{}'.format(params["malicious_target"]) if ext_lib is not None: remove_folder(contiki_rpl) copy_folder(ext_lib, contiki_rpl) apply_replacements(contiki_rpl, replacements) logger.debug(" > Making '{}'...".format(malicious)) stderr(local)("make malicious CONTIKI={} TARGET={}".format( contiki, params["malicious_target"]), capture=True) # temporary move compiled malicious mote, clean the compilation artifacts, move the malicious mote back # from the temporary location and copy compiled root and sensor motes move_files(with_malicious, without_malicious, malicious) local('make clean') move_files(without_malicious, with_malicious, malicious) copy_files(without_malicious, with_malicious, croot, csensor) # finally, remove compilation sources remove_files(with_malicious, 'malicious.c') remove_folder(contiki)
def setup(silent=False, **kwargs): """ Setup the framework. """ recompile = False # install Cooja modifications if not check_cooja(COOJA_FOLDER): logger.debug(" > Installing Cooja add-ons...") # modify Cooja.java and adapt build.xml and ~/.cooja.user.properties modify_cooja(COOJA_FOLDER) update_cooja_build(COOJA_FOLDER) update_cooja_user_properties() recompile = True # install VisualizerScreenshot plugin in Cooja visualizer = join(COOJA_FOLDER, 'apps', 'visualizer_screenshot') if not exists(visualizer): logger.debug(" > Installing VisualizerScreenshot Cooja plugin...") copy_folder('src/visualizer_screenshot', visualizer) recompile = True # recompile Cooja for making the changes take effect if recompile: with lcd(COOJA_FOLDER): logger.debug(" > Recompiling Cooja...") with settings(warn_only=True): local("ant clean") local("ant jar") else: logger.debug(" > Cooja is up-to-date") # install imagemagick with hide(*HIDDEN_ALL): imagemagick_apt_output = local('apt-cache policy imagemagick', capture=True) if 'Unable to locate package' in imagemagick_apt_output: logger.debug(" > Installing imagemagick package...") sudo("apt-get install imagemagick -y &") else: logger.debug(" > Imagemagick is installed") # install msp430 (GCC) upgrade with hide(*HIDDEN_ALL): msp430_version_output = local('msp430-gcc --version', capture=True) if 'msp430-gcc (GCC) 4.7.0 20120322' not in msp430_version_output: txt = "In order to extend msp430x memory support, it is necessary to upgrade msp430-gcc.\n" \ "Would you like to upgrade it now ? (yes|no) [default: no] " answer = std_input(txt, 'yellow') if answer == "yes": logger.debug( " > Upgrading msp430-gcc from version 4.6.3 to 4.7.0...") logger.warning( "If you encounter problems with this upgrade, please refer to:\n" "https://github.com/contiki-os/contiki/wiki/MSP430X") with lcd('src/'): logger.warning( " > Upgrade now starts, this may take up to 30 minutes...") sudo('./upgrade-msp430.sh') sudo('rm -r tmp/') local('export PATH=/usr/local/msp430/bin:$PATH') register_new_path_in_profile() else: logger.warning("Upgrade of library msp430-gcc aborted") logger.warning( "You may experience problems of mote memory size at compilation" ) else: logger.debug(" > Library msp430-gcc is up-to-date (version 4.7.0)")
def __remake(name, build=False, **kwargs): """ Remake the malicious mote of an experiment. (meaning that it lets all simulation's files unchanged except ./motes/malicious.[target]) :param name: experiment name :param path: expanded path of the experiment (dynamically filled in through 'command' decorator with 'expand' """ set_logging(kwargs.get('loglevel')) path = kwargs['path'] logger.debug(" > Retrieving parameters...") params = read_config(path) ext_lib = params.get("ext_lib") if ext_lib and not exists(ext_lib): logger.error("External library does not exist !") logger.critical("Make aborted.") return False logger.debug(" > Recompiling malicious mote...") # remove former compiled malicious mote and prepare the template templates = get_path(path, 'templates', create=True) get_path(templates, 'motes', create=True) copy_files( (TEMPLATES_FOLDER, 'experiment'), templates, ('motes/{}.c'.format(params["mtype_malicious"]), 'motes/malicious.c')) # recreate malicious C file from template and clean the temporary template replacements = render_templates(path, only_malicious=True, **params) # then clean the temporary folder with templates remove_folder(templates) # now recompile with settings(hide(*HIDDEN_ALL), warn_only=True): with_malicious = join(path, 'with-malicious', 'motes') without_malicious = join(path, 'without-malicious', 'motes') contiki = join(with_malicious, split(CONTIKI_FOLDER)[-1]) contiki_rpl = join(contiki, 'core', 'net', 'rpl') with lcd(with_malicious): malicious = 'malicious.{}'.format(params["malicious_target"]) croot, csensor = 'root.{}'.format( params["target"]), 'sensor.{}'.format(params["target"]) # handle the malicious mote recompilation copy_folder(CONTIKI_FOLDER, with_malicious, includes=get_contiki_includes( params["malicious_target"])) if ext_lib is not None: remove_folder(contiki_rpl) copy_folder(ext_lib, contiki_rpl) apply_replacements(contiki_rpl, replacements) if build: logger.debug(" > Building '{}'...".format(malicious)) stderr(local)( "sudo make malicious.upload CONTIKI={} TARGET={}".format( contiki, params["malicious_target"])) build = get_path(path, 'build', create=True) move_files(with_malicious, build, 'tmpimage.ihex') copy_files(with_malicious, build, malicious) else: logger.debug(" > Making '{}'...".format(malicious)) stderr(local)("make malicious CONTIKI={} TARGET={}".format( contiki, params["malicious_target"]), capture=True) move_files(with_malicious, without_malicious, malicious) local('make clean') remove_files(with_malicious, 'malicious.c') move_files(without_malicious, with_malicious, malicious) copy_files(without_malicious, with_malicious, croot, csensor) remove_folder(contiki)
def __run(name, **kwargs): """ Run an experiment. :param name: experiment name :param path: expanded path of the experiment (dynamically filled in through 'command' decorator with 'expand' """ set_logging(kwargs.get('loglevel')) path = kwargs['path'] check_structure(path, remove=True) with settings(hide(*HIDDEN_ALL), warn_only=True): for sim in ["without", "with"]: sim_path = join(path, "{}-malicious".format(sim)) data, results = join(sim_path, 'data'), join(sim_path, 'results') # the Makefile is at experiment's root ('path') logger.debug( " > Running simulation {} the malicious mote...".format(sim)) with lcd(sim_path): output = local("make run TASK={}".format(kwargs['task']), capture=True) remove_files(sim_path, '.{}'.format(kwargs['task'])) error, interrupt, error_buffer = False, False, [] for line in output.split('\n'): if line.strip().startswith("FATAL") or line.strip().startswith( "ERROR"): error, interrupt = True, True elif line.strip().startswith("INFO"): error = False if len(error_buffer) > 0: logger.error('Cooja error:\n' + '\n'.join(error_buffer)) error_buffer = [] if error: error_buffer.append(line) if interrupt: logger.warn( "Cooja failed to execute ; 'run' interrupted (no parsing done)" ) raise Exception("Cooja failed to execute") # once the execution is over, gather the screenshots into a single GIF and keep the first and # the last screenshots ; move these to the results folder logger.debug(" > Gathering screenshots in an animated GIF...") with lcd(data): local( 'convert -delay 10 -loop 0 network*.png wsn-{}-malicious.gif' .format(sim), capture=True) network_images = { int(fn.split('.')[0].split('_')[-1]): fn for fn in listdir(data) if fn.startswith('network_') } move_files(data, results, 'wsn-{}-malicious.gif'.format(sim)) net_start_old = network_images[min(network_images.keys())] net_start, ext = splitext(net_start_old) net_start_new = 'wsn-{}-malicious_start{}'.format(sim, ext) net_end_old = network_images[max(network_images.keys())] net_end, ext = splitext(net_end_old) net_end_new = 'wsn-{}-malicious_end{}'.format(sim, ext) move_files(data, results, (net_start_old, net_start_new), (net_end_old, net_end_new)) remove_files(data, *network_images.values()) # then start the parsing functions to derive more results logger.debug(" > Parsing simulation results...") parsing_chain(sim_path) move_files(sim_path, results, 'COOJA.log')
def setup(silent=False, **kwargs): """ Setup the framework. """ recompile = False # adapt IPv6 debug mode modify_ipv6_debug(CONTIKI_FOLDER) # install Cooja modifications if not check_cooja(COOJA_FOLDER): logger.debug(" > Installing Cooja add-ons...") # modify Cooja.java and adapt build.xml and ~/.cooja.user.properties modify_cooja(COOJA_FOLDER) update_cooja_build(COOJA_FOLDER) update_cooja_user_properties() recompile = True # install VisualizerScreenshot plugin in Cooja visualizer = join(COOJA_FOLDER, 'apps', 'visualizer_screenshot') if not exists(visualizer): logger.debug(" > Installing VisualizerScreenshot Cooja plugin...") copy_folder('src/visualizer_screenshot', visualizer) recompile = True # recompile Cooja for making the changes take effect if recompile: with lcd(CONTIKI_FOLDER): local('git submodule update --init') with lcd(COOJA_FOLDER): logger.debug(" > Recompiling Cooja...") with hide(*HIDDEN_ALL): for cmd in ["clean", "jar"]: output = local("ant {}".format(cmd), capture=True) info, error = False, False for line in output.split('\n'): if line.strip() == "": info, error = False, False elif line.startswith("BUILD"): info, error = "SUCCESSFUL" in line, "FAILED" in line if info or error: getattr( logger, "debug" if info else "error" if error else "warn")(line) else: logger.debug(" > Cooja is up-to-date") # install imagemagick with hide(*HIDDEN_ALL): imagemagick_apt_output = local('apt-cache policy imagemagick', capture=True) if 'Unable to locate package' in imagemagick_apt_output: logger.debug(" > Installing imagemagick package...") local('sudo apt-get install imagemagick -y &') else: logger.debug(" > Imagemagick is installed") # install msp430 (GCC) upgrade with hide(*HIDDEN_ALL): msp430_version_output = local('msp430-gcc --version', capture=True) if 'msp430-gcc (GCC) 4.7.0 20120322' not in msp430_version_output: txt = "In order to extend msp430x memory support, it is necessary to upgrade msp430-gcc.\n" \ "Would you like to upgrade it now ? (yes|no) [default: no] " if silent or std_input(txt, 'yellow') == "yes": logger.debug( " > Upgrading msp430-gcc from version 4.6.3 to 4.7.0...") logger.warning( "If you encounter problems with this upgrade, please refer to:\n" "https://github.com/contiki-os/contiki/wiki/MSP430X") with lcd('src/'): logger.warning( " > Upgrade now starts, this may take up to 30 minutes...") local('sudo ./upgrade-msp430.sh') local('sudo rm -r tmp/') local('export PATH=/usr/local/msp430/bin:$PATH') register_new_path_in_profile() else: logger.warning("Upgrade of library msp430-gcc aborted") logger.warning( "You may experience problems of mote memory size at compilation" ) else: logger.debug(" > Library msp430-gcc is up-to-date (version 4.7.0)") # create a new desktop shortcut for the framework desktop = expanduser('~/Desktop') shortcut = join(desktop, 'rpl-attacks-framework.desktop') if not exists(desktop): makedirs(desktop) if not exists(shortcut): with hide(*HIDDEN_ALL): local('sudo cp {} /usr/share/icons/hicolor/scalable/apps/'.format( join(FRAMEWORK_FOLDER, 'src/rpla-icon.svg'))) local('sudo gtk-update-icon-cache /usr/share/icons/hicolor') with open(shortcut, 'w+') as f: f.write(SHORTCUT.format(path=FRAMEWORK_FOLDER)) chmod(shortcut, int('775', 8)) logger.debug(" > Desktop shortcut created") else: logger.debug(" > Desktop shortcut already exists")
def __make(name, ask=True, **kwargs): """ Make a new experiment. :param name: experiment name (or path to the experiment, if expanded in the 'command' decorator) :param ask: ask confirmation :param path: expanded path of the experiment (dynamically filled in through 'command' decorator with 'expand' :param kwargs: simulation keyword arguments (see the documentation for more information) """ global reuse_bin_path path = kwargs['path'] logger.debug(" > Validating parameters...") params = validated_parameters(kwargs) ext_lib = params.get("ext_lib") if ext_lib and not exists(ext_lib): logger.error("External library does not exist !") logger.critical("Make aborded.") return False logger.debug(" > Creating simulation...") # create experiment's directories check_structure(path, create=True, remove=True) templates = get_path(path, 'templates', create=True) get_path(templates, 'motes', create=True) # select the right malicious mote template and duplicate the simulation file copy_files((TEMPLATES_FOLDER, 'experiment'), templates, ('motes/{}.c'.format(params["mtype_root"]), 'motes/root.c'), ('motes/{}.c'.format(params["mtype_sensor"]), 'motes/sensor.c'), ('motes/{}.c'.format(params["mtype_malicious"]), 'motes/malicious.c'), 'motes/Makefile', 'Makefile', 'simulation.csc', 'script.js') # create experiment's files from templates then clean the templates folder replacements = render_templates(path, **params) remove_folder(templates) # now, write the config file without the list of motes del params['motes'] write_config(path, params) # now compile with settings(hide(*HIDDEN_ALL), warn_only=True): with_malicious = join(path, 'with-malicious', 'motes') without_malicious = join(path, 'without-malicious', 'motes') contiki = join(with_malicious, 'contiki') contiki_rpl = join(contiki, 'core', 'net', 'rpl') # copy a reduced version of Contiki where the debug flags can be set for RPL files set in DEBUG_FILES copy_folder(CONTIKI_FOLDER, with_malicious, includes=get_contiki_includes(params["target"], params["malicious_target"])) apply_debug_flags(contiki_rpl, debug=['NONE', 'PRINT'][params["debug"]]) with lcd(with_malicious): # first, compile root and sensor mote types croot, csensor = 'root.{}'.format(params["target"]), 'sensor.{}'.format(params["target"]) if reuse_bin_path is None or reuse_bin_path == with_malicious: logger.debug(" > Making '{}'...".format(croot)) stderr(local)("make root CONTIKI={}".format(contiki), capture=True) logger.debug(" > Making '{}'...".format(csensor)) stderr(local)("make sensor CONTIKI={}".format(contiki), capture=True) # here, files are moved ; otherwise, 'make clean' would also remove *.z1 move_files(with_malicious, without_malicious, croot, csensor) # after compiling, clean artifacts local('make clean') remove_files(with_malicious, 'root.c', 'sensor.c') else: copy_files(reuse_bin_path, without_malicious, croot, csensor) # second, handle the malicious mote compilation malicious = 'malicious.{}'.format(params["malicious_target"]) if ext_lib is not None: remove_folder(contiki_rpl) copy_folder(ext_lib, contiki_rpl) apply_replacements(contiki_rpl, replacements) logger.debug(" > Making '{}'...".format(malicious)) stderr(local)("make malicious CONTIKI={} TARGET={}" .format(contiki, params["malicious_target"]), capture=True) # temporary move compiled malicious mote, clean the compilation artifacts, move the malicious mote back # from the temporary location and copy compiled root and sensor motes move_files(with_malicious, without_malicious, malicious) local('make clean') move_files(without_malicious, with_malicious, malicious) copy_files(without_malicious, with_malicious, croot, csensor) # finally, remove compilation sources remove_files(with_malicious, 'malicious.c') remove_folder(contiki)
def setup(silent=False, **kwargs): """ Setup the framework. """ recompile = False # install Cooja modifications if not check_cooja(COOJA_FOLDER): logger.debug(" > Installing Cooja add-ons...") # modify Cooja.java and adapt build.xml and ~/.cooja.user.properties modify_cooja(COOJA_FOLDER) update_cooja_build(COOJA_FOLDER) update_cooja_user_properties() recompile = True # install VisualizerScreenshot plugin in Cooja visualizer = join(COOJA_FOLDER, 'apps', 'visualizer_screenshot') if not exists(visualizer): logger.debug(" > Installing VisualizerScreenshot Cooja plugin...") copy_folder('src/visualizer_screenshot', visualizer) recompile = True # recompile Cooja for making the changes take effect if recompile: with lcd(COOJA_FOLDER): logger.debug(" > Recompiling Cooja...") with settings(warn_only=True): local("ant clean") local("ant jar") else: logger.debug(" > Cooja is up-to-date") # install imagemagick with hide(*HIDDEN_ALL): imagemagick_apt_output = local('apt-cache policy imagemagick', capture=True) if 'Unable to locate package' in imagemagick_apt_output: logger.debug(" > Installing imagemagick package...") sudo("apt-get install imagemagick -y &") else: logger.debug(" > Imagemagick is installed") # install msp430 (GCC) upgrade with hide(*HIDDEN_ALL): msp430_version_output = local('msp430-gcc --version', capture=True) if 'msp430-gcc (GCC) 4.7.0 20120322' not in msp430_version_output: txt = "In order to extend msp430x memory support, it is necessary to upgrade msp430-gcc.\n" \ "Would you like to upgrade it now ? (yes|no) [default: no] " answer = std_input(txt, 'yellow') if answer == "yes": logger.debug(" > Upgrading msp430-gcc from version 4.6.3 to 4.7.0...") logger.warning("If you encounter problems with this upgrade, please refer to:\n" "https://github.com/contiki-os/contiki/wiki/MSP430X") with lcd('src/'): logger.warning(" > Upgrade now starts, this may take up to 30 minutes...") sudo('./upgrade-msp430.sh') sudo('rm -r tmp/') local('export PATH=/usr/local/msp430/bin:$PATH') register_new_path_in_profile() else: logger.warning("Upgrade of library msp430-gcc aborted") logger.warning("You may experience problems of mote memory size at compilation") else: logger.debug(" > Library msp430-gcc is up-to-date (version 4.7.0)")