]> git.proxmox.com Git - mirror_ovs.git/blob - utilities/ovs-tcpdump.in
ovs-tcpdump: Remove the newline from readline().
[mirror_ovs.git] / utilities / ovs-tcpdump.in
1 #! /usr/bin/env @PYTHON@
2 #
3 # Copyright (c) 2016 Red Hat, Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at:
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 import fcntl
18 import netifaces
19 import os
20 import pwd
21 import select
22 import struct
23 import subprocess
24 import sys
25 import time
26
27 try:
28 from ovs.db import idl
29 from ovs import jsonrpc
30 from ovs.poller import Poller
31 from ovs.stream import Stream
32 except Exception:
33 print("ERROR: Please install the correct Open vSwitch python support")
34 print(" libraries (version @VERSION@).")
35 print(" Alternatively, check that your PYTHONPATH is pointing to")
36 print(" the correct location.")
37 sys.exit(1)
38
39 tapdev_fd = None
40 _make_taps = {}
41
42
43 def _doexec(*args, **kwargs):
44 """Executes an application and returns a set of pipes"""
45
46 shell = len(args) == 1
47 proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell,
48 bufsize=0)
49 return proc
50
51
52 def _install_tap_linux(tap_name):
53 """Uses /dev/net/tun to create a tap device"""
54 global tapdev_fd
55
56 IFF_TAP = 0x0002
57 IFF_NO_PI = 0x1000
58 TUNSETIFF = 0x400454CA # This is derived by printf() of TUNSETIFF
59 TUNSETOWNER = TUNSETIFF + 2
60
61 tapdev_fd = open('/dev/net/tun', 'rw')
62 ifr = struct.pack('16sH', tap_name, IFF_TAP | IFF_NO_PI)
63 fcntl.ioctl(tapdev_fd, TUNSETIFF, ifr)
64 fcntl.ioctl(tapdev_fd, TUNSETOWNER, os.getegid())
65
66 time.sleep(1) # required to give the new device settling time
67 pipe = _doexec(
68 *(['ip', 'link', 'set', 'dev', str(tap_name), 'up']))
69 pipe.wait()
70
71 _make_taps['linux'] = _install_tap_linux
72 _make_taps['linux2'] = _install_tap_linux
73
74
75 def username():
76 return pwd.getpwuid(os.getuid())[0]
77
78
79 def usage():
80 print("""\
81 %(prog)s: Open vSwitch tcpdump helper.
82 usage: %(prog)s -i interface [TCPDUMP OPTIONS]
83 where TCPDUMP OPTIONS represents the options normally passed to tcpdump.
84
85 The following options are available:
86 -h, --help display this help message
87 -V, --version display version information
88 --db-sock A connection string to reach the Open vSwitch
89 ovsdb-server.
90 Default 'unix:@RUNDIR@/db.sock'
91 --dump-cmd Command to use for tcpdump (default 'tcpdump')
92 -i, --interface Open vSwitch interface to mirror and tcpdump
93 --mirror-to The name for the mirror port to use (optional)
94 Default 'miINTERFACE'
95 """ % {'prog': sys.argv[0]})
96 sys.exit(0)
97
98
99 class OVSDBException(Exception):
100 pass
101
102
103 class OVSDB(object):
104 @staticmethod
105 def wait_for_db_change(idl):
106 seq = idl.change_seqno
107 stop = time.time() + 10
108 while idl.change_seqno == seq and not idl.run():
109 poller = Poller()
110 idl.wait(poller)
111 poller.block()
112 if time.time() >= stop:
113 raise Exception('Retry Timeout')
114
115 def __init__(self, db_sock):
116 self._db_sock = db_sock
117 self._txn = None
118 schema = self._get_schema()
119 schema.register_all()
120 self._idl_conn = idl.Idl(db_sock, schema)
121 OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB
122
123 def _get_schema(self):
124 error, strm = Stream.open_block(Stream.open(self._db_sock))
125 if error:
126 raise Exception("Unable to connect to %s" % self._db_sock)
127 rpc = jsonrpc.Connection(strm)
128 req = jsonrpc.Message.create_request('get_schema', ['Open_vSwitch'])
129 error, resp = rpc.transact_block(req)
130 rpc.close()
131
132 if error or resp.error:
133 raise Exception('Unable to retrieve schema.')
134 return idl.SchemaHelper(None, resp.result)
135
136 def get_table(self, table_name):
137 return self._idl_conn.tables[table_name]
138
139 def _start_txn(self):
140 if self._txn is not None:
141 raise OVSDBException("ERROR: A transaction was started already")
142 self._idl_conn.change_seqno += 1
143 self._txn = idl.Transaction(self._idl_conn)
144 return self._txn
145
146 def _complete_txn(self, try_again_fn):
147 if self._txn is None:
148 raise OVSDBException("ERROR: Not in a transaction")
149 status = self._txn.commit_block()
150 if status is idl.Transaction.TRY_AGAIN:
151 if self._idl_conn._session.rpc.status != 0:
152 self._idl_conn.force_reconnect()
153 OVSDB.wait_for_db_change(self._idl_conn)
154 return try_again_fn(self)
155 elif status is idl.Transaction.ERROR:
156 return False
157
158 def _find_row(self, table_name, find):
159 return next(
160 (row for row in self.get_table(table_name).rows.values()
161 if find(row)), None)
162
163 def _find_row_by_name(self, table_name, value):
164 return self._find_row(table_name, lambda row: row.name == value)
165
166 def port_exists(self, port_name):
167 return bool(self._find_row_by_name('Port', port_name))
168
169 def port_bridge(self, port_name):
170 try:
171 port = self._find_row_by_name('Port', port_name)
172 br = self._find_row('Bridge', lambda x: port in x.ports)
173 return br.name
174 except Exception:
175 raise OVSDBException('Unable to find port %s bridge' % port_name)
176
177 def interface_exists(self, intf_name):
178 return bool(self._find_row_by_name('Interface', intf_name))
179
180 def mirror_exists(self, mirror_name):
181 return bool(self._find_row_by_name('Mirror', mirror_name))
182
183 def interface_uuid(self, intf_name):
184 row = self._find_row_by_name('Interface', intf_name)
185 if bool(row):
186 return row.uuid
187 raise OVSDBException('No such interface: %s' % intf_name)
188
189 def make_interface(self, intf_name, execute_transaction=True):
190 if self.interface_exists(intf_name):
191 print("INFO: Interface exists.")
192 return self.interface_uuid(intf_name)
193
194 txn = self._start_txn()
195 tmp_row = txn.insert(self.get_table('Interface'))
196 tmp_row.name = intf_name
197
198 def try_again(db_entity):
199 db_entity.make_interface(intf_name)
200
201 if not execute_transaction:
202 return tmp_row
203
204 txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s"
205 % (username(), intf_name))
206 status = self._complete_txn(try_again)
207 if status is False:
208 raise OVSDBException('Unable to create Interface %s: %s' %
209 (intf_name, txn.get_error()))
210 result = txn.get_insert_uuid(tmp_row.uuid)
211 self._txn = None
212 return result
213
214 def destroy_port(self, port_name, bridge_name):
215 if not self.interface_exists(port_name):
216 return
217 txn = self._start_txn()
218 br = self._find_row_by_name('Bridge', bridge_name)
219 ports = [port for port in br.ports if port.name != port_name]
220 br.ports = ports
221
222 def try_again(db_entity):
223 db_entity.destroy_port(port_name)
224
225 txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s"
226 % (username(), port_name))
227 status = self._complete_txn(try_again)
228 if status is False:
229 raise OVSDBException('unable to delete Port %s: %s' %
230 (port_name, txn.get_error()))
231 self._txn = None
232
233 def destroy_mirror(self, mirror_name, bridge_name):
234 if not self.mirror_exists(mirror_name):
235 return
236 txn = self._start_txn()
237 mirror_row = self._find_row_by_name('Mirror', mirror_name)
238 br = self._find_row_by_name('Bridge', bridge_name)
239 mirrors = [mirror for mirror in br.mirrors
240 if mirror.uuid != mirror_row.uuid]
241 br.mirrors = mirrors
242
243 def try_again(db_entity):
244 db_entity.destroy_mirror(mirror_name, bridge_name)
245
246 txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s"
247 % (username(), mirror_name))
248 status = self._complete_txn(try_again)
249 if status is False:
250 raise OVSDBException('Unable to delete Mirror %s: %s' %
251 (mirror_name, txn.get_error()))
252 self._txn = None
253
254 def make_port(self, port_name, bridge_name):
255 iface_row = self.make_interface(port_name, False)
256 txn = self._txn
257
258 br = self._find_row_by_name('Bridge', bridge_name)
259 if not br:
260 raise OVSDBException('Bad bridge name %s' % bridge_name)
261
262 port = txn.insert(self.get_table('Port'))
263 port.name = port_name
264
265 br.verify('ports')
266 ports = getattr(br, 'ports', [])
267 ports.append(port)
268 br.ports = ports
269
270 port.verify('interfaces')
271 ifaces = getattr(port, 'interfaces', [])
272 ifaces.append(iface_row)
273 port.interfaces = ifaces
274
275 def try_again(db_entity):
276 db_entity.make_port(port_name, bridge_name)
277
278 txn.add_comment("ovs-tcpdump: user=%s,create_port=%s"
279 % (username(), port_name))
280 status = self._complete_txn(try_again)
281 if status is False:
282 raise OVSDBException('Unable to create Port %s: %s' %
283 (port_name, txn.get_error()))
284 result = txn.get_insert_uuid(port.uuid)
285 self._txn = None
286 return result
287
288 def bridge_mirror(self, intf_name, mirror_intf_name, br_name):
289
290 txn = self._start_txn()
291 mirror = txn.insert(self.get_table('Mirror'))
292 mirror.name = 'm_%s' % intf_name
293
294 mirror.select_all = False
295
296 mirrored_port = self._find_row_by_name('Port', intf_name)
297
298 mirror.verify('select_dst_port')
299 dst_port = getattr(mirror, 'select_dst_port', [])
300 dst_port.append(mirrored_port)
301 mirror.select_dst_port = dst_port
302
303 mirror.verify('select_src_port')
304 src_port = getattr(mirror, 'select_src_port', [])
305 src_port.append(mirrored_port)
306 mirror.select_src_port = src_port
307
308 output_port = self._find_row_by_name('Port', mirror_intf_name)
309
310 mirror.verify('output_port')
311 out_port = getattr(mirror, 'output_port', [])
312 out_port.append(output_port.uuid)
313 mirror.output_port = out_port
314
315 br = self._find_row_by_name('Bridge', br_name)
316 br.verify('mirrors')
317 mirrors = getattr(br, 'mirrors', [])
318 mirrors.append(mirror.uuid)
319 br.mirrors = mirrors
320
321 def try_again(db_entity):
322 db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name)
323
324 txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s"
325 % (username(), mirror.name))
326 status = self._complete_txn(try_again)
327 if status is False:
328 raise OVSDBException('Unable to create Mirror %s: %s' %
329 (mirror_intf_name, txn.get_error()))
330 result = txn.get_insert_uuid(mirror.uuid)
331 self._txn = None
332 return result
333
334
335 def argv_tuples(lst):
336 cur, nxt = iter(lst), iter(lst)
337 next(nxt, None)
338
339 try:
340 while True:
341 yield next(cur), next(nxt, None)
342 except StopIteration:
343 pass
344
345
346 def main():
347 db_sock = 'unix:@RUNDIR@/db.sock'
348 interface = None
349 tcpdargs = []
350
351 skip_next = False
352 mirror_interface = None
353 dump_cmd = 'tcpdump'
354
355 for cur, nxt in argv_tuples(sys.argv[1:]):
356 if skip_next:
357 skip_next = False
358 continue
359 if cur in ['-h', '--help']:
360 usage()
361 elif cur in ['-V', '--version']:
362 print("ovs-tcpdump (Open vSwitch) @VERSION@")
363 sys.exit(0)
364 elif cur in ['--db-sock']:
365 db_sock = nxt
366 skip_next = True
367 continue
368 elif cur in ['--dump-cmd']:
369 dump_cmd = nxt
370 skip_next = True
371 continue
372 elif cur in ['-i', '--interface']:
373 interface = nxt
374 skip_next = True
375 continue
376 elif cur in ['--mirror-to']:
377 mirror_interface = nxt
378 skip_next = True
379 continue
380 tcpdargs.append(cur)
381
382 if interface is None:
383 print("Error: must at least specify an interface with '-i' option")
384 sys.exit(1)
385
386 if '-l' not in tcpdargs:
387 tcpdargs.insert(0, '-l')
388
389 if '-vv' in tcpdargs:
390 print("TCPDUMP Args: %s" % ' '.join(tcpdargs))
391
392 ovsdb = OVSDB(db_sock)
393 mirror_interface = mirror_interface or "mi%s" % interface
394
395 if sys.platform in _make_taps and \
396 mirror_interface not in netifaces.interfaces():
397 _make_taps[sys.platform](mirror_interface)
398
399 if mirror_interface not in netifaces.interfaces():
400 print("ERROR: Please create an interface called `%s`" %
401 mirror_interface)
402 print("See your OS guide for how to do this.")
403 print("Ex: ip link add %s type veth peer name %s" %
404 (mirror_interface, mirror_interface + "2"))
405 sys.exit(1)
406
407 if not ovsdb.port_exists(interface):
408 print("ERROR: Port %s does not exist." % interface)
409 sys.exit(1)
410 if ovsdb.port_exists(mirror_interface):
411 print("ERROR: Mirror port (%s) exists for port %s." %
412 (mirror_interface, interface))
413 sys.exit(1)
414 try:
415 ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface))
416 ovsdb.bridge_mirror(interface, mirror_interface,
417 ovsdb.port_bridge(interface))
418 except OVSDBException as oe:
419 print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
420 try:
421 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
422 except Exception:
423 pass
424 sys.exit(1)
425
426 pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
427 try:
428 while pipes.poll() is None:
429 data = pipes.stdout.readline().strip('\n')
430 if len(data) == 0:
431 raise KeyboardInterrupt
432 print(data)
433 if select.select([sys.stdin], [], [], 0.0)[0]:
434 data_in = sys.stdin.read()
435 pipes.stdin.write(data_in)
436 raise KeyboardInterrupt
437 except KeyboardInterrupt:
438 pipes.terminate()
439 ovsdb.destroy_mirror('m%s' % interface, ovsdb.port_bridge(interface))
440 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
441 except Exception:
442 print("Unable to tear down the ports and mirrors.")
443 print("Please use ovs-vsctl to remove the ports and mirrors created.")
444 print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
445 mirror_interface))
446 sys.exit(1)
447
448 sys.exit(0)
449
450
451 if __name__ == '__main__':
452 main()
453
454 # Local variables:
455 # mode: python
456 # End: