1*cfb92d14SAndroid Build Coastguard Worker#!/usr/bin/env python 2*cfb92d14SAndroid Build Coastguard Worker# 3*cfb92d14SAndroid Build Coastguard Worker# Copyright (c) 2020, The OpenThread Authors. 4*cfb92d14SAndroid Build Coastguard Worker# All rights reserved. 5*cfb92d14SAndroid Build Coastguard Worker# 6*cfb92d14SAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without 7*cfb92d14SAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are met: 8*cfb92d14SAndroid Build Coastguard Worker# 1. Redistributions of source code must retain the above copyright 9*cfb92d14SAndroid Build Coastguard Worker# notice, this list of conditions and the following disclaimer. 10*cfb92d14SAndroid Build Coastguard Worker# 2. Redistributions in binary form must reproduce the above copyright 11*cfb92d14SAndroid Build Coastguard Worker# notice, this list of conditions and the following disclaimer in the 12*cfb92d14SAndroid Build Coastguard Worker# documentation and/or other materials provided with the distribution. 13*cfb92d14SAndroid Build Coastguard Worker# 3. Neither the name of the copyright holder nor the 14*cfb92d14SAndroid Build Coastguard Worker# names of its contributors may be used to endorse or promote products 15*cfb92d14SAndroid Build Coastguard Worker# derived from this software without specific prior written permission. 16*cfb92d14SAndroid Build Coastguard Worker# 17*cfb92d14SAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18*cfb92d14SAndroid Build Coastguard Worker# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19*cfb92d14SAndroid Build Coastguard Worker# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20*cfb92d14SAndroid Build Coastguard Worker# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21*cfb92d14SAndroid Build Coastguard Worker# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22*cfb92d14SAndroid Build Coastguard Worker# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23*cfb92d14SAndroid Build Coastguard Worker# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24*cfb92d14SAndroid Build Coastguard Worker# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25*cfb92d14SAndroid Build Coastguard Worker# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26*cfb92d14SAndroid Build Coastguard Worker# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27*cfb92d14SAndroid Build Coastguard Worker# POSSIBILITY OF SUCH DAMAGE. 28*cfb92d14SAndroid Build Coastguard Worker""" 29*cfb92d14SAndroid Build Coastguard Worker>> Thread Host Controller Interface 30*cfb92d14SAndroid Build Coastguard Worker>> Device : OpenThread_BR THCI 31*cfb92d14SAndroid Build Coastguard Worker>> Class : OpenThread_BR 32*cfb92d14SAndroid Build Coastguard Worker""" 33*cfb92d14SAndroid Build Coastguard Workerimport logging 34*cfb92d14SAndroid Build Coastguard Workerimport re 35*cfb92d14SAndroid Build Coastguard Workerimport sys 36*cfb92d14SAndroid Build Coastguard Workerimport time 37*cfb92d14SAndroid Build Coastguard Workerimport ipaddress 38*cfb92d14SAndroid Build Coastguard Worker 39*cfb92d14SAndroid Build Coastguard Workerimport serial 40*cfb92d14SAndroid Build Coastguard Workerfrom IThci import IThci 41*cfb92d14SAndroid Build Coastguard Workerfrom THCI.OpenThread import OpenThreadTHCI, watched, API 42*cfb92d14SAndroid Build Coastguard Worker 43*cfb92d14SAndroid Build Coastguard WorkerRPI_FULL_PROMPT = 'pi@raspberrypi:~$ ' 44*cfb92d14SAndroid Build Coastguard WorkerRPI_USERNAME_PROMPT = 'raspberrypi login: ' 45*cfb92d14SAndroid Build Coastguard WorkerRPI_PASSWORD_PROMPT = 'Password: ' 46*cfb92d14SAndroid Build Coastguard Worker"""regex: used to split lines""" 47*cfb92d14SAndroid Build Coastguard WorkerLINESEPX = re.compile(r'\r\n|\n') 48*cfb92d14SAndroid Build Coastguard Worker 49*cfb92d14SAndroid Build Coastguard WorkerLOGX = re.compile(r'.*Under-voltage detected!') 50*cfb92d14SAndroid Build Coastguard Worker"""regex: used to filter logging""" 51*cfb92d14SAndroid Build Coastguard Worker 52*cfb92d14SAndroid Build Coastguard Workerassert LOGX.match('[57522.618196] Under-voltage detected! (0x00050005)') 53*cfb92d14SAndroid Build Coastguard Worker 54*cfb92d14SAndroid Build Coastguard WorkerOTBR_AGENT_SYSLOG_PATTERN = re.compile(r'raspberrypi otbr-agent\[\d+\]: (.*)') 55*cfb92d14SAndroid Build Coastguard Workerassert OTBR_AGENT_SYSLOG_PATTERN.search( 56*cfb92d14SAndroid Build Coastguard Worker 'Jun 23 05:21:22 raspberrypi otbr-agent[323]: =========[[THCI] direction=send | type=JOIN_FIN.req | len=039]==========]' 57*cfb92d14SAndroid Build Coastguard Worker).group(1) == '=========[[THCI] direction=send | type=JOIN_FIN.req | len=039]==========]' 58*cfb92d14SAndroid Build Coastguard Worker 59*cfb92d14SAndroid Build Coastguard Workerlogging.getLogger('paramiko').setLevel(logging.WARNING) 60*cfb92d14SAndroid Build Coastguard Worker 61*cfb92d14SAndroid Build Coastguard Worker 62*cfb92d14SAndroid Build Coastguard Workerclass SSHHandle(object): 63*cfb92d14SAndroid Build Coastguard Worker # Unit: second 64*cfb92d14SAndroid Build Coastguard Worker KEEPALIVE_INTERVAL = 30 65*cfb92d14SAndroid Build Coastguard Worker 66*cfb92d14SAndroid Build Coastguard Worker def __init__(self, ip, port, username, password): 67*cfb92d14SAndroid Build Coastguard Worker self.ip = ip 68*cfb92d14SAndroid Build Coastguard Worker self.port = int(port) 69*cfb92d14SAndroid Build Coastguard Worker self.username = username 70*cfb92d14SAndroid Build Coastguard Worker self.password = password 71*cfb92d14SAndroid Build Coastguard Worker self.__handle = None 72*cfb92d14SAndroid Build Coastguard Worker 73*cfb92d14SAndroid Build Coastguard Worker self.__connect() 74*cfb92d14SAndroid Build Coastguard Worker 75*cfb92d14SAndroid Build Coastguard Worker def __connect(self): 76*cfb92d14SAndroid Build Coastguard Worker import paramiko 77*cfb92d14SAndroid Build Coastguard Worker 78*cfb92d14SAndroid Build Coastguard Worker self.close() 79*cfb92d14SAndroid Build Coastguard Worker 80*cfb92d14SAndroid Build Coastguard Worker self.__handle = paramiko.SSHClient() 81*cfb92d14SAndroid Build Coastguard Worker self.__handle.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 82*cfb92d14SAndroid Build Coastguard Worker try: 83*cfb92d14SAndroid Build Coastguard Worker self.__handle.connect(self.ip, port=self.port, username=self.username, password=self.password) 84*cfb92d14SAndroid Build Coastguard Worker except paramiko.ssh_exception.AuthenticationException: 85*cfb92d14SAndroid Build Coastguard Worker if not self.password: 86*cfb92d14SAndroid Build Coastguard Worker self.__handle.get_transport().auth_none(self.username) 87*cfb92d14SAndroid Build Coastguard Worker else: 88*cfb92d14SAndroid Build Coastguard Worker raise 89*cfb92d14SAndroid Build Coastguard Worker 90*cfb92d14SAndroid Build Coastguard Worker # Avoid SSH disconnection after idle for a long time 91*cfb92d14SAndroid Build Coastguard Worker self.__handle.get_transport().set_keepalive(self.KEEPALIVE_INTERVAL) 92*cfb92d14SAndroid Build Coastguard Worker 93*cfb92d14SAndroid Build Coastguard Worker def close(self): 94*cfb92d14SAndroid Build Coastguard Worker if self.__handle is not None: 95*cfb92d14SAndroid Build Coastguard Worker self.__handle.close() 96*cfb92d14SAndroid Build Coastguard Worker self.__handle = None 97*cfb92d14SAndroid Build Coastguard Worker 98*cfb92d14SAndroid Build Coastguard Worker def bash(self, cmd, timeout): 99*cfb92d14SAndroid Build Coastguard Worker from paramiko import SSHException 100*cfb92d14SAndroid Build Coastguard Worker retry = 3 101*cfb92d14SAndroid Build Coastguard Worker for i in range(retry): 102*cfb92d14SAndroid Build Coastguard Worker try: 103*cfb92d14SAndroid Build Coastguard Worker stdin, stdout, stderr = self.__handle.exec_command(cmd, timeout=timeout) 104*cfb92d14SAndroid Build Coastguard Worker 105*cfb92d14SAndroid Build Coastguard Worker sys.stderr.write(stderr.read()) 106*cfb92d14SAndroid Build Coastguard Worker output = [r.encode('utf8').rstrip('\r\n') for r in stdout.readlines()] 107*cfb92d14SAndroid Build Coastguard Worker return output 108*cfb92d14SAndroid Build Coastguard Worker 109*cfb92d14SAndroid Build Coastguard Worker except Exception: 110*cfb92d14SAndroid Build Coastguard Worker if i < retry - 1: 111*cfb92d14SAndroid Build Coastguard Worker print('SSH connection is lost, try reconnect after 1 second.') 112*cfb92d14SAndroid Build Coastguard Worker time.sleep(1) 113*cfb92d14SAndroid Build Coastguard Worker self.__connect() 114*cfb92d14SAndroid Build Coastguard Worker else: 115*cfb92d14SAndroid Build Coastguard Worker raise 116*cfb92d14SAndroid Build Coastguard Worker 117*cfb92d14SAndroid Build Coastguard Worker def log(self, fmt, *args): 118*cfb92d14SAndroid Build Coastguard Worker try: 119*cfb92d14SAndroid Build Coastguard Worker msg = fmt % args 120*cfb92d14SAndroid Build Coastguard Worker print('%s - %s - %s' % (self.port, time.strftime('%b %d %H:%M:%S'), msg)) 121*cfb92d14SAndroid Build Coastguard Worker except Exception: 122*cfb92d14SAndroid Build Coastguard Worker pass 123*cfb92d14SAndroid Build Coastguard Worker 124*cfb92d14SAndroid Build Coastguard Worker 125*cfb92d14SAndroid Build Coastguard Workerclass SerialHandle: 126*cfb92d14SAndroid Build Coastguard Worker 127*cfb92d14SAndroid Build Coastguard Worker def __init__(self, port, baudrate): 128*cfb92d14SAndroid Build Coastguard Worker self.port = port 129*cfb92d14SAndroid Build Coastguard Worker self.__handle = serial.Serial(port, baudrate, timeout=0) 130*cfb92d14SAndroid Build Coastguard Worker 131*cfb92d14SAndroid Build Coastguard Worker self.__lines = [''] 132*cfb92d14SAndroid Build Coastguard Worker assert len(self.__lines) >= 1, self.__lines 133*cfb92d14SAndroid Build Coastguard Worker 134*cfb92d14SAndroid Build Coastguard Worker self.log("inputing username ...") 135*cfb92d14SAndroid Build Coastguard Worker self.__bashWriteLine('pi') 136*cfb92d14SAndroid Build Coastguard Worker deadline = time.time() + 20 137*cfb92d14SAndroid Build Coastguard Worker loginOk = False 138*cfb92d14SAndroid Build Coastguard Worker while time.time() < deadline: 139*cfb92d14SAndroid Build Coastguard Worker time.sleep(1) 140*cfb92d14SAndroid Build Coastguard Worker 141*cfb92d14SAndroid Build Coastguard Worker lastLine = None 142*cfb92d14SAndroid Build Coastguard Worker while True: 143*cfb92d14SAndroid Build Coastguard Worker line = self.__bashReadLine(timeout=1) 144*cfb92d14SAndroid Build Coastguard Worker 145*cfb92d14SAndroid Build Coastguard Worker if not line: 146*cfb92d14SAndroid Build Coastguard Worker break 147*cfb92d14SAndroid Build Coastguard Worker 148*cfb92d14SAndroid Build Coastguard Worker lastLine = line 149*cfb92d14SAndroid Build Coastguard Worker 150*cfb92d14SAndroid Build Coastguard Worker if lastLine == RPI_FULL_PROMPT: 151*cfb92d14SAndroid Build Coastguard Worker self.log("prompt found, login success!") 152*cfb92d14SAndroid Build Coastguard Worker loginOk = True 153*cfb92d14SAndroid Build Coastguard Worker break 154*cfb92d14SAndroid Build Coastguard Worker 155*cfb92d14SAndroid Build Coastguard Worker if lastLine == RPI_PASSWORD_PROMPT: 156*cfb92d14SAndroid Build Coastguard Worker self.log("inputing password ...") 157*cfb92d14SAndroid Build Coastguard Worker self.__bashWriteLine('raspberry') 158*cfb92d14SAndroid Build Coastguard Worker elif lastLine == RPI_USERNAME_PROMPT: 159*cfb92d14SAndroid Build Coastguard Worker self.log("inputing username ...") 160*cfb92d14SAndroid Build Coastguard Worker self.__bashWriteLine('pi') 161*cfb92d14SAndroid Build Coastguard Worker elif not lastLine: 162*cfb92d14SAndroid Build Coastguard Worker self.log("inputing username ...") 163*cfb92d14SAndroid Build Coastguard Worker self.__bashWriteLine('pi') 164*cfb92d14SAndroid Build Coastguard Worker 165*cfb92d14SAndroid Build Coastguard Worker if not loginOk: 166*cfb92d14SAndroid Build Coastguard Worker raise Exception('login fail') 167*cfb92d14SAndroid Build Coastguard Worker 168*cfb92d14SAndroid Build Coastguard Worker self.bash('stty cols 256') 169*cfb92d14SAndroid Build Coastguard Worker 170*cfb92d14SAndroid Build Coastguard Worker def log(self, fmt, *args): 171*cfb92d14SAndroid Build Coastguard Worker try: 172*cfb92d14SAndroid Build Coastguard Worker msg = fmt % args 173*cfb92d14SAndroid Build Coastguard Worker print('%s - %s - %s' % (self.port, time.strftime('%b %d %H:%M:%S'), msg)) 174*cfb92d14SAndroid Build Coastguard Worker except Exception: 175*cfb92d14SAndroid Build Coastguard Worker pass 176*cfb92d14SAndroid Build Coastguard Worker 177*cfb92d14SAndroid Build Coastguard Worker def close(self): 178*cfb92d14SAndroid Build Coastguard Worker self.__handle.close() 179*cfb92d14SAndroid Build Coastguard Worker 180*cfb92d14SAndroid Build Coastguard Worker def bash(self, cmd, timeout=10): 181*cfb92d14SAndroid Build Coastguard Worker """ 182*cfb92d14SAndroid Build Coastguard Worker Execute the command in bash. 183*cfb92d14SAndroid Build Coastguard Worker """ 184*cfb92d14SAndroid Build Coastguard Worker self.__bashClearLines() 185*cfb92d14SAndroid Build Coastguard Worker self.__bashWriteLine(cmd) 186*cfb92d14SAndroid Build Coastguard Worker self.__bashExpect(cmd, timeout=timeout, endswith=True) 187*cfb92d14SAndroid Build Coastguard Worker 188*cfb92d14SAndroid Build Coastguard Worker response = [] 189*cfb92d14SAndroid Build Coastguard Worker 190*cfb92d14SAndroid Build Coastguard Worker deadline = time.time() + timeout 191*cfb92d14SAndroid Build Coastguard Worker while time.time() < deadline: 192*cfb92d14SAndroid Build Coastguard Worker line = self.__bashReadLine() 193*cfb92d14SAndroid Build Coastguard Worker if line is None: 194*cfb92d14SAndroid Build Coastguard Worker time.sleep(0.01) 195*cfb92d14SAndroid Build Coastguard Worker continue 196*cfb92d14SAndroid Build Coastguard Worker 197*cfb92d14SAndroid Build Coastguard Worker if line == RPI_FULL_PROMPT: 198*cfb92d14SAndroid Build Coastguard Worker # return response lines without prompt 199*cfb92d14SAndroid Build Coastguard Worker return response 200*cfb92d14SAndroid Build Coastguard Worker 201*cfb92d14SAndroid Build Coastguard Worker response.append(line) 202*cfb92d14SAndroid Build Coastguard Worker 203*cfb92d14SAndroid Build Coastguard Worker self.__bashWrite('\x03') 204*cfb92d14SAndroid Build Coastguard Worker raise Exception('%s: failed to find end of response' % self.port) 205*cfb92d14SAndroid Build Coastguard Worker 206*cfb92d14SAndroid Build Coastguard Worker def __bashExpect(self, expected, timeout=20, endswith=False): 207*cfb92d14SAndroid Build Coastguard Worker self.log('Expecting [%r]' % (expected)) 208*cfb92d14SAndroid Build Coastguard Worker 209*cfb92d14SAndroid Build Coastguard Worker deadline = time.time() + timeout 210*cfb92d14SAndroid Build Coastguard Worker while time.time() < deadline: 211*cfb92d14SAndroid Build Coastguard Worker line = self.__bashReadLine() 212*cfb92d14SAndroid Build Coastguard Worker if line is None: 213*cfb92d14SAndroid Build Coastguard Worker time.sleep(0.01) 214*cfb92d14SAndroid Build Coastguard Worker continue 215*cfb92d14SAndroid Build Coastguard Worker 216*cfb92d14SAndroid Build Coastguard Worker print('[%s] Got line [%r]' % (self.port, line)) 217*cfb92d14SAndroid Build Coastguard Worker 218*cfb92d14SAndroid Build Coastguard Worker if endswith: 219*cfb92d14SAndroid Build Coastguard Worker matched = line.endswith(expected) 220*cfb92d14SAndroid Build Coastguard Worker else: 221*cfb92d14SAndroid Build Coastguard Worker matched = line == expected 222*cfb92d14SAndroid Build Coastguard Worker 223*cfb92d14SAndroid Build Coastguard Worker if matched: 224*cfb92d14SAndroid Build Coastguard Worker print('[%s] Expected [%r]' % (self.port, expected)) 225*cfb92d14SAndroid Build Coastguard Worker return 226*cfb92d14SAndroid Build Coastguard Worker 227*cfb92d14SAndroid Build Coastguard Worker # failed to find the expected string 228*cfb92d14SAndroid Build Coastguard Worker # send Ctrl+C to terminal 229*cfb92d14SAndroid Build Coastguard Worker self.__bashWrite('\x03') 230*cfb92d14SAndroid Build Coastguard Worker raise Exception('failed to find expected string[%s]' % expected) 231*cfb92d14SAndroid Build Coastguard Worker 232*cfb92d14SAndroid Build Coastguard Worker def __bashRead(self, timeout=1): 233*cfb92d14SAndroid Build Coastguard Worker deadline = time.time() + timeout 234*cfb92d14SAndroid Build Coastguard Worker data = '' 235*cfb92d14SAndroid Build Coastguard Worker while True: 236*cfb92d14SAndroid Build Coastguard Worker piece = self.__handle.read() 237*cfb92d14SAndroid Build Coastguard Worker data = data + piece.decode('utf8') 238*cfb92d14SAndroid Build Coastguard Worker if piece: 239*cfb92d14SAndroid Build Coastguard Worker continue 240*cfb92d14SAndroid Build Coastguard Worker 241*cfb92d14SAndroid Build Coastguard Worker if data or time.time() >= deadline: 242*cfb92d14SAndroid Build Coastguard Worker break 243*cfb92d14SAndroid Build Coastguard Worker 244*cfb92d14SAndroid Build Coastguard Worker if data: 245*cfb92d14SAndroid Build Coastguard Worker self.log('>>> %r', data) 246*cfb92d14SAndroid Build Coastguard Worker 247*cfb92d14SAndroid Build Coastguard Worker return data 248*cfb92d14SAndroid Build Coastguard Worker 249*cfb92d14SAndroid Build Coastguard Worker def __bashReadLine(self, timeout=1): 250*cfb92d14SAndroid Build Coastguard Worker line = self.__bashGetNextLine() 251*cfb92d14SAndroid Build Coastguard Worker if line is not None: 252*cfb92d14SAndroid Build Coastguard Worker return line 253*cfb92d14SAndroid Build Coastguard Worker 254*cfb92d14SAndroid Build Coastguard Worker assert len(self.__lines) == 1, self.__lines 255*cfb92d14SAndroid Build Coastguard Worker tail = self.__lines.pop() 256*cfb92d14SAndroid Build Coastguard Worker 257*cfb92d14SAndroid Build Coastguard Worker try: 258*cfb92d14SAndroid Build Coastguard Worker tail += self.__bashRead(timeout=timeout) 259*cfb92d14SAndroid Build Coastguard Worker tail = tail.replace(RPI_FULL_PROMPT, RPI_FULL_PROMPT + '\r\n') 260*cfb92d14SAndroid Build Coastguard Worker tail = tail.replace(RPI_USERNAME_PROMPT, RPI_USERNAME_PROMPT + '\r\n') 261*cfb92d14SAndroid Build Coastguard Worker tail = tail.replace(RPI_PASSWORD_PROMPT, RPI_PASSWORD_PROMPT + '\r\n') 262*cfb92d14SAndroid Build Coastguard Worker finally: 263*cfb92d14SAndroid Build Coastguard Worker self.__lines += [l.rstrip('\r') for l in LINESEPX.split(tail)] 264*cfb92d14SAndroid Build Coastguard Worker assert len(self.__lines) >= 1, self.__lines 265*cfb92d14SAndroid Build Coastguard Worker 266*cfb92d14SAndroid Build Coastguard Worker return self.__bashGetNextLine() 267*cfb92d14SAndroid Build Coastguard Worker 268*cfb92d14SAndroid Build Coastguard Worker def __bashGetNextLine(self): 269*cfb92d14SAndroid Build Coastguard Worker assert len(self.__lines) >= 1, self.__lines 270*cfb92d14SAndroid Build Coastguard Worker while len(self.__lines) > 1: 271*cfb92d14SAndroid Build Coastguard Worker line = self.__lines.pop(0) 272*cfb92d14SAndroid Build Coastguard Worker assert len(self.__lines) >= 1, self.__lines 273*cfb92d14SAndroid Build Coastguard Worker if LOGX.match(line): 274*cfb92d14SAndroid Build Coastguard Worker logging.info('LOG: %s', line) 275*cfb92d14SAndroid Build Coastguard Worker continue 276*cfb92d14SAndroid Build Coastguard Worker else: 277*cfb92d14SAndroid Build Coastguard Worker return line 278*cfb92d14SAndroid Build Coastguard Worker assert len(self.__lines) >= 1, self.__lines 279*cfb92d14SAndroid Build Coastguard Worker return None 280*cfb92d14SAndroid Build Coastguard Worker 281*cfb92d14SAndroid Build Coastguard Worker def __bashWrite(self, data): 282*cfb92d14SAndroid Build Coastguard Worker self.__handle.write(data) 283*cfb92d14SAndroid Build Coastguard Worker self.log("<<< %r", data) 284*cfb92d14SAndroid Build Coastguard Worker 285*cfb92d14SAndroid Build Coastguard Worker def __bashClearLines(self): 286*cfb92d14SAndroid Build Coastguard Worker assert len(self.__lines) >= 1, self.__lines 287*cfb92d14SAndroid Build Coastguard Worker while self.__bashReadLine(timeout=0) is not None: 288*cfb92d14SAndroid Build Coastguard Worker pass 289*cfb92d14SAndroid Build Coastguard Worker assert len(self.__lines) >= 1, self.__lines 290*cfb92d14SAndroid Build Coastguard Worker 291*cfb92d14SAndroid Build Coastguard Worker def __bashWriteLine(self, line): 292*cfb92d14SAndroid Build Coastguard Worker self.__bashWrite(line + '\n') 293*cfb92d14SAndroid Build Coastguard Worker 294*cfb92d14SAndroid Build Coastguard Worker 295*cfb92d14SAndroid Build Coastguard Workerclass OpenThread_BR(OpenThreadTHCI, IThci): 296*cfb92d14SAndroid Build Coastguard Worker DEFAULT_COMMAND_TIMEOUT = 20 297*cfb92d14SAndroid Build Coastguard Worker 298*cfb92d14SAndroid Build Coastguard Worker IsBorderRouter = True 299*cfb92d14SAndroid Build Coastguard Worker __is_root = False 300*cfb92d14SAndroid Build Coastguard Worker 301*cfb92d14SAndroid Build Coastguard Worker def _getHandle(self): 302*cfb92d14SAndroid Build Coastguard Worker if self.connectType == 'ip': 303*cfb92d14SAndroid Build Coastguard Worker return SSHHandle(self.telnetIp, self.telnetPort, self.telnetUsername, self.telnetPassword) 304*cfb92d14SAndroid Build Coastguard Worker else: 305*cfb92d14SAndroid Build Coastguard Worker return SerialHandle(self.port, 115200) 306*cfb92d14SAndroid Build Coastguard Worker 307*cfb92d14SAndroid Build Coastguard Worker def _connect(self): 308*cfb92d14SAndroid Build Coastguard Worker self.log("logging in to Raspberry Pi ...") 309*cfb92d14SAndroid Build Coastguard Worker self.__cli_output_lines = [] 310*cfb92d14SAndroid Build Coastguard Worker self.__syslog_skip_lines = None 311*cfb92d14SAndroid Build Coastguard Worker self.__syslog_last_read_ts = 0 312*cfb92d14SAndroid Build Coastguard Worker 313*cfb92d14SAndroid Build Coastguard Worker self.__handle = self._getHandle() 314*cfb92d14SAndroid Build Coastguard Worker if self.connectType == 'ip': 315*cfb92d14SAndroid Build Coastguard Worker self.__is_root = self.telnetUsername == 'root' 316*cfb92d14SAndroid Build Coastguard Worker 317*cfb92d14SAndroid Build Coastguard Worker def _disconnect(self): 318*cfb92d14SAndroid Build Coastguard Worker if self.__handle: 319*cfb92d14SAndroid Build Coastguard Worker self.__handle.close() 320*cfb92d14SAndroid Build Coastguard Worker self.__handle = None 321*cfb92d14SAndroid Build Coastguard Worker 322*cfb92d14SAndroid Build Coastguard Worker def _deviceBeforeReset(self): 323*cfb92d14SAndroid Build Coastguard Worker if self.isPowerDown: 324*cfb92d14SAndroid Build Coastguard Worker self.log('Powering up the device') 325*cfb92d14SAndroid Build Coastguard Worker self.powerUp() 326*cfb92d14SAndroid Build Coastguard Worker if self.IsHost: 327*cfb92d14SAndroid Build Coastguard Worker self.__stopRadvdService() 328*cfb92d14SAndroid Build Coastguard Worker self.bash('ip -6 addr del 910b::1 dev %s || true' % self.backboneNetif) 329*cfb92d14SAndroid Build Coastguard Worker self.bash('ip -6 addr del fd00:7d03:7d03:7d03::1 dev %s || true' % self.backboneNetif) 330*cfb92d14SAndroid Build Coastguard Worker 331*cfb92d14SAndroid Build Coastguard Worker self.stopListeningToAddrAll() 332*cfb92d14SAndroid Build Coastguard Worker 333*cfb92d14SAndroid Build Coastguard Worker def _deviceAfterReset(self): 334*cfb92d14SAndroid Build Coastguard Worker self.__dumpSyslog() 335*cfb92d14SAndroid Build Coastguard Worker self.__truncateSyslog() 336*cfb92d14SAndroid Build Coastguard Worker self.__enableAcceptRa() 337*cfb92d14SAndroid Build Coastguard Worker if not self.IsHost: 338*cfb92d14SAndroid Build Coastguard Worker self._restartAgentService() 339*cfb92d14SAndroid Build Coastguard Worker time.sleep(2) 340*cfb92d14SAndroid Build Coastguard Worker 341*cfb92d14SAndroid Build Coastguard Worker def __enableAcceptRa(self): 342*cfb92d14SAndroid Build Coastguard Worker self.bash('sysctl net.ipv6.conf.%s.accept_ra=2' % self.backboneNetif) 343*cfb92d14SAndroid Build Coastguard Worker 344*cfb92d14SAndroid Build Coastguard Worker def _beforeRegisterMulticast(self, sAddr='ff04::1234:777a:1', timeout=300): 345*cfb92d14SAndroid Build Coastguard Worker """subscribe to the given ipv6 address (sAddr) in interface and send MLR.req OTA 346*cfb92d14SAndroid Build Coastguard Worker 347*cfb92d14SAndroid Build Coastguard Worker Args: 348*cfb92d14SAndroid Build Coastguard Worker sAddr : str : Multicast address to be subscribed and notified OTA. 349*cfb92d14SAndroid Build Coastguard Worker """ 350*cfb92d14SAndroid Build Coastguard Worker 351*cfb92d14SAndroid Build Coastguard Worker if self.externalCommissioner is not None: 352*cfb92d14SAndroid Build Coastguard Worker self.externalCommissioner.MLR([sAddr], timeout) 353*cfb92d14SAndroid Build Coastguard Worker return True 354*cfb92d14SAndroid Build Coastguard Worker 355*cfb92d14SAndroid Build Coastguard Worker cmd = 'nohup ~/repo/openthread/tests/scripts/thread-cert/mcast6.py wpan0 %s' % sAddr 356*cfb92d14SAndroid Build Coastguard Worker cmd = cmd + ' > /dev/null 2>&1 &' 357*cfb92d14SAndroid Build Coastguard Worker self.bash(cmd) 358*cfb92d14SAndroid Build Coastguard Worker 359*cfb92d14SAndroid Build Coastguard Worker @API 360*cfb92d14SAndroid Build Coastguard Worker def setupHost(self, setDp=False, setDua=False): 361*cfb92d14SAndroid Build Coastguard Worker self.IsHost = True 362*cfb92d14SAndroid Build Coastguard Worker 363*cfb92d14SAndroid Build Coastguard Worker self.bash('ip -6 addr add 910b::1 dev %s' % self.backboneNetif) 364*cfb92d14SAndroid Build Coastguard Worker 365*cfb92d14SAndroid Build Coastguard Worker if setDua: 366*cfb92d14SAndroid Build Coastguard Worker self.bash('ip -6 addr add fd00:7d03:7d03:7d03::1 dev %s' % self.backboneNetif) 367*cfb92d14SAndroid Build Coastguard Worker 368*cfb92d14SAndroid Build Coastguard Worker self.__startRadvdService(setDp) 369*cfb92d14SAndroid Build Coastguard Worker 370*cfb92d14SAndroid Build Coastguard Worker def _deviceEscapeEscapable(self, string): 371*cfb92d14SAndroid Build Coastguard Worker """Escape CLI escapable characters in the given string. 372*cfb92d14SAndroid Build Coastguard Worker 373*cfb92d14SAndroid Build Coastguard Worker Args: 374*cfb92d14SAndroid Build Coastguard Worker string (str): UTF-8 input string. 375*cfb92d14SAndroid Build Coastguard Worker 376*cfb92d14SAndroid Build Coastguard Worker Returns: 377*cfb92d14SAndroid Build Coastguard Worker [str]: The modified string with escaped characters. 378*cfb92d14SAndroid Build Coastguard Worker """ 379*cfb92d14SAndroid Build Coastguard Worker return '"' + string + '"' 380*cfb92d14SAndroid Build Coastguard Worker 381*cfb92d14SAndroid Build Coastguard Worker @watched 382*cfb92d14SAndroid Build Coastguard Worker def bash(self, cmd, timeout=DEFAULT_COMMAND_TIMEOUT, sudo=True): 383*cfb92d14SAndroid Build Coastguard Worker return self.bash_unwatched(cmd, timeout=timeout, sudo=sudo) 384*cfb92d14SAndroid Build Coastguard Worker 385*cfb92d14SAndroid Build Coastguard Worker def bash_unwatched(self, cmd, timeout=DEFAULT_COMMAND_TIMEOUT, sudo=True): 386*cfb92d14SAndroid Build Coastguard Worker if sudo and not self.__is_root: 387*cfb92d14SAndroid Build Coastguard Worker cmd = 'sudo ' + cmd 388*cfb92d14SAndroid Build Coastguard Worker 389*cfb92d14SAndroid Build Coastguard Worker return self.__handle.bash(cmd, timeout=timeout) 390*cfb92d14SAndroid Build Coastguard Worker 391*cfb92d14SAndroid Build Coastguard Worker # Override send_udp 392*cfb92d14SAndroid Build Coastguard Worker @API 393*cfb92d14SAndroid Build Coastguard Worker def send_udp(self, interface, dst, port, payload): 394*cfb92d14SAndroid Build Coastguard Worker if interface == 0: # Thread Interface 395*cfb92d14SAndroid Build Coastguard Worker super(OpenThread_BR, self).send_udp(interface, dst, port, payload) 396*cfb92d14SAndroid Build Coastguard Worker return 397*cfb92d14SAndroid Build Coastguard Worker 398*cfb92d14SAndroid Build Coastguard Worker if interface == 1: 399*cfb92d14SAndroid Build Coastguard Worker ifname = self.backboneNetif 400*cfb92d14SAndroid Build Coastguard Worker else: 401*cfb92d14SAndroid Build Coastguard Worker raise AssertionError('Invalid interface set to send UDP: {} ' 402*cfb92d14SAndroid Build Coastguard Worker 'Available interface options: 0 - Thread; 1 - Ethernet'.format(interface)) 403*cfb92d14SAndroid Build Coastguard Worker cmd = '/home/pi/reference-device/send_udp.py %s %s %s %s' % (ifname, dst, port, payload) 404*cfb92d14SAndroid Build Coastguard Worker self.bash(cmd) 405*cfb92d14SAndroid Build Coastguard Worker 406*cfb92d14SAndroid Build Coastguard Worker @API 407*cfb92d14SAndroid Build Coastguard Worker def mldv2_query(self): 408*cfb92d14SAndroid Build Coastguard Worker ifname = self.backboneNetif 409*cfb92d14SAndroid Build Coastguard Worker dst = 'ff02::1' 410*cfb92d14SAndroid Build Coastguard Worker 411*cfb92d14SAndroid Build Coastguard Worker cmd = '/home/pi/reference-device/send_mld_query.py %s %s' % (ifname, dst) 412*cfb92d14SAndroid Build Coastguard Worker self.bash(cmd) 413*cfb92d14SAndroid Build Coastguard Worker 414*cfb92d14SAndroid Build Coastguard Worker @API 415*cfb92d14SAndroid Build Coastguard Worker def ip_neighbors_flush(self): 416*cfb92d14SAndroid Build Coastguard Worker # clear neigh cache on linux 417*cfb92d14SAndroid Build Coastguard Worker cmd1 = 'sudo ip -6 neigh flush nud all nud failed nud noarp dev %s' % self.backboneNetif 418*cfb92d14SAndroid Build Coastguard Worker cmd2 = ('sudo ip -6 neigh list nud all dev %s ' 419*cfb92d14SAndroid Build Coastguard Worker '| cut -d " " -f1 ' 420*cfb92d14SAndroid Build Coastguard Worker '| sudo xargs -I{} ip -6 neigh delete {} dev %s') % (self.backboneNetif, self.backboneNetif) 421*cfb92d14SAndroid Build Coastguard Worker cmd = '%s ; %s' % (cmd1, cmd2) 422*cfb92d14SAndroid Build Coastguard Worker self.bash(cmd, sudo=False) 423*cfb92d14SAndroid Build Coastguard Worker 424*cfb92d14SAndroid Build Coastguard Worker @API 425*cfb92d14SAndroid Build Coastguard Worker def ip_neighbors_add(self, addr, lladdr, nud='noarp'): 426*cfb92d14SAndroid Build Coastguard Worker cmd1 = 'sudo ip -6 neigh delete %s dev %s' % (addr, self.backboneNetif) 427*cfb92d14SAndroid Build Coastguard Worker cmd2 = 'sudo ip -6 neigh add %s dev %s lladdr %s nud %s' % (addr, self.backboneNetif, lladdr, nud) 428*cfb92d14SAndroid Build Coastguard Worker cmd = '%s ; %s' % (cmd1, cmd2) 429*cfb92d14SAndroid Build Coastguard Worker self.bash(cmd, sudo=False) 430*cfb92d14SAndroid Build Coastguard Worker 431*cfb92d14SAndroid Build Coastguard Worker @API 432*cfb92d14SAndroid Build Coastguard Worker def get_eth_ll(self): 433*cfb92d14SAndroid Build Coastguard Worker cmd = "ip -6 addr list dev %s | grep 'inet6 fe80' | awk '{print $2}'" % self.backboneNetif 434*cfb92d14SAndroid Build Coastguard Worker ret = self.bash(cmd)[0].split('/')[0] 435*cfb92d14SAndroid Build Coastguard Worker return ret 436*cfb92d14SAndroid Build Coastguard Worker 437*cfb92d14SAndroid Build Coastguard Worker @API 438*cfb92d14SAndroid Build Coastguard Worker def ping(self, strDestination, ilength=0, hop_limit=5, timeout=5): 439*cfb92d14SAndroid Build Coastguard Worker """ send ICMPv6 echo request with a given length to a unicast destination 440*cfb92d14SAndroid Build Coastguard Worker address 441*cfb92d14SAndroid Build Coastguard Worker 442*cfb92d14SAndroid Build Coastguard Worker Args: 443*cfb92d14SAndroid Build Coastguard Worker strDestination: the unicast destination address of ICMPv6 echo request 444*cfb92d14SAndroid Build Coastguard Worker ilength: the size of ICMPv6 echo request payload 445*cfb92d14SAndroid Build Coastguard Worker hop_limit: the hop limit 446*cfb92d14SAndroid Build Coastguard Worker timeout: time before ping() stops 447*cfb92d14SAndroid Build Coastguard Worker """ 448*cfb92d14SAndroid Build Coastguard Worker if hop_limit is None: 449*cfb92d14SAndroid Build Coastguard Worker hop_limit = 5 450*cfb92d14SAndroid Build Coastguard Worker 451*cfb92d14SAndroid Build Coastguard Worker if self.IsHost or self.IsBorderRouter: 452*cfb92d14SAndroid Build Coastguard Worker ifName = self.backboneNetif 453*cfb92d14SAndroid Build Coastguard Worker else: 454*cfb92d14SAndroid Build Coastguard Worker ifName = 'wpan0' 455*cfb92d14SAndroid Build Coastguard Worker 456*cfb92d14SAndroid Build Coastguard Worker cmd = 'ping -6 -I %s %s -c 1 -s %d -W %d -t %d' % ( 457*cfb92d14SAndroid Build Coastguard Worker ifName, 458*cfb92d14SAndroid Build Coastguard Worker strDestination, 459*cfb92d14SAndroid Build Coastguard Worker int(ilength), 460*cfb92d14SAndroid Build Coastguard Worker int(timeout), 461*cfb92d14SAndroid Build Coastguard Worker int(hop_limit), 462*cfb92d14SAndroid Build Coastguard Worker ) 463*cfb92d14SAndroid Build Coastguard Worker 464*cfb92d14SAndroid Build Coastguard Worker self.bash(cmd, sudo=False) 465*cfb92d14SAndroid Build Coastguard Worker time.sleep(timeout) 466*cfb92d14SAndroid Build Coastguard Worker 467*cfb92d14SAndroid Build Coastguard Worker def multicast_Ping(self, destination, length=20): 468*cfb92d14SAndroid Build Coastguard Worker """send ICMPv6 echo request with a given length to a multicast destination 469*cfb92d14SAndroid Build Coastguard Worker address 470*cfb92d14SAndroid Build Coastguard Worker 471*cfb92d14SAndroid Build Coastguard Worker Args: 472*cfb92d14SAndroid Build Coastguard Worker destination: the multicast destination address of ICMPv6 echo request 473*cfb92d14SAndroid Build Coastguard Worker length: the size of ICMPv6 echo request payload 474*cfb92d14SAndroid Build Coastguard Worker """ 475*cfb92d14SAndroid Build Coastguard Worker hop_limit = 5 476*cfb92d14SAndroid Build Coastguard Worker 477*cfb92d14SAndroid Build Coastguard Worker if self.IsHost or self.IsBorderRouter: 478*cfb92d14SAndroid Build Coastguard Worker ifName = self.backboneNetif 479*cfb92d14SAndroid Build Coastguard Worker else: 480*cfb92d14SAndroid Build Coastguard Worker ifName = 'wpan0' 481*cfb92d14SAndroid Build Coastguard Worker 482*cfb92d14SAndroid Build Coastguard Worker cmd = 'ping -6 -I %s %s -c 1 -s %d -t %d' % (ifName, destination, str(length), hop_limit) 483*cfb92d14SAndroid Build Coastguard Worker 484*cfb92d14SAndroid Build Coastguard Worker self.bash(cmd, sudo=False) 485*cfb92d14SAndroid Build Coastguard Worker 486*cfb92d14SAndroid Build Coastguard Worker @API 487*cfb92d14SAndroid Build Coastguard Worker def getGUA(self, filterByPrefix=None, eth=False): 488*cfb92d14SAndroid Build Coastguard Worker """get expected global unicast IPv6 address of Thread device 489*cfb92d14SAndroid Build Coastguard Worker 490*cfb92d14SAndroid Build Coastguard Worker note: existing filterByPrefix are string of in lowercase. e.g. 491*cfb92d14SAndroid Build Coastguard Worker '2001' or '2001:0db8:0001:0000". 492*cfb92d14SAndroid Build Coastguard Worker 493*cfb92d14SAndroid Build Coastguard Worker Args: 494*cfb92d14SAndroid Build Coastguard Worker filterByPrefix: a given expected global IPv6 prefix to be matched 495*cfb92d14SAndroid Build Coastguard Worker 496*cfb92d14SAndroid Build Coastguard Worker Returns: 497*cfb92d14SAndroid Build Coastguard Worker a global IPv6 address 498*cfb92d14SAndroid Build Coastguard Worker """ 499*cfb92d14SAndroid Build Coastguard Worker # get global addrs set if multiple 500*cfb92d14SAndroid Build Coastguard Worker if eth: 501*cfb92d14SAndroid Build Coastguard Worker return self.__getEthGUA(filterByPrefix=filterByPrefix) 502*cfb92d14SAndroid Build Coastguard Worker else: 503*cfb92d14SAndroid Build Coastguard Worker return super(OpenThread_BR, self).getGUA(filterByPrefix=filterByPrefix) 504*cfb92d14SAndroid Build Coastguard Worker 505*cfb92d14SAndroid Build Coastguard Worker def __getEthGUA(self, filterByPrefix=None): 506*cfb92d14SAndroid Build Coastguard Worker globalAddrs = [] 507*cfb92d14SAndroid Build Coastguard Worker 508*cfb92d14SAndroid Build Coastguard Worker cmd = 'ip -6 addr list dev %s | grep inet6' % self.backboneNetif 509*cfb92d14SAndroid Build Coastguard Worker output = self.bash(cmd, sudo=False) 510*cfb92d14SAndroid Build Coastguard Worker for line in output: 511*cfb92d14SAndroid Build Coastguard Worker # example: inet6 2401:fa00:41:23:274a:1329:3ab9:d953/64 scope global dynamic noprefixroute 512*cfb92d14SAndroid Build Coastguard Worker line = line.strip().split() 513*cfb92d14SAndroid Build Coastguard Worker 514*cfb92d14SAndroid Build Coastguard Worker if len(line) < 4 or line[2] != 'scope': 515*cfb92d14SAndroid Build Coastguard Worker continue 516*cfb92d14SAndroid Build Coastguard Worker 517*cfb92d14SAndroid Build Coastguard Worker if line[3] != 'global': 518*cfb92d14SAndroid Build Coastguard Worker continue 519*cfb92d14SAndroid Build Coastguard Worker 520*cfb92d14SAndroid Build Coastguard Worker addr = line[1].split('/')[0] 521*cfb92d14SAndroid Build Coastguard Worker addr = str(ipaddress.IPv6Address(addr.decode()).exploded) 522*cfb92d14SAndroid Build Coastguard Worker globalAddrs.append(addr) 523*cfb92d14SAndroid Build Coastguard Worker 524*cfb92d14SAndroid Build Coastguard Worker if not filterByPrefix: 525*cfb92d14SAndroid Build Coastguard Worker return globalAddrs[0] 526*cfb92d14SAndroid Build Coastguard Worker else: 527*cfb92d14SAndroid Build Coastguard Worker if filterByPrefix[-2:] != '::': 528*cfb92d14SAndroid Build Coastguard Worker filterByPrefix = '%s::' % filterByPrefix 529*cfb92d14SAndroid Build Coastguard Worker prefix = ipaddress.IPv6Network((filterByPrefix + '/64').decode()) 530*cfb92d14SAndroid Build Coastguard Worker for fullIp in globalAddrs: 531*cfb92d14SAndroid Build Coastguard Worker address = ipaddress.IPv6Address(fullIp.decode()) 532*cfb92d14SAndroid Build Coastguard Worker if address in prefix: 533*cfb92d14SAndroid Build Coastguard Worker return fullIp 534*cfb92d14SAndroid Build Coastguard Worker 535*cfb92d14SAndroid Build Coastguard Worker def _cliReadLine(self): 536*cfb92d14SAndroid Build Coastguard Worker # read commissioning log if it's commissioning 537*cfb92d14SAndroid Build Coastguard Worker if not self.__cli_output_lines: 538*cfb92d14SAndroid Build Coastguard Worker self.__readSyslogToCli() 539*cfb92d14SAndroid Build Coastguard Worker 540*cfb92d14SAndroid Build Coastguard Worker if self.__cli_output_lines: 541*cfb92d14SAndroid Build Coastguard Worker return self.__cli_output_lines.pop(0) 542*cfb92d14SAndroid Build Coastguard Worker 543*cfb92d14SAndroid Build Coastguard Worker return None 544*cfb92d14SAndroid Build Coastguard Worker 545*cfb92d14SAndroid Build Coastguard Worker @watched 546*cfb92d14SAndroid Build Coastguard Worker def _deviceGetEtherMac(self): 547*cfb92d14SAndroid Build Coastguard Worker # Harness wants it in string. Because wireshark filter for eth 548*cfb92d14SAndroid Build Coastguard Worker # cannot be applies in hex 549*cfb92d14SAndroid Build Coastguard Worker return self.bash('ip addr list dev %s | grep ether' % self.backboneNetif, sudo=False)[0].strip().split()[1] 550*cfb92d14SAndroid Build Coastguard Worker 551*cfb92d14SAndroid Build Coastguard Worker @watched 552*cfb92d14SAndroid Build Coastguard Worker def _onCommissionStart(self): 553*cfb92d14SAndroid Build Coastguard Worker assert self.__syslog_skip_lines is None 554*cfb92d14SAndroid Build Coastguard Worker self.__syslog_skip_lines = int(self.bash('wc -l /var/log/syslog', sudo=False)[0].split()[0]) 555*cfb92d14SAndroid Build Coastguard Worker self.__syslog_last_read_ts = 0 556*cfb92d14SAndroid Build Coastguard Worker 557*cfb92d14SAndroid Build Coastguard Worker @watched 558*cfb92d14SAndroid Build Coastguard Worker def _onCommissionStop(self): 559*cfb92d14SAndroid Build Coastguard Worker assert self.__syslog_skip_lines is not None 560*cfb92d14SAndroid Build Coastguard Worker self.__syslog_skip_lines = None 561*cfb92d14SAndroid Build Coastguard Worker 562*cfb92d14SAndroid Build Coastguard Worker @watched 563*cfb92d14SAndroid Build Coastguard Worker def __startRadvdService(self, setDp=False): 564*cfb92d14SAndroid Build Coastguard Worker assert self.IsHost, "radvd service runs on Host only" 565*cfb92d14SAndroid Build Coastguard Worker 566*cfb92d14SAndroid Build Coastguard Worker conf = "EOF" 567*cfb92d14SAndroid Build Coastguard Worker conf += "\ninterface %s" % self.backboneNetif 568*cfb92d14SAndroid Build Coastguard Worker conf += "\n{" 569*cfb92d14SAndroid Build Coastguard Worker conf += "\n AdvSendAdvert on;" 570*cfb92d14SAndroid Build Coastguard Worker conf += "\n" 571*cfb92d14SAndroid Build Coastguard Worker conf += "\n MinRtrAdvInterval 3;" 572*cfb92d14SAndroid Build Coastguard Worker conf += "\n MaxRtrAdvInterval 30;" 573*cfb92d14SAndroid Build Coastguard Worker conf += "\n AdvDefaultPreference low;" 574*cfb92d14SAndroid Build Coastguard Worker conf += "\n" 575*cfb92d14SAndroid Build Coastguard Worker conf += "\n prefix 910b::/64" 576*cfb92d14SAndroid Build Coastguard Worker conf += "\n {" 577*cfb92d14SAndroid Build Coastguard Worker conf += "\n AdvOnLink on;" 578*cfb92d14SAndroid Build Coastguard Worker conf += "\n AdvAutonomous on;" 579*cfb92d14SAndroid Build Coastguard Worker conf += "\n AdvRouterAddr on;" 580*cfb92d14SAndroid Build Coastguard Worker conf += "\n };" 581*cfb92d14SAndroid Build Coastguard Worker if setDp: 582*cfb92d14SAndroid Build Coastguard Worker conf += "\n" 583*cfb92d14SAndroid Build Coastguard Worker conf += "\n prefix fd00:7d03:7d03:7d03::/64" 584*cfb92d14SAndroid Build Coastguard Worker conf += "\n {" 585*cfb92d14SAndroid Build Coastguard Worker conf += "\n AdvOnLink on;" 586*cfb92d14SAndroid Build Coastguard Worker conf += "\n AdvAutonomous off;" 587*cfb92d14SAndroid Build Coastguard Worker conf += "\n AdvRouterAddr off;" 588*cfb92d14SAndroid Build Coastguard Worker conf += "\n };" 589*cfb92d14SAndroid Build Coastguard Worker conf += "\n};" 590*cfb92d14SAndroid Build Coastguard Worker conf += "\nEOF" 591*cfb92d14SAndroid Build Coastguard Worker cmd = 'sh -c "cat >/etc/radvd.conf <<%s"' % conf 592*cfb92d14SAndroid Build Coastguard Worker 593*cfb92d14SAndroid Build Coastguard Worker self.bash(cmd) 594*cfb92d14SAndroid Build Coastguard Worker self.bash(self.extraParams.get('cmd-restart-radvd', 'service radvd restart')) 595*cfb92d14SAndroid Build Coastguard Worker self.bash('service radvd status') 596*cfb92d14SAndroid Build Coastguard Worker 597*cfb92d14SAndroid Build Coastguard Worker @watched 598*cfb92d14SAndroid Build Coastguard Worker def __stopRadvdService(self): 599*cfb92d14SAndroid Build Coastguard Worker assert self.IsHost, "radvd service runs on Host only" 600*cfb92d14SAndroid Build Coastguard Worker self.bash('service radvd stop') 601*cfb92d14SAndroid Build Coastguard Worker 602*cfb92d14SAndroid Build Coastguard Worker def __readSyslogToCli(self): 603*cfb92d14SAndroid Build Coastguard Worker if self.__syslog_skip_lines is None: 604*cfb92d14SAndroid Build Coastguard Worker return 0 605*cfb92d14SAndroid Build Coastguard Worker 606*cfb92d14SAndroid Build Coastguard Worker # read syslog once per second 607*cfb92d14SAndroid Build Coastguard Worker if time.time() < self.__syslog_last_read_ts + 1: 608*cfb92d14SAndroid Build Coastguard Worker return 0 609*cfb92d14SAndroid Build Coastguard Worker 610*cfb92d14SAndroid Build Coastguard Worker self.__syslog_last_read_ts = time.time() 611*cfb92d14SAndroid Build Coastguard Worker 612*cfb92d14SAndroid Build Coastguard Worker lines = self.bash_unwatched('tail +%d /var/log/syslog' % self.__syslog_skip_lines, sudo=False) 613*cfb92d14SAndroid Build Coastguard Worker for line in lines: 614*cfb92d14SAndroid Build Coastguard Worker m = OTBR_AGENT_SYSLOG_PATTERN.search(line) 615*cfb92d14SAndroid Build Coastguard Worker if not m: 616*cfb92d14SAndroid Build Coastguard Worker continue 617*cfb92d14SAndroid Build Coastguard Worker 618*cfb92d14SAndroid Build Coastguard Worker self.__cli_output_lines.append(m.group(1)) 619*cfb92d14SAndroid Build Coastguard Worker 620*cfb92d14SAndroid Build Coastguard Worker self.__syslog_skip_lines += len(lines) 621*cfb92d14SAndroid Build Coastguard Worker return len(lines) 622*cfb92d14SAndroid Build Coastguard Worker 623*cfb92d14SAndroid Build Coastguard Worker def _cliWriteLine(self, line): 624*cfb92d14SAndroid Build Coastguard Worker cmd = 'ot-ctl -- %s' % line 625*cfb92d14SAndroid Build Coastguard Worker output = self.bash(cmd) 626*cfb92d14SAndroid Build Coastguard Worker # fake the line echo back 627*cfb92d14SAndroid Build Coastguard Worker self.__cli_output_lines.append(line) 628*cfb92d14SAndroid Build Coastguard Worker for line in output: 629*cfb92d14SAndroid Build Coastguard Worker self.__cli_output_lines.append(line) 630*cfb92d14SAndroid Build Coastguard Worker 631*cfb92d14SAndroid Build Coastguard Worker def _restartAgentService(self): 632*cfb92d14SAndroid Build Coastguard Worker restart_cmd = self.extraParams.get('cmd-restart-otbr-agent', 'systemctl restart otbr-agent') 633*cfb92d14SAndroid Build Coastguard Worker self.bash(restart_cmd) 634*cfb92d14SAndroid Build Coastguard Worker 635*cfb92d14SAndroid Build Coastguard Worker def __truncateSyslog(self): 636*cfb92d14SAndroid Build Coastguard Worker self.bash('truncate -s 0 /var/log/syslog') 637*cfb92d14SAndroid Build Coastguard Worker 638*cfb92d14SAndroid Build Coastguard Worker def __dumpSyslog(self): 639*cfb92d14SAndroid Build Coastguard Worker cmd = self.extraParams.get('cmd-dump-otbr-log', 'grep "otbr-agent" /var/log/syslog') 640*cfb92d14SAndroid Build Coastguard Worker output = self.bash_unwatched(cmd) 641*cfb92d14SAndroid Build Coastguard Worker for line in output: 642*cfb92d14SAndroid Build Coastguard Worker self.log('%s', line) 643*cfb92d14SAndroid Build Coastguard Worker 644*cfb92d14SAndroid Build Coastguard Worker @API 645*cfb92d14SAndroid Build Coastguard Worker def get_eth_addrs(self): 646*cfb92d14SAndroid Build Coastguard Worker cmd = "ip -6 addr list dev %s | grep 'inet6 ' | awk '{print $2}'" % self.backboneNetif 647*cfb92d14SAndroid Build Coastguard Worker addrs = self.bash(cmd) 648*cfb92d14SAndroid Build Coastguard Worker return [addr.split('/')[0] for addr in addrs] 649*cfb92d14SAndroid Build Coastguard Worker 650*cfb92d14SAndroid Build Coastguard Worker @API 651*cfb92d14SAndroid Build Coastguard Worker def mdns_query(self, service='_meshcop._udp.local', addrs_allowlist=(), addrs_denylist=()): 652*cfb92d14SAndroid Build Coastguard Worker try: 653*cfb92d14SAndroid Build Coastguard Worker for deny_addr in addrs_denylist: 654*cfb92d14SAndroid Build Coastguard Worker self.bash('ip6tables -A INPUT -p udp --dport 5353 -s %s -j DROP' % deny_addr) 655*cfb92d14SAndroid Build Coastguard Worker 656*cfb92d14SAndroid Build Coastguard Worker if addrs_allowlist: 657*cfb92d14SAndroid Build Coastguard Worker for allow_addr in addrs_allowlist: 658*cfb92d14SAndroid Build Coastguard Worker self.bash('ip6tables -A INPUT -p udp --dport 5353 -s %s -j ACCEPT' % allow_addr) 659*cfb92d14SAndroid Build Coastguard Worker 660*cfb92d14SAndroid Build Coastguard Worker self.bash('ip6tables -A INPUT -p udp --dport 5353 -j DROP') 661*cfb92d14SAndroid Build Coastguard Worker 662*cfb92d14SAndroid Build Coastguard Worker return self._mdns_query_impl(service, find_active=(addrs_allowlist or addrs_denylist)) 663*cfb92d14SAndroid Build Coastguard Worker 664*cfb92d14SAndroid Build Coastguard Worker finally: 665*cfb92d14SAndroid Build Coastguard Worker self.bash('ip6tables -F INPUT') 666*cfb92d14SAndroid Build Coastguard Worker time.sleep(1) 667*cfb92d14SAndroid Build Coastguard Worker 668*cfb92d14SAndroid Build Coastguard Worker def _mdns_query_impl(self, service, find_active): 669*cfb92d14SAndroid Build Coastguard Worker # For BBR-TC-03 or DH test cases (empty arguments) just send a query 670*cfb92d14SAndroid Build Coastguard Worker output = self.bash('python3 ~/repo/openthread/tests/scripts/thread-cert/find_border_agents.py') 671*cfb92d14SAndroid Build Coastguard Worker 672*cfb92d14SAndroid Build Coastguard Worker if not find_active: 673*cfb92d14SAndroid Build Coastguard Worker return 674*cfb92d14SAndroid Build Coastguard Worker 675*cfb92d14SAndroid Build Coastguard Worker # For MATN-TC-17 and MATN-TC-18 use Zeroconf to get the BBR address and border agent port 676*cfb92d14SAndroid Build Coastguard Worker for line in output: 677*cfb92d14SAndroid Build Coastguard Worker print(line) 678*cfb92d14SAndroid Build Coastguard Worker alias, addr, port, thread_status = eval(line) 679*cfb92d14SAndroid Build Coastguard Worker if thread_status == 2 and addr: 680*cfb92d14SAndroid Build Coastguard Worker if ipaddress.IPv6Address(addr.decode()).is_link_local: 681*cfb92d14SAndroid Build Coastguard Worker addr = '%s%%%s' % (addr, self.backboneNetif) 682*cfb92d14SAndroid Build Coastguard Worker return addr, port 683*cfb92d14SAndroid Build Coastguard Worker 684*cfb92d14SAndroid Build Coastguard Worker raise Exception('No active Border Agents found') 685*cfb92d14SAndroid Build Coastguard Worker 686*cfb92d14SAndroid Build Coastguard Worker # Override powerDown 687*cfb92d14SAndroid Build Coastguard Worker @API 688*cfb92d14SAndroid Build Coastguard Worker def powerDown(self): 689*cfb92d14SAndroid Build Coastguard Worker self.log('Powering down BBR') 690*cfb92d14SAndroid Build Coastguard Worker super(OpenThread_BR, self).powerDown() 691*cfb92d14SAndroid Build Coastguard Worker stop_cmd = self.extraParams.get('cmd-stop-otbr-agent', 'systemctl stop otbr-agent') 692*cfb92d14SAndroid Build Coastguard Worker self.bash(stop_cmd) 693*cfb92d14SAndroid Build Coastguard Worker 694*cfb92d14SAndroid Build Coastguard Worker # Override powerUp 695*cfb92d14SAndroid Build Coastguard Worker @API 696*cfb92d14SAndroid Build Coastguard Worker def powerUp(self): 697*cfb92d14SAndroid Build Coastguard Worker self.log('Powering up BBR') 698*cfb92d14SAndroid Build Coastguard Worker start_cmd = self.extraParams.get('cmd-start-otbr-agent', 'systemctl start otbr-agent') 699*cfb92d14SAndroid Build Coastguard Worker self.bash(start_cmd) 700*cfb92d14SAndroid Build Coastguard Worker super(OpenThread_BR, self).powerUp() 701*cfb92d14SAndroid Build Coastguard Worker 702*cfb92d14SAndroid Build Coastguard Worker # Override forceSetSlaac 703*cfb92d14SAndroid Build Coastguard Worker @API 704*cfb92d14SAndroid Build Coastguard Worker def forceSetSlaac(self, slaacAddress): 705*cfb92d14SAndroid Build Coastguard Worker self.bash('ip -6 addr add %s/64 dev wpan0' % slaacAddress) 706*cfb92d14SAndroid Build Coastguard Worker 707*cfb92d14SAndroid Build Coastguard Worker # Override stopListeningToAddr 708*cfb92d14SAndroid Build Coastguard Worker @API 709*cfb92d14SAndroid Build Coastguard Worker def stopListeningToAddr(self, sAddr): 710*cfb92d14SAndroid Build Coastguard Worker """ 711*cfb92d14SAndroid Build Coastguard Worker Unsubscribe to a given IPv6 address which was subscribed earlier with `registerMulticast`. 712*cfb92d14SAndroid Build Coastguard Worker 713*cfb92d14SAndroid Build Coastguard Worker Args: 714*cfb92d14SAndroid Build Coastguard Worker sAddr : str : Multicast address to be unsubscribed. Use an empty string to unsubscribe 715*cfb92d14SAndroid Build Coastguard Worker all the active multicast addresses. 716*cfb92d14SAndroid Build Coastguard Worker """ 717*cfb92d14SAndroid Build Coastguard Worker cmd = 'pkill -f mcast6.*%s' % sAddr 718*cfb92d14SAndroid Build Coastguard Worker self.bash(cmd) 719*cfb92d14SAndroid Build Coastguard Worker 720*cfb92d14SAndroid Build Coastguard Worker def stopListeningToAddrAll(self): 721*cfb92d14SAndroid Build Coastguard Worker return self.stopListeningToAddr('') 722*cfb92d14SAndroid Build Coastguard Worker 723*cfb92d14SAndroid Build Coastguard Worker @API 724*cfb92d14SAndroid Build Coastguard Worker def deregisterMulticast(self, sAddr): 725*cfb92d14SAndroid Build Coastguard Worker """ 726*cfb92d14SAndroid Build Coastguard Worker Unsubscribe to a given IPv6 address. 727*cfb92d14SAndroid Build Coastguard Worker Only used by External Commissioner. 728*cfb92d14SAndroid Build Coastguard Worker 729*cfb92d14SAndroid Build Coastguard Worker Args: 730*cfb92d14SAndroid Build Coastguard Worker sAddr : str : Multicast address to be unsubscribed. 731*cfb92d14SAndroid Build Coastguard Worker """ 732*cfb92d14SAndroid Build Coastguard Worker self.externalCommissioner.MLR([sAddr], 0) 733*cfb92d14SAndroid Build Coastguard Worker return True 734*cfb92d14SAndroid Build Coastguard Worker 735*cfb92d14SAndroid Build Coastguard Worker @watched 736*cfb92d14SAndroid Build Coastguard Worker def _waitBorderRoutingStabilize(self): 737*cfb92d14SAndroid Build Coastguard Worker """ 738*cfb92d14SAndroid Build Coastguard Worker Wait for Network Data to stabilize if BORDER_ROUTING is enabled. 739*cfb92d14SAndroid Build Coastguard Worker """ 740*cfb92d14SAndroid Build Coastguard Worker if not self.isBorderRoutingEnabled(): 741*cfb92d14SAndroid Build Coastguard Worker return 742*cfb92d14SAndroid Build Coastguard Worker 743*cfb92d14SAndroid Build Coastguard Worker MAX_TIMEOUT = 30 744*cfb92d14SAndroid Build Coastguard Worker MIN_TIMEOUT = 15 745*cfb92d14SAndroid Build Coastguard Worker CHECK_INTERVAL = 3 746*cfb92d14SAndroid Build Coastguard Worker 747*cfb92d14SAndroid Build Coastguard Worker time.sleep(MIN_TIMEOUT) 748*cfb92d14SAndroid Build Coastguard Worker 749*cfb92d14SAndroid Build Coastguard Worker lastNetData = self.getNetworkData() 750*cfb92d14SAndroid Build Coastguard Worker for i in range((MAX_TIMEOUT - MIN_TIMEOUT) // CHECK_INTERVAL): 751*cfb92d14SAndroid Build Coastguard Worker time.sleep(CHECK_INTERVAL) 752*cfb92d14SAndroid Build Coastguard Worker curNetData = self.getNetworkData() 753*cfb92d14SAndroid Build Coastguard Worker 754*cfb92d14SAndroid Build Coastguard Worker # Wait until the Network Data is not changing, and there is OMR Prefix and External Routes available 755*cfb92d14SAndroid Build Coastguard Worker if curNetData == lastNetData and len(curNetData['Prefixes']) > 0 and len(curNetData['Routes']) > 0: 756*cfb92d14SAndroid Build Coastguard Worker break 757*cfb92d14SAndroid Build Coastguard Worker 758*cfb92d14SAndroid Build Coastguard Worker lastNetData = curNetData 759*cfb92d14SAndroid Build Coastguard Worker 760*cfb92d14SAndroid Build Coastguard Worker return lastNetData 761