blob: b9eea51964922db3c67ba8584549c70860cb766b [file] [log] [blame]
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001#!/usr/bin/env python
2"""
32015-2016
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -07004Copyright 2016 Open Networking Foundation (ONF)
Jeremy Songsterae01bba2016-07-11 15:39:17 -07005
6Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
Jeremy Songster1f39bf02016-01-20 17:17:25 -08009
10TestON is free software: you can redistribute it and/or modify
11it under the terms of the GNU General Public License as published by
12the Free Software Foundation, either version 2 of the License, or
13( at your option ) any later version.
14
15TestON is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with TestON. If not, see <http://www.gnu.org/licenses/>.
22
23ScapyCliDriver is the basic driver which will handle the Scapy functions
24
25TODO: Add Explanation on how to install scapy
26"""
27import pexpect
28import re
29import sys
Jeremy Songster1f39bf02016-01-20 17:17:25 -080030import os
31from drivers.common.cli.emulatordriver import Emulator
32
33
34class ScapyCliDriver( Emulator ):
35
36 """
37 ScapyCliDriver is the basic driver which will handle
38 the Scapy functions"""
39 def __init__( self ):
Devin Limdc78e202017-06-09 18:30:07 -070040 super( ScapyCliDriver, self ).__init__()
Jeremy Songster1f39bf02016-01-20 17:17:25 -080041 self.handle = self
42 self.name = None
Jon Hall06fd0df2021-01-25 15:50:06 -080043 self.home = "~/"
Jeremy Songster1f39bf02016-01-20 17:17:25 -080044 self.wrapped = sys.modules[ __name__ ]
45 self.flag = 0
46 # TODO: Refactor driver to use these everywhere
You Wangdafb6e22018-01-22 17:01:00 -080047 self.hostPrompt = "\$"
Jeremy Songster1f39bf02016-01-20 17:17:25 -080048 self.scapyPrompt = ">>>"
Jon Hall06fd0df2021-01-25 15:50:06 -080049 self.sudoRequired = True
50 self.ifaceName = None
Jon Hallb0c6ae02021-09-01 10:53:39 -070051 self.scapyPath = "scapy"
Jeremy Songster1f39bf02016-01-20 17:17:25 -080052
53 def connect( self, **connectargs ):
54 """
55 Here the main is the TestON instance after creating
56 all the log handles."""
57 try:
58 for key in connectargs:
59 vars( self )[ key ] = connectargs[ key ]
Jon Hall06fd0df2021-01-25 15:50:06 -080060 for key in self.options:
61 if key == "home":
62 self.home = self.options[ key ]
63 elif key == "name":
64 self.name = self.options[ key ]
65 elif key == "sudo_required":
66 self.sudoRequired = False if self.options[ key ] == "false" else True
67 elif key == "ifaceName":
68 self.ifaceName = self.options[ key ]
Jon Hallb0c6ae02021-09-01 10:53:39 -070069 elif key == "scapy_path":
70 self.scapyPath = self.options[ key ]
Jon Hall06fd0df2021-01-25 15:50:06 -080071 if self.ifaceName is None:
72 self.ifaceName = self.name + "-eth0"
Jon Hall43060f62020-06-23 13:13:33 -070073
74 # Parse route config
75 self.routes = []
76 routes = self.options.get( 'routes' )
77 if routes:
78 for route in routes:
79 route = routes[ route ]
80 iface = route.get( 'interface' )
81 if not iface:
82 iface = None
83 self.routes.append( { 'network': route[ 'network' ],
84 'netmask': route[ 'netmask' ],
85 'gw': route.get( 'gw' ),
86 'interface': iface } )
Jeremy Songster1f39bf02016-01-20 17:17:25 -080087 try:
88 if os.getenv( str( self.ip_address ) ) is not None:
89 self.ip_address = os.getenv( str( self.ip_address ) )
90 else:
91 main.log.info( self.name +
92 ": Trying to connect to " +
93 self.ip_address )
94
95 except KeyError:
Jon Hall43060f62020-06-23 13:13:33 -070096 main.log.info( self.name + ": Invalid host name," +
Jeremy Songster1f39bf02016-01-20 17:17:25 -080097 " connecting to local host instead" )
98 self.ip_address = 'localhost'
99 except Exception as inst:
Jon Hall43060f62020-06-23 13:13:33 -0700100 main.log.error( self.name + ": Uncaught exception: " + str( inst ) )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800101
102 self.handle = super(
103 ScapyCliDriver,
104 self ).connect(
105 user_name=self.user_name,
106 ip_address=self.ip_address,
107 port=None,
108 pwd=self.pwd )
109
110 if self.handle:
Jon Hall43060f62020-06-23 13:13:33 -0700111 main.log.info( self.name + ": Connection successful to the host " +
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800112 self.user_name +
113 "@" +
114 self.ip_address )
You Wang4cc61912018-08-28 10:10:58 -0700115 return self.handle
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800116 else:
117 main.log.error( "Connection failed to the host " +
118 self.user_name +
119 "@" +
120 self.ip_address )
Jon Hall3a03cad2021-04-07 11:21:55 -0700121 main.log.error( "Failed to connect to the Host" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800122 return main.FALSE
123 except pexpect.EOF:
124 main.log.error( self.name + ": EOF exception found" )
125 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -0700126 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800127 except Exception:
128 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700129 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800130
131 def disconnect( self ):
132 """
133 Called at the end of the test to stop the scapy component and
134 disconnect the handle.
135 """
Jon Hall43060f62020-06-23 13:13:33 -0700136 main.log.debug( self.name + ": Disconnecting" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800137 response = main.TRUE
You Wangdafb6e22018-01-22 17:01:00 -0800138 try:
139 if self.handle:
140 self.handle.sendline( "exit" )
141 self.handle.expect( "closed" )
142 except pexpect.EOF:
143 main.log.error( self.name + ": EOF exception found" )
144 main.log.error( self.name + ": " + self.handle.before )
145 except Exception:
146 main.log.exception( self.name + ": Connection failed to the host" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800147 response = main.FALSE
148 return response
149
Jon Hall43060f62020-06-23 13:13:33 -0700150 def startScapy( self, mplsPath="", ifaceName=None ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800151 """
152 Start the Scapy cli
153 optional:
154 mplsPath - The path where the MPLS class is located
155 NOTE: This can be a relative path from the user's home dir
Jon Hall43060f62020-06-23 13:13:33 -0700156 ifaceName - the name of the default interface to use.
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800157 """
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700158 mplsLines = [ 'import imp',
159 'imp.load_source( "mplsClass", "{}mplsClass.py" )'.format( mplsPath ),
160 'from mplsClass import MPLS',
161 'bind_layers(Ether, MPLS, type = 0x8847)',
162 'bind_layers(MPLS, MPLS, bottom_of_label_stack = 0)',
163 'bind_layers(MPLS, IP)' ]
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800164
165 try:
Jon Hall43060f62020-06-23 13:13:33 -0700166 main.log.debug( self.name + ": Starting scapy" )
Jon Hall06fd0df2021-01-25 15:50:06 -0800167 if self.sudoRequired:
Jon Hallb0c6ae02021-09-01 10:53:39 -0700168 self.handle.sendline( "sudo %s" % self.scapyPath )
Jon Hall06fd0df2021-01-25 15:50:06 -0800169 else:
Jon Hallb0c6ae02021-09-01 10:53:39 -0700170 self.handle.sendline( self.scapyPath )
Jon Hall06fd0df2021-01-25 15:50:06 -0800171 i = self.handle.expect( [ "not found", "password for", self.scapyPrompt ] )
172 if i == 1:
173 main.log.debug( "Sudo asking for password" )
Siddeshd9840842021-08-06 19:26:05 +0000174 self.handle.sendline( self.pwd )
Jon Hall06fd0df2021-01-25 15:50:06 -0800175 i = self.handle.expect( [ "not found", self.scapyPrompt ] )
176 if i == 0:
177 output = self.handle.before + self.handle.after
178 self.handle.expect( self.prompt )
179 output += self.handle.before + self.handle.after
180 main.log.debug( self.name + ": Scapy not installed, aborting test. \n" + output )
181 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800182 self.handle.sendline( "conf.color_theme = NoTheme()" )
183 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700184 response = self.cleanOutput( self.handle.before )
185 self.handle.sendline( "conf.fancy_prompt = False" )
186 self.handle.expect( self.scapyPrompt )
187 response = self.cleanOutput( self.handle.before )
188 self.handle.sendline( "conf.interactive = False" )
189 self.handle.expect( "interactive" )
190 self.handle.expect( self.scapyPrompt )
191 response = self.cleanOutput( self.handle.before )
192 self.handle.sendline( "" )
193 self.handle.expect( self.scapyPrompt )
194 response = self.cleanOutput( self.handle.before )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800195 if mplsPath:
Jon Hall43060f62020-06-23 13:13:33 -0700196 main.log.debug( self.name + ": Adding MPLS class" )
197 main.log.debug( self.name + ": MPLS class path: " + mplsPath )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800198 for line in mplsLines:
Jon Hall43060f62020-06-23 13:13:33 -0700199 main.log.debug( self.name + ": sending line: " + line )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800200 self.handle.sendline( line )
201 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700202 response = self.cleanOutput( self.handle.before )
203
204 # Set interface
205 if ifaceName:
206 self.handle.sendline( 'conf.iface = "' + ifaceName + '"' )
207 self.clearBuffer()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800208 return main.TRUE
209 except pexpect.TIMEOUT:
210 main.log.exception( self.name + ": Command timed out" )
211 return main.FALSE
212 except pexpect.EOF:
213 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700214 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800215 except Exception:
216 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700217 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800218
219 def stopScapy( self ):
220 """
221 Exit the Scapy cli
222 """
223 try:
Jon Hall43060f62020-06-23 13:13:33 -0700224 main.log.debug( self.name + ": Stopping scapy" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800225 self.handle.sendline( "exit()" )
226 self.handle.expect( self.hostPrompt )
227 return main.TRUE
228 except pexpect.TIMEOUT:
229 main.log.exception( self.name + ": Command timed out" )
230 return main.FALSE
231 except pexpect.EOF:
232 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700233 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800234 except Exception:
235 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700236 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800237
238 def buildEther( self, **kwargs ):
239 """
240 Build an Ethernet frame
241
242 Will create a frame class with the given options. If a field is
243 left blank it will default to the below value unless it is
244 overwritten by the next frame.
245 Default frame:
246 ###[ Ethernet ]###
247 dst= ff:ff:ff:ff:ff:ff
248 src= 00:00:00:00:00:00
249 type= 0x800
250
251 Returns main.TRUE or main.FALSE on error
252 """
253 try:
Jon Hall43060f62020-06-23 13:13:33 -0700254 main.log.debug( self.name + ": Building Ethernet Frame" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800255 # Set the Ethernet frame
256 cmd = 'ether = Ether( '
257 options = []
258 for key, value in kwargs.iteritems():
259 if isinstance( value, str ):
260 value = '"' + value + '"'
261 options.append( str( key ) + "=" + str( value ) )
262 cmd += ", ".join( options )
263 cmd += ' )'
264 self.handle.sendline( cmd )
265 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700266 response = self.cleanOutput( self.handle.before )
267 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800268 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700269 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800270 return main.FALSE
271 self.handle.sendline( "packet = ether" )
272 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700273 response = self.cleanOutput( self.handle.before )
274 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800275 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700276 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800277 return main.FALSE
278 return main.TRUE
279 except pexpect.TIMEOUT:
280 main.log.exception( self.name + ": Command timed out" )
281 return main.FALSE
282 except pexpect.EOF:
283 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700284 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800285 except Exception:
286 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700287 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800288
Siddeshd9840842021-08-06 19:26:05 +0000289 def buildIP( self, vlan=False, **kwargs ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800290 """
291 Build an IP frame
292
293 Will create a frame class with the given options. If a field is
294 left blank it will default to the below value unless it is
295 overwritten by the next frame.
296 Default frame:
297 ###[ IP ]###
298 version= 4
299 ihl= None
300 tos= 0x0
301 len= None
302 id= 1
303 flags=
304 frag= 0
305 ttl= 64
306 proto= hopopt
307 chksum= None
308 src= 127.0.0.1
309 dst= 127.0.0.1
310 \options\
311
312 Returns main.TRUE or main.FALSE on error
313 """
314 try:
Jon Hall43060f62020-06-23 13:13:33 -0700315 main.log.debug( self.name + ": Building IP Frame" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800316 # Set the IP frame
317 cmd = 'ip = IP( '
318 options = []
319 for key, value in kwargs.iteritems():
320 if isinstance( value, str ):
321 value = '"' + value + '"'
322 options.append( str( key ) + "=" + str( value ) )
323 cmd += ", ".join( options )
324 cmd += ' )'
325 self.handle.sendline( cmd )
326 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700327 response = self.cleanOutput( self.handle.before )
328 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800329 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700330 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800331 return main.FALSE
Siddeshd9840842021-08-06 19:26:05 +0000332 if vlan:
333 self.handle.sendline( "packet = ether/vlan/ip" )
334 else:
335 self.handle.sendline( "packet = ether/ip" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800336 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700337 response = self.cleanOutput( self.handle.before )
338 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800339 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700340 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800341 return main.FALSE
342 return main.TRUE
343 except pexpect.TIMEOUT:
344 main.log.exception( self.name + ": Command timed out" )
345 return main.FALSE
346 except pexpect.EOF:
347 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700348 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800349 except Exception:
350 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700351 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800352
Siddesha19e3c82021-06-09 22:45:27 +0000353 def buildVLAN( self, **kwargs ):
354 """
355 Build a VLAN frame
356 """
357 try:
358 main.log.debug( self.name + ": Building VLAN Frame" )
359 # Set the IP frame
360 cmd = 'vlan = Dot1Q( '
361 options = []
362 for key, value in kwargs.iteritems():
363 if isinstance( value, str ):
364 value = '"' + value + '"'
365 options.append( str( key ) + "=" + str( value ) )
366 cmd += ", ".join( options )
367 cmd += ' )'
368 self.handle.sendline( cmd )
369 self.handle.expect( self.scapyPrompt )
370 response = self.cleanOutput( self.handle.before )
371 if "Traceback" in response:
372 # KeyError, SyntaxError, ...
373 main.log.error( "Error in sending command: " + response )
374 return main.FALSE
Siddeshd9840842021-08-06 19:26:05 +0000375 self.handle.sendline( "packet = ether/vlan" )
Siddesha19e3c82021-06-09 22:45:27 +0000376 self.handle.expect( self.scapyPrompt )
377 response = self.cleanOutput( self.handle.before )
378 if "Traceback" in response:
379 # KeyError, SyntaxError, ...
380 main.log.error( "Error in sending command: " + response )
381 return main.FALSE
382 return main.TRUE
383 except pexpect.TIMEOUT:
384 main.log.exception( self.name + ": Command timed out" )
385 return main.FALSE
386 except pexpect.EOF:
387 main.log.exception( self.name + ": connection closed." )
388 main.cleanAndExit()
389 except Exception:
390 main.log.exception( self.name + ": Uncaught exception!" )
391 main.cleanAndExit()
392
Siddeshd9840842021-08-06 19:26:05 +0000393 def buildIPv6( self, vlan=False, **kwargs ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800394 """
395 Build an IPv6 frame
396
397 Will create a frame class with the given options. If a field is
398 left blank it will default to the below value unless it is
399 overwritten by the next frame.
400 Default frame:
401 ###[ IPv6 ]###
402 version= 6
403 tc= 0
404 fl= 0
405 plen= None
406 nh= No Next Header
407 hlim= 64
408 src= ::1
409 dst= ::1
410
411 Returns main.TRUE or main.FALSE on error
412 """
413 try:
Jon Hall43060f62020-06-23 13:13:33 -0700414 main.log.debug( self.name + ": Building IPv6 Frame" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800415 # Set the IPv6 frame
416 cmd = 'ipv6 = IPv6( '
417 options = []
418 for key, value in kwargs.iteritems():
419 if isinstance( value, str ):
420 value = '"' + value + '"'
421 options.append( str( key ) + "=" + str( value ) )
422 cmd += ", ".join( options )
423 cmd += ' )'
424 self.handle.sendline( cmd )
425 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700426 response = self.cleanOutput( self.handle.before )
427 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800428 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700429 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800430 return main.FALSE
Siddeshd9840842021-08-06 19:26:05 +0000431 if vlan:
432 self.handle.sendline( "packet = ether/vlan/ipv6" )
433 else:
434 self.handle.sendline( "packet = ether/ipv6" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800435 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700436 response = self.cleanOutput( self.handle.before )
437 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800438 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700439 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800440 return main.FALSE
441 return main.TRUE
442 except pexpect.TIMEOUT:
443 main.log.exception( self.name + ": Command timed out" )
444 return main.FALSE
445 except pexpect.EOF:
446 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700447 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800448 except Exception:
449 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700450 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800451
452 def buildTCP( self, ipVersion=4, **kwargs ):
453 """
454 Build an TCP frame
455
456 Will create a frame class with the given options. If a field is
457 left blank it will default to the below value unless it is
458 overwritten by the next frame.
459
460 NOTE: Some arguments require quotes around them. It's up to you to
461 know which ones and to add them yourself. Arguments with an asterisk
462 do not need quotes.
463
464 Options:
465 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
466 frame to use to encapsulate into
467 Default frame:
468 ###[ TCP ]###
469 sport= ftp_data *
470 dport= http *
471 seq= 0
472 ack= 0
473 dataofs= None
474 reserved= 0
475 flags= S
476 window= 8192
477 chksum= None
478 urgptr= 0
479 options= {}
480
481 Returns main.TRUE or main.FALSE on error
482 """
483 try:
Jon Hall43060f62020-06-23 13:13:33 -0700484 main.log.debug( self.name + ": Building TCP" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800485 # Set the TCP frame
486 cmd = 'tcp = TCP( '
487 options = []
488 for key, value in kwargs.iteritems():
489 options.append( str( key ) + "=" + str( value ) )
490 cmd += ", ".join( options )
491 cmd += ' )'
492 self.handle.sendline( cmd )
493 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700494 response = self.cleanOutput( self.handle.before )
495 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800496 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700497 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800498 return main.FALSE
499 if str( ipVersion ) is '4':
500 self.handle.sendline( "packet = ether/ip/tcp" )
501 elif str( ipVersion ) is '6':
502 self.handle.sendline( "packet = ether/ipv6/tcp" )
503 else:
504 main.log.error( "Unrecognized option for ipVersion, given " +
505 repr( ipVersion ) )
506 return main.FALSE
507 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700508 response = self.cleanOutput( self.handle.before )
509 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800510 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700511 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800512 return main.FALSE
513 return main.TRUE
514 except pexpect.TIMEOUT:
515 main.log.exception( self.name + ": Command timed out" )
516 return main.FALSE
517 except pexpect.EOF:
518 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700519 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800520 except Exception:
521 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700522 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800523
524 def buildUDP( self, ipVersion=4, **kwargs ):
525 """
526 Build an UDP frame
527
528 Will create a frame class with the given options. If a field is
529 left blank it will default to the below value unless it is
530 overwritten by the next frame.
531
532 NOTE: Some arguments require quotes around them. It's up to you to
533 know which ones and to add them yourself. Arguments with an asterisk
534 do not need quotes.
535
536 Options:
537 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
538 frame to use to encapsulate into
539 Default frame:
540 ###[ UDP ]###
541 sport= domain *
542 dport= domain *
543 len= None
544 chksum= None
545
546 Returns main.TRUE or main.FALSE on error
547 """
548 try:
Jon Hall43060f62020-06-23 13:13:33 -0700549 main.log.debug( self.name + ": Building UDP Frame" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800550 # Set the UDP frame
551 cmd = 'udp = UDP( '
552 options = []
553 for key, value in kwargs.iteritems():
554 options.append( str( key ) + "=" + str( value ) )
555 cmd += ", ".join( options )
556 cmd += ' )'
557 self.handle.sendline( cmd )
558 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700559 response = self.cleanOutput( self.handle.before )
560 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800561 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700562 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800563 return main.FALSE
564 if str( ipVersion ) is '4':
565 self.handle.sendline( "packet = ether/ip/udp" )
566 elif str( ipVersion ) is '6':
567 self.handle.sendline( "packet = ether/ipv6/udp" )
568 else:
569 main.log.error( "Unrecognized option for ipVersion, given " +
570 repr( ipVersion ) )
571 return main.FALSE
572 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700573 response = self.cleanOutput( self.handle.before )
574 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800575 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700576 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800577 return main.FALSE
578 return main.TRUE
579 except pexpect.TIMEOUT:
580 main.log.exception( self.name + ": Command timed out" )
581 return main.FALSE
582 except pexpect.EOF:
583 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700584 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800585 except Exception:
586 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700587 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800588
alisone14d7b02016-07-06 10:31:51 -0700589 def buildSCTP( self, ipVersion=4, **kwargs ):
590 """
591 Build an SCTP frame
592
593 Will create a frame class with the given options. If a field is
594 left blank it will default to the below value unless it is
595 overwritten by the next frame.
596
597 NOTE: Some arguments require quotes around them. It's up to you to
598 know which ones and to add them yourself. Arguments with an asterisk
599 do not need quotes.
600
601 Options:
602 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
603 frame to use to encapsulate into
604 Default frame:
605 ###[ SCTP ]###
606 sport= domain *
607 dport= domain *
608 tag = None
609 chksum = None
610
611 Returns main.TRUE or main.FALSE on error
612 """
613 try:
Jon Hall43060f62020-06-23 13:13:33 -0700614 main.log.debug( self.name + ": Building SCTP Frame" )
alisone14d7b02016-07-06 10:31:51 -0700615 # Set the SCTP frame
616 cmd = 'sctp = SCTP( '
617 options = [ ]
618 for key, value in kwargs.iteritems( ):
619 options.append( str( key ) + "=" + str( value ) )
620 cmd += ", ".join( options )
621 cmd += ' )'
622 self.handle.sendline( cmd )
623 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700624 response = self.cleanOutput( self.handle.before )
625 if "Traceback" in response:
alisone14d7b02016-07-06 10:31:51 -0700626 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700627 main.log.error( "Error in sending command: " + response )
alisone14d7b02016-07-06 10:31:51 -0700628 return main.FALSE
629 if str( ipVersion ) is '4':
630 self.handle.sendline( "packet = ether/ip/sctp" )
631 elif str( ipVersion ) is '6':
632 self.handle.sendline( "packet = ether/ipv6/sctp" )
633 else:
634 main.log.error( "Unrecognized option for ipVersion, given " +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700635 repr( ipVersion ) )
alisone14d7b02016-07-06 10:31:51 -0700636 return main.FALSE
637 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700638 response = self.cleanOutput( self.handle.before )
639 if "Traceback" in response:
alisone14d7b02016-07-06 10:31:51 -0700640 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700641 main.log.error( "Error in sending command: " + response )
alisone14d7b02016-07-06 10:31:51 -0700642 return main.FALSE
643 return main.TRUE
644 except pexpect.TIMEOUT:
645 main.log.exception( self.name + ": Command timed out" )
646 return main.FALSE
647 except pexpect.EOF:
648 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700649 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700650 except Exception:
651 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700652 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700653
654 def buildARP( self, **kwargs ):
655 """
656 Build an ARP frame
657
658 Will create a frame class with the given options. If a field is
659 left blank it will default to the below value unless it is
660 overwritten by the next frame.
661
662 NOTE: Some arguments require quotes around them. It's up to you to
663 know which ones and to add them yourself. Arguments with an asterisk
664 do not need quotes.
665
666 Default frame:
667 ###[ ARP ]###
668 hwtype : XShortField = (1)
669 ptype : XShortEnumField = (2048)
670 hwlen : ByteField = (6)
671 plen : ByteField = (4)
672 op : ShortEnumField = (1)
673 hwsrc : ARPSourceMACField = (None)
674 psrc : SourceIPField = (None)
675 hwdst : MACField = ('00:00:00:00:00:00')
676 pdst : IPField = ('0.0.0.0')
677
678 Returns main.TRUE or main.FALSE on error
679 """
680 try:
Jon Hall43060f62020-06-23 13:13:33 -0700681 main.log.debug( self.name + ": Building ARP Frame" )
alisone14d7b02016-07-06 10:31:51 -0700682 # Set the ARP frame
683 cmd = 'arp = ARP( '
684 options = []
685 for key, value in kwargs.iteritems( ):
686 if isinstance( value, str ):
687 value = '"' + value + '"'
688 options.append( str( key ) + "=" + str( value ) )
689 cmd += ", ".join( options )
690 cmd += ' )'
691 self.handle.sendline( cmd )
692 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700693 response = self.cleanOutput( self.handle.before )
694 if "Traceback" in response:
alisone14d7b02016-07-06 10:31:51 -0700695 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700696 main.log.error( "Error in sending command: " + response )
alisone14d7b02016-07-06 10:31:51 -0700697 return main.FALSE
698 self.handle.sendline( "packet = ether/arp" )
699 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700700 response = self.cleanOutput( self.handle.before )
701 if "Traceback" in response:
alisone14d7b02016-07-06 10:31:51 -0700702 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700703 main.log.error( "Error in sending command: " + response )
alisone14d7b02016-07-06 10:31:51 -0700704 return main.FALSE
705 return main.TRUE
706 except pexpect.TIMEOUT:
707 main.log.exception( self.name + ": Command timed out" )
708 return main.FALSE
709 except pexpect.EOF:
710 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700711 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700712 except Exception:
713 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700714 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700715
Siddeshd9840842021-08-06 19:26:05 +0000716 def buildICMP( self, ipVersion=4, vlan=False, **kwargs ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800717 """
718 Build an ICMP frame
719
720 Will create a frame class with the given options. If a field is
721 left blank it will default to the below value unless it is
722 overwritten by the next frame.
723 Default frame:
724 ###[ ICMP ]###
725 type= echo-request
726 code= 0
727 chksum= None
728 id= 0x0
729 seq= 0x0
730
alisone14d7b02016-07-06 10:31:51 -0700731 Options:
732 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
733 frame to use to encapsulate into
734
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800735 Returns main.TRUE or main.FALSE on error
736 """
737 try:
Jon Hall43060f62020-06-23 13:13:33 -0700738 main.log.debug( self.name + ": Building ICMP Frame" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800739 # Set the ICMP frame
alisone14d7b02016-07-06 10:31:51 -0700740 if str( ipVersion ) is '4':
741 cmd = 'icmp = ICMP( '
742 elif str( ipVersion ) is '6':
743 cmd = 'icmp6 = ICMPv6EchoReply( '
744 else:
745 main.log.error( "Unrecognized option for ipVersion, given " +
746 repr( ipVersion ) )
747 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800748 options = []
alisone14d7b02016-07-06 10:31:51 -0700749 for key, value in kwargs.iteritems( ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800750 if isinstance( value, str ):
751 value = '"' + value + '"'
752 options.append( str( key ) + "=" + str( value ) )
753 cmd += ", ".join( options )
754 cmd += ' )'
755 self.handle.sendline( cmd )
756 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700757 response = self.cleanOutput( self.handle.before )
758 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800759 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700760 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800761 return main.FALSE
Siddeshd9840842021-08-06 19:26:05 +0000762 if vlan:
763 if str( ipVersion ) is '4':
764 self.handle.sendline( "packet = ether/vlan/ip/icmp" )
765 elif str( ipVersion ) is '6':
766 self.handle.sendline( "packet = ether/vlan/ipv6/icmp6" )
alisone14d7b02016-07-06 10:31:51 -0700767 else:
Siddeshd9840842021-08-06 19:26:05 +0000768 if str( ipVersion ) is '4':
769 self.handle.sendline( "packet = ether/ip/icmp" )
770 elif str( ipVersion ) is '6':
771 self.handle.sendline( "packet = ether/ipv6/icmp6" )
772 else:
773 main.log.error( "Unrecognized option for ipVersion, given " +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700774 repr( ipVersion ) )
Siddeshd9840842021-08-06 19:26:05 +0000775
alisone14d7b02016-07-06 10:31:51 -0700776 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800777 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700778 response = self.cleanOutput( self.handle.before )
779 if "Traceback" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800780 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700781 main.log.error( "Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800782 return main.FALSE
783 return main.TRUE
784 except pexpect.TIMEOUT:
785 main.log.exception( self.name + ": Command timed out" )
786 return main.FALSE
787 except pexpect.EOF:
788 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700789 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800790 except Exception:
791 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700792 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800793
Jon Hall43060f62020-06-23 13:13:33 -0700794 def clearBuffer( self, debug=False ):
795 """
796 Keep reading from buffer until its empty
797 Everything seems to be printed twice in newer versions of
798 scapy, even when turning off fancy output
799 """
800 i = 0
801 response = ''
802 while True:
803 try:
804 i += 1
805 # clear buffer
806 if debug:
807 main.log.warn( "%s expect loop iteration" % i )
Jon Hall627b1572020-12-01 12:01:15 -0800808 self.handle.expect( self.scapyPrompt, timeout=5 )
Jon Hall43060f62020-06-23 13:13:33 -0700809 response += self.cleanOutput( self.handle.before, debug )
810 except pexpect.TIMEOUT:
811 return response
812
813 def sendPacket( self, iface=None, packet=None, timeout=1, debug=True ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800814 """
815 Send a packet with either the given scapy packet command, or use the
816 packet saved in the variable 'packet'.
817
818 Examples of a valid string for packet:
819
820 Simple IP packet
821 packet='Ether(dst="a6:d9:26:df:1d:4b")/IP(dst="10.0.0.2")'
822
823 A Ping with two vlan tags
824 packet='Ether(dst='ff:ff:ff:ff:ff:ff')/Dot1Q(vlan=1)/Dot1Q(vlan=10)/
825 IP(dst='255.255.255.255', src='192.168.0.1')/ICMP()'
826
827 Returns main.TRUE or main.FALSE on error
828 """
829 try:
Jon Hall43060f62020-06-23 13:13:33 -0700830 main.log.debug( self.name + ": Sending Packet" )
831 if debug:
832 self.handle.sendline( "packet.summary()" )
833 self.handle.expect( self.scapyPrompt )
834 self.clearBuffer()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800835 # TODO: add all params, or use kwargs
Jon Hall43060f62020-06-23 13:13:33 -0700836 sendCmd = 'sendp( '
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800837 if packet:
838 sendCmd += packet
839 else:
840 sendCmd += "packet"
841 if iface:
842 sendCmd += ", iface='{}'".format( iface )
843
Jon Hall43060f62020-06-23 13:13:33 -0700844 if debug:
845 sendCmd += ', return_packets=True).summary()' # show packet(s) sent
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800846 self.handle.sendline( sendCmd )
847 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700848 response = self.cleanOutput( self.handle.before )
849 main.log.debug( self.name + ": Send packet response: {}".format( response ) )
Jon Hall50a00012021-03-08 11:06:11 -0800850 if "Traceback" in response or "Errno" in response or "Error" in response:
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800851 # KeyError, SyntaxError, ...
Jon Hall43060f62020-06-23 13:13:33 -0700852 main.log.error( self.name + ": Error in sending command: " + response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800853 return main.FALSE
854 # TODO: Check # of packets sent?
855 return main.TRUE
856 except pexpect.TIMEOUT:
857 main.log.exception( self.name + ": Command timed out" )
858 return main.FALSE
859 except pexpect.EOF:
860 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700861 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800862 except Exception:
863 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700864 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800865
866 def startFilter( self, ifaceName=None, sniffCount=1, pktFilter="ip" ):
867 """
868 Listen for packets using the given filters
869
870 Options:
871 ifaceName - the name of the interface to listen on. If none is given,
You Wangdafb6e22018-01-22 17:01:00 -0800872 defaults to self.ifaceName which is <host name>-eth0
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800873 pktFilter - A string in Berkeley Packet Filter (BPF) format which
874 specifies which packets to sniff
875 sniffCount - The number of matching packets to capture before returning
876
877 Returns main.TRUE or main.FALSE on error
878 """
879 try:
Jon Hall43060f62020-06-23 13:13:33 -0700880 main.log.info( self.name + ": Starting filter on interface %s" % ifaceName )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800881 # TODO: add all params, or use kwargs
You Wangdafb6e22018-01-22 17:01:00 -0800882 ifaceName = str( ifaceName ) if ifaceName else self.ifaceName
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800883 # Set interface
You Wangbd848ed2018-03-23 13:57:42 -0700884 self.handle.sendline( 'conf.iface = "' + ifaceName + '"' )
Jon Hall43060f62020-06-23 13:13:33 -0700885 self.handle.expect( ifaceName )
886 self.cleanOutput( self.handle.before + self.handle.after )
887 self.cleanOutput( self.handle.before )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800888 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700889 response = self.handle.before + self.handle.after
890 self.cleanOutput( response )
891 cmd = 'pkts = sniff(count = %s, filter = "%s", prn=lambda p: p.summary() )' % ( sniffCount, pktFilter )
892 main.log.info( self.name + ": Starting filter on " + self.name + ' > ' + cmd )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800893 self.handle.sendline( cmd )
Jon Hall43060f62020-06-23 13:13:33 -0700894 response = self.clearBuffer()
895
You Wang3a5f74c2018-08-03 14:58:15 -0700896 # Make sure the sniff function didn't exit due to failures
897 i = self.handle.expect( [ self.scapyPrompt, pexpect.TIMEOUT ], timeout=3 )
Jon Hall43060f62020-06-23 13:13:33 -0700898 response = self.cleanOutput( self.handle.before + str( self.handle.after ) )
You Wang3a5f74c2018-08-03 14:58:15 -0700899 if i == 0:
900 # sniff exited
901 main.log.error( self.name + ": sniff function exited" )
Jon Hall43060f62020-06-23 13:13:33 -0700902 main.log.error( self.name + ": " + response )
You Wang3a5f74c2018-08-03 14:58:15 -0700903 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800904 return main.TRUE
905 except pexpect.TIMEOUT:
906 main.log.exception( self.name + ": Command timed out" )
907 return main.FALSE
908 except pexpect.EOF:
909 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700910 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800911 except Exception:
912 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700913 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800914
915 def checkFilter( self, timeout=10 ):
916 """
Jon Hall43060f62020-06-23 13:13:33 -0700917 Check if a filter is still running.
918 Returns:
919 main.TRUE if the filter stopped
920 main.FALSE if the filter is still running
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800921 """
922 try:
Jon Hall43060f62020-06-23 13:13:33 -0700923 main.log.debug( self.name + ": Checking Filter" )
924 self.handle.sendline( "" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800925 i = self.handle.expect( [ self.scapyPrompt, pexpect.TIMEOUT ], timeout=timeout )
Jon Hall43060f62020-06-23 13:13:33 -0700926 response = self.cleanOutput( self.handle.before + str( self.handle.after ), debug=True )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800927 if i == 0:
928 return main.TRUE
929 else:
930 return main.FALSE
931 except pexpect.EOF:
932 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700933 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800934 except Exception:
935 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700936 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800937
938 def killFilter( self ):
939 """
940 Kill a scapy filter
941 """
942 try:
Jon Hall43060f62020-06-23 13:13:33 -0700943 main.log.debug( self.name + ": Killing scapy filter" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800944 self.handle.send( "\x03" ) # Send a ctrl-c to kill the filter
945 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -0700946 output = self.cleanOutput( self.handle.before, debug=True )
947 return output
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800948 except pexpect.TIMEOUT:
949 main.log.exception( self.name + ": Command timed out" )
950 return None
951 except pexpect.EOF:
952 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700953 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800954 except Exception:
955 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700956 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800957
You Wang548db382020-08-12 09:17:13 -0700958 def readPackets( self, detailed=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800959 """
960 Read all the packets captured by the previous filter
961 """
962 try:
Jon Hall43060f62020-06-23 13:13:33 -0700963 main.log.debug( self.name + ": Reading Packets" )
964 main.log.debug( self.name + ": Begin clear buffer" )
965 self.clearBuffer()
966 main.log.debug( self.name + ": end clear buffer" )
967
You Wang548db382020-08-12 09:17:13 -0700968 if detailed:
969 self.handle.sendline( "[p for p in pkts]")
970 else:
971 self.handle.sendline( "pkts.summary()")
Jon Hall43060f62020-06-23 13:13:33 -0700972 output = self.clearBuffer()
Jon Hall50a00012021-03-08 11:06:11 -0800973 if "Traceback" in output or "Errno" in output or "Error" in output:
974 # KeyError, SyntaxError, IOError, NameError, ...
975 main.log.error( self.name + ": Error in sending command: " + output )
976 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800977 except pexpect.TIMEOUT:
978 main.log.exception( self.name + ": Command timed out" )
979 return None
980 except pexpect.EOF:
981 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700982 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800983 except Exception:
984 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700985 main.cleanAndExit()
Jon Hall43060f62020-06-23 13:13:33 -0700986 return output
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800987
alisone14d7b02016-07-06 10:31:51 -0700988 def updateSelf( self, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800989 """
990 Updates local MAC and IP fields
991 """
992 self.hostMac = self.getMac()
alisone14d7b02016-07-06 10:31:51 -0700993 if IPv6:
994 self.hostIp = self.getIp( IPv6=True )
995 else:
996 self.hostIp = self.getIp()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800997
998 def getMac( self, ifaceName=None ):
999 """
1000 Save host's MAC address
1001 """
1002 try:
You Wangdafb6e22018-01-22 17:01:00 -08001003 ifaceName = str( ifaceName ) if ifaceName else self.ifaceName
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001004 cmd = 'get_if_hwaddr("' + str( ifaceName ) + '")'
1005 self.handle.sendline( cmd )
1006 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -07001007 response = self.cleanOutput( self.handle.before )
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001008 pattern = r'(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))'
Jon Hall43060f62020-06-23 13:13:33 -07001009 match = re.search( pattern, response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001010 if match:
1011 return match.group()
1012 else:
1013 # the command will have an exception if iface doesn't exist
1014 return None
1015 except pexpect.TIMEOUT:
1016 main.log.exception( self.name + ": Command timed out" )
1017 return None
1018 except pexpect.EOF:
1019 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -07001020 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001021 except Exception:
1022 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001023 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001024
alisone14d7b02016-07-06 10:31:51 -07001025 def getIp( self, ifaceName=None, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001026 """
1027 Save host's IP address
1028
1029 Returns the IP of the first interface that is not a loopback device.
1030 If no IP could be found then it will return 0.0.0.0.
alisone14d7b02016-07-06 10:31:51 -07001031
1032 If IPv6 is equal to True, returns IPv6 of the first interface that is not a loopback device.
1033 If no IPv6 could be found then it will return :: .
1034
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001035 """
1036 def getIPofInterface( ifaceName ):
1037 cmd = 'get_if_addr("' + str( ifaceName ) + '")'
alisone14d7b02016-07-06 10:31:51 -07001038 if IPv6:
1039 cmd = 'get_if_raw_addr6("' + str( ifaceName ) + '")'
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001040 self.handle.sendline( cmd )
1041 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -07001042 response = self.cleanOutput( self.handle.before )
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001043
1044 pattern = r'(((2[0-5]|1[0-9]|[0-9])?[0-9]\.){3}((2[0-5]|1[0-9]|[0-9])?[0-9]))'
alisone14d7b02016-07-06 10:31:51 -07001045 if IPv6:
1046 pattern = r'(\\x([0-9]|[a-f]|[A-F])([0-9]|[a-f]|[A-F])){16}'
Jon Hall43060f62020-06-23 13:13:33 -07001047 match = re.search( pattern, response )
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001048 if match:
1049 # NOTE: The command will return 0.0.0.0 if the iface doesn't exist
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001050 if IPv6 is not True:
alisone14d7b02016-07-06 10:31:51 -07001051 if match.group() == '0.0.0.0':
1052 main.log.warn( 'iface {0} has no IPv4 address'.format( ifaceName ) )
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001053 return match.group()
1054 else:
1055 return None
1056 try:
1057 if not ifaceName:
1058 # Get list of interfaces
1059 ifList = self.getIfList()
alisone14d7b02016-07-06 10:31:51 -07001060 if IPv6:
1061 for ifaceName in ifList:
1062 if ifaceName == "lo":
1063 continue
1064 ip = getIPofInterface( ifaceName )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001065 if ip is not None:
1066 newip = ip
alisone14d7b02016-07-06 10:31:51 -07001067 tmp = newip.split( "\\x" )
1068 ip = ""
1069 counter = 0
1070 for i in tmp:
1071 if i != "":
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001072 counter = counter + 1
alisone14d7b02016-07-06 10:31:51 -07001073 if counter % 2 == 0 and counter < 16:
1074 ip = ip + i + ":"
1075 else:
1076 ip = ip + i
1077 return ip
1078 return "::"
1079 else:
1080 for ifaceName in ifList:
1081 if ifaceName == "lo":
1082 continue
1083 ip = getIPofInterface( ifaceName )
1084 if ip != "0.0.0.0":
1085 return ip
1086 return "0.0.0.0"
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001087 else:
1088 return getIPofInterface( ifaceName )
1089
1090 except pexpect.TIMEOUT:
1091 main.log.exception( self.name + ": Command timed out" )
1092 return None
1093 except pexpect.EOF:
1094 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -07001095 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001096 except Exception:
1097 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001098 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001099
Jon Hall43060f62020-06-23 13:13:33 -07001100 def addRoute( self, network, gateway, interface=None ):
1101 """
1102 Add a route to the current scapy session
1103 """
1104 main.log.info( self.name + ": Adding route to scapy session; %s via %s out of interface %s" % ( network, gateway, interface ) )
1105 if gateway is None:
1106 main.log.error( self.name + ": Gateway is None, cannot set route" )
1107 return main.FALSE
Jon Hall06fd0df2021-01-25 15:50:06 -08001108 if network is None or "None" in network:
1109 main.log.error( self.name + ": Network is None, cannot set route" )
1110 return main.FALSE
Jon Hall43060f62020-06-23 13:13:33 -07001111 try:
1112 cmdStr = 'conf.route.add( net="%s", gw="%s"' % ( network, gateway )
1113 if interface:
1114 cmdStr += ', dev="%s"' % interface
1115 cmdStr += ')'
1116 self.handle.sendline( cmdStr )
1117 self.handle.expect( self.scapyPrompt )
1118 response = self.cleanOutput( self.handle.before )
1119 if "Traceback" in response:
1120 main.log.error( self.name + ": Error in adding route to scappy session" )
1121 main.log.debug( response )
1122 return main.FALSE
1123 return main.TRUE
1124 except pexpect.TIMEOUT:
1125 main.log.exception( self.name + ": Command timed out" )
1126 return None
1127 except pexpect.EOF:
1128 main.log.exception( self.name + ": connection closed." )
1129 main.cleanAndExit()
1130 except Exception:
1131 main.log.exception( self.name + ": Uncaught exception!" )
1132 main.cleanAndExit()
1133
1134 def addRoutes( self ):
1135 """
1136 Add any routes configured for the host
1137 """
1138 returnValues = []
1139 for route in self.routes:
1140 gw = route.get( 'gw' )
1141 iface = route.get( 'interface' )
1142 returnValues .append( self.addRoute( "%s/%s" % ( route.get( 'network' ), route.get( 'netmask' ) ),
Jon Hall50a00012021-03-08 11:06:11 -08001143 gw if gw else main.Cluster.active(0).address,
Jon Hall43060f62020-06-23 13:13:33 -07001144 interface=iface if iface else self.interfaces[ 0 ].get( 'name' ) ) )
1145 return returnValues
1146
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001147 def getIfList( self ):
1148 """
1149 Return List of Interfaces
1150 """
1151 try:
1152 self.handle.sendline( 'get_if_list()' )
1153 self.handle.expect( self.scapyPrompt )
Jon Hall43060f62020-06-23 13:13:33 -07001154 response = self.cleanOutput( self.handle.before )
1155 ifList = response.split( '\r\n' )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001156 ifList = ifList[ 1 ].replace( "'", "" )[ 1:-1 ].split( ', ' )
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001157 return ifList
1158
1159 except pexpect.TIMEOUT:
1160 main.log.exception( self.name + ": Command timed out" )
1161 return None
1162 except pexpect.EOF:
1163 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -07001164 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001165 except Exception:
1166 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001167 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001168
1169if __name__ != "__main__":
1170 sys.modules[ __name__ ] = ScapyCliDriver()