| #!/usr/bin/env python |
| |
| |
| # |
| # Copyright (c) 2016-2017 Nest Labs, Inc. |
| # All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| # |
| # @file |
| # Implementes WeaveiSecurityPing class that tests Weave Security Ping(CASE, PASE) among Weave Nodes. |
| # |
| |
| import os |
| import sys |
| import time |
| |
| from happy.ReturnMsg import ReturnMsg |
| from happy.Utils import * |
| from happy.HappyNode import HappyNode |
| from happy.HappyNetwork import HappyNetwork |
| |
| from plugin.WeaveTest import WeaveTest |
| import plugin.WeaveUtilities as WeaveUtilities |
| import plugins.plaid.Plaid as Plaid |
| |
| |
| options = {} |
| options["client"] = None |
| options["server"] = None |
| options["count"] = None |
| options["udp"] = True |
| options["wrmp"] = False |
| options["tcp"] = False |
| options["quiet"] = False |
| options["CASE"] = True |
| options["PASE"] = False |
| options["group_key"] = False |
| options["group_key_id"] = "0x00005536" |
| options["tap"] = None |
| options["client_faults"] = None |
| options["server_faults"] = None |
| options["iterations"] = None |
| options["plaid"] = False |
| options["test_tag"] = "" |
| options["strace"] = False |
| options["use_persistent_storage"] = True |
| |
| |
| def option(): |
| return options.copy() |
| |
| |
| class WeaveSecurityPing(HappyNode, HappyNetwork, WeaveTest): |
| """ |
| weave-security-ping [-h --help] [-q --quiet] [-o --origin <NAME>] [-s --server <NAME>] |
| [-c --count <NUMBER>] [-u --udp] [-t --tcp] [-C --CASE] [-P --PASE] [-G --group-key [--group-key-id <key-id>]] |
| [-p --tap <TAP_INTERFACE>] [--client_faults <fault-injection configuration>] |
| [--server_faults <fault-injection configuration>] |
| |
| commands: |
| $ weave-security-ping -o node01 -s node02 -u -C |
| weave ping test between node01 and node02 via udp using CASE authenticate |
| |
| $ weave-security-ping -o node01 -s node02 -t -C |
| weave ping test between node01 and node02 via tcp using CASE authenticate |
| |
| $ weave-security-ping -o node01 -s node02 -t -P |
| weave ping test between node01 and node02 via tcp using PASE authenticate |
| |
| $ weave-security-ping -o node01 -s node02 -u --wrmp -G |
| weave ping test between node01 and node02 via wrmp over udp using group-key encryption, |
| using the default 0x00005536 key-id |
| |
| $ weave-security-ping -o node01 -s node02 -u --wrmp -G --group-key-id 0x00005536 |
| weave ping test between node01 and node02 via wrmp over udp using group-key encryption |
| |
| $ weave-security-ping -o node01 -s node02 -t -C --server_faults Weave_CASEKeyConfirm_s0_f1 |
| weave ping test between node01 and node02 via tcp using CASE authenticate; |
| the server will fail the CASE key confirmation step |
| |
| return: |
| 0-100 percentage of the lost packets |
| |
| """ |
| def __init__(self, opts = options): |
| HappyNode.__init__(self) |
| HappyNetwork.__init__(self) |
| WeaveTest.__init__(self) |
| |
| self.quiet = opts["quiet"] |
| self.tcp = opts["tcp"] |
| self.udp = opts["udp"] |
| self.wrmp = opts["wrmp"] |
| self.case = opts["CASE"] |
| self.pase = opts["PASE"] |
| self.group_key = opts["group_key"] |
| self.group_key_id = opts["group_key_id"] |
| self.count = opts["count"] |
| self.client = opts["client"] |
| self.server = opts["server"] |
| self.tap = opts["tap"] |
| self.client_faults = opts["client_faults"] |
| self.server_faults = opts["server_faults"] |
| self.iterations = opts["iterations"] |
| self.strace = opts["strace"] |
| self.use_persistent_storage = opts["use_persistent_storage"] |
| |
| self.no_service = False |
| |
| self.server_process_tag = "WEAVE-SECURITY-PING-SERVER" + opts["test_tag"] |
| self.client_process_tag = "WEAVE-SECURITY-PING-CLIENT" + opts["test_tag"] |
| self.plaid_server_process_tag = "PLAID_SERVER" + opts["test_tag"] |
| |
| self.client_node_id = None |
| self.server_node_id = None |
| |
| plaid_opts = Plaid.default_options() |
| plaid_opts['quiet'] = self.quiet |
| self.plaid_server_node_id = 'node03' |
| plaid_opts['server_node_id'] = self.plaid_server_node_id |
| plaid_opts['num_clients'] = 2 |
| plaid_opts['server_ip_address'] = self.getNodeWeaveIPAddress(self.plaid_server_node_id) |
| plaid_opts['interface'] = 'wlan0' |
| self.plaid = Plaid.Plaid(plaid_opts) |
| |
| self.use_plaid = opts["plaid"] |
| if opts["plaid"] == "auto": |
| if self.server == "service": |
| # can't use plaid when talking to an external service |
| self.use_plaid = False |
| else: |
| self.use_plaid = self.plaid.isPlaidConfigured() |
| |
| |
| def __pre_check(self): |
| # Check if Weave Ping client node is given. |
| if self.client == None: |
| emsg = "Missing name or address of the Weave Ping client node." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| # Check if Weave Ping server node is given. |
| if self.server == None: |
| emsg = "Missing name or address of the Weave Ping server node." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| # Make sure that fabric was created |
| if self.getFabricId() == None: |
| emsg = "Weave Fabric has not been created yet." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| if self.count != None and self.count.isdigit(): |
| self.count = int(float(self.count)) |
| else: |
| self.count = 1 |
| |
| # Check if Weave Ping client node exists. |
| if self._nodeExists(self.client): |
| self.client_node_id = self.client |
| |
| # Check if Weave Ping server node exists. |
| if self._nodeExists(self.server): |
| self.server_node_id = self.server |
| |
| # Check if client is provided in a form of IP address |
| if self.isIpAddress(self.client): |
| self.client_node_id = self.getNodeIdFromAddress(self.client) |
| |
| # Check if server is provided in a form of IP address |
| if self.isIpAddress(self.server): |
| self.no_service = True |
| self.server_ip = self.server |
| self.server_weave_id = self.IPv6toWeaveId(self.server) |
| else: |
| # Check if server is a true clound service instance |
| if self.getNodeType(self.server) == self.node_type_service: |
| self.no_service = True |
| |
| if self.client_node_id == None: |
| emsg = "Unknown identity of the client node." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| if not self.no_service and self.server_node_id == None: |
| emsg = "Unknown identity of the server node." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| if self.getNodeType(self.client_node_id) == "service": |
| self.client_ip = self.getServiceWeaveIPAddress("Echo", self.client_node_id) |
| self.client_weave_id = self.getServiceWeaveID("Echo", self.client_node_id) |
| else: |
| self.client_ip = self.getNodeWeaveIPAddress(self.client_node_id) |
| self.client_weave_id = self.getWeaveNodeID(self.client_node_id) |
| |
| if self.getNodeType(self.server_node_id) == "service": |
| self.server_ip = self.getServiceWeaveIPAddress("Echo", self.server_node_id) |
| self.server_weave_id = self.getServiceWeaveID("Echo", self.server_node_id) |
| else: |
| if not self.no_service: |
| self.server_ip = self.getNodeWeaveIPAddress(self.server_node_id) |
| self.server_weave_id = self.getWeaveNodeID(self.server_node_id) |
| |
| # Check if all unknowns were found |
| |
| if self.client_ip == None: |
| emsg = "Could not find IP address of the client node." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| if self.server_ip == None: |
| emsg = "Could not find IP address of the server node." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| if self.client_weave_id == None: |
| emsg = "Could not find Weave node ID of the client node." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| if not self.no_service and self.server_weave_id == None: |
| emsg = "Could not find Weave node ID of the server node." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| num_security_modes = 0 |
| if self.case == True: |
| num_security_modes += 1 |
| if self.pase == True: |
| num_security_modes += 1 |
| if self.group_key == True: |
| num_security_modes += 1 |
| |
| if num_security_modes > 1: |
| emsg = "Only one security mode can be enabled." |
| self.logger.error("[localhost] WeaveSecurityPing: %s" % (emsg)) |
| sys.exit(1) |
| |
| |
| |
| def __process_results(self, output): |
| # search for '%' e.g. 1/1(100.00%) |
| loss_percentage = None |
| |
| # only consider the last iteration |
| iterations = output.split("Iteration ") |
| |
| output = iterations[-1] |
| |
| for line in output.split("\n"): |
| if not "%" in line: |
| continue |
| |
| for token in line.split(): |
| if "%" in token: |
| loss_percentage = token.strip().split("(")[1] |
| loss_percentage = loss_percentage.translate(None, "()%") |
| |
| if loss_percentage == None: |
| # hmmm, we haven't found our line |
| loss_percentage = 100 |
| else: |
| loss_percentage = float(loss_percentage) |
| loss_percentage = 100 - loss_percentage |
| |
| if self.quiet == False: |
| print "weave-security-ping from node %s (%s) to node %s (%s) : " % \ |
| (self.client_node_id, self.client_ip, |
| self.server_node_id, self.server_ip), |
| |
| if loss_percentage == 0: |
| print hgreen("%d%% packet loss" % (loss_percentage)) |
| else: |
| print hred("%d%% packet loss" % (loss_percentage)) |
| |
| return (loss_percentage, output) # indicate the loss for each client |
| |
| def __start_plaid_server(self): |
| |
| self.plaid.startPlaidServerProcess() |
| |
| emsg = "plaid-server should be running." |
| self.logger.debug("[%s] WeaveSecurityPing: %s" % (self.plaid_server_node_id, emsg)) |
| |
| def __start_server_side(self): |
| if self.no_service: |
| return |
| |
| cmd = self.getWeavePingPath() |
| if not cmd: |
| return |
| |
| cmd += " --debug-resource-usage --print-fault-counters" |
| |
| if self.tcp: |
| cmd += " --tcp" |
| else: |
| # default is UDP |
| cmd += " --udp" |
| if self.wrmp: |
| cmd += " --wrmp" |
| |
| if self.pase: |
| cmd += " --pase --pairing-code test" |
| elif self.group_key: |
| cmd += " --group-enc --group-enc-key-id " + self.group_key_id |
| |
| if self.tap: |
| cmd += " --interface " + self.tap |
| |
| if self.server_faults: |
| cmd += " --faults " + self.server_faults |
| |
| if self.server_faults or self.client_faults: |
| cmd += " --extra-cleanup-time 10000" |
| |
| custom_env = {} |
| if self.use_plaid: |
| custom_env = self.plaid.getPlaidClientLibEnv(self.server_node_id) |
| |
| self.start_simple_weave_server(cmd, self.server_ip, |
| self.server_node_id, self.server_process_tag, strace=self.strace, env=custom_env, use_persistent_storage=self.use_persistent_storage) |
| |
| |
| def __start_client_side(self, pase_fail = False): |
| cmd = self.getWeavePingPath() |
| if not cmd: |
| return |
| |
| cmd += " --debug-resource-usage --print-fault-counters" |
| |
| if self.tcp: |
| cmd += " --tcp" |
| else: |
| # default is UDP |
| cmd += " --udp" |
| if self.wrmp: |
| cmd += " --wrmp" |
| |
| if self.pase: |
| if pase_fail: |
| cmd += " --pase --pairing-code fail" |
| else: |
| cmd += " --pase --pairing-code test" |
| elif self.case: |
| cmd += " --case" |
| elif self.group_key: |
| cmd += " --group-enc --group-enc-key-id 0x00005536" |
| |
| cmd += " --count " + str(self.count) |
| |
| if self.tap: |
| cmd += " --interface " + self.tap |
| |
| if self.client_faults: |
| cmd += " --faults " + self.client_faults |
| |
| if self.iterations: |
| cmd += " --iterations " + str(self.iterations) |
| |
| if self.server_faults or self.client_faults: |
| cmd += " --extra-cleanup-time 10000" |
| |
| custom_env = {} |
| if self.use_plaid: |
| custom_env = self.plaid.getPlaidClientLibEnv(self.client_node_id) |
| |
| self.start_simple_weave_client(cmd, self.client_ip, |
| self.server_ip, self.server_weave_id, |
| self.client_node_id, self.client_process_tag, strace=self.strace, env=custom_env, use_persistent_storage=self.use_persistent_storage) |
| |
| |
| def __wait_for_client(self): |
| self.wait_for_test_to_end(self.client_node_id, self.client_process_tag) |
| |
| |
| def __stop_plaid_server(self): |
| self.plaid.stopPlaidServerProcess() |
| |
| |
| def __stop_server_side(self): |
| if self.no_service: |
| return |
| |
| self.stop_weave_process(self.server_node_id, self.server_process_tag) |
| |
| |
| def run(self): |
| self.logger.debug("[localhost] WeaveSecurityPing: Run.") |
| |
| self.__pre_check() |
| |
| if self.use_plaid: |
| self.__start_plaid_server() |
| |
| self.__start_server_side() |
| |
| emsg = "WeaveSecurityPing %s should be running." % (self.server_process_tag) |
| self.logger.debug("[%s] WeaveSecurityPing: %s" % (self.server_node_id, emsg)) |
| |
| self.__start_client_side(False) |
| self.__wait_for_client() |
| |
| client_output_value, client_output_data = \ |
| self.get_test_output(self.client_node_id, self.client_process_tag, True) |
| if self.strace: |
| client_strace_value, client_strace_data = \ |
| self.get_test_strace(self.client_node_id, self.client_process_tag, True) |
| |
| |
| if self.no_service: |
| server_output_data = "" |
| server_strace_data = "" |
| else: |
| self.__stop_server_side() |
| if self.use_plaid: |
| self.__stop_plaid_server() |
| server_output_value, server_output_data = \ |
| self.get_test_output(self.server_node_id, self.server_process_tag, True) |
| if self.strace: |
| server_strace_value, server_strace_data = \ |
| self.get_test_strace(self.server_node_id, self.server_process_tag, True) |
| |
| avg, results = self.__process_results(client_output_data) |
| client_parser_error, client_leak_detected = WeaveUtilities.scan_for_leaks_and_parser_errors(client_output_data) |
| server_parser_error, server_leak_detected = WeaveUtilities.scan_for_leaks_and_parser_errors(server_output_data) |
| |
| data = {} |
| data["client_output"] = client_output_data |
| data["server_output"] = server_output_data |
| if self.strace: |
| data["client_strace"] = client_strace_data |
| data["server_strace"] = server_strace_data |
| data["other_failure"] = client_parser_error or client_leak_detected or server_parser_error or server_leak_detected |
| |
| self.logger.debug("[localhost] WeaveSecurityPing: Done.") |
| |
| return ReturnMsg(avg, data) |
| |