1 #! /usr/bin/env @PYTHON@
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.
28 from ovs.db import idl
29 from ovs import jsonrpc
30 from ovs.poller import Poller
31 from ovs.stream import Stream
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.")
43 def _doexec(*args, **kwargs):
44 """Executes an application and returns a set of pipes"""
46 shell = len(args) == 1
47 proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell,
52 def _install_tap_linux(tap_name):
53 """Uses /dev/net/tun to create a tap device"""
58 TUNSETIFF = 0x400454CA # This is derived by printf() of TUNSETIFF
59 TUNSETOWNER = TUNSETIFF + 2
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())
66 time.sleep(1) # required to give the new device settling time
68 *(['ip', 'link', 'set', 'dev', str(tap_name), 'up']))
71 _make_taps['linux'] = _install_tap_linux
72 _make_taps['linux2'] = _install_tap_linux
76 return pwd.getpwuid(os.getuid())[0]
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.
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
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)
95 """ % {'prog': sys.argv[0]})
99 class OVSDBException(Exception):
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():
112 if time.time() >= stop:
113 raise Exception('Retry Timeout')
115 def __init__(self, db_sock):
116 self._db_sock = db_sock
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
123 def _get_schema(self):
124 error, strm = Stream.open_block(Stream.open(self._db_sock))
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)
132 if error or resp.error:
133 raise Exception('Unable to retrieve schema.')
134 return idl.SchemaHelper(None, resp.result)
136 def get_table(self, table_name):
137 return self._idl_conn.tables[table_name]
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)
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:
158 def _find_row(self, table_name, find):
160 (row for row in self.get_table(table_name).rows.values()
163 def _find_row_by_name(self, table_name, value):
164 return self._find_row(table_name, lambda row: row.name == value)
166 def port_exists(self, port_name):
167 return bool(self._find_row_by_name('Port', port_name))
169 def port_bridge(self, port_name):
171 row = self._find_row_by_name('Interface', port_name)
172 port = self._find_row('Port', lambda x: row in x.interfaces)
173 br = self._find_row('Bridge', lambda x: port in x.ports)
176 raise OVSDBException('Unable to find port %s bridge' % port_name)
178 def interface_exists(self, intf_name):
179 return bool(self._find_row_by_name('Interface', intf_name))
181 def mirror_exists(self, mirror_name):
182 return bool(self._find_row_by_name('Mirror', mirror_name))
184 def interface_uuid(self, intf_name):
185 row = self._find_row_by_name('Interface', intf_name)
188 raise OVSDBException('No such interface: %s' % intf_name)
190 def make_interface(self, intf_name, execute_transaction=True):
191 if self.interface_exists(intf_name):
192 print("INFO: Interface exists.")
193 return self.interface_uuid(intf_name)
195 txn = self._start_txn()
196 tmp_row = txn.insert(self.get_table('Interface'))
197 tmp_row.name = intf_name
199 def try_again(db_entity):
200 db_entity.make_interface(intf_name)
202 if not execute_transaction:
205 txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s"
206 % (username(), intf_name))
207 status = self._complete_txn(try_again)
209 raise OVSDBException('Unable to create Interface %s: %s' %
210 (intf_name, txn.get_error()))
211 result = txn.get_insert_uuid(tmp_row.uuid)
215 def destroy_port(self, port_name, bridge_name):
216 if not self.interface_exists(port_name):
218 txn = self._start_txn()
219 br = self._find_row_by_name('Bridge', bridge_name)
220 ports = [port for port in br.ports if port.name != port_name]
223 def try_again(db_entity):
224 db_entity.destroy_port(port_name)
226 txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s"
227 % (username(), port_name))
228 status = self._complete_txn(try_again)
230 raise OVSDBException('unable to delete Port %s: %s' %
231 (port_name, txn.get_error()))
234 def destroy_mirror(self, mirror_name, bridge_name):
235 if not self.mirror_exists(mirror_name):
237 txn = self._start_txn()
238 mirror_row = self._find_row_by_name('Mirror', mirror_name)
239 br = self._find_row_by_name('Bridge', bridge_name)
240 mirrors = [mirror for mirror in br.mirrors
241 if mirror.uuid != mirror_row.uuid]
244 def try_again(db_entity):
245 db_entity.destroy_mirror(mirror_name, bridge_name)
247 txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s"
248 % (username(), mirror_name))
249 status = self._complete_txn(try_again)
251 raise OVSDBException('Unable to delete Mirror %s: %s' %
252 (mirror_name, txn.get_error()))
255 def make_port(self, port_name, bridge_name):
256 iface_row = self.make_interface(port_name, False)
259 br = self._find_row_by_name('Bridge', bridge_name)
261 raise OVSDBException('Bad bridge name %s' % bridge_name)
263 port = txn.insert(self.get_table('Port'))
264 port.name = port_name
267 ports = getattr(br, 'ports', [])
271 port.verify('interfaces')
272 ifaces = getattr(port, 'interfaces', [])
273 ifaces.append(iface_row)
274 port.interfaces = ifaces
276 def try_again(db_entity):
277 db_entity.make_port(port_name, bridge_name)
279 txn.add_comment("ovs-tcpdump: user=%s,create_port=%s"
280 % (username(), port_name))
281 status = self._complete_txn(try_again)
283 raise OVSDBException('Unable to create Port %s: %s' %
284 (port_name, txn.get_error()))
285 result = txn.get_insert_uuid(port.uuid)
289 def bridge_mirror(self, intf_name, mirror_intf_name, br_name):
291 txn = self._start_txn()
292 mirror = txn.insert(self.get_table('Mirror'))
293 mirror.name = 'm_%s' % intf_name
295 mirror.select_all = False
297 mirrored_port = self._find_row_by_name('Port', intf_name)
299 mirror.verify('select_dst_port')
300 dst_port = getattr(mirror, 'select_dst_port', [])
301 dst_port.append(mirrored_port)
302 mirror.select_dst_port = dst_port
304 mirror.verify('select_src_port')
305 src_port = getattr(mirror, 'select_src_port', [])
306 src_port.append(mirrored_port)
307 mirror.select_src_port = src_port
309 output_port = self._find_row_by_name('Port', mirror_intf_name)
311 mirror.verify('output_port')
312 out_port = getattr(mirror, 'output_port', [])
313 out_port.append(output_port.uuid)
314 mirror.output_port = out_port
316 br = self._find_row_by_name('Bridge', br_name)
318 mirrors = getattr(br, 'mirrors', [])
319 mirrors.append(mirror.uuid)
322 def try_again(db_entity):
323 db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name)
325 txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s"
326 % (username(), mirror.name))
327 status = self._complete_txn(try_again)
329 raise OVSDBException('Unable to create Mirror %s: %s' %
330 (mirror_intf_name, txn.get_error()))
331 result = txn.get_insert_uuid(mirror.uuid)
336 def argv_tuples(lst):
337 cur, nxt = iter(lst), iter(lst)
342 yield next(cur), next(nxt, None)
343 except StopIteration:
348 db_sock = 'unix:@RUNDIR@/db.sock'
353 mirror_interface = None
356 for cur, nxt in argv_tuples(sys.argv[1:]):
360 if cur in ['-h', '--help']:
362 elif cur in ['-V', '--version']:
363 print("ovs-tcpdump (Open vSwitch) @VERSION@")
365 elif cur in ['--db-sock']:
369 elif cur in ['--dump-cmd']:
373 elif cur in ['-i', '--interface']:
377 elif cur in ['--mirror-to']:
378 mirror_interface = nxt
383 if interface is None:
384 print("Error: must at least specify an interface with '-i' option")
387 if '-l' not in tcpdargs:
388 tcpdargs.insert(0, '-l')
390 if '-vv' in tcpdargs:
391 print("TCPDUMP Args: %s" % ' '.join(tcpdargs))
393 ovsdb = OVSDB(db_sock)
394 mirror_interface = mirror_interface or "mi%s" % interface
396 if sys.platform in _make_taps and \
397 mirror_interface not in netifaces.interfaces():
398 _make_taps[sys.platform](mirror_interface)
400 if mirror_interface not in netifaces.interfaces():
401 print("ERROR: Please create an interface called `%s`" %
403 print("See your OS guide for how to do this.")
404 print("Ex: ip link add %s type veth peer name %s" %
405 (mirror_interface, mirror_interface + "2"))
408 if not ovsdb.port_exists(interface):
409 print("ERROR: Port %s does not exist." % interface)
411 if ovsdb.port_exists(mirror_interface):
412 print("ERROR: Mirror port (%s) exists for port %s." %
413 (mirror_interface, interface))
416 ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface))
417 ovsdb.bridge_mirror(interface, mirror_interface,
418 ovsdb.port_bridge(interface))
419 except OVSDBException as oe:
420 print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
422 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
427 pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
430 print(pipes.stdout.readline())
431 if select.select([sys.stdin], [], [], 0.0)[0]:
432 data_in = sys.stdin.read()
433 pipes.stdin.write(data_in)
434 except KeyboardInterrupt:
436 ovsdb.destroy_mirror('m%s' % interface, ovsdb.port_bridge(interface))
437 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
439 print("Unable to tear down the ports and mirrors.")
440 print("Please use ovs-vsctl to remove the ports and mirrors created.")
441 print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
448 if __name__ == '__main__':