blob: f61536c09f5b98b34e041c5329d83307bef41d39 [file] [log] [blame]
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001#!/usr/bin/env python
2"""
32015-2016
Jeremy Songsterae01bba2016-07-11 15:39:17 -07004Modified 2016 by ON.Lab
5
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 )
107 main.cleanup()
108 main.exit()
109 except Exception:
110 main.log.exception( self.name + ": Uncaught exception!" )
111 main.cleanup()
112 main.exit()
113
114 def disconnect( self ):
115 """
116 Called at the end of the test to stop the scapy component and
117 disconnect the handle.
118 """
119 self.handle.sendline( '' )
120 i = self.handle.expect( [ 'mininet>', pexpect.EOF, pexpect.TIMEOUT ],
121 timeout=2 )
122 response = main.TRUE
123 if i == 0:
124 response = self.stopNet()
125 elif i == 1:
126 return main.TRUE
127 # print "Disconnecting Mininet"
128 if self.handle:
129 self.handle.sendline( "exit" )
130 self.handle.expect( "exit" )
131 self.handle.expect( "(.*)" )
132 else:
133 main.log.error( "Connection failed to the host" )
134 return response
135
136 def stopNet( self, fileName="", timeout=5 ):
137 """
138 Stops mininet.
139 Returns main.TRUE if the mininet successfully stops and
140 main.FALSE if the pexpect handle does not exist.
141
142 Will cleanup and exit the test if scapy fails to stop
143 """
144 main.log.info( self.name + ": Stopping scapy..." )
145 response = ''
146 if self.handle:
147 try:
148 self.handle.sendline( "" )
149 i = self.handle.expect( [ '>>>',
Devin Limdc78e202017-06-09 18:30:07 -0700150 self.prompt,
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800151 pexpect.EOF,
152 pexpect.TIMEOUT ],
153 timeout )
154 if i == 0:
155 main.log.info( "Exiting scapy..." )
156 response = self.execute(
157 cmd="exit",
158 prompt="(.*)",
159 timeout=120 )
160 main.log.info( self.name + ": Stopped" )
161 response = main.TRUE
162
163 if i == 1:
164 main.log.info( " Mininet trying to exit while not " +
165 "in the mininet prompt" )
166 elif i == 2:
167 main.log.error( "Something went wrong exiting mininet" )
168 elif i == 3: # timeout
169 main.log.error( "Something went wrong exiting mininet " +
170 "TIMEOUT" )
171
172 if fileName:
173 self.handle.sendline( "" )
Devin Limdc78e202017-06-09 18:30:07 -0700174 self.handle.expect( self.prompt )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800175 self.handle.sendline(
176 "sudo kill -9 \`ps -ef | grep \"" +
177 fileName +
178 "\" | grep -v grep | awk '{print $2}'\`" )
179 except pexpect.EOF:
180 main.log.error( self.name + ": EOF exception found" )
181 main.log.error( self.name + ": " + self.handle.before )
182 main.cleanup()
183 main.exit()
184 else:
185 main.log.error( self.name + ": Connection failed to the host" )
186 response = main.FALSE
187 return response
188
189 def createHostComponent( self, name ):
190 """
191 Creates a new mininet cli component with the same parameters as self.
192 This new component is intended to be used to login to the hosts created
193 by mininet.
194
195 Arguments:
196 name - The string of the name of this component. The new component
197 will be assigned to main.<name> .
198 In addition, main.<name>.name = str( name )
199 """
200 try:
201 # look to see if this component already exists
202 getattr( main, name )
203 except AttributeError:
204 # namespace is clear, creating component
205 main.componentDictionary[name] = main.componentDictionary[self.name].copy()
206 main.componentDictionary[name]['connect_order'] = str( int( main.componentDictionary[name]['connect_order'] ) + 1 )
207 main.componentInit( name )
208 except Exception:
209 main.log.exception( self.name + ": Uncaught exception!" )
210 main.cleanup()
211 main.exit()
212 else:
213 # namespace is not clear!
214 main.log.error( name + " component already exists!" )
215 main.cleanup()
216 main.exit()
217
218 def removeHostComponent( self, name ):
219 """
220 Remove host component
221 Arguments:
222 name - The string of the name of the component to delete.
223 """
224 try:
225 # Get host component
226 component = getattr( main, name )
227 except AttributeError:
228 main.log.error( "Component " + name + " does not exist." )
229 return main.FALSE
230 try:
231 # Disconnect from component
232 component.disconnect()
233 # Delete component
234 delattr( main, name )
235 # Delete component from ComponentDictionary
236 del( main.componentDictionary[name] )
237 return main.TRUE
238 except Exception:
239 main.log.exception( self.name + ": Uncaught exception!" )
240 main.cleanup()
241 main.exit()
242
243 def startHostCli( self, host=None ):
244 """
245 Use the mininet m utility to connect to the host's cli
246 """
247 # These are fields that can be used by scapy packets. Initialized to None
248 self.hostIp = None
249 self.hostMac = None
250 try:
251 if not host:
252 host = self.name
253 self.handle.sendline( self.home + "/util/m " + host )
254 self.handle.expect( self.hostPrompt )
255 return main.TRUE
256 except pexpect.TIMEOUT:
257 main.log.exception( self.name + ": Command timed out" )
258 return main.FALSE
259 except pexpect.EOF:
260 main.log.exception( self.name + ": connection closed." )
261 main.cleanup()
262 main.exit()
263 except Exception:
264 main.log.exception( self.name + ": Uncaught exception!" )
265 main.cleanup()
266 main.exit()
267
268 def startScapy( self, mplsPath="" ):
269 """
270 Start the Scapy cli
271 optional:
272 mplsPath - The path where the MPLS class is located
273 NOTE: This can be a relative path from the user's home dir
274 """
275 mplsLines = ['import imp',
276 'imp.load_source( "mplsClass", "{}mplsClass.py" )'.format(mplsPath),
277 'from mplsClass import MPLS',
278 'bind_layers(Ether, MPLS, type = 0x8847)',
279 'bind_layers(MPLS, MPLS, bottom_of_label_stack = 0)',
280 'bind_layers(MPLS, IP)']
281
282 try:
283 self.handle.sendline( "scapy" )
284 self.handle.expect( self.scapyPrompt )
285 self.handle.sendline( "conf.color_theme = NoTheme()" )
286 self.handle.expect( self.scapyPrompt )
287 if mplsPath:
288 main.log.info( "Adding MPLS class" )
289 main.log.info( "MPLS class path: " + mplsPath )
290 for line in mplsLines:
291 main.log.info( "sending line: " + line )
292 self.handle.sendline( line )
293 self.handle.expect( self.scapyPrompt )
294 return main.TRUE
295 except pexpect.TIMEOUT:
296 main.log.exception( self.name + ": Command timed out" )
297 return main.FALSE
298 except pexpect.EOF:
299 main.log.exception( self.name + ": connection closed." )
300 main.cleanup()
301 main.exit()
302 except Exception:
303 main.log.exception( self.name + ": Uncaught exception!" )
304 main.cleanup()
305 main.exit()
306
307 def stopScapy( self ):
308 """
309 Exit the Scapy cli
310 """
311 try:
312 self.handle.sendline( "exit()" )
313 self.handle.expect( self.hostPrompt )
314 return main.TRUE
315 except pexpect.TIMEOUT:
316 main.log.exception( self.name + ": Command timed out" )
317 return main.FALSE
318 except pexpect.EOF:
319 main.log.exception( self.name + ": connection closed." )
320 main.cleanup()
321 main.exit()
322 except Exception:
323 main.log.exception( self.name + ": Uncaught exception!" )
324 main.cleanup()
325 main.exit()
326
327 def buildEther( self, **kwargs ):
328 """
329 Build an Ethernet frame
330
331 Will create a frame class with the given options. If a field is
332 left blank it will default to the below value unless it is
333 overwritten by the next frame.
334 Default frame:
335 ###[ Ethernet ]###
336 dst= ff:ff:ff:ff:ff:ff
337 src= 00:00:00:00:00:00
338 type= 0x800
339
340 Returns main.TRUE or main.FALSE on error
341 """
342 try:
343 # Set the Ethernet frame
344 cmd = 'ether = Ether( '
345 options = []
346 for key, value in kwargs.iteritems():
347 if isinstance( value, str ):
348 value = '"' + value + '"'
349 options.append( str( key ) + "=" + str( value ) )
350 cmd += ", ".join( options )
351 cmd += ' )'
352 self.handle.sendline( cmd )
353 self.handle.expect( self.scapyPrompt )
354 if "Traceback" in self.handle.before:
355 # KeyError, SyntaxError, ...
356 main.log.error( "Error in sending command: " + self.handle.before )
357 return main.FALSE
358 self.handle.sendline( "packet = ether" )
359 self.handle.expect( self.scapyPrompt )
360 if "Traceback" in self.handle.before:
361 # KeyError, SyntaxError, ...
362 main.log.error( "Error in sending command: " + self.handle.before )
363 return main.FALSE
364 return main.TRUE
365 except pexpect.TIMEOUT:
366 main.log.exception( self.name + ": Command timed out" )
367 return main.FALSE
368 except pexpect.EOF:
369 main.log.exception( self.name + ": connection closed." )
370 main.cleanup()
371 main.exit()
372 except Exception:
373 main.log.exception( self.name + ": Uncaught exception!" )
374 main.cleanup()
375 main.exit()
376
377 def buildIP( self, **kwargs ):
378 """
379 Build an IP frame
380
381 Will create a frame class with the given options. If a field is
382 left blank it will default to the below value unless it is
383 overwritten by the next frame.
384 Default frame:
385 ###[ IP ]###
386 version= 4
387 ihl= None
388 tos= 0x0
389 len= None
390 id= 1
391 flags=
392 frag= 0
393 ttl= 64
394 proto= hopopt
395 chksum= None
396 src= 127.0.0.1
397 dst= 127.0.0.1
398 \options\
399
400 Returns main.TRUE or main.FALSE on error
401 """
402 try:
403 # Set the IP frame
404 cmd = 'ip = IP( '
405 options = []
406 for key, value in kwargs.iteritems():
407 if isinstance( value, str ):
408 value = '"' + value + '"'
409 options.append( str( key ) + "=" + str( value ) )
410 cmd += ", ".join( options )
411 cmd += ' )'
412 self.handle.sendline( cmd )
413 self.handle.expect( self.scapyPrompt )
414 if "Traceback" in self.handle.before:
415 # KeyError, SyntaxError, ...
416 main.log.error( "Error in sending command: " + self.handle.before )
417 return main.FALSE
418 self.handle.sendline( "packet = ether/ip" )
419 self.handle.expect( self.scapyPrompt )
420 if "Traceback" in self.handle.before:
421 # KeyError, SyntaxError, ...
422 main.log.error( "Error in sending command: " + self.handle.before )
423 return main.FALSE
424 return main.TRUE
425 except pexpect.TIMEOUT:
426 main.log.exception( self.name + ": Command timed out" )
427 return main.FALSE
428 except pexpect.EOF:
429 main.log.exception( self.name + ": connection closed." )
430 main.cleanup()
431 main.exit()
432 except Exception:
433 main.log.exception( self.name + ": Uncaught exception!" )
434 main.cleanup()
435 main.exit()
436
437 def buildIPv6( self, **kwargs ):
438 """
439 Build an IPv6 frame
440
441 Will create a frame class with the given options. If a field is
442 left blank it will default to the below value unless it is
443 overwritten by the next frame.
444 Default frame:
445 ###[ IPv6 ]###
446 version= 6
447 tc= 0
448 fl= 0
449 plen= None
450 nh= No Next Header
451 hlim= 64
452 src= ::1
453 dst= ::1
454
455 Returns main.TRUE or main.FALSE on error
456 """
457 try:
458 # Set the IPv6 frame
459 cmd = 'ipv6 = IPv6( '
460 options = []
461 for key, value in kwargs.iteritems():
462 if isinstance( value, str ):
463 value = '"' + value + '"'
464 options.append( str( key ) + "=" + str( value ) )
465 cmd += ", ".join( options )
466 cmd += ' )'
467 self.handle.sendline( cmd )
468 self.handle.expect( self.scapyPrompt )
469 if "Traceback" in self.handle.before:
470 # KeyError, SyntaxError, ...
471 main.log.error( "Error in sending command: " + self.handle.before )
472 return main.FALSE
473 self.handle.sendline( "packet = ether/ipv6" )
474 self.handle.expect( self.scapyPrompt )
475 if "Traceback" in self.handle.before:
476 # KeyError, SyntaxError, ...
477 main.log.error( "Error in sending command: " + self.handle.before )
478 return main.FALSE
479 return main.TRUE
480 except pexpect.TIMEOUT:
481 main.log.exception( self.name + ": Command timed out" )
482 return main.FALSE
483 except pexpect.EOF:
484 main.log.exception( self.name + ": connection closed." )
485 main.cleanup()
486 main.exit()
487 except Exception:
488 main.log.exception( self.name + ": Uncaught exception!" )
489 main.cleanup()
490 main.exit()
491
492 def buildTCP( self, ipVersion=4, **kwargs ):
493 """
494 Build an TCP frame
495
496 Will create a frame class with the given options. If a field is
497 left blank it will default to the below value unless it is
498 overwritten by the next frame.
499
500 NOTE: Some arguments require quotes around them. It's up to you to
501 know which ones and to add them yourself. Arguments with an asterisk
502 do not need quotes.
503
504 Options:
505 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
506 frame to use to encapsulate into
507 Default frame:
508 ###[ TCP ]###
509 sport= ftp_data *
510 dport= http *
511 seq= 0
512 ack= 0
513 dataofs= None
514 reserved= 0
515 flags= S
516 window= 8192
517 chksum= None
518 urgptr= 0
519 options= {}
520
521 Returns main.TRUE or main.FALSE on error
522 """
523 try:
524 # Set the TCP frame
525 cmd = 'tcp = TCP( '
526 options = []
527 for key, value in kwargs.iteritems():
528 options.append( str( key ) + "=" + str( value ) )
529 cmd += ", ".join( options )
530 cmd += ' )'
531 self.handle.sendline( cmd )
532 self.handle.expect( self.scapyPrompt )
533 if "Traceback" in self.handle.before:
534 # KeyError, SyntaxError, ...
535 main.log.error( "Error in sending command: " + self.handle.before )
536 return main.FALSE
537 if str( ipVersion ) is '4':
538 self.handle.sendline( "packet = ether/ip/tcp" )
539 elif str( ipVersion ) is '6':
540 self.handle.sendline( "packet = ether/ipv6/tcp" )
541 else:
542 main.log.error( "Unrecognized option for ipVersion, given " +
543 repr( ipVersion ) )
544 return main.FALSE
545 self.handle.expect( self.scapyPrompt )
546 if "Traceback" in self.handle.before:
547 # KeyError, SyntaxError, ...
548 main.log.error( "Error in sending command: " + self.handle.before )
549 return main.FALSE
550 return main.TRUE
551 except pexpect.TIMEOUT:
552 main.log.exception( self.name + ": Command timed out" )
553 return main.FALSE
554 except pexpect.EOF:
555 main.log.exception( self.name + ": connection closed." )
556 main.cleanup()
557 main.exit()
558 except Exception:
559 main.log.exception( self.name + ": Uncaught exception!" )
560 main.cleanup()
561 main.exit()
562
563 def buildUDP( self, ipVersion=4, **kwargs ):
564 """
565 Build an UDP frame
566
567 Will create a frame class with the given options. If a field is
568 left blank it will default to the below value unless it is
569 overwritten by the next frame.
570
571 NOTE: Some arguments require quotes around them. It's up to you to
572 know which ones and to add them yourself. Arguments with an asterisk
573 do not need quotes.
574
575 Options:
576 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
577 frame to use to encapsulate into
578 Default frame:
579 ###[ UDP ]###
580 sport= domain *
581 dport= domain *
582 len= None
583 chksum= None
584
585 Returns main.TRUE or main.FALSE on error
586 """
587 try:
588 # Set the UDP frame
589 cmd = 'udp = UDP( '
590 options = []
591 for key, value in kwargs.iteritems():
592 options.append( str( key ) + "=" + str( value ) )
593 cmd += ", ".join( options )
594 cmd += ' )'
595 self.handle.sendline( cmd )
596 self.handle.expect( self.scapyPrompt )
597 if "Traceback" in self.handle.before:
598 # KeyError, SyntaxError, ...
599 main.log.error( "Error in sending command: " + self.handle.before )
600 return main.FALSE
601 if str( ipVersion ) is '4':
602 self.handle.sendline( "packet = ether/ip/udp" )
603 elif str( ipVersion ) is '6':
604 self.handle.sendline( "packet = ether/ipv6/udp" )
605 else:
606 main.log.error( "Unrecognized option for ipVersion, given " +
607 repr( ipVersion ) )
608 return main.FALSE
609 self.handle.expect( self.scapyPrompt )
610 if "Traceback" in self.handle.before:
611 # KeyError, SyntaxError, ...
612 main.log.error( "Error in sending command: " + self.handle.before )
613 return main.FALSE
614 return main.TRUE
615 except pexpect.TIMEOUT:
616 main.log.exception( self.name + ": Command timed out" )
617 return main.FALSE
618 except pexpect.EOF:
619 main.log.exception( self.name + ": connection closed." )
620 main.cleanup()
621 main.exit()
622 except Exception:
623 main.log.exception( self.name + ": Uncaught exception!" )
624 main.cleanup()
625 main.exit()
626
alisone14d7b02016-07-06 10:31:51 -0700627 def buildSCTP( self, ipVersion=4, **kwargs ):
628 """
629 Build an SCTP frame
630
631 Will create a frame class with the given options. If a field is
632 left blank it will default to the below value unless it is
633 overwritten by the next frame.
634
635 NOTE: Some arguments require quotes around them. It's up to you to
636 know which ones and to add them yourself. Arguments with an asterisk
637 do not need quotes.
638
639 Options:
640 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
641 frame to use to encapsulate into
642 Default frame:
643 ###[ SCTP ]###
644 sport= domain *
645 dport= domain *
646 tag = None
647 chksum = None
648
649 Returns main.TRUE or main.FALSE on error
650 """
651 try:
652 # Set the SCTP frame
653 cmd = 'sctp = SCTP( '
654 options = [ ]
655 for key, value in kwargs.iteritems( ):
656 options.append( str( key ) + "=" + str( value ) )
657 cmd += ", ".join( options )
658 cmd += ' )'
659 self.handle.sendline( cmd )
660 self.handle.expect( self.scapyPrompt )
661 if "Traceback" in self.handle.before:
662 # KeyError, SyntaxError, ...
663 main.log.error( "Error in sending command: " + self.handle.before )
664 return main.FALSE
665 if str( ipVersion ) is '4':
666 self.handle.sendline( "packet = ether/ip/sctp" )
667 elif str( ipVersion ) is '6':
668 self.handle.sendline( "packet = ether/ipv6/sctp" )
669 else:
670 main.log.error( "Unrecognized option for ipVersion, given " +
671 repr( ipVersion ) )
672 return main.FALSE
673 self.handle.expect( self.scapyPrompt )
674 if "Traceback" in self.handle.before:
675 # KeyError, SyntaxError, ...
676 main.log.error( "Error in sending command: " + self.handle.before )
677 return main.FALSE
678 return main.TRUE
679 except pexpect.TIMEOUT:
680 main.log.exception( self.name + ": Command timed out" )
681 return main.FALSE
682 except pexpect.EOF:
683 main.log.exception( self.name + ": connection closed." )
684 main.cleanup( )
685 main.exit( )
686 except Exception:
687 main.log.exception( self.name + ": Uncaught exception!" )
688 main.cleanup( )
689 main.exit( )
690
691 def buildARP( self, **kwargs ):
692 """
693 Build an ARP frame
694
695 Will create a frame class with the given options. If a field is
696 left blank it will default to the below value unless it is
697 overwritten by the next frame.
698
699 NOTE: Some arguments require quotes around them. It's up to you to
700 know which ones and to add them yourself. Arguments with an asterisk
701 do not need quotes.
702
703 Default frame:
704 ###[ ARP ]###
705 hwtype : XShortField = (1)
706 ptype : XShortEnumField = (2048)
707 hwlen : ByteField = (6)
708 plen : ByteField = (4)
709 op : ShortEnumField = (1)
710 hwsrc : ARPSourceMACField = (None)
711 psrc : SourceIPField = (None)
712 hwdst : MACField = ('00:00:00:00:00:00')
713 pdst : IPField = ('0.0.0.0')
714
715 Returns main.TRUE or main.FALSE on error
716 """
717 try:
718 # Set the ARP frame
719 cmd = 'arp = ARP( '
720 options = []
721 for key, value in kwargs.iteritems( ):
722 if isinstance( value, str ):
723 value = '"' + value + '"'
724 options.append( str( key ) + "=" + str( value ) )
725 cmd += ", ".join( options )
726 cmd += ' )'
727 self.handle.sendline( cmd )
728 self.handle.expect( self.scapyPrompt )
729 if "Traceback" in self.handle.before:
730 # KeyError, SyntaxError, ...
731 main.log.error( "Error in sending command: " + self.handle.before )
732 return main.FALSE
733 self.handle.sendline( "packet = ether/arp" )
734 self.handle.expect( self.scapyPrompt )
735 if "Traceback" in self.handle.before:
736 # KeyError, SyntaxError, ...
737 main.log.error( "Error in sending command: " + self.handle.before )
738 return main.FALSE
739 return main.TRUE
740 except pexpect.TIMEOUT:
741 main.log.exception( self.name + ": Command timed out" )
742 return main.FALSE
743 except pexpect.EOF:
744 main.log.exception( self.name + ": connection closed." )
745 main.cleanup( )
746 main.exit( )
747 except Exception:
748 main.log.exception( self.name + ": Uncaught exception!" )
749 main.cleanup( )
750 main.exit( )
751
752 def buildICMP( self, ipVersion=4, **kwargs ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800753 """
754 Build an ICMP frame
755
756 Will create a frame class with the given options. If a field is
757 left blank it will default to the below value unless it is
758 overwritten by the next frame.
759 Default frame:
760 ###[ ICMP ]###
761 type= echo-request
762 code= 0
763 chksum= None
764 id= 0x0
765 seq= 0x0
766
alisone14d7b02016-07-06 10:31:51 -0700767 Options:
768 ipVersion - Either 4 (default) or 6, indicates what Internet Protocol
769 frame to use to encapsulate into
770
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800771 Returns main.TRUE or main.FALSE on error
772 """
773 try:
774 # Set the ICMP frame
alisone14d7b02016-07-06 10:31:51 -0700775 if str( ipVersion ) is '4':
776 cmd = 'icmp = ICMP( '
777 elif str( ipVersion ) is '6':
778 cmd = 'icmp6 = ICMPv6EchoReply( '
779 else:
780 main.log.error( "Unrecognized option for ipVersion, given " +
781 repr( ipVersion ) )
782 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800783 options = []
alisone14d7b02016-07-06 10:31:51 -0700784 for key, value in kwargs.iteritems( ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800785 if isinstance( value, str ):
786 value = '"' + value + '"'
787 options.append( str( key ) + "=" + str( value ) )
788 cmd += ", ".join( options )
789 cmd += ' )'
790 self.handle.sendline( cmd )
791 self.handle.expect( self.scapyPrompt )
792 if "Traceback" in self.handle.before:
793 # KeyError, SyntaxError, ...
794 main.log.error( "Error in sending command: " + self.handle.before )
795 return main.FALSE
alisone14d7b02016-07-06 10:31:51 -0700796
797 if str( ipVersion ) is '4':
798 self.handle.sendline( "packet = ether/ip/icmp" )
799 elif str( ipVersion ) is '6':
800 self.handle.sendline( "packet = ether/ipv6/icmp6" )
801 else:
802 main.log.error( "Unrecognized option for ipVersion, given " +
803 repr( ipVersion ) )
804 return main.FALSE
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800805 self.handle.expect( self.scapyPrompt )
806 if "Traceback" in self.handle.before:
807 # KeyError, SyntaxError, ...
808 main.log.error( "Error in sending command: " + self.handle.before )
809 return main.FALSE
810 return main.TRUE
811 except pexpect.TIMEOUT:
812 main.log.exception( self.name + ": Command timed out" )
813 return main.FALSE
814 except pexpect.EOF:
815 main.log.exception( self.name + ": connection closed." )
alisone14d7b02016-07-06 10:31:51 -0700816 main.cleanup( )
817 main.exit( )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800818 except Exception:
819 main.log.exception( self.name + ": Uncaught exception!" )
alisone14d7b02016-07-06 10:31:51 -0700820 main.cleanup( )
821 main.exit( )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800822
823 def sendPacket( self, iface=None, packet=None, timeout=1 ):
824 """
825 Send a packet with either the given scapy packet command, or use the
826 packet saved in the variable 'packet'.
827
828 Examples of a valid string for packet:
829
830 Simple IP packet
831 packet='Ether(dst="a6:d9:26:df:1d:4b")/IP(dst="10.0.0.2")'
832
833 A Ping with two vlan tags
834 packet='Ether(dst='ff:ff:ff:ff:ff:ff')/Dot1Q(vlan=1)/Dot1Q(vlan=10)/
835 IP(dst='255.255.255.255', src='192.168.0.1')/ICMP()'
836
837 Returns main.TRUE or main.FALSE on error
838 """
839 try:
840 # TODO: add all params, or use kwargs
841 sendCmd = 'srp( '
842 if packet:
843 sendCmd += packet
844 else:
845 sendCmd += "packet"
846 if iface:
847 sendCmd += ", iface='{}'".format( iface )
848
849 sendCmd += ', timeout=' + str( timeout ) + ')'
850 self.handle.sendline( sendCmd )
851 self.handle.expect( self.scapyPrompt )
Jon Halla510a8a2016-05-04 15:09:28 -0700852 # main.log.warn( "Send packet response: {}".format( self.handle.before ) )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800853 if "Traceback" in self.handle.before:
854 # KeyError, SyntaxError, ...
855 main.log.error( "Error in sending command: " + self.handle.before )
856 return main.FALSE
857 # TODO: Check # of packets sent?
858 return main.TRUE
859 except pexpect.TIMEOUT:
860 main.log.exception( self.name + ": Command timed out" )
861 return main.FALSE
862 except pexpect.EOF:
863 main.log.exception( self.name + ": connection closed." )
864 main.cleanup()
865 main.exit()
866 except Exception:
867 main.log.exception( self.name + ": Uncaught exception!" )
868 main.cleanup()
869 main.exit()
870
871 def startFilter( self, ifaceName=None, sniffCount=1, pktFilter="ip" ):
872 """
873 Listen for packets using the given filters
874
875 Options:
876 ifaceName - the name of the interface to listen on. If none is given,
877 defaults to <host name>-eth0
878 pktFilter - A string in Berkeley Packet Filter (BPF) format which
879 specifies which packets to sniff
880 sniffCount - The number of matching packets to capture before returning
881
882 Returns main.TRUE or main.FALSE on error
883 """
884 try:
885 # TODO: add all params, or use kwargs
886 ifaceName = str( ifaceName ) if ifaceName else self.name + "-eth0"
887 # Set interface
888 self.handle.sendline( ' conf.iface = "' + ifaceName + '"' )
889 self.handle.expect( self.scapyPrompt )
890 cmd = 'pkt = sniff(count = ' + str( sniffCount ) +\
891 ', filter = "' + str( pktFilter ) + '")'
Jon Halla510a8a2016-05-04 15:09:28 -0700892 main.log.info( "Filter on " + self.name + ' > ' + cmd )
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800893 self.handle.sendline( cmd )
894 self.handle.expect( '"\)\r\n' )
895 # TODO: parse this?
896 return main.TRUE
897 except pexpect.TIMEOUT:
898 main.log.exception( self.name + ": Command timed out" )
899 return main.FALSE
900 except pexpect.EOF:
901 main.log.exception( self.name + ": connection closed." )
902 main.cleanup()
903 main.exit()
904 except Exception:
905 main.log.exception( self.name + ": Uncaught exception!" )
906 main.cleanup()
907 main.exit()
908
909 def checkFilter( self, timeout=10 ):
910 """
911 Check that a filter returned and returns the reponse
912 """
913 try:
914 i = self.handle.expect( [ self.scapyPrompt, pexpect.TIMEOUT ], timeout=timeout )
915 if i == 0:
916 return main.TRUE
917 else:
918 return main.FALSE
919 except pexpect.EOF:
920 main.log.exception( self.name + ": connection closed." )
921 main.cleanup()
922 main.exit()
923 except Exception:
924 main.log.exception( self.name + ": Uncaught exception!" )
925 main.cleanup()
926 main.exit()
927
928 def killFilter( self ):
929 """
930 Kill a scapy filter
931 """
932 try:
933 self.handle.send( "\x03" ) # Send a ctrl-c to kill the filter
934 self.handle.expect( self.scapyPrompt )
935 return self.handle.before
936 except pexpect.TIMEOUT:
937 main.log.exception( self.name + ": Command timed out" )
938 return None
939 except pexpect.EOF:
940 main.log.exception( self.name + ": connection closed." )
941 main.cleanup()
942 main.exit()
943 except Exception:
944 main.log.exception( self.name + ": Uncaught exception!" )
945 main.cleanup()
946 main.exit()
947
948 def readPackets( self ):
949 """
950 Read all the packets captured by the previous filter
951 """
952 try:
953 self.handle.sendline( "for p in pkt: p \n")
954 self.handle.expect( "for p in pkt: p \r\n... \r\n" )
955 self.handle.expect( self.scapyPrompt )
956 except pexpect.TIMEOUT:
957 main.log.exception( self.name + ": Command timed out" )
958 return None
959 except pexpect.EOF:
960 main.log.exception( self.name + ": connection closed." )
961 main.cleanup()
962 main.exit()
963 except Exception:
964 main.log.exception( self.name + ": Uncaught exception!" )
965 main.cleanup()
966 main.exit()
967 return self.handle.before
968
alisone14d7b02016-07-06 10:31:51 -0700969 def updateSelf( self, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800970 """
971 Updates local MAC and IP fields
972 """
973 self.hostMac = self.getMac()
alisone14d7b02016-07-06 10:31:51 -0700974 if IPv6:
975 self.hostIp = self.getIp( IPv6=True )
976 else:
977 self.hostIp = self.getIp()
Jeremy Songster1f39bf02016-01-20 17:17:25 -0800978
979 def getMac( self, ifaceName=None ):
980 """
981 Save host's MAC address
982 """
983 try:
984 ifaceName = str( ifaceName ) if ifaceName else self.name + "-eth0"
985 cmd = 'get_if_hwaddr("' + str( ifaceName ) + '")'
986 self.handle.sendline( cmd )
987 self.handle.expect( self.scapyPrompt )
988 pattern = r'(([0-9a-f]{2}[:-]){5}([0-9a-f]{2}))'
989 match = re.search( pattern, self.handle.before )
990 if match:
991 return match.group()
992 else:
993 # the command will have an exception if iface doesn't exist
994 return None
995 except pexpect.TIMEOUT:
996 main.log.exception( self.name + ": Command timed out" )
997 return None
998 except pexpect.EOF:
999 main.log.exception( self.name + ": connection closed." )
1000 main.cleanup()
1001 main.exit()
1002 except Exception:
1003 main.log.exception( self.name + ": Uncaught exception!" )
1004 main.cleanup()
1005 main.exit()
1006
alisone14d7b02016-07-06 10:31:51 -07001007 def getIp( self, ifaceName=None, IPv6=False ):
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001008 """
1009 Save host's IP address
1010
1011 Returns the IP of the first interface that is not a loopback device.
1012 If no IP could be found then it will return 0.0.0.0.
alisone14d7b02016-07-06 10:31:51 -07001013
1014 If IPv6 is equal to True, returns IPv6 of the first interface that is not a loopback device.
1015 If no IPv6 could be found then it will return :: .
1016
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001017 """
1018 def getIPofInterface( ifaceName ):
1019 cmd = 'get_if_addr("' + str( ifaceName ) + '")'
alisone14d7b02016-07-06 10:31:51 -07001020 if IPv6:
1021 cmd = 'get_if_raw_addr6("' + str( ifaceName ) + '")'
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001022 self.handle.sendline( cmd )
1023 self.handle.expect( self.scapyPrompt )
1024
1025 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 -07001026 if IPv6:
1027 pattern = r'(\\x([0-9]|[a-f]|[A-F])([0-9]|[a-f]|[A-F])){16}'
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001028 match = re.search( pattern, self.handle.before )
1029 if match:
1030 # NOTE: The command will return 0.0.0.0 if the iface doesn't exist
alisone14d7b02016-07-06 10:31:51 -07001031 if IPv6 != True:
1032 if match.group() == '0.0.0.0':
1033 main.log.warn( 'iface {0} has no IPv4 address'.format( ifaceName ) )
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001034 return match.group()
1035 else:
1036 return None
1037 try:
1038 if not ifaceName:
1039 # Get list of interfaces
1040 ifList = self.getIfList()
alisone14d7b02016-07-06 10:31:51 -07001041 if IPv6:
1042 for ifaceName in ifList:
1043 if ifaceName == "lo":
1044 continue
1045 ip = getIPofInterface( ifaceName )
1046 if ip != None:
1047 newip =ip
1048 tmp = newip.split( "\\x" )
1049 ip = ""
1050 counter = 0
1051 for i in tmp:
1052 if i != "":
1053 counter = counter + 1;
1054 if counter % 2 == 0 and counter < 16:
1055 ip = ip + i + ":"
1056 else:
1057 ip = ip + i
1058 return ip
1059 return "::"
1060 else:
1061 for ifaceName in ifList:
1062 if ifaceName == "lo":
1063 continue
1064 ip = getIPofInterface( ifaceName )
1065 if ip != "0.0.0.0":
1066 return ip
1067 return "0.0.0.0"
Jeremy Songster1f39bf02016-01-20 17:17:25 -08001068 else:
1069 return getIPofInterface( ifaceName )
1070
1071 except pexpect.TIMEOUT:
1072 main.log.exception( self.name + ": Command timed out" )
1073 return None
1074 except pexpect.EOF:
1075 main.log.exception( self.name + ": connection closed." )
1076 main.cleanup()
1077 main.exit()
1078 except Exception:
1079 main.log.exception( self.name + ": Uncaught exception!" )
1080 main.cleanup()
1081 main.exit()
1082
1083 def getIfList( self ):
1084 """
1085 Return List of Interfaces
1086 """
1087 try:
1088 self.handle.sendline( 'get_if_list()' )
1089 self.handle.expect( self.scapyPrompt )
1090 ifList = self.handle.before.split( '\r\n' )
1091 ifList = ifList[ 1 ].replace( "'","" )[ 1:-1 ].split( ', ' )
1092 return ifList
1093
1094 except pexpect.TIMEOUT:
1095 main.log.exception( self.name + ": Command timed out" )
1096 return None
1097 except pexpect.EOF:
1098 main.log.exception( self.name + ": connection closed." )
1099 main.cleanup()
1100 main.exit()
1101 except Exception:
1102 main.log.exception( self.name + ": Uncaught exception!" )
1103 main.cleanup()
1104 main.exit()
1105
1106if __name__ != "__main__":
1107 sys.modules[ __name__ ] = ScapyCliDriver()