-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathofp_sniffer.py
executable file
·248 lines (202 loc) · 7.89 KB
/
ofp_sniffer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#!/usr/bin/env python3
"""
This code is the AmLight OpenFlow Sniffer
Author: AmLight Dev Team <[email protected]>
"""
import sys
import logging.config
import time
import threading
import yaml
import traceback
from libs.core.printing import PrintingOptions
from libs.core.sanitizer import Sanitizer
from libs.core.topo_reader import TopoReader
from libs.core.cli import get_params
from libs.core.save_to_file import save_to_file
from libs.core.custom_exceptions import *
from libs.gen.packet import Packet
from apps.oess_fvd import OessFvdTracer
from apps.ofp_stats import OFStats
from apps.ofp_proxies import OFProxy
from apps.influx_client import InfluxClient
from apps.notifications import Notifications
class RunSniffer(object):
"""
The RunSniffer class is the main class for the OpenFlow Sniffer.
This class instantiate all auxiliary classes, captures the packets,
instantiate new OpenFlow messages and triggers all applications.
"""
def __init__(self):
self.printing_options = PrintingOptions()
self.sanitizer = Sanitizer()
self.oft = None
self.stats = None
self.influx = None
self.notifications = None
self.trigger_event = threading.Event()
self.cap = None
self.packet_number = None
self.load_apps = dict()
self.packet_count = 1
self.topo_reader = TopoReader()
self.save_to_file = None
self.ofp_proxy = None
self.load_config()
def load_config(self):
"""
Parses the parameters received and instantiates the
apps requested.
"""
# Get CLI params and call the pcapy loop
self.cap, self.packet_number, \
self.load_apps, sanitizer, \
topo_file, is_to_save = get_params(sys.argv)
self.sanitizer.process_filters(sanitizer)
# Load TopologyReader
self.topo_reader.readfile(topo_file)
# Save to File
self.save_to_file = save_to_file(is_to_save)
# Start Apps
self.ofp_proxy = OFProxy()
if 'oess_fvd' in self.load_apps:
self.oft = OessFvdTracer(self.load_apps['oess_fvd'])
if 'statistics' in self.load_apps:
self.stats = OFStats()
if 'influx' in self.load_apps:
self.influx = InfluxClient(trigger_event=self.trigger_event)
if 'notifications' in self.load_apps:
self.notifications = Notifications(self.load_apps['notifications'])
def run(self):
"""
cap.loop continuously capture packets w/ pcapy. For every
captured packet, self.process_packet method is called.
Exits:
0 - Normal, reached end of file
1 - Normal, user requested with CRTL + C
2 - Error
3 - Interface or file not found
"""
exit_code = 0
# DEBUG:
# self.cap.loop(-1, self.process_packet)
try:
self.cap.loop(-1, self.process_packet)
except EndOfPcapFile:
exit_code = 3
except KeyboardInterrupt:
exit_code = 1
except Exception as exception:
err = traceback.format_exc().replace("\n", ", ")
print('Error on packet %s: %s error: %s' % (self.packet_count, exception, err))
exit_code = 2
finally:
if 'statistics' in self.load_apps:
# If OFP_Stats is running, set a timer
# before closing the app. Useful in cases
# where the ofp_sniffer is reading from a
# pcap file instead of a NIC.
time.sleep(200)
# pass
print('Exiting with code: %s' % exit_code)
# gracefully shut down
if 'influx' in self.load_apps:
self.influx.stop_event.set()
sys.exit(exit_code)
def process_packet(self, header, packet):
"""
Every packet captured by cap.loop is then processed here.
If packets are bigger than 62 Bytes, we process them.
If it is 0, means there are no more packets. If it is
something in between, it is a fragment, we ignore for now.
Args:
header: header of the captured packet
packet: packet captured from file or interface
"""
if len(packet) >= 54:
# Verify if user asked for just one specific packet
if self.was_packet_number_defined():
if not self.is_the_packet_number_specified():
self.packet_count += 1
return
# DEBUG:
# print("Packet Number: %s" % self.packet_count)
pkt = Packet(packet, self.packet_count, header)
if pkt.reconnect_error:
if isinstance(self.stats, OFStats):
# OFStats counts reconnects
self.stats.process_packet(pkt)
if isinstance(self.notifications, Notifications):
# Send notifications via Slack
self.notifications.send_msg(pkt)
elif pkt.is_openflow_packet:
valid_result = pkt.process_openflow_messages()
if valid_result:
# Apps go here:
if isinstance(self.oft, OessFvdTracer):
# FVD_Tracer does not print the packets
self.oft.process_packet(pkt)
if isinstance(self.ofp_proxy, OFProxy):
# OFP_PROXY associates IP:PORT to DPID
self.ofp_proxy.process_packet(pkt)
if isinstance(self.stats, OFStats):
# OFStats print the packets
self.stats.process_packet(pkt)
if isinstance(self.notifications, Notifications):
# Send notifications via Slack
self.notifications.send_msg(pkt)
if not isinstance(self.oft, OessFvdTracer):
# Print Packets
pkt.print_packet()
if self.influx:
# tell influx to wake up and update immediately
self.trigger_event.set()
del pkt
if self.is_the_packet_number_specified():
# If a specific packet was selected, end here.
raise EndOfPcapFile
elif len(packet) == 0:
return 3
self.packet_count += 1
def was_packet_number_defined(self):
"""
In case user wants to see a specific packet inside a
specific pcap file, provide file name with the specific
packet number after ":"
-r file.pcap:packet_number
Returns:
True if a packet number was specified
False: if a packet number was not specified
"""
if self.packet_number != 0:
return True
return False
def is_the_packet_number_specified(self):
"""
If user wants to see a specific packet inside a
specific pcap file and the packet_count is that
number, return True. Otherwise, return false
Returns:
True if packet_count matches
False: if packet_count does not match
"""
return True if self.packet_count == self.packet_number else False
def main():
"""
Main function.
Instantiates RunSniffer and run it
"""
try:
logging.config.dictConfig(yaml.safe_load(open('logging.yml', 'r')))
logger = logging.getLogger(__name__)
sniffer = RunSniffer()
logger.info("OFP_Sniffer started.")
sniffer.run()
except ErrorFilterFile as msg:
print(msg)
sys.exit(4)
except FileNotFoundError as msg:
print(msg)
sys.exit(5)
if __name__ == "__main__":
main()