]> git.proxmox.com Git - mirror_ovs.git/blob - utilities/ovs-tcpdump.in
bridge: Fix null dereference on ct_timeout_policy record
[mirror_ovs.git] / utilities / ovs-tcpdump.in
1 #! @PYTHON3@
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
19 import os
20 import pwd
21 from random import randint
22 import struct
23 import subprocess
24 import sys
25 import time
26
27 try:
28 from netifaces import interfaces
29 except 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)
42
43 try:
44 from ovs.db import idl
45 from ovs import jsonrpc
46 from ovs.poller import Poller
47 from ovs.stream import Stream
48 except 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
55 tapdev_fd = None
56 _make_taps = {}
57 _make_mirror_name = {}
58 IFNAMSIZ_LINUX = 15 # this is the max name size, excluding the null byte.
59
60
61 def _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
70 def _install_tap_linux(tap_name, mtu_value=None):
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
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)
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
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
91 pipe = _doexec(
92 *(['ip', 'link', 'set', 'dev', str(tap_name), 'up']))
93 pipe.wait()
94
95
96 def _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
102 _make_taps['linux'] = _install_tap_linux
103 _make_taps['linux2'] = _install_tap_linux
104 _make_mirror_name['linux'] = _make_linux_mirror_name
105 _make_mirror_name['linux2'] = _make_linux_mirror_name
106
107
108 def username():
109 return pwd.getpwuid(os.getuid())[0]
110
111
112 def usage():
113 print("""\
114 %(prog)s: Open vSwitch tcpdump helper.
115 usage: %(prog)s -i interface [TCPDUMP OPTIONS]
116 where TCPDUMP OPTIONS represents the options normally passed to tcpdump.
117
118 The 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'
128 --span If specified, mirror all ports (optional)
129 """ % {'prog': sys.argv[0]})
130 sys.exit(0)
131
132
133 class OVSDBException(Exception):
134 pass
135
136
137 class 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:
205 port = self._find_row_by_name('Port', port_name)
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
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
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
274 def destroy_mirror(self, intf_name, bridge_name):
275 mirror_name = 'm_%s' % intf_name
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
330 def bridge_mirror(self, intf_name, mirror_intf_name, br_name,
331 mirror_select_all=False):
332
333 txn = self._start_txn()
334 mirror = txn.insert(self.get_table('Mirror'))
335 mirror.name = 'm_%s' % intf_name
336
337 mirror.select_all = mirror_select_all
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
378 def 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
389 def py_which(executable):
390 return any(os.access(os.path.join(path, executable), os.X_OK)
391 for path in os.environ["PATH"].split(os.pathsep))
392
393
394 def main():
395 db_sock = 'unix:@RUNDIR@/db.sock'
396 interface = None
397 tcpdargs = []
398
399 skip_next = False
400 mirror_interface = None
401 mirror_select_all = False
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
429 elif cur in ['--span']:
430 mirror_select_all = True
431 continue
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
438 if not py_which(dump_cmd):
439 print("Error: unable to execute '%s' (check PATH)" % dump_cmd)
440 sys.exit(1)
441
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)
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)
453
454 if sys.platform in _make_taps and \
455 mirror_interface not in interfaces():
456 _make_taps[sys.platform](mirror_interface,
457 ovsdb.interface_mtu(interface))
458
459 if mirror_interface not in interfaces():
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,
477 ovsdb.port_bridge(interface),
478 mirror_select_all)
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:
489 while pipes.poll() is None:
490 data = pipes.stdout.readline().strip(b'\n')
491 if len(data) == 0:
492 raise KeyboardInterrupt
493 print(data.decode('utf-8'))
494 raise KeyboardInterrupt
495 except KeyboardInterrupt:
496 if pipes.poll() is None:
497 pipes.terminate()
498
499 ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
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
511 if __name__ == '__main__':
512 main()
513
514 # Local variables:
515 # mode: python
516 # End: