3 # Copyright (c) 2016 Red Hat, Inc.
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:
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
21 from random import randint
30 from ovs.db import idl
31 from ovs import jsonrpc
32 from ovs.poller import Poller
33 from ovs.stream import Stream
35 print("ERROR: Please install the correct Open vSwitch python support")
36 print(" libraries (version @VERSION@).")
37 print(" Alternatively, check that your PYTHONPATH is pointing to")
38 print(" the correct location.")
43 _make_mirror_name = {}
44 IFNAMSIZ_LINUX = 15 # this is the max name size, excluding the null byte.
47 def _doexec(*args, **kwargs):
48 """Executes an application and returns a set of pipes"""
50 shell = len(args) == 1
51 proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell,
56 def _install_tap_linux(tap_name, mtu_value=None):
57 """Uses /dev/net/tun to create a tap device"""
62 TUNSETIFF = 0x400454CA # This is derived by printf() of TUNSETIFF
63 TUNSETOWNER = TUNSETIFF + 2
65 tapdev_fd = os.open('/dev/net/tun', os.O_RDWR)
66 ifr = struct.pack('16sH', tap_name.encode('utf8'), IFF_TAP | IFF_NO_PI)
67 fcntl.ioctl(tapdev_fd, TUNSETIFF, ifr)
68 fcntl.ioctl(tapdev_fd, TUNSETOWNER, os.getegid())
70 time.sleep(1) # required to give the new device settling time
71 if mtu_value is not None:
73 *(['ip', 'link', 'set', 'dev', str(tap_name), 'mtu',
78 *(['ip', 'link', 'set', 'dev', str(tap_name), 'up']))
82 def _make_linux_mirror_name(interface_name):
83 if len(interface_name) > IFNAMSIZ_LINUX - 2:
84 return "ovsmi%06d" % randint(1, 999999)
85 return "mi%s" % interface_name
88 _make_taps['linux'] = _install_tap_linux
89 _make_taps['linux2'] = _install_tap_linux
90 _make_mirror_name['linux'] = _make_linux_mirror_name
91 _make_mirror_name['linux2'] = _make_linux_mirror_name
95 return pwd.getpwuid(os.getuid())[0]
100 %(prog)s: Open vSwitch tcpdump helper.
101 usage: %(prog)s -i interface [TCPDUMP OPTIONS]
102 where TCPDUMP OPTIONS represents the options normally passed to tcpdump.
104 The following options are available:
105 -h, --help display this help message
106 -V, --version display version information
107 --db-sock A connection string to reach the Open vSwitch
109 Default 'unix:@RUNDIR@/db.sock'
110 --dump-cmd Command to use for tcpdump (default 'tcpdump')
111 -i, --interface Open vSwitch interface to mirror and tcpdump
112 --mirror-to The name for the mirror port to use (optional)
113 Default 'miINTERFACE'
114 """ % {'prog': sys.argv[0]})
118 class OVSDBException(Exception):
124 def wait_for_db_change(idl):
125 seq = idl.change_seqno
126 stop = time.time() + 10
127 while idl.change_seqno == seq and not idl.run():
131 if time.time() >= stop:
132 raise Exception('Retry Timeout')
134 def __init__(self, db_sock):
135 self._db_sock = db_sock
137 schema = self._get_schema()
138 schema.register_all()
139 self._idl_conn = idl.Idl(db_sock, schema)
140 OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB
142 def _get_schema(self):
143 error, strm = Stream.open_block(Stream.open(self._db_sock))
145 raise Exception("Unable to connect to %s" % self._db_sock)
146 rpc = jsonrpc.Connection(strm)
147 req = jsonrpc.Message.create_request('get_schema', ['Open_vSwitch'])
148 error, resp = rpc.transact_block(req)
151 if error or resp.error:
152 raise Exception('Unable to retrieve schema.')
153 return idl.SchemaHelper(None, resp.result)
155 def get_table(self, table_name):
156 return self._idl_conn.tables[table_name]
158 def _start_txn(self):
159 if self._txn is not None:
160 raise OVSDBException("ERROR: A transaction was started already")
161 self._idl_conn.change_seqno += 1
162 self._txn = idl.Transaction(self._idl_conn)
165 def _complete_txn(self, try_again_fn):
166 if self._txn is None:
167 raise OVSDBException("ERROR: Not in a transaction")
168 status = self._txn.commit_block()
169 if status is idl.Transaction.TRY_AGAIN:
170 if self._idl_conn._session.rpc.status != 0:
171 self._idl_conn.force_reconnect()
172 OVSDB.wait_for_db_change(self._idl_conn)
173 return try_again_fn(self)
174 elif status is idl.Transaction.ERROR:
177 def _find_row(self, table_name, find):
179 (row for row in self.get_table(table_name).rows.values()
182 def _find_row_by_name(self, table_name, value):
183 return self._find_row(table_name, lambda row: row.name == value)
185 def port_exists(self, port_name):
186 return bool(self._find_row_by_name('Port', port_name))
188 def port_bridge(self, port_name):
190 port = self._find_row_by_name('Port', port_name)
191 br = self._find_row('Bridge', lambda x: port in x.ports)
194 raise OVSDBException('Unable to find port %s bridge' % port_name)
196 def interface_mtu(self, intf_name):
198 intf = self._find_row_by_name('Interface', intf_name)
203 def interface_exists(self, intf_name):
204 return bool(self._find_row_by_name('Interface', intf_name))
206 def mirror_exists(self, mirror_name):
207 return bool(self._find_row_by_name('Mirror', mirror_name))
209 def interface_uuid(self, intf_name):
210 row = self._find_row_by_name('Interface', intf_name)
213 raise OVSDBException('No such interface: %s' % intf_name)
215 def make_interface(self, intf_name, execute_transaction=True):
216 if self.interface_exists(intf_name):
217 print("INFO: Interface exists.")
218 return self.interface_uuid(intf_name)
220 txn = self._start_txn()
221 tmp_row = txn.insert(self.get_table('Interface'))
222 tmp_row.name = intf_name
224 def try_again(db_entity):
225 db_entity.make_interface(intf_name)
227 if not execute_transaction:
230 txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s"
231 % (username(), intf_name))
232 status = self._complete_txn(try_again)
234 raise OVSDBException('Unable to create Interface %s: %s' %
235 (intf_name, txn.get_error()))
236 result = txn.get_insert_uuid(tmp_row.uuid)
240 def destroy_port(self, port_name, bridge_name):
241 if not self.interface_exists(port_name):
243 txn = self._start_txn()
244 br = self._find_row_by_name('Bridge', bridge_name)
245 ports = [port for port in br.ports if port.name != port_name]
248 def try_again(db_entity):
249 db_entity.destroy_port(port_name)
251 txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s"
252 % (username(), port_name))
253 status = self._complete_txn(try_again)
255 raise OVSDBException('unable to delete Port %s: %s' %
256 (port_name, txn.get_error()))
259 def destroy_mirror(self, intf_name, bridge_name):
260 mirror_name = 'm_%s' % intf_name
261 if not self.mirror_exists(mirror_name):
263 txn = self._start_txn()
264 mirror_row = self._find_row_by_name('Mirror', mirror_name)
265 br = self._find_row_by_name('Bridge', bridge_name)
266 mirrors = [mirror for mirror in br.mirrors
267 if mirror.uuid != mirror_row.uuid]
270 def try_again(db_entity):
271 db_entity.destroy_mirror(mirror_name, bridge_name)
273 txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s"
274 % (username(), mirror_name))
275 status = self._complete_txn(try_again)
277 raise OVSDBException('Unable to delete Mirror %s: %s' %
278 (mirror_name, txn.get_error()))
281 def make_port(self, port_name, bridge_name):
282 iface_row = self.make_interface(port_name, False)
285 br = self._find_row_by_name('Bridge', bridge_name)
287 raise OVSDBException('Bad bridge name %s' % bridge_name)
289 port = txn.insert(self.get_table('Port'))
290 port.name = port_name
293 ports = getattr(br, 'ports', [])
297 port.verify('interfaces')
298 ifaces = getattr(port, 'interfaces', [])
299 ifaces.append(iface_row)
300 port.interfaces = ifaces
302 def try_again(db_entity):
303 db_entity.make_port(port_name, bridge_name)
305 txn.add_comment("ovs-tcpdump: user=%s,create_port=%s"
306 % (username(), port_name))
307 status = self._complete_txn(try_again)
309 raise OVSDBException('Unable to create Port %s: %s' %
310 (port_name, txn.get_error()))
311 result = txn.get_insert_uuid(port.uuid)
315 def bridge_mirror(self, intf_name, mirror_intf_name, br_name):
317 txn = self._start_txn()
318 mirror = txn.insert(self.get_table('Mirror'))
319 mirror.name = 'm_%s' % intf_name
321 mirror.select_all = False
323 mirrored_port = self._find_row_by_name('Port', intf_name)
325 mirror.verify('select_dst_port')
326 dst_port = getattr(mirror, 'select_dst_port', [])
327 dst_port.append(mirrored_port)
328 mirror.select_dst_port = dst_port
330 mirror.verify('select_src_port')
331 src_port = getattr(mirror, 'select_src_port', [])
332 src_port.append(mirrored_port)
333 mirror.select_src_port = src_port
335 output_port = self._find_row_by_name('Port', mirror_intf_name)
337 mirror.verify('output_port')
338 out_port = getattr(mirror, 'output_port', [])
339 out_port.append(output_port.uuid)
340 mirror.output_port = out_port
342 br = self._find_row_by_name('Bridge', br_name)
344 mirrors = getattr(br, 'mirrors', [])
345 mirrors.append(mirror.uuid)
348 def try_again(db_entity):
349 db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name)
351 txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s"
352 % (username(), mirror.name))
353 status = self._complete_txn(try_again)
355 raise OVSDBException('Unable to create Mirror %s: %s' %
356 (mirror_intf_name, txn.get_error()))
357 result = txn.get_insert_uuid(mirror.uuid)
362 def argv_tuples(lst):
363 cur, nxt = iter(lst), iter(lst)
368 yield next(cur), next(nxt, None)
369 except StopIteration:
374 db_sock = 'unix:@RUNDIR@/db.sock'
379 mirror_interface = None
382 for cur, nxt in argv_tuples(sys.argv[1:]):
386 if cur in ['-h', '--help']:
388 elif cur in ['-V', '--version']:
389 print("ovs-tcpdump (Open vSwitch) @VERSION@")
391 elif cur in ['--db-sock']:
395 elif cur in ['--dump-cmd']:
399 elif cur in ['-i', '--interface']:
403 elif cur in ['--mirror-to']:
404 mirror_interface = nxt
409 if interface is None:
410 print("Error: must at least specify an interface with '-i' option")
413 if '-l' not in tcpdargs:
414 tcpdargs.insert(0, '-l')
416 if '-vv' in tcpdargs:
417 print("TCPDUMP Args: %s" % ' '.join(tcpdargs))
419 ovsdb = OVSDB(db_sock)
420 if mirror_interface is None:
421 mirror_interface = "mi%s" % interface
422 if sys.platform in _make_mirror_name:
423 mirror_interface = _make_mirror_name[sys.platform](interface)
425 if sys.platform in _make_taps and \
426 mirror_interface not in netifaces.interfaces():
427 _make_taps[sys.platform](mirror_interface,
428 ovsdb.interface_mtu(interface))
430 if mirror_interface not in netifaces.interfaces():
431 print("ERROR: Please create an interface called `%s`" %
433 print("See your OS guide for how to do this.")
434 print("Ex: ip link add %s type veth peer name %s" %
435 (mirror_interface, mirror_interface + "2"))
438 if not ovsdb.port_exists(interface):
439 print("ERROR: Port %s does not exist." % interface)
441 if ovsdb.port_exists(mirror_interface):
442 print("ERROR: Mirror port (%s) exists for port %s." %
443 (mirror_interface, interface))
446 ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface))
447 ovsdb.bridge_mirror(interface, mirror_interface,
448 ovsdb.port_bridge(interface))
449 except OVSDBException as oe:
450 print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
452 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
457 pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
459 while pipes.poll() is None:
460 data = pipes.stdout.readline().strip(b'\n')
462 raise KeyboardInterrupt
463 print(data.decode('utf-8'))
464 raise KeyboardInterrupt
465 except KeyboardInterrupt:
466 if pipes.poll() is None:
469 ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
470 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
472 print("Unable to tear down the ports and mirrors.")
473 print("Please use ovs-vsctl to remove the ports and mirrors created.")
474 print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
481 if __name__ == '__main__':