blob: 6ef63c4b6c1b99197b9a85e47eb4c204fa2a55be [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
30import types
31import os
32from drivers.common.cli.emulatordriver import Emulator
33
34
35class ScapyCliDriver( Emulator ):
36
37 """
38 ScapyCliDriver is the basic driver which will handle
39 the Scapy functions"""
40 def __init__( self ):
Devin Limdc78e202017-06-09 18:30:07 -070041 super( ScapyCliDriver, self ).__init__()
Jeremy Songster1f39bf02016-01-20 17:17:25 -080042 self.handle = self
43 self.name = None
44 self.home = None
45 self.wrapped = sys.modules[ __name__ ]
46 self.flag = 0
47 # TODO: Refactor driver to use these everywhere
48 self.hostPrompt = "~#"
49 self.bashPrompt = "\$"
50 self.scapyPrompt = ">>>"
51
52 def connect( self, **connectargs ):
53 """
54 Here the main is the TestON instance after creating
55 all the log handles."""
56 try:
57 for key in connectargs:
58 vars( self )[ key ] = connectargs[ key ]
59 self.home = "~/mininet"
60 self.name = self.options[ 'name' ]
61 for key in self.options:
62 if key == "home":
63 self.home = self.options[ 'home' ]
64 break
65 if self.home is None or self.home == "":
66 self.home = "~/mininet"
67
68 try:
69 if os.getenv( str( self.ip_address ) ) is not None:
70 self.ip_address = os.getenv( str( self.ip_address ) )
71 else:
72 main.log.info( self.name +
73 ": Trying to connect to " +
74 self.ip_address )
75
76 except KeyError:
77 main.log.info( "Invalid host name," +
78 " connecting to local host instead" )
79 self.ip_address = 'localhost'
80 except Exception as inst:
81 main.log.error( "Uncaught exception: " + str( inst ) )
82
83 self.handle = super(
84 ScapyCliDriver,
85 self ).connect(
86 user_name=self.user_name,
87 ip_address=self.ip_address,
88 port=None,
89 pwd=self.pwd )
90
91 if self.handle:
92 main.log.info( "Connection successful to the host " +
93 self.user_name +
94 "@" +
95 self.ip_address )
96 return main.TRUE
97 else:
98 main.log.error( "Connection failed to the host " +
99 self.user_name +
100 "@" +
101 self.ip_address )
102 main.log.error( "Failed to connect to the Mininet Host" )
103 return main.FALSE
104 except pexpect.EOF:
105 main.log.error( self.name + ": EOF exception found" )
106 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -0700107 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800108 except Exception:
109 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700110 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800111
112 def disconnect( self ):
113 """
114 Called at the end of the test to stop the scapy component and
115 disconnect the handle.
116 """
117 self.handle.sendline( '' )
118 i = self.handle.expect( [ 'mininet>', pexpect.EOF, pexpect.TIMEOUT ],
119 timeout=2 )
120 response = main.TRUE
121 if i == 0:
122 response = self.stopNet()
123 elif i == 1:
124 return main.TRUE
125 # print "Disconnecting Mininet"
126 if self.handle:
127 self.handle.sendline( "exit" )
128 self.handle.expect( "exit" )
129 self.handle.expect( "(.*)" )
130 else:
131 main.log.error( "Connection failed to the host" )
132 return response
133
134 def stopNet( self, fileName="", timeout=5 ):
135 """
136 Stops mininet.
137 Returns main.TRUE if the mininet successfully stops and
138 main.FALSE if the pexpect handle does not exist.
139
140 Will cleanup and exit the test if scapy fails to stop
141 """
142 main.log.info( self.name + ": Stopping scapy..." )
143 response = ''
144 if self.handle:
145 try:
146 self.handle.sendline( "" )
147 i = self.handle.expect( [ '>>>',
Devin Limdc78e202017-06-09 18:30:07 -0700148 self.prompt,
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800149 pexpect.EOF,
150 pexpect.TIMEOUT ],
151 timeout )
152 if i == 0:
153 main.log.info( "Exiting scapy..." )
154 response = self.execute(
155 cmd="exit",
156 prompt="(.*)",
157 timeout=120 )
158 main.log.info( self.name + ": Stopped" )
159 response = main.TRUE
160
161 if i == 1:
162 main.log.info( " Mininet trying to exit while not " +
163 "in the mininet prompt" )
164 elif i == 2:
165 main.log.error( "Something went wrong exiting mininet" )
166 elif i == 3: # timeout
167 main.log.error( "Something went wrong exiting mininet " +
168 "TIMEOUT" )
169
170 if fileName:
171 self.handle.sendline( "" )
Devin Limdc78e202017-06-09 18:30:07 -0700172 self.handle.expect( self.prompt )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800173 self.handle.sendline(
174 "sudo kill -9 \`ps -ef | grep \"" +
175 fileName +
176 "\" | grep -v grep | awk '{print $2}'\`" )
177 except pexpect.EOF:
178 main.log.error( self.name + ": EOF exception found" )
179 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -0700180 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800181 else:
182 main.log.error( self.name + ": Connection failed to the host" )
183 response = main.FALSE
184 return response
185
186 def createHostComponent( self, name ):
187 """
188 Creates a new mininet cli component with the same parameters as self.
189 This new component is intended to be used to login to the hosts created
190 by mininet.
191
192 Arguments:
193 name - The string of the name of this component. The new component
194 will be assigned to main.<name> .
195 In addition, main.<name>.name = str( name )
196 """
197 try:
198 # look to see if this component already exists
199 getattr( main, name )
200 except AttributeError:
201 # namespace is clear, creating component
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700202 main.componentDictionary[ name ] = main.componentDictionary[ self.name ].copy()
203 main.componentDictionary[ name ][ 'connect_order' ] = str( int( main.componentDictionary[ name ][ 'connect_order' ] ) + 1 )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800204 main.componentInit( name )
205 except Exception:
206 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700207 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800208 else:
209 # namespace is not clear!
210 main.log.error( name + " component already exists!" )
Devin Lim44075962017-08-11 10:56:37 -0700211 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800212
213 def removeHostComponent( self, name ):
214 """
215 Remove host component
216 Arguments:
217 name - The string of the name of the component to delete.
218 """
219 try:
220 # Get host component
221 component = getattr( main, name )
222 except AttributeError:
223 main.log.error( "Component " + name + " does not exist." )
224 return main.FALSE
225 try:
226 # Disconnect from component
227 component.disconnect()
228 # Delete component
229 delattr( main, name )
230 # Delete component from ComponentDictionary
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700231 del( main.componentDictionary[ name ] )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800232 return main.TRUE
233 except Exception:
234 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700235 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800236
237 def startHostCli( self, host=None ):
238 """
239 Use the mininet m utility to connect to the host's cli
240 """
241 # These are fields that can be used by scapy packets. Initialized to None
242 self.hostIp = None
243 self.hostMac = None
244 try:
245 if not host:
246 host = self.name
247 self.handle.sendline( self.home + "/util/m " + host )
Jeremy Ronquillo0f2008a2017-06-23 15:32:51 -0700248 self.handle.sendline( "cd" )
249 self.handle.expect( self.hostPrompt )
250 self.handle.sendline( "" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800251 self.handle.expect( self.hostPrompt )
252 return main.TRUE
253 except pexpect.TIMEOUT:
254 main.log.exception( self.name + ": Command timed out" )
255 return main.FALSE
256 except pexpect.EOF:
257 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700258 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800259 except Exception:
260 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700261 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800262
263 def startScapy( self, mplsPath="" ):
264 """
265 Start the Scapy cli
266 optional:
267 mplsPath - The path where the MPLS class is located
268 NOTE: This can be a relative path from the user's home dir
269 """
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700270 mplsLines = [ 'import imp',
271 'imp.load_source( "mplsClass", "{}mplsClass.py" )'.format( mplsPath ),
272 'from mplsClass import MPLS',
273 'bind_layers(Ether, MPLS, type = 0x8847)',
274 'bind_layers(MPLS, MPLS, bottom_of_label_stack = 0)',
275 'bind_layers(MPLS, IP)' ]
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800276
277 try:
278 self.handle.sendline( "scapy" )
279 self.handle.expect( self.scapyPrompt )
280 self.handle.sendline( "conf.color_theme = NoTheme()" )
281 self.handle.expect( self.scapyPrompt )
282 if mplsPath:
283 main.log.info( "Adding MPLS class" )
284 main.log.info( "MPLS class path: " + mplsPath )
285 for line in mplsLines:
286 main.log.info( "sending line: " + line )
287 self.handle.sendline( line )
288 self.handle.expect( self.scapyPrompt )
289 return main.TRUE
290 except pexpect.TIMEOUT:
291 main.log.exception( self.name + ": Command timed out" )
292 return main.FALSE
293 except pexpect.EOF:
294 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700295 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800296 except Exception:
297 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700298 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800299
300 def stopScapy( self ):
301 """
302 Exit the Scapy cli
303 """
304 try:
305 self.handle.sendline( "exit()" )
306 self.handle.expect( self.hostPrompt )
307 return main.TRUE
308 except pexpect.TIMEOUT:
309 main.log.exception( self.name + ": Command timed out" )
310 return main.FALSE
311 except pexpect.EOF:
312 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700313 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800314 except Exception:
315 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700316 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800317
318 def buildEther( self, **kwargs ):
319 """
320 Build an Ethernet frame
321
322 Will create a frame class with the given options. If a field is
323 left blank it will default to the below value unless it is
324 overwritten by the next frame.
325 Default frame:
326 ###[ Ethernet ]###
327 dst= ff:ff:ff:ff:ff:ff
328 src= 00:00:00:00:00:00
329 type= 0x800
330
331 Returns main.TRUE or main.FALSE on error
332 """
333 try:
334 # Set the Ethernet frame
335 cmd = 'ether = Ether( '
336 options = []
337 for key, value in kwargs.iteritems():
338 if isinstance( value, str ):
339 value = '"' + value + '"'
340 options.append( str( key ) + "=" + str( value ) )
341 cmd += ", ".join( options )
342 cmd += ' )'
343 self.handle.sendline( cmd )
344 self.handle.expect( self.scapyPrompt )
345 if "Traceback" in self.handle.before:
346 # KeyError, SyntaxError, ...
347 main.log.error( "Error in sending command: " + self.handle.before )
348 return main.FALSE
349 self.handle.sendline( "packet = ether" )
350 self.handle.expect( self.scapyPrompt )
351 if "Traceback" in self.handle.before:
352 # KeyError, SyntaxError, ...
353 main.log.error( "Error in sending command: " + self.handle.before )
354 return main.FALSE
355 return main.TRUE
356 except pexpect.TIMEOUT:
357 main.log.exception( self.name + ": Command timed out" )
358 return main.FALSE
359 except pexpect.EOF:
360 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700361 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800362 except Exception:
363 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700364 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800365
366 def buildIP( self, **kwargs ):
367 """
368 Build an IP frame
369
370 Will create a frame class with the given options. If a field is
371 left blank it will default to the below value unless it is
372 overwritten by the next frame.
373 Default frame:
374 ###[ IP ]###
375 version= 4
376 ihl= None
377 tos= 0x0
378 len= None
379 id= 1
380 flags=
381 frag= 0
382 ttl= 64
383 proto= hopopt
384 chksum= None
385 src= 127.0.0.1
386 dst= 127.0.0.1
387 \options\
388
389 Returns main.TRUE or main.FALSE on error
390 """
391 try:
392 # Set the IP frame
393 cmd = 'ip = IP( '
394 options = []
395 for key, value in kwargs.iteritems():
396 if isinstance( value, str ):
397 value = '"' + value + '"'
398 options.append( str( key ) + "=" + str( value ) )
399 cmd += ", ".join( options )
400 cmd += ' )'
401 self.handle.sendline( cmd )
402 self.handle.expect( self.scapyPrompt )
403 if "Traceback" in self.handle.before:
404 # KeyError, SyntaxError, ...
405 main.log.error( "Error in sending command: " + self.handle.before )
406 return main.FALSE
407 self.handle.sendline( "packet = ether/ip" )
408 self.handle.expect( self.scapyPrompt )
409 if "Traceback" in self.handle.before:
410 # KeyError, SyntaxError, ...
411 main.log.error( "Error in sending command: " + self.handle.before )
412 return main.FALSE
413 return main.TRUE
414 except pexpect.TIMEOUT:
415 main.log.exception( self.name + ": Command timed out" )
416 return main.FALSE
417 except pexpect.EOF:
418 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700419 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800420 except Exception:
421 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700422 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800423
424 def buildIPv6( self, **kwargs ):
425 """
426 Build an IPv6 frame
427
428 Will create a frame class with the given options. If a field is
429 left blank it will default to the below value unless it is
430 overwritten by the next frame.
431 Default frame:
432 ###[ IPv6 ]###
433 version= 6
434 tc= 0
435 fl= 0
436 plen= None
437 nh= No Next Header
438 hlim= 64
439 src= ::1
440 dst= ::1
441
442 Returns main.TRUE or main.FALSE on error
443 """
444 try:
445 # Set the IPv6 frame
446 cmd = 'ipv6 = IPv6( '
447 options = []
448 for key, value in kwargs.iteritems():
449 if isinstance( value, str ):
450 value = '"' + value + '"'
451 options.append( str( key ) + "=" + str( value ) )
452 cmd += ", ".join( options )
453 cmd += ' )'
454 self.handle.sendline( cmd )
455 self.handle.expect( self.scapyPrompt )
456 if "Traceback" in self.handle.before:
457 # KeyError, SyntaxError, ...
458 main.log.error( "Error in sending command: " + self.handle.before )
459 return main.FALSE
460 self.handle.sendline( "packet = ether/ipv6" )
461 self.handle.expect( self.scapyPrompt )
462 if "Traceback" in self.handle.before:
463 # KeyError, SyntaxError, ...
464 main.log.error( "Error in sending command: " + self.handle.before )
465 return main.FALSE
466 return main.TRUE
467 except pexpect.TIMEOUT:
468 main.log.exception( self.name + ": Command timed out" )
469 return main.FALSE
470 except pexpect.EOF:
471 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700472 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800473 except Exception:
474 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700475 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800476
477 def buildTCP( self, ipVersion=4, **kwargs ):
478 """
479 Build an TCP frame
480
481 Will create a frame class with the given options. If a field is
482 left blank it will default to the below value unless it is
483 overwritten by the next frame.
484
485 NOTE: Some arguments require quotes around them. It's up to you to
486 know which ones and to add them yourself. Arguments with an asterisk
487 do not need quotes.
488
489 Options:
490 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
491 frame to use to encapsulate into
492 Default frame:
493 ###[ TCP ]###
494 sport= ftp_data *
495 dport= http *
496 seq= 0
497 ack= 0
498 dataofs= None
499 reserved= 0
500 flags= S
501 window= 8192
502 chksum= None
503 urgptr= 0
504 options= {}
505
506 Returns main.TRUE or main.FALSE on error
507 """
508 try:
509 # Set the TCP frame
510 cmd = 'tcp = TCP( '
511 options = []
512 for key, value in kwargs.iteritems():
513 options.append( str( key ) + "=" + str( value ) )
514 cmd += ", ".join( options )
515 cmd += ' )'
516 self.handle.sendline( cmd )
517 self.handle.expect( self.scapyPrompt )
518 if "Traceback" in self.handle.before:
519 # KeyError, SyntaxError, ...
520 main.log.error( "Error in sending command: " + self.handle.before )
521 return main.FALSE
522 if str( ipVersion ) is '4':
523 self.handle.sendline( "packet = ether/ip/tcp" )
524 elif str( ipVersion ) is '6':
525 self.handle.sendline( "packet = ether/ipv6/tcp" )
526 else:
527 main.log.error( "Unrecognized option for ipVersion, given " +
528 repr( ipVersion ) )
529 return main.FALSE
530 self.handle.expect( self.scapyPrompt )
531 if "Traceback" in self.handle.before:
532 # KeyError, SyntaxError, ...
533 main.log.error( "Error in sending command: " + self.handle.before )
534 return main.FALSE
535 return main.TRUE
536 except pexpect.TIMEOUT:
537 main.log.exception( self.name + ": Command timed out" )
538 return main.FALSE
539 except pexpect.EOF:
540 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700541 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800542 except Exception:
543 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700544 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800545
546 def buildUDP( self, ipVersion=4, **kwargs ):
547 """
548 Build an UDP frame
549
550 Will create a frame class with the given options. If a field is
551 left blank it will default to the below value unless it is
552 overwritten by the next frame.
553
554 NOTE: Some arguments require quotes around them. It's up to you to
555 know which ones and to add them yourself. Arguments with an asterisk
556 do not need quotes.
557
558 Options:
559 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
560 frame to use to encapsulate into
561 Default frame:
562 ###[ UDP ]###
563 sport= domain *
564 dport= domain *
565 len= None
566 chksum= None
567
568 Returns main.TRUE or main.FALSE on error
569 """
570 try:
571 # Set the UDP frame
572 cmd = 'udp = UDP( '
573 options = []
574 for key, value in kwargs.iteritems():
575 options.append( str( key ) + "=" + str( value ) )
576 cmd += ", ".join( options )
577 cmd += ' )'
578 self.handle.sendline( cmd )
579 self.handle.expect( self.scapyPrompt )
580 if "Traceback" in self.handle.before:
581 # KeyError, SyntaxError, ...
582 main.log.error( "Error in sending command: " + self.handle.before )
583 return main.FALSE
584 if str( ipVersion ) is '4':
585 self.handle.sendline( "packet = ether/ip/udp" )
586 elif str( ipVersion ) is '6':
587 self.handle.sendline( "packet = ether/ipv6/udp" )
588 else:
589 main.log.error( "Unrecognized option for ipVersion, given " +
590 repr( ipVersion ) )
591 return main.FALSE
592 self.handle.expect( self.scapyPrompt )
593 if "Traceback" in self.handle.before:
594 # KeyError, SyntaxError, ...
595 main.log.error( "Error in sending command: " + self.handle.before )
596 return main.FALSE
597 return main.TRUE
598 except pexpect.TIMEOUT:
599 main.log.exception( self.name + ": Command timed out" )
600 return main.FALSE
601 except pexpect.EOF:
602 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700603 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800604 except Exception:
605 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700606 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800607
alisone14d7b02016-07-06 10:31:51 -0700608 def buildSCTP( self, ipVersion=4, **kwargs ):
609 """
610 Build an SCTP frame
611
612 Will create a frame class with the given options. If a field is
613 left blank it will default to the below value unless it is
614 overwritten by the next frame.
615
616 NOTE: Some arguments require quotes around them. It's up to you to
617 know which ones and to add them yourself. Arguments with an asterisk
618 do not need quotes.
619
620 Options:
621 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
622 frame to use to encapsulate into
623 Default frame:
624 ###[ SCTP ]###
625 sport= domain *
626 dport= domain *
627 tag = None
628 chksum = None
629
630 Returns main.TRUE or main.FALSE on error
631 """
632 try:
633 # Set the SCTP frame
634 cmd = 'sctp = SCTP( '
635 options = [ ]
636 for key, value in kwargs.iteritems( ):
637 options.append( str( key ) + "=" + str( value ) )
638 cmd += ", ".join( options )
639 cmd += ' )'
640 self.handle.sendline( cmd )
641 self.handle.expect( self.scapyPrompt )
642 if "Traceback" in self.handle.before:
643 # KeyError, SyntaxError, ...
644 main.log.error( "Error in sending command: " + self.handle.before )
645 return main.FALSE
646 if str( ipVersion ) is '4':
647 self.handle.sendline( "packet = ether/ip/sctp" )
648 elif str( ipVersion ) is '6':
649 self.handle.sendline( "packet = ether/ipv6/sctp" )
650 else:
651 main.log.error( "Unrecognized option for ipVersion, given " +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700652 repr( ipVersion ) )
alisone14d7b02016-07-06 10:31:51 -0700653 return main.FALSE
654 self.handle.expect( self.scapyPrompt )
655 if "Traceback" in self.handle.before:
656 # KeyError, SyntaxError, ...
657 main.log.error( "Error in sending command: " + self.handle.before )
658 return main.FALSE
659 return main.TRUE
660 except pexpect.TIMEOUT:
661 main.log.exception( self.name + ": Command timed out" )
662 return main.FALSE
663 except pexpect.EOF:
664 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700665 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700666 except Exception:
667 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700668 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700669
670 def buildARP( self, **kwargs ):
671 """
672 Build an ARP frame
673
674 Will create a frame class with the given options. If a field is
675 left blank it will default to the below value unless it is
676 overwritten by the next frame.
677
678 NOTE: Some arguments require quotes around them. It's up to you to
679 know which ones and to add them yourself. Arguments with an asterisk
680 do not need quotes.
681
682 Default frame:
683 ###[ ARP ]###
684 hwtype : XShortField = (1)
685 ptype : XShortEnumField = (2048)
686 hwlen : ByteField = (6)
687 plen : ByteField = (4)
688 op : ShortEnumField = (1)
689 hwsrc : ARPSourceMACField = (None)
690 psrc : SourceIPField = (None)
691 hwdst : MACField = ('00:00:00:00:00:00')
692 pdst : IPField = ('0.0.0.0')
693
694 Returns main.TRUE or main.FALSE on error
695 """
696 try:
697 # Set the ARP frame
698 cmd = 'arp = ARP( '
699 options = []
700 for key, value in kwargs.iteritems( ):
701 if isinstance( value, str ):
702 value = '"' + value + '"'
703 options.append( str( key ) + "=" + str( value ) )
704 cmd += ", ".join( options )
705 cmd += ' )'
706 self.handle.sendline( cmd )
707 self.handle.expect( self.scapyPrompt )
708 if "Traceback" in self.handle.before:
709 # KeyError, SyntaxError, ...
710 main.log.error( "Error in sending command: " + self.handle.before )
711 return main.FALSE
712 self.handle.sendline( "packet = ether/arp" )
713 self.handle.expect( self.scapyPrompt )
714 if "Traceback" in self.handle.before:
715 # KeyError, SyntaxError, ...
716 main.log.error( "Error in sending command: " + self.handle.before )
717 return main.FALSE
718 return main.TRUE
719 except pexpect.TIMEOUT:
720 main.log.exception( self.name + ": Command timed out" )
721 return main.FALSE
722 except pexpect.EOF:
723 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700724 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700725 except Exception:
726 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700727 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700728
729 def buildICMP( self, ipVersion=4, **kwargs ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800730 """
731 Build an ICMP frame
732
733 Will create a frame class with the given options. If a field is
734 left blank it will default to the below value unless it is
735 overwritten by the next frame.
736 Default frame:
737 ###[ ICMP ]###
738 type= echo-request
739 code= 0
740 chksum= None
741 id= 0x0
742 seq= 0x0
743
alisone14d7b02016-07-06 10:31:51 -0700744 Options:
745 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
746 frame to use to encapsulate into
747
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800748 Returns main.TRUE or main.FALSE on error
749 """
750 try:
751 # Set the ICMP frame
alisone14d7b02016-07-06 10:31:51 -0700752 if str( ipVersion ) is '4':
753 cmd = 'icmp = ICMP( '
754 elif str( ipVersion ) is '6':
755 cmd = 'icmp6 = ICMPv6EchoReply( '
756 else:
757 main.log.error( "Unrecognized option for ipVersion, given " +
758 repr( ipVersion ) )
759 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800760 options = []
alisone14d7b02016-07-06 10:31:51 -0700761 for key, value in kwargs.iteritems( ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800762 if isinstance( value, str ):
763 value = '"' + value + '"'
764 options.append( str( key ) + "=" + str( value ) )
765 cmd += ", ".join( options )
766 cmd += ' )'
767 self.handle.sendline( cmd )
768 self.handle.expect( self.scapyPrompt )
769 if "Traceback" in self.handle.before:
770 # KeyError, SyntaxError, ...
771 main.log.error( "Error in sending command: " + self.handle.before )
772 return main.FALSE
alisone14d7b02016-07-06 10:31:51 -0700773
774 if str( ipVersion ) is '4':
775 self.handle.sendline( "packet = ether/ip/icmp" )
776 elif str( ipVersion ) is '6':
777 self.handle.sendline( "packet = ether/ipv6/icmp6" )
778 else:
779 main.log.error( "Unrecognized option for ipVersion, given " +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700780 repr( ipVersion ) )
alisone14d7b02016-07-06 10:31:51 -0700781 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800782 self.handle.expect( self.scapyPrompt )
783 if "Traceback" in self.handle.before:
784 # KeyError, SyntaxError, ...
785 main.log.error( "Error in sending command: " + self.handle.before )
786 return main.FALSE
787 return main.TRUE
788 except pexpect.TIMEOUT:
789 main.log.exception( self.name + ": Command timed out" )
790 return main.FALSE
791 except pexpect.EOF:
792 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700793 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800794 except Exception:
795 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700796 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800797
798 def sendPacket( self, iface=None, packet=None, timeout=1 ):
799 """
800 Send a packet with either the given scapy packet command, or use the
801 packet saved in the variable 'packet'.
802
803 Examples of a valid string for packet:
804
805 Simple IP packet
806 packet='Ether(dst="a6:d9:26:df:1d:4b")/IP(dst="10.0.0.2")'
807
808 A Ping with two vlan tags
809 packet='Ether(dst='ff:ff:ff:ff:ff:ff')/Dot1Q(vlan=1)/Dot1Q(vlan=10)/
810 IP(dst='255.255.255.255', src='192.168.0.1')/ICMP()'
811
812 Returns main.TRUE or main.FALSE on error
813 """
814 try:
815 # TODO: add all params, or use kwargs
816 sendCmd = 'srp( '
817 if packet:
818 sendCmd += packet
819 else:
820 sendCmd += "packet"
821 if iface:
822 sendCmd += ", iface='{}'".format( iface )
823
824 sendCmd += ', timeout=' + str( timeout ) + ')'
825 self.handle.sendline( sendCmd )
826 self.handle.expect( self.scapyPrompt )
Jon Halla510a8a2016-05-04 15:09:28 -0700827 # main.log.warn( "Send packet response: {}".format( self.handle.before ) )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800828 if "Traceback" in self.handle.before:
829 # KeyError, SyntaxError, ...
830 main.log.error( "Error in sending command: " + self.handle.before )
831 return main.FALSE
832 # TODO: Check # of packets sent?
833 return main.TRUE
834 except pexpect.TIMEOUT:
835 main.log.exception( self.name + ": Command timed out" )
836 return main.FALSE
837 except pexpect.EOF:
838 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700839 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800840 except Exception:
841 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700842 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800843
844 def startFilter( self, ifaceName=None, sniffCount=1, pktFilter="ip" ):
845 """
846 Listen for packets using the given filters
847
848 Options:
849 ifaceName - the name of the interface to listen on. If none is given,
850 defaults to <host name>-eth0
851 pktFilter - A string in Berkeley Packet Filter (BPF) format which
852 specifies which packets to sniff
853 sniffCount - The number of matching packets to capture before returning
854
855 Returns main.TRUE or main.FALSE on error
856 """
857 try:
858 # TODO: add all params, or use kwargs
859 ifaceName = str( ifaceName ) if ifaceName else self.name + "-eth0"
860 # Set interface
861 self.handle.sendline( ' conf.iface = "' + ifaceName + '"' )
862 self.handle.expect( self.scapyPrompt )
863 cmd = 'pkt = sniff(count = ' + str( sniffCount ) +\
864 ', filter = "' + str( pktFilter ) + '")'
Jon Halla510a8a2016-05-04 15:09:28 -0700865 main.log.info( "Filter on " + self.name + ' > ' + cmd )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800866 self.handle.sendline( cmd )
867 self.handle.expect( '"\)\r\n' )
868 # TODO: parse this?
869 return main.TRUE
870 except pexpect.TIMEOUT:
871 main.log.exception( self.name + ": Command timed out" )
872 return main.FALSE
873 except pexpect.EOF:
874 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700875 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800876 except Exception:
877 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700878 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800879
880 def checkFilter( self, timeout=10 ):
881 """
882 Check that a filter returned and returns the reponse
883 """
884 try:
885 i = self.handle.expect( [ self.scapyPrompt, pexpect.TIMEOUT ], timeout=timeout )
886 if i == 0:
887 return main.TRUE
888 else:
889 return main.FALSE
890 except pexpect.EOF:
891 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700892 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800893 except Exception:
894 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700895 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800896
897 def killFilter( self ):
898 """
899 Kill a scapy filter
900 """
901 try:
902 self.handle.send( "\x03" ) # Send a ctrl-c to kill the filter
903 self.handle.expect( self.scapyPrompt )
904 return self.handle.before
905 except pexpect.TIMEOUT:
906 main.log.exception( self.name + ": Command timed out" )
907 return None
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 readPackets( self ):
916 """
917 Read all the packets captured by the previous filter
918 """
919 try:
920 self.handle.sendline( "for p in pkt: p \n")
921 self.handle.expect( "for p in pkt: p \r\n... \r\n" )
922 self.handle.expect( self.scapyPrompt )
923 except pexpect.TIMEOUT:
924 main.log.exception( self.name + ": Command timed out" )
925 return None
926 except pexpect.EOF:
927 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700928 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800929 except Exception:
930 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700931 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800932 return self.handle.before
933
alisone14d7b02016-07-06 10:31:51 -0700934 def updateSelf( self, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800935 """
936 Updates local MAC and IP fields
937 """
938 self.hostMac = self.getMac()
alisone14d7b02016-07-06 10:31:51 -0700939 if IPv6:
940 self.hostIp = self.getIp( IPv6=True )
941 else:
942 self.hostIp = self.getIp()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800943
944 def getMac( self, ifaceName=None ):
945 """
946 Save host's MAC address
947 """
948 try:
949 ifaceName = str( ifaceName ) if ifaceName else self.name + "-eth0"
950 cmd = 'get_if_hwaddr("' + str( ifaceName ) + '")'
951 self.handle.sendline( cmd )
952 self.handle.expect( self.scapyPrompt )
953 pattern = r'(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))'
954 match = re.search( pattern, self.handle.before )
955 if match:
956 return match.group()
957 else:
958 # the command will have an exception if iface doesn't exist
959 return None
960 except pexpect.TIMEOUT:
961 main.log.exception( self.name + ": Command timed out" )
962 return None
963 except pexpect.EOF:
964 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700965 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800966 except Exception:
967 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700968 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800969
alisone14d7b02016-07-06 10:31:51 -0700970 def getIp( self, ifaceName=None, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800971 """
972 Save host's IP address
973
974 Returns the IP of the first interface that is not a loopback device.
975 If no IP could be found then it will return 0.0.0.0.
alisone14d7b02016-07-06 10:31:51 -0700976
977 If IPv6 is equal to True, returns IPv6 of the first interface that is not a loopback device.
978 If no IPv6 could be found then it will return :: .
979
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800980 """
981 def getIPofInterface( ifaceName ):
982 cmd = 'get_if_addr("' + str( ifaceName ) + '")'
alisone14d7b02016-07-06 10:31:51 -0700983 if IPv6:
984 cmd = 'get_if_raw_addr6("' + str( ifaceName ) + '")'
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800985 self.handle.sendline( cmd )
986 self.handle.expect( self.scapyPrompt )
987
988 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 -0700989 if IPv6:
990 pattern = r'(\\x([0-9]|[a-f]|[A-F])([0-9]|[a-f]|[A-F])){16}'
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800991 match = re.search( pattern, self.handle.before )
992 if match:
993 # NOTE: The command will return 0.0.0.0 if the iface doesn't exist
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700994 if IPv6 is not True:
alisone14d7b02016-07-06 10:31:51 -0700995 if match.group() == '0.0.0.0':
996 main.log.warn( 'iface {0} has no IPv4 address'.format( ifaceName ) )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800997 return match.group()
998 else:
999 return None
1000 try:
1001 if not ifaceName:
1002 # Get list of interfaces
1003 ifList = self.getIfList()
alisone14d7b02016-07-06 10:31:51 -07001004 if IPv6:
1005 for ifaceName in ifList:
1006 if ifaceName == "lo":
1007 continue
1008 ip = getIPofInterface( ifaceName )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001009 if ip is not None:
1010 newip = ip
alisone14d7b02016-07-06 10:31:51 -07001011 tmp = newip.split( "\\x" )
1012 ip = ""
1013 counter = 0
1014 for i in tmp:
1015 if i != "":
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001016 counter = counter + 1
alisone14d7b02016-07-06 10:31:51 -07001017 if counter % 2 == 0 and counter < 16:
1018 ip = ip + i + ":"
1019 else:
1020 ip = ip + i
1021 return ip
1022 return "::"
1023 else:
1024 for ifaceName in ifList:
1025 if ifaceName == "lo":
1026 continue
1027 ip = getIPofInterface( ifaceName )
1028 if ip != "0.0.0.0":
1029 return ip
1030 return "0.0.0.0"
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001031 else:
1032 return getIPofInterface( ifaceName )
1033
1034 except pexpect.TIMEOUT:
1035 main.log.exception( self.name + ": Command timed out" )
1036 return None
1037 except pexpect.EOF:
1038 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -07001039 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001040 except Exception:
1041 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001042 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001043
1044 def getIfList( self ):
1045 """
1046 Return List of Interfaces
1047 """
1048 try:
1049 self.handle.sendline( 'get_if_list()' )
1050 self.handle.expect( self.scapyPrompt )
1051 ifList = self.handle.before.split( '\r\n' )
Jeremy Ronquillo82705492017-10-18 14:19:55 -07001052 ifList = ifList[ 1 ].replace( "'", "" )[ 1:-1 ].split( ', ' )
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001053 return ifList
1054
1055 except pexpect.TIMEOUT:
1056 main.log.exception( self.name + ": Command timed out" )
1057 return None
1058 except pexpect.EOF:
1059 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -07001060 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001061 except Exception:
1062 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -07001063 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001064
1065if __name__ != "__main__":
1066 sys.modules[ __name__ ] = ScapyCliDriver()