blob: 157351a8f3aef129c89a37d18820110cbc846717 [file] [log] [blame]
#!/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)