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 port = self._find_row_by_name('Port', port_name)
172 br = self._find_row('Bridge', lambda x: port in x.ports)
175 raise OVSDBException('Unable to find port %s bridge' % port_name)
177 def interface_exists(self, intf_name):
178 return bool(self._find_row_by_name('Interface', intf_name))
180 def mirror_exists(self, mirror_name):
181 return bool(self._find_row_by_name('Mirror', mirror_name))
183 def interface_uuid(self, intf_name):
184 row = self._find_row_by_name('Interface', intf_name)
187 raise OVSDBException('No such interface: %s' % intf_name)
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)
194 txn = self._start_txn()
195 tmp_row = txn.insert(self.get_table('Interface'))
196 tmp_row.name = intf_name
198 def try_again(db_entity):
199 db_entity.make_interface(intf_name)
201 if not execute_transaction:
204 txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s"
205 % (username(), intf_name))
206 status = self._complete_txn(try_again)
208 raise OVSDBException('Unable to create Interface %s: %s' %
209 (intf_name, txn.get_error()))
210 result = txn.get_insert_uuid(tmp_row.uuid)
214 def destroy_port(self, port_name, bridge_name):
215 if not self.interface_exists(port_name):
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]
222 def try_again(db_entity):
223 db_entity.destroy_port(port_name)
225 txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s"
226 % (username(), port_name))
227 status = self._complete_txn(try_again)
229 raise OVSDBException('unable to delete Port %s: %s' %
230 (port_name, txn.get_error()))
233 def destroy_mirror(self, mirror_name, bridge_name):
234 if not self.mirror_exists(mirror_name):
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]
243 def try_again(db_entity):
244 db_entity.destroy_mirror(mirror_name, bridge_name)
246 txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s"
247 % (username(), mirror_name))
248 status = self._complete_txn(try_again)
250 raise OVSDBException('Unable to delete Mirror %s: %s' %
251 (mirror_name, txn.get_error()))
254 def make_port(self, port_name, bridge_name):
255 iface_row = self.make_interface(port_name, False)
258 br = self._find_row_by_name('Bridge', bridge_name)
260 raise OVSDBException('Bad bridge name %s' % bridge_name)
262 port = txn.insert(self.get_table('Port'))
263 port.name = port_name
266 ports = getattr(br, 'ports', [])
270 port.verify('interfaces')
271 ifaces = getattr(port, 'interfaces', [])
272 ifaces.append(iface_row)
273 port.interfaces = ifaces
275 def try_again(db_entity):
276 db_entity.make_port(port_name, bridge_name)
278 txn.add_comment("ovs-tcpdump: user=%s,create_port=%s"
279 % (username(), port_name))
280 status = self._complete_txn(try_again)
282 raise OVSDBException('Unable to create Port %s: %s' %
283 (port_name, txn.get_error()))
284 result = txn.get_insert_uuid(port.uuid)
288 def bridge_mirror(self, intf_name, mirror_intf_name, br_name):
290 txn = self._start_txn()
291 mirror = txn.insert(self.get_table('Mirror'))
292 mirror.name = 'm_%s' % intf_name
294 mirror.select_all = False
296 mirrored_port = self._find_row_by_name('Port', intf_name)
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
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
308 output_port = self._find_row_by_name('Port', mirror_intf_name)
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
315 br = self._find_row_by_name('Bridge', br_name)
317 mirrors = getattr(br, 'mirrors', [])
318 mirrors.append(mirror.uuid)
321 def try_again(db_entity):
322 db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name)
324 txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s"
325 % (username(), mirror.name))
326 status = self._complete_txn(try_again)
328 raise OVSDBException('Unable to create Mirror %s: %s' %
329 (mirror_intf_name, txn.get_error()))
330 result = txn.get_insert_uuid(mirror.uuid)
335 def argv_tuples(lst):
336 cur, nxt = iter(lst), iter(lst)
341 yield next(cur), next(nxt, None)
342 except StopIteration:
347 db_sock = 'unix:@RUNDIR@/db.sock'
352 mirror_interface = None
355 for cur, nxt in argv_tuples(sys.argv[1:]):
359 if cur in ['-h', '--help']:
361 elif cur in ['-V', '--version']:
362 print("ovs-tcpdump (Open vSwitch) @VERSION@")
364 elif cur in ['--db-sock']:
368 elif cur in ['--dump-cmd']:
372 elif cur in ['-i', '--interface']:
376 elif cur in ['--mirror-to']:
377 mirror_interface = nxt
382 if interface is None:
383 print("Error: must at least specify an interface with '-i' option")
386 if '-l' not in tcpdargs:
387 tcpdargs.insert(0, '-l')
389 if '-vv' in tcpdargs:
390 print("TCPDUMP Args: %s" % ' '.join(tcpdargs))
392 ovsdb = OVSDB(db_sock)
393 mirror_interface = mirror_interface or "mi%s" % interface
395 if sys.platform in _make_taps and \
396 mirror_interface not in netifaces.interfaces():
397 _make_taps[sys.platform](mirror_interface)
399 if mirror_interface not in netifaces.interfaces():
400 print("ERROR: Please create an interface called `%s`" %
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"))
407 if not ovsdb.port_exists(interface):
408 print("ERROR: Port %s does not exist." % interface)
410 if ovsdb.port_exists(mirror_interface):
411 print("ERROR: Mirror port (%s) exists for port %s." %
412 (mirror_interface, interface))
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))
421 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
426 pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
428 while pipes.poll() is None:
429 data = pipes.stdout.readline()
431 raise KeyboardInterrupt
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:
439 ovsdb.destroy_mirror('m%s' % interface, ovsdb.port_bridge(interface))
440 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
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,
451 if __name__ == '__main__':