blob: 418aa9ec1a2864c1f2f10747b55c99aaedf31d11 [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
721 self.handle.sendline( ' conf.iface = "' + ifaceName + '"' )
722 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' )
728 # TODO: parse this?
729 return main.TRUE
730 except pexpect.TIMEOUT:
731 main.log.exception( self.name + ": Command timed out" )
732 return main.FALSE
733 except pexpect.EOF:
734 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700735 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800736 except Exception:
737 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700738 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800739
740 def checkFilter( self, timeout=10 ):
741 """
742 Check that a filter returned and returns the reponse
743 """
744 try:
745 i = self.handle.expect( [ self.scapyPrompt, pexpect.TIMEOUT ], timeout=timeout )
746 if i == 0:
747 return main.TRUE
748 else:
749 return main.FALSE
750 except pexpect.EOF:
751 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700752 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800753 except Exception:
754 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700755 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800756
757 def killFilter( self ):
758 """
759 Kill a scapy filter
760 """
761 try:
762 self.handle.send( "\x03" ) # Send a ctrl-c to kill the filter
763 self.handle.expect( self.scapyPrompt )
764 return self.handle.before
765 except pexpect.TIMEOUT:
766 main.log.exception( self.name + ": Command timed out" )
767 return None
768 except pexpect.EOF:
769 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700770 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800771 except Exception:
772 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700773 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800774
775 def readPackets( self ):
776 """
777 Read all the packets captured by the previous filter
778 """
779 try:
780 self.handle.sendline( "for p in pkt: p \n")
781 self.handle.expect( "for p in pkt: p \r\n... \r\n" )
782 self.handle.expect( self.scapyPrompt )
783 except pexpect.TIMEOUT:
784 main.log.exception( self.name + ": Command timed out" )
785 return None
786 except pexpect.EOF:
787 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700788 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800789 except Exception:
790 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700791 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800792 return self.handle.before
793
alisone14d7b02016-07-06 10:31:51 -0700794 def updateSelf( self, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800795 """
796 Updates local MAC and IP fields
797 """
798 self.hostMac = self.getMac()
alisone14d7b02016-07-06 10:31:51 -0700799 if IPv6:
800 self.hostIp = self.getIp( IPv6=True )
801 else:
802 self.hostIp = self.getIp()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800803
804 def getMac( self, ifaceName=None ):
805 """
806 Save host's MAC address
807 """
808 try:
You Wangdafb6e22018-01-22 17:01:00 -0800809 ifaceName = str( ifaceName ) if ifaceName else self.ifaceName
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800810 cmd = 'get_if_hwaddr("' + str( ifaceName ) + '")'
811 self.handle.sendline( cmd )
812 self.handle.expect( self.scapyPrompt )
813 pattern = r'(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))'
814 match = re.search( pattern, self.handle.before )
815 if match:
816 return match.group()
817 else:
818 # the command will have an exception if iface doesn't exist
819 return None
820 except pexpect.TIMEOUT:
821 main.log.exception( self.name + ": Command timed out" )
822 return None
823 except pexpect.EOF:
824 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700825 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800826 except Exception:
827 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700828 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800829
alisone14d7b02016-07-06 10:31:51 -0700830 def getIp( self, ifaceName=None, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800831 """
832 Save host's IP address
833
834 Returns the IP of the first interface that is not a loopback device.
835 If no IP could be found then it will return 0.0.0.0.
alisone14d7b02016-07-06 10:31:51 -0700836
837 If IPv6 is equal to True, returns IPv6 of the first interface that is not a loopback device.
838 If no IPv6 could be found then it will return :: .
839
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800840 """
841 def getIPofInterface( ifaceName ):
842 cmd = 'get_if_addr("' + str( ifaceName ) + '")'
alisone14d7b02016-07-06 10:31:51 -0700843 if IPv6:
844 cmd = 'get_if_raw_addr6("' + str( ifaceName ) + '")'
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800845 self.handle.sendline( cmd )
846 self.handle.expect( self.scapyPrompt )
847
848 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 -0700849 if IPv6:
850 pattern = r'(\\x([0-9]|[a-f]|[A-F])([0-9]|[a-f]|[A-F])){16}'
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800851 match = re.search( pattern, self.handle.before )
852 if match:
853 # NOTE: The command will return 0.0.0.0 if the iface doesn't exist
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700854 if IPv6 is not True:
alisone14d7b02016-07-06 10:31:51 -0700855 if match.group() == '0.0.0.0':
856 main.log.warn( 'iface {0} has no IPv4 address'.format( ifaceName ) )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800857 return match.group()
858 else:
859 return None
860 try:
861 if not ifaceName:
862 # Get list of interfaces
863 ifList = self.getIfList()
alisone14d7b02016-07-06 10:31:51 -0700864 if IPv6:
865 for ifaceName in ifList:
866 if ifaceName == "lo":
867 continue
868 ip = getIPofInterface( ifaceName )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700869 if ip is not None:
870 newip = ip
alisone14d7b02016-07-06 10:31:51 -0700871 tmp = newip.split( "\\x" )
872 ip = ""
873 counter = 0
874 for i in tmp:
875 if i != "":
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700876 counter = counter + 1
alisone14d7b02016-07-06 10:31:51 -0700877 if counter % 2 == 0 and counter < 16:
878 ip = ip + i + ":"
879 else:
880 ip = ip + i
881 return ip
882 return "::"
883 else:
884 for ifaceName in ifList:
885 if ifaceName == "lo":
886 continue
887 ip = getIPofInterface( ifaceName )
888 if ip != "0.0.0.0":
889 return ip
890 return "0.0.0.0"
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800891 else:
892 return getIPofInterface( ifaceName )
893
894 except pexpect.TIMEOUT:
895 main.log.exception( self.name + ": Command timed out" )
896 return None
897 except pexpect.EOF:
898 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700899 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800900 except Exception:
901 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700902 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800903
904 def getIfList( self ):
905 """
906 Return List of Interfaces
907 """
908 try:
909 self.handle.sendline( 'get_if_list()' )
910 self.handle.expect( self.scapyPrompt )
911 ifList = self.handle.before.split( '\r\n' )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700912 ifList = ifList[ 1 ].replace( "'", "" )[ 1:-1 ].split( ', ' )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800913 return ifList
914
915 except pexpect.TIMEOUT:
916 main.log.exception( self.name + ": Command timed out" )
917 return None
918 except pexpect.EOF:
919 main.log.exception( self.name + ": connection closed." )
Devin Lim44075962017-08-11 10:56:37 -0700920 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800921 except Exception:
922 main.log.exception( self.name + ": Uncaught exception!" )
Devin Lim44075962017-08-11 10:56:37 -0700923 main.cleanAndExit()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800924
925if __name__ != "__main__":
926 sys.modules[ __name__ ] = ScapyCliDriver()