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