]> git.proxmox.com Git - ovs.git/blame - utilities/ovs-tcpdump.in
dpctl: Fix dpctl process command parameter error.
[ovs.git] / utilities / ovs-tcpdump.in
CommitLineData
1ca0323e 1#! @PYTHON3@
314ce647
AC
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
17import fcntl
6c7050b5 18
314ce647
AC
19import os
20import pwd
b4027b17 21from random import randint
314ce647
AC
22import struct
23import subprocess
24import sys
25import time
26
02707cd4
TR
27try:
28 from netifaces import interfaces
29except ImportError:
30 if sys.platform in ['linux', 'linux2']:
31 def interfaces():
32 devices = []
33 with open("/proc/net/dev", "r") as f_netdev:
34 for line in f_netdev:
35 if ":" not in line:
36 continue
37 devices.append(line.split(":")[0].strip())
38 return devices
39 else:
40 print("ERROR: Please install netifaces Python library.")
41 sys.exit(1)
6c7050b5 42
314ce647
AC
43try:
44 from ovs.db import idl
45 from ovs import jsonrpc
46 from ovs.poller import Poller
47 from ovs.stream import Stream
48except Exception:
49 print("ERROR: Please install the correct Open vSwitch python support")
50 print(" libraries (version @VERSION@).")
51 print(" Alternatively, check that your PYTHONPATH is pointing to")
52 print(" the correct location.")
53 sys.exit(1)
54
55tapdev_fd = None
56_make_taps = {}
b4027b17
AC
57_make_mirror_name = {}
58IFNAMSIZ_LINUX = 15 # this is the max name size, excluding the null byte.
314ce647
AC
59
60
61def _doexec(*args, **kwargs):
62 """Executes an application and returns a set of pipes"""
63
64 shell = len(args) == 1
65 proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell,
66 bufsize=0)
67 return proc
68
69
26cea6ad 70def _install_tap_linux(tap_name, mtu_value=None):
314ce647
AC
71 """Uses /dev/net/tun to create a tap device"""
72 global tapdev_fd
73
74 IFF_TAP = 0x0002
75 IFF_NO_PI = 0x1000
76 TUNSETIFF = 0x400454CA # This is derived by printf() of TUNSETIFF
77 TUNSETOWNER = TUNSETIFF + 2
78
793bdb6c
TR
79 tapdev_fd = os.open('/dev/net/tun', os.O_RDWR)
80 ifr = struct.pack('16sH', tap_name.encode('utf8'), IFF_TAP | IFF_NO_PI)
314ce647
AC
81 fcntl.ioctl(tapdev_fd, TUNSETIFF, ifr)
82 fcntl.ioctl(tapdev_fd, TUNSETOWNER, os.getegid())
83
84 time.sleep(1) # required to give the new device settling time
26cea6ad
AC
85 if mtu_value is not None:
86 pipe = _doexec(
87 *(['ip', 'link', 'set', 'dev', str(tap_name), 'mtu',
88 str(mtu_value)]))
89 pipe.wait()
90
314ce647
AC
91 pipe = _doexec(
92 *(['ip', 'link', 'set', 'dev', str(tap_name), 'up']))
93 pipe.wait()
94
81a86b9a 95
b4027b17
AC
96def _make_linux_mirror_name(interface_name):
97 if len(interface_name) > IFNAMSIZ_LINUX - 2:
98 return "ovsmi%06d" % randint(1, 999999)
99 return "mi%s" % interface_name
100
101
314ce647
AC
102_make_taps['linux'] = _install_tap_linux
103_make_taps['linux2'] = _install_tap_linux
b4027b17
AC
104_make_mirror_name['linux'] = _make_linux_mirror_name
105_make_mirror_name['linux2'] = _make_linux_mirror_name
314ce647
AC
106
107
108def username():
109 return pwd.getpwuid(os.getuid())[0]
110
111
112def usage():
113 print("""\
114%(prog)s: Open vSwitch tcpdump helper.
115usage: %(prog)s -i interface [TCPDUMP OPTIONS]
116where TCPDUMP OPTIONS represents the options normally passed to tcpdump.
117
118The following options are available:
119 -h, --help display this help message
120 -V, --version display version information
121 --db-sock A connection string to reach the Open vSwitch
122 ovsdb-server.
123 Default 'unix:@RUNDIR@/db.sock'
124 --dump-cmd Command to use for tcpdump (default 'tcpdump')
125 -i, --interface Open vSwitch interface to mirror and tcpdump
126 --mirror-to The name for the mirror port to use (optional)
127 Default 'miINTERFACE'
0475db71 128 --span If specified, mirror all ports (optional)
314ce647
AC
129""" % {'prog': sys.argv[0]})
130 sys.exit(0)
131
132
133class OVSDBException(Exception):
134 pass
135
136
137class OVSDB(object):
138 @staticmethod
139 def wait_for_db_change(idl):
140 seq = idl.change_seqno
141 stop = time.time() + 10
142 while idl.change_seqno == seq and not idl.run():
143 poller = Poller()
144 idl.wait(poller)
145 poller.block()
146 if time.time() >= stop:
147 raise Exception('Retry Timeout')
148
149 def __init__(self, db_sock):
150 self._db_sock = db_sock
151 self._txn = None
152 schema = self._get_schema()
153 schema.register_all()
154 self._idl_conn = idl.Idl(db_sock, schema)
155 OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB
156
157 def _get_schema(self):
158 error, strm = Stream.open_block(Stream.open(self._db_sock))
159 if error:
160 raise Exception("Unable to connect to %s" % self._db_sock)
161 rpc = jsonrpc.Connection(strm)
162 req = jsonrpc.Message.create_request('get_schema', ['Open_vSwitch'])
163 error, resp = rpc.transact_block(req)
164 rpc.close()
165
166 if error or resp.error:
167 raise Exception('Unable to retrieve schema.')
168 return idl.SchemaHelper(None, resp.result)
169
170 def get_table(self, table_name):
171 return self._idl_conn.tables[table_name]
172
173 def _start_txn(self):
174 if self._txn is not None:
175 raise OVSDBException("ERROR: A transaction was started already")
176 self._idl_conn.change_seqno += 1
177 self._txn = idl.Transaction(self._idl_conn)
178 return self._txn
179
180 def _complete_txn(self, try_again_fn):
181 if self._txn is None:
182 raise OVSDBException("ERROR: Not in a transaction")
183 status = self._txn.commit_block()
184 if status is idl.Transaction.TRY_AGAIN:
185 if self._idl_conn._session.rpc.status != 0:
186 self._idl_conn.force_reconnect()
187 OVSDB.wait_for_db_change(self._idl_conn)
188 return try_again_fn(self)
189 elif status is idl.Transaction.ERROR:
190 return False
191
192 def _find_row(self, table_name, find):
193 return next(
194 (row for row in self.get_table(table_name).rows.values()
195 if find(row)), None)
196
197 def _find_row_by_name(self, table_name, value):
198 return self._find_row(table_name, lambda row: row.name == value)
199
200 def port_exists(self, port_name):
201 return bool(self._find_row_by_name('Port', port_name))
202
203 def port_bridge(self, port_name):
204 try:
31235267 205 port = self._find_row_by_name('Port', port_name)
314ce647
AC
206 br = self._find_row('Bridge', lambda x: port in x.ports)
207 return br.name
208 except Exception:
209 raise OVSDBException('Unable to find port %s bridge' % port_name)
210
26cea6ad
AC
211 def interface_mtu(self, intf_name):
212 try:
213 intf = self._find_row_by_name('Interface', intf_name)
214 return intf.mtu[0]
215 except Exception:
216 return None
217
314ce647
AC
218 def interface_exists(self, intf_name):
219 return bool(self._find_row_by_name('Interface', intf_name))
220
221 def mirror_exists(self, mirror_name):
222 return bool(self._find_row_by_name('Mirror', mirror_name))
223
224 def interface_uuid(self, intf_name):
225 row = self._find_row_by_name('Interface', intf_name)
226 if bool(row):
227 return row.uuid
228 raise OVSDBException('No such interface: %s' % intf_name)
229
230 def make_interface(self, intf_name, execute_transaction=True):
231 if self.interface_exists(intf_name):
232 print("INFO: Interface exists.")
233 return self.interface_uuid(intf_name)
234
235 txn = self._start_txn()
236 tmp_row = txn.insert(self.get_table('Interface'))
237 tmp_row.name = intf_name
238
239 def try_again(db_entity):
240 db_entity.make_interface(intf_name)
241
242 if not execute_transaction:
243 return tmp_row
244
245 txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s"
246 % (username(), intf_name))
247 status = self._complete_txn(try_again)
248 if status is False:
249 raise OVSDBException('Unable to create Interface %s: %s' %
250 (intf_name, txn.get_error()))
251 result = txn.get_insert_uuid(tmp_row.uuid)
252 self._txn = None
253 return result
254
255 def destroy_port(self, port_name, bridge_name):
256 if not self.interface_exists(port_name):
257 return
258 txn = self._start_txn()
259 br = self._find_row_by_name('Bridge', bridge_name)
260 ports = [port for port in br.ports if port.name != port_name]
261 br.ports = ports
262
263 def try_again(db_entity):
264 db_entity.destroy_port(port_name)
265
266 txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s"
267 % (username(), port_name))
268 status = self._complete_txn(try_again)
269 if status is False:
270 raise OVSDBException('unable to delete Port %s: %s' %
271 (port_name, txn.get_error()))
272 self._txn = None
273
efeb3e44 274 def destroy_mirror(self, intf_name, bridge_name):
275 mirror_name = 'm_%s' % intf_name
314ce647
AC
276 if not self.mirror_exists(mirror_name):
277 return
278 txn = self._start_txn()
279 mirror_row = self._find_row_by_name('Mirror', mirror_name)
280 br = self._find_row_by_name('Bridge', bridge_name)
281 mirrors = [mirror for mirror in br.mirrors
282 if mirror.uuid != mirror_row.uuid]
283 br.mirrors = mirrors
284
285 def try_again(db_entity):
286 db_entity.destroy_mirror(mirror_name, bridge_name)
287
288 txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s"
289 % (username(), mirror_name))
290 status = self._complete_txn(try_again)
291 if status is False:
292 raise OVSDBException('Unable to delete Mirror %s: %s' %
293 (mirror_name, txn.get_error()))
294 self._txn = None
295
296 def make_port(self, port_name, bridge_name):
297 iface_row = self.make_interface(port_name, False)
298 txn = self._txn
299
300 br = self._find_row_by_name('Bridge', bridge_name)
301 if not br:
302 raise OVSDBException('Bad bridge name %s' % bridge_name)
303
304 port = txn.insert(self.get_table('Port'))
305 port.name = port_name
306
307 br.verify('ports')
308 ports = getattr(br, 'ports', [])
309 ports.append(port)
310 br.ports = ports
311
312 port.verify('interfaces')
313 ifaces = getattr(port, 'interfaces', [])
314 ifaces.append(iface_row)
315 port.interfaces = ifaces
316
317 def try_again(db_entity):
318 db_entity.make_port(port_name, bridge_name)
319
320 txn.add_comment("ovs-tcpdump: user=%s,create_port=%s"
321 % (username(), port_name))
322 status = self._complete_txn(try_again)
323 if status is False:
324 raise OVSDBException('Unable to create Port %s: %s' %
325 (port_name, txn.get_error()))
326 result = txn.get_insert_uuid(port.uuid)
327 self._txn = None
328 return result
329
0475db71
MF
330 def bridge_mirror(self, intf_name, mirror_intf_name, br_name,
331 mirror_select_all=False):
314ce647
AC
332
333 txn = self._start_txn()
334 mirror = txn.insert(self.get_table('Mirror'))
335 mirror.name = 'm_%s' % intf_name
336
0475db71 337 mirror.select_all = mirror_select_all
314ce647
AC
338
339 mirrored_port = self._find_row_by_name('Port', intf_name)
340
341 mirror.verify('select_dst_port')
342 dst_port = getattr(mirror, 'select_dst_port', [])
343 dst_port.append(mirrored_port)
344 mirror.select_dst_port = dst_port
345
346 mirror.verify('select_src_port')
347 src_port = getattr(mirror, 'select_src_port', [])
348 src_port.append(mirrored_port)
349 mirror.select_src_port = src_port
350
351 output_port = self._find_row_by_name('Port', mirror_intf_name)
352
353 mirror.verify('output_port')
354 out_port = getattr(mirror, 'output_port', [])
355 out_port.append(output_port.uuid)
356 mirror.output_port = out_port
357
358 br = self._find_row_by_name('Bridge', br_name)
359 br.verify('mirrors')
360 mirrors = getattr(br, 'mirrors', [])
361 mirrors.append(mirror.uuid)
362 br.mirrors = mirrors
363
364 def try_again(db_entity):
365 db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name)
366
367 txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s"
368 % (username(), mirror.name))
369 status = self._complete_txn(try_again)
370 if status is False:
371 raise OVSDBException('Unable to create Mirror %s: %s' %
372 (mirror_intf_name, txn.get_error()))
373 result = txn.get_insert_uuid(mirror.uuid)
374 self._txn = None
375 return result
376
377
378def argv_tuples(lst):
379 cur, nxt = iter(lst), iter(lst)
380 next(nxt, None)
381
382 try:
383 while True:
384 yield next(cur), next(nxt, None)
385 except StopIteration:
386 pass
387
388
2eeadf73 389def py_which(executable):
eeecd1b2
IM
390 return any(os.access(os.path.join(path, executable), os.X_OK)
391 for path in os.environ["PATH"].split(os.pathsep))
2eeadf73
LC
392
393
314ce647
AC
394def main():
395 db_sock = 'unix:@RUNDIR@/db.sock'
396 interface = None
397 tcpdargs = []
398
399 skip_next = False
400 mirror_interface = None
6012a420 401 mirror_select_all = False
314ce647
AC
402 dump_cmd = 'tcpdump'
403
404 for cur, nxt in argv_tuples(sys.argv[1:]):
405 if skip_next:
406 skip_next = False
407 continue
408 if cur in ['-h', '--help']:
409 usage()
410 elif cur in ['-V', '--version']:
411 print("ovs-tcpdump (Open vSwitch) @VERSION@")
412 sys.exit(0)
413 elif cur in ['--db-sock']:
414 db_sock = nxt
415 skip_next = True
416 continue
417 elif cur in ['--dump-cmd']:
418 dump_cmd = nxt
419 skip_next = True
420 continue
421 elif cur in ['-i', '--interface']:
422 interface = nxt
423 skip_next = True
424 continue
425 elif cur in ['--mirror-to']:
426 mirror_interface = nxt
427 skip_next = True
428 continue
0475db71
MF
429 elif cur in ['--span']:
430 mirror_select_all = True
431 continue
314ce647
AC
432 tcpdargs.append(cur)
433
434 if interface is None:
435 print("Error: must at least specify an interface with '-i' option")
436 sys.exit(1)
437
2eeadf73
LC
438 if not py_which(dump_cmd):
439 print("Error: unable to execute '%s' (check PATH)" % dump_cmd)
440 sys.exit(1)
441
314ce647
AC
442 if '-l' not in tcpdargs:
443 tcpdargs.insert(0, '-l')
444
445 if '-vv' in tcpdargs:
446 print("TCPDUMP Args: %s" % ' '.join(tcpdargs))
447
448 ovsdb = OVSDB(db_sock)
b4027b17
AC
449 if mirror_interface is None:
450 mirror_interface = "mi%s" % interface
451 if sys.platform in _make_mirror_name:
452 mirror_interface = _make_mirror_name[sys.platform](interface)
314ce647
AC
453
454 if sys.platform in _make_taps and \
02707cd4 455 mirror_interface not in interfaces():
26cea6ad
AC
456 _make_taps[sys.platform](mirror_interface,
457 ovsdb.interface_mtu(interface))
314ce647 458
02707cd4 459 if mirror_interface not in interfaces():
314ce647
AC
460 print("ERROR: Please create an interface called `%s`" %
461 mirror_interface)
462 print("See your OS guide for how to do this.")
463 print("Ex: ip link add %s type veth peer name %s" %
464 (mirror_interface, mirror_interface + "2"))
465 sys.exit(1)
466
467 if not ovsdb.port_exists(interface):
468 print("ERROR: Port %s does not exist." % interface)
469 sys.exit(1)
470 if ovsdb.port_exists(mirror_interface):
471 print("ERROR: Mirror port (%s) exists for port %s." %
472 (mirror_interface, interface))
473 sys.exit(1)
474 try:
475 ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface))
476 ovsdb.bridge_mirror(interface, mirror_interface,
0475db71
MF
477 ovsdb.port_bridge(interface),
478 mirror_select_all)
314ce647
AC
479 except OVSDBException as oe:
480 print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
481 try:
482 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
483 except Exception:
484 pass
485 sys.exit(1)
486
487 pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
488 try:
52654c4a 489 while pipes.poll() is None:
793bdb6c 490 data = pipes.stdout.readline().strip(b'\n')
52654c4a
AC
491 if len(data) == 0:
492 raise KeyboardInterrupt
793bdb6c 493 print(data.decode('utf-8'))
52654c4a 494 raise KeyboardInterrupt
314ce647 495 except KeyboardInterrupt:
68b2a37d 496 if pipes.poll() is None:
497 pipes.terminate()
498
efeb3e44 499 ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
314ce647
AC
500 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
501 except Exception:
502 print("Unable to tear down the ports and mirrors.")
503 print("Please use ovs-vsctl to remove the ports and mirrors created.")
504 print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
505 mirror_interface))
506 sys.exit(1)
507
508 sys.exit(0)
509
510
511if __name__ == '__main__':
512 main()
513
514# Local variables:
515# mode: python
516# End: