blob: 0dd3b6bf705158d751078ab087a4659629b9b664 [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
You Wangdafb6e22018-01-22 17:01:00 -080048 self.hostPrompt = "\$"
Jeremy Songster1f39bf02016-01-20 17:17:25 -080049 self.scapyPrompt = ">>>"
50
51 def connect( self, **connectargs ):
52 """
53 Here the main is the TestON instance after creating
54 all the log handles."""
55 try:
56 for key in connectargs:
57 vars( self )[ key ] = connectargs[ key ]
You Wangdafb6e22018-01-22 17:01:00 -080058 self.home = self.options[ 'home' ] if 'home' in self.options.keys() else "~/"
Jeremy Songster1f39bf02016-01-20 17:17:25 -080059 self.name = self.options[ 'name' ]
You Wangdafb6e22018-01-22 17:01:00 -080060 self.ifaceName = self.options[ 'ifaceName' ] if 'ifaceName' in self.options.keys() else self.name + "-eth0"
Jeremy Songster1f39bf02016-01-20 17:17:25 -080061 try:
62 if os.getenv( str( self.ip_address ) ) is not None:
63 self.ip_address = os.getenv( str( self.ip_address ) )
64 else:
65 main.log.info( self.name +
66 ": Trying to connect to " +
67 self.ip_address )
68
69 except KeyError:
70 main.log.info( "Invalid host name," +
71 " connecting to local host instead" )
72 self.ip_address = 'localhost'
73 except Exception as inst:
74 main.log.error( "Uncaught exception: " + str( inst ) )
75
76 self.handle = super(
77 ScapyCliDriver,
78 self ).connect(
79 user_name=self.user_name,
80 ip_address=self.ip_address,
81 port=None,
82 pwd=self.pwd )
83
84 if self.handle:
85 main.log.info( "Connection successful to the host " +
86 self.user_name +
87 "@" +
88 self.ip_address )
89 return main.TRUE
90 else:
91 main.log.error( "Connection failed to the host " +
92 self.user_name +
93 "@" +
94 self.ip_address )
95 main.log.error( "Failed to connect to the Mininet Host" )
96 return main.FALSE
97 except pexpect.EOF:
98 main.log.error( self.name + ": EOF exception found" )
99 main.log.error( self.name + ": " + self.handle.before )
Devin Lim44075962017-08-11 10:56:37 -0700100 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800101 except Exception:
102 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700103 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800104
105 def disconnect( self ):
106 """
107 Called at the end of the test to stop the scapy component and
108 disconnect the handle.
109 """
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800110 response = main.TRUE
You Wangdafb6e22018-01-22 17:01:00 -0800111 try:
112 if self.handle:
113 self.handle.sendline( "exit" )
114 self.handle.expect( "closed" )
115 except pexpect.EOF:
116 main.log.error( self.name + ": EOF exception found" )
117 main.log.error( self.name + ": " + self.handle.before )
118 except Exception:
119 main.log.exception( self.name + ": Connection failed to the host" )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800120 response = main.FALSE
121 return response
122
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800123 def startScapy( self, mplsPath="" ):
124 """
125 Start the Scapy cli
126 optional:
127 mplsPath - The path where the MPLS class is located
128 NOTE: This can be a relative path from the user's home dir
129 """
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700130 mplsLines = [ 'import imp',
131 'imp.load_source( "mplsClass", "{}mplsClass.py" )'.format( mplsPath ),
132 'from mplsClass import MPLS',
133 'bind_layers(Ether, MPLS, type = 0x8847)',
134 'bind_layers(MPLS, MPLS, bottom_of_label_stack = 0)',
135 'bind_layers(MPLS, IP)' ]
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800136
137 try:
138 self.handle.sendline( "scapy" )
139 self.handle.expect( self.scapyPrompt )
140 self.handle.sendline( "conf.color_theme = NoTheme()" )
141 self.handle.expect( self.scapyPrompt )
142 if mplsPath:
143 main.log.info( "Adding MPLS class" )
144 main.log.info( "MPLS class path: " + mplsPath )
145 for line in mplsLines:
146 main.log.info( "sending line: " + line )
147 self.handle.sendline( line )
148 self.handle.expect( self.scapyPrompt )
149 return main.TRUE
150 except pexpect.TIMEOUT:
151 main.log.exception( self.name + ": Command timed out" )
152 return main.FALSE
153 except pexpect.EOF:
154 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700155 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800156 except Exception:
157 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700158 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800159
160 def stopScapy( self ):
161 """
162 Exit the Scapy cli
163 """
164 try:
165 self.handle.sendline( "exit()" )
166 self.handle.expect( self.hostPrompt )
167 return main.TRUE
168 except pexpect.TIMEOUT:
169 main.log.exception( self.name + ": Command timed out" )
170 return main.FALSE
171 except pexpect.EOF:
172 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700173 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800174 except Exception:
175 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700176 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800177
178 def buildEther( self, **kwargs ):
179 """
180 Build an Ethernet frame
181
182 Will create a frame class with the given options. If a field is
183 left blank it will default to the below value unless it is
184 overwritten by the next frame.
185 Default frame:
186 ###[ Ethernet ]###
187 dst= ff:ff:ff:ff:ff:ff
188 src= 00:00:00:00:00:00
189 type= 0x800
190
191 Returns main.TRUE or main.FALSE on error
192 """
193 try:
194 # Set the Ethernet frame
195 cmd = 'ether = Ether( '
196 options = []
197 for key, value in kwargs.iteritems():
198 if isinstance( value, str ):
199 value = '"' + value + '"'
200 options.append( str( key ) + "=" + str( value ) )
201 cmd += ", ".join( options )
202 cmd += ' )'
203 self.handle.sendline( cmd )
204 self.handle.expect( self.scapyPrompt )
205 if "Traceback" in self.handle.before:
206 # KeyError, SyntaxError, ...
207 main.log.error( "Error in sending command: " + self.handle.before )
208 return main.FALSE
209 self.handle.sendline( "packet = ether" )
210 self.handle.expect( self.scapyPrompt )
211 if "Traceback" in self.handle.before:
212 # KeyError, SyntaxError, ...
213 main.log.error( "Error in sending command: " + self.handle.before )
214 return main.FALSE
215 return main.TRUE
216 except pexpect.TIMEOUT:
217 main.log.exception( self.name + ": Command timed out" )
218 return main.FALSE
219 except pexpect.EOF:
220 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700221 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800222 except Exception:
223 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700224 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800225
226 def buildIP( self, **kwargs ):
227 """
228 Build an IP frame
229
230 Will create a frame class with the given options. If a field is
231 left blank it will default to the below value unless it is
232 overwritten by the next frame.
233 Default frame:
234 ###[ IP ]###
235 version= 4
236 ihl= None
237 tos= 0x0
238 len= None
239 id= 1
240 flags=
241 frag= 0
242 ttl= 64
243 proto= hopopt
244 chksum= None
245 src= 127.0.0.1
246 dst= 127.0.0.1
247 \options\
248
249 Returns main.TRUE or main.FALSE on error
250 """
251 try:
252 # Set the IP frame
253 cmd = 'ip = IP( '
254 options = []
255 for key, value in kwargs.iteritems():
256 if isinstance( value, str ):
257 value = '"' + value + '"'
258 options.append( str( key ) + "=" + str( value ) )
259 cmd += ", ".join( options )
260 cmd += ' )'
261 self.handle.sendline( cmd )
262 self.handle.expect( self.scapyPrompt )
263 if "Traceback" in self.handle.before:
264 # KeyError, SyntaxError, ...
265 main.log.error( "Error in sending command: " + self.handle.before )
266 return main.FALSE
267 self.handle.sendline( "packet = ether/ip" )
268 self.handle.expect( self.scapyPrompt )
269 if "Traceback" in self.handle.before:
270 # KeyError, SyntaxError, ...
271 main.log.error( "Error in sending command: " + self.handle.before )
272 return main.FALSE
273 return main.TRUE
274 except pexpect.TIMEOUT:
275 main.log.exception( self.name + ": Command timed out" )
276 return main.FALSE
277 except pexpect.EOF:
278 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700279 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800280 except Exception:
281 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700282 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800283
284 def buildIPv6( self, **kwargs ):
285 """
286 Build an IPv6 frame
287
288 Will create a frame class with the given options. If a field is
289 left blank it will default to the below value unless it is
290 overwritten by the next frame.
291 Default frame:
292 ###[ IPv6 ]###
293 version= 6
294 tc= 0
295 fl= 0
296 plen= None
297 nh= No Next Header
298 hlim= 64
299 src= ::1
300 dst= ::1
301
302 Returns main.TRUE or main.FALSE on error
303 """
304 try:
305 # Set the IPv6 frame
306 cmd = 'ipv6 = IPv6( '
307 options = []
308 for key, value in kwargs.iteritems():
309 if isinstance( value, str ):
310 value = '"' + value + '"'
311 options.append( str( key ) + "=" + str( value ) )
312 cmd += ", ".join( options )
313 cmd += ' )'
314 self.handle.sendline( cmd )
315 self.handle.expect( self.scapyPrompt )
316 if "Traceback" in self.handle.before:
317 # KeyError, SyntaxError, ...
318 main.log.error( "Error in sending command: " + self.handle.before )
319 return main.FALSE
320 self.handle.sendline( "packet = ether/ipv6" )
321 self.handle.expect( self.scapyPrompt )
322 if "Traceback" in self.handle.before:
323 # KeyError, SyntaxError, ...
324 main.log.error( "Error in sending command: " + self.handle.before )
325 return main.FALSE
326 return main.TRUE
327 except pexpect.TIMEOUT:
328 main.log.exception( self.name + ": Command timed out" )
329 return main.FALSE
330 except pexpect.EOF:
331 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700332 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800333 except Exception:
334 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700335 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800336
337 def buildTCP( self, ipVersion=4, **kwargs ):
338 """
339 Build an TCP frame
340
341 Will create a frame class with the given options. If a field is
342 left blank it will default to the below value unless it is
343 overwritten by the next frame.
344
345 NOTE: Some arguments require quotes around them. It's up to you to
346 know which ones and to add them yourself. Arguments with an asterisk
347 do not need quotes.
348
349 Options:
350 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
351 frame to use to encapsulate into
352 Default frame:
353 ###[ TCP ]###
354 sport= ftp_data *
355 dport= http *
356 seq= 0
357 ack= 0
358 dataofs= None
359 reserved= 0
360 flags= S
361 window= 8192
362 chksum= None
363 urgptr= 0
364 options= {}
365
366 Returns main.TRUE or main.FALSE on error
367 """
368 try:
369 # Set the TCP frame
370 cmd = 'tcp = TCP( '
371 options = []
372 for key, value in kwargs.iteritems():
373 options.append( str( key ) + "=" + str( value ) )
374 cmd += ", ".join( options )
375 cmd += ' )'
376 self.handle.sendline( cmd )
377 self.handle.expect( self.scapyPrompt )
378 if "Traceback" in self.handle.before:
379 # KeyError, SyntaxError, ...
380 main.log.error( "Error in sending command: " + self.handle.before )
381 return main.FALSE
382 if str( ipVersion ) is '4':
383 self.handle.sendline( "packet = ether/ip/tcp" )
384 elif str( ipVersion ) is '6':
385 self.handle.sendline( "packet = ether/ipv6/tcp" )
386 else:
387 main.log.error( "Unrecognized option for ipVersion, given " +
388 repr( ipVersion ) )
389 return main.FALSE
390 self.handle.expect( self.scapyPrompt )
391 if "Traceback" in self.handle.before:
392 # KeyError, SyntaxError, ...
393 main.log.error( "Error in sending command: " + self.handle.before )
394 return main.FALSE
395 return main.TRUE
396 except pexpect.TIMEOUT:
397 main.log.exception( self.name + ": Command timed out" )
398 return main.FALSE
399 except pexpect.EOF:
400 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700401 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800402 except Exception:
403 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700404 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800405
406 def buildUDP( self, ipVersion=4, **kwargs ):
407 """
408 Build an UDP frame
409
410 Will create a frame class with the given options. If a field is
411 left blank it will default to the below value unless it is
412 overwritten by the next frame.
413
414 NOTE: Some arguments require quotes around them. It's up to you to
415 know which ones and to add them yourself. Arguments with an asterisk
416 do not need quotes.
417
418 Options:
419 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
420 frame to use to encapsulate into
421 Default frame:
422 ###[ UDP ]###
423 sport= domain *
424 dport= domain *
425 len= None
426 chksum= None
427
428 Returns main.TRUE or main.FALSE on error
429 """
430 try:
431 # Set the UDP frame
432 cmd = 'udp = UDP( '
433 options = []
434 for key, value in kwargs.iteritems():
435 options.append( str( key ) + "=" + str( value ) )
436 cmd += ", ".join( options )
437 cmd += ' )'
438 self.handle.sendline( cmd )
439 self.handle.expect( self.scapyPrompt )
440 if "Traceback" in self.handle.before:
441 # KeyError, SyntaxError, ...
442 main.log.error( "Error in sending command: " + self.handle.before )
443 return main.FALSE
444 if str( ipVersion ) is '4':
445 self.handle.sendline( "packet = ether/ip/udp" )
446 elif str( ipVersion ) is '6':
447 self.handle.sendline( "packet = ether/ipv6/udp" )
448 else:
449 main.log.error( "Unrecognized option for ipVersion, given " +
450 repr( ipVersion ) )
451 return main.FALSE
452 self.handle.expect( self.scapyPrompt )
453 if "Traceback" in self.handle.before:
454 # KeyError, SyntaxError, ...
455 main.log.error( "Error in sending command: " + self.handle.before )
456 return main.FALSE
457 return main.TRUE
458 except pexpect.TIMEOUT:
459 main.log.exception( self.name + ": Command timed out" )
460 return main.FALSE
461 except pexpect.EOF:
462 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700463 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800464 except Exception:
465 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700466 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800467
alisone14d7b02016-07-06 10:31:51 -0700468 def buildSCTP( self, ipVersion=4, **kwargs ):
469 """
470 Build an SCTP frame
471
472 Will create a frame class with the given options. If a field is
473 left blank it will default to the below value unless it is
474 overwritten by the next frame.
475
476 NOTE: Some arguments require quotes around them. It's up to you to
477 know which ones and to add them yourself. Arguments with an asterisk
478 do not need quotes.
479
480 Options:
481 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
482 frame to use to encapsulate into
483 Default frame:
484 ###[ SCTP ]###
485 sport= domain *
486 dport= domain *
487 tag = None
488 chksum = None
489
490 Returns main.TRUE or main.FALSE on error
491 """
492 try:
493 # Set the SCTP frame
494 cmd = 'sctp = SCTP( '
495 options = [ ]
496 for key, value in kwargs.iteritems( ):
497 options.append( str( key ) + "=" + str( value ) )
498 cmd += ", ".join( options )
499 cmd += ' )'
500 self.handle.sendline( cmd )
501 self.handle.expect( self.scapyPrompt )
502 if "Traceback" in self.handle.before:
503 # KeyError, SyntaxError, ...
504 main.log.error( "Error in sending command: " + self.handle.before )
505 return main.FALSE
506 if str( ipVersion ) is '4':
507 self.handle.sendline( "packet = ether/ip/sctp" )
508 elif str( ipVersion ) is '6':
509 self.handle.sendline( "packet = ether/ipv6/sctp" )
510 else:
511 main.log.error( "Unrecognized option for ipVersion, given " +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700512 repr( ipVersion ) )
alisone14d7b02016-07-06 10:31:51 -0700513 return main.FALSE
514 self.handle.expect( self.scapyPrompt )
515 if "Traceback" in self.handle.before:
516 # KeyError, SyntaxError, ...
517 main.log.error( "Error in sending command: " + self.handle.before )
518 return main.FALSE
519 return main.TRUE
520 except pexpect.TIMEOUT:
521 main.log.exception( self.name + ": Command timed out" )
522 return main.FALSE
523 except pexpect.EOF:
524 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700525 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700526 except Exception:
527 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700528 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700529
530 def buildARP( self, **kwargs ):
531 """
532 Build an ARP frame
533
534 Will create a frame class with the given options. If a field is
535 left blank it will default to the below value unless it is
536 overwritten by the next frame.
537
538 NOTE: Some arguments require quotes around them. It's up to you to
539 know which ones and to add them yourself. Arguments with an asterisk
540 do not need quotes.
541
542 Default frame:
543 ###[ ARP ]###
544 hwtype : XShortField = (1)
545 ptype : XShortEnumField = (2048)
546 hwlen : ByteField = (6)
547 plen : ByteField = (4)
548 op : ShortEnumField = (1)
549 hwsrc : ARPSourceMACField = (None)
550 psrc : SourceIPField = (None)
551 hwdst : MACField = ('00:00:00:00:00:00')
552 pdst : IPField = ('0.0.0.0')
553
554 Returns main.TRUE or main.FALSE on error
555 """
556 try:
557 # Set the ARP frame
558 cmd = 'arp = ARP( '
559 options = []
560 for key, value in kwargs.iteritems( ):
561 if isinstance( value, str ):
562 value = '"' + value + '"'
563 options.append( str( key ) + "=" + str( value ) )
564 cmd += ", ".join( options )
565 cmd += ' )'
566 self.handle.sendline( cmd )
567 self.handle.expect( self.scapyPrompt )
568 if "Traceback" in self.handle.before:
569 # KeyError, SyntaxError, ...
570 main.log.error( "Error in sending command: " + self.handle.before )
571 return main.FALSE
572 self.handle.sendline( "packet = ether/arp" )
573 self.handle.expect( self.scapyPrompt )
574 if "Traceback" in self.handle.before:
575 # KeyError, SyntaxError, ...
576 main.log.error( "Error in sending command: " + self.handle.before )
577 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()
alisone14d7b02016-07-06 10:31:51 -0700585 except Exception:
586 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700587 main.cleanAndExit()
alisone14d7b02016-07-06 10:31:51 -0700588
589 def buildICMP( self, ipVersion=4, **kwargs ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800590 """
591 Build an ICMP 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 Default frame:
597 ###[ ICMP ]###
598 type= echo-request
599 code= 0
600 chksum= None
601 id= 0x0
602 seq= 0x0
603
alisone14d7b02016-07-06 10:31:51 -0700604 Options:
605 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
606 frame to use to encapsulate into
607
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800608 Returns main.TRUE or main.FALSE on error
609 """
610 try:
611 # Set the ICMP frame
alisone14d7b02016-07-06 10:31:51 -0700612 if str( ipVersion ) is '4':
613 cmd = 'icmp = ICMP( '
614 elif str( ipVersion ) is '6':
615 cmd = 'icmp6 = ICMPv6EchoReply( '
616 else:
617 main.log.error( "Unrecognized option for ipVersion, given " +
618 repr( ipVersion ) )
619 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800620 options = []
alisone14d7b02016-07-06 10:31:51 -0700621 for key, value in kwargs.iteritems( ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800622 if isinstance( value, str ):
623 value = '"' + value + '"'
624 options.append( str( key ) + "=" + str( value ) )
625 cmd += ", ".join( options )
626 cmd += ' )'
627 self.handle.sendline( cmd )
628 self.handle.expect( self.scapyPrompt )
629 if "Traceback" in self.handle.before:
630 # KeyError, SyntaxError, ...
631 main.log.error( "Error in sending command: " + self.handle.before )
632 return main.FALSE
alisone14d7b02016-07-06 10:31:51 -0700633
634 if str( ipVersion ) is '4':
635 self.handle.sendline( "packet = ether/ip/icmp" )
636 elif str( ipVersion ) is '6':
637 self.handle.sendline( "packet = ether/ipv6/icmp6" )
638 else:
639 main.log.error( "Unrecognized option for ipVersion, given " +
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700640 repr( ipVersion ) )
alisone14d7b02016-07-06 10:31:51 -0700641 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800642 self.handle.expect( self.scapyPrompt )
643 if "Traceback" in self.handle.before:
644 # KeyError, SyntaxError, ...
645 main.log.error( "Error in sending command: " + self.handle.before )
646 return main.FALSE
647 return main.TRUE
648 except pexpect.TIMEOUT:
649 main.log.exception( self.name + ": Command timed out" )
650 return main.FALSE
651 except pexpect.EOF:
652 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700653 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800654 except Exception:
655 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700656 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800657
658 def sendPacket( self, iface=None, packet=None, timeout=1 ):
659 """
660 Send a packet with either the given scapy packet command, or use the
661 packet saved in the variable 'packet'.
662
663 Examples of a valid string for packet:
664
665 Simple IP packet
666 packet='Ether(dst="a6:d9:26:df:1d:4b")/IP(dst="10.0.0.2")'
667
668 A Ping with two vlan tags
669 packet='Ether(dst='ff:ff:ff:ff:ff:ff')/Dot1Q(vlan=1)/Dot1Q(vlan=10)/
670 IP(dst='255.255.255.255', src='192.168.0.1')/ICMP()'
671
672 Returns main.TRUE or main.FALSE on error
673 """
674 try:
675 # TODO: add all params, or use kwargs
676 sendCmd = 'srp( '
677 if packet:
678 sendCmd += packet
679 else:
680 sendCmd += "packet"
681 if iface:
682 sendCmd += ", iface='{}'".format( iface )
683
684 sendCmd += ', timeout=' + str( timeout ) + ')'
685 self.handle.sendline( sendCmd )
686 self.handle.expect( self.scapyPrompt )
Jon Halla510a8a2016-05-04 15:09:28 -0700687 # main.log.warn( "Send packet response: {}".format( self.handle.before ) )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800688 if "Traceback" in self.handle.before:
689 # KeyError, SyntaxError, ...
690 main.log.error( "Error in sending command: " + self.handle.before )
691 return main.FALSE
692 # TODO: Check # of packets sent?
693 return main.TRUE
694 except pexpect.TIMEOUT:
695 main.log.exception( self.name + ": Command timed out" )
696 return main.FALSE
697 except pexpect.EOF:
698 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700699 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800700 except Exception:
701 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700702 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800703
704 def startFilter( self, ifaceName=None, sniffCount=1, pktFilter="ip" ):
705 """
706 Listen for packets using the given filters
707
708 Options:
709 ifaceName - the name of the interface to listen on. If none is given,
You Wangdafb6e22018-01-22 17:01:00 -0800710 defaults to self.ifaceName which is <host name>-eth0
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800711 pktFilter - A string in Berkeley Packet Filter (BPF) format which
712 specifies which packets to sniff
713 sniffCount - The number of matching packets to capture before returning
714
715 Returns main.TRUE or main.FALSE on error
716 """
717 try:
718 # TODO: add all params, or use kwargs
You Wangdafb6e22018-01-22 17:01:00 -0800719 ifaceName = str( ifaceName ) if ifaceName else self.ifaceName
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800720 # Set interface
You Wangbd848ed2018-03-23 13:57:42 -0700721 self.handle.sendline( 'conf.iface = "' + ifaceName + '"' )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800722 self.handle.expect( self.scapyPrompt )
723 cmd = 'pkt = sniff(count = ' + str( sniffCount ) +\
724 ', filter = "' + str( pktFilter ) + '")'
Jon Halla510a8a2016-05-04 15:09:28 -0700725 main.log.info( "Filter on " + self.name + ' > ' + cmd )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800726 self.handle.sendline( cmd )
727 self.handle.expect( '"\)\r\n' )
You Wang3a5f74c2018-08-03 14:58:15 -0700728 # Make sure the sniff function didn't exit due to failures
729 i = self.handle.expect( [ self.scapyPrompt, pexpect.TIMEOUT ], timeout=3 )
730 if i == 0:
731 # sniff exited
732 main.log.error( self.name + ": sniff function exited" )
733 main.log.error( self.name + ": " + self.handle.before )
734 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800735 # TODO: parse this?
736 return main.TRUE
737 except pexpect.TIMEOUT:
738 main.log.exception( self.name + ": Command timed out" )
739 return main.FALSE
740 except pexpect.EOF:
741 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700742 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800743 except Exception:
744 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700745 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800746
747 def checkFilter( self, timeout=10 ):
748 """
749 Check that a filter returned and returns the reponse
750 """
751 try:
752 i = self.handle.expect( [ self.scapyPrompt, pexpect.TIMEOUT ], timeout=timeout )
753 if i == 0:
754 return main.TRUE
755 else:
756 return main.FALSE
757 except pexpect.EOF:
758 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700759 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800760 except Exception:
761 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700762 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800763
764 def killFilter( self ):
765 """
766 Kill a scapy filter
767 """
768 try:
769 self.handle.send( "\x03" ) # Send a ctrl-c to kill the filter
770 self.handle.expect( self.scapyPrompt )
771 return self.handle.before
772 except pexpect.TIMEOUT:
773 main.log.exception( self.name + ": Command timed out" )
774 return None
775 except pexpect.EOF:
776 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700777 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800778 except Exception:
779 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700780 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800781
782 def readPackets( self ):
783 """
784 Read all the packets captured by the previous filter
785 """
786 try:
787 self.handle.sendline( "for p in pkt: p \n")
788 self.handle.expect( "for p in pkt: p \r\n... \r\n" )
789 self.handle.expect( self.scapyPrompt )
790 except pexpect.TIMEOUT:
791 main.log.exception( self.name + ": Command timed out" )
792 return None
793 except pexpect.EOF:
794 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700795 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800796 except Exception:
797 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700798 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800799 return self.handle.before
800
alisone14d7b02016-07-06 10:31:51 -0700801 def updateSelf( self, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800802 """
803 Updates local MAC and IP fields
804 """
805 self.hostMac = self.getMac()
alisone14d7b02016-07-06 10:31:51 -0700806 if IPv6:
807 self.hostIp = self.getIp( IPv6=True )
808 else:
809 self.hostIp = self.getIp()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800810
811 def getMac( self, ifaceName=None ):
812 """
813 Save host's MAC address
814 """
815 try:
You Wangdafb6e22018-01-22 17:01:00 -0800816 ifaceName = str( ifaceName ) if ifaceName else self.ifaceName
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800817 cmd = 'get_if_hwaddr("' + str( ifaceName ) + '")'
818 self.handle.sendline( cmd )
819 self.handle.expect( self.scapyPrompt )
820 pattern = r'(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))'
821 match = re.search( pattern, self.handle.before )
822 if match:
823 return match.group()
824 else:
825 # the command will have an exception if iface doesn't exist
826 return None
827 except pexpect.TIMEOUT:
828 main.log.exception( self.name + ": Command timed out" )
829 return None
830 except pexpect.EOF:
831 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700832 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800833 except Exception:
834 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700835 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800836
alisone14d7b02016-07-06 10:31:51 -0700837 def getIp( self, ifaceName=None, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800838 """
839 Save host's IP address
840
841 Returns the IP of the first interface that is not a loopback device.
842 If no IP could be found then it will return 0.0.0.0.
alisone14d7b02016-07-06 10:31:51 -0700843
844 If IPv6 is equal to True, returns IPv6 of the first interface that is not a loopback device.
845 If no IPv6 could be found then it will return :: .
846
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800847 """
848 def getIPofInterface( ifaceName ):
849 cmd = 'get_if_addr("' + str( ifaceName ) + '")'
alisone14d7b02016-07-06 10:31:51 -0700850 if IPv6:
851 cmd = 'get_if_raw_addr6("' + str( ifaceName ) + '")'
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800852 self.handle.sendline( cmd )
853 self.handle.expect( self.scapyPrompt )
854
855 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 -0700856 if IPv6:
857 pattern = r'(\\x([0-9]|[a-f]|[A-F])([0-9]|[a-f]|[A-F])){16}'
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800858 match = re.search( pattern, self.handle.before )
859 if match:
860 # NOTE: The command will return 0.0.0.0 if the iface doesn't exist
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700861 if IPv6 is not True:
alisone14d7b02016-07-06 10:31:51 -0700862 if match.group() == '0.0.0.0':
863 main.log.warn( 'iface {0} has no IPv4 address'.format( ifaceName ) )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800864 return match.group()
865 else:
866 return None
867 try:
868 if not ifaceName:
869 # Get list of interfaces
870 ifList = self.getIfList()
alisone14d7b02016-07-06 10:31:51 -0700871 if IPv6:
872 for ifaceName in ifList:
873 if ifaceName == "lo":
874 continue
875 ip = getIPofInterface( ifaceName )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700876 if ip is not None:
877 newip = ip
alisone14d7b02016-07-06 10:31:51 -0700878 tmp = newip.split( "\\x" )
879 ip = ""
880 counter = 0
881 for i in tmp:
882 if i != "":
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700883 counter = counter + 1
alisone14d7b02016-07-06 10:31:51 -0700884 if counter % 2 == 0 and counter < 16:
885 ip = ip + i + ":"
886 else:
887 ip = ip + i
888 return ip
889 return "::"
890 else:
891 for ifaceName in ifList:
892 if ifaceName == "lo":
893 continue
894 ip = getIPofInterface( ifaceName )
895 if ip != "0.0.0.0":
896 return ip
897 return "0.0.0.0"
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800898 else:
899 return getIPofInterface( ifaceName )
900
901 except pexpect.TIMEOUT:
902 main.log.exception( self.name + ": Command timed out" )
903 return None
904 except pexpect.EOF:
905 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700906 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800907 except Exception:
908 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700909 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800910
911 def getIfList( self ):
912 """
913 Return List of Interfaces
914 """
915 try:
916 self.handle.sendline( 'get_if_list()' )
917 self.handle.expect( self.scapyPrompt )
918 ifList = self.handle.before.split( '\r\n' )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700919 ifList = ifList[ 1 ].replace( "'", "" )[ 1:-1 ].split( ', ' )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800920 return ifList
921
922 except pexpect.TIMEOUT:
923 main.log.exception( self.name + ": Command timed out" )
924 return None
925 except pexpect.EOF:
926 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700927 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800928 except Exception:
929 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700930 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800931
932if __name__ != "__main__":
933 sys.modules[ __name__ ] = ScapyCliDriver()