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.
29 from ovs.db import idl
30 from ovs import jsonrpc
31 from ovs.poller import Poller
32 from ovs.stream import Stream
34 print("ERROR: Please install the correct Open vSwitch python support")
35 print(" libraries (version @VERSION@).")
36 print(" Alternatively, check that your PYTHONPATH is pointing to")
37 print(" the correct location.")
44 def _doexec(*args, **kwargs):
45 """Executes an application and returns a set of pipes"""
47 shell = len(args) == 1
48 proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell,
53 def _install_tap_linux(tap_name, mtu_value=None):
54 """Uses /dev/net/tun to create a tap device"""
59 TUNSETIFF = 0x400454CA # This is derived by printf() of TUNSETIFF
60 TUNSETOWNER = TUNSETIFF + 2
62 tapdev_fd = open('/dev/net/tun', 'rw')
63 ifr = struct.pack('16sH', tap_name, IFF_TAP | IFF_NO_PI)
64 fcntl.ioctl(tapdev_fd, TUNSETIFF, ifr)
65 fcntl.ioctl(tapdev_fd, TUNSETOWNER, os.getegid())
67 time.sleep(1) # required to give the new device settling time
68 if mtu_value is not None:
70 *(['ip', 'link', 'set', 'dev', str(tap_name), 'mtu',
75 *(['ip', 'link', 'set', 'dev', str(tap_name), 'up']))
79 _make_taps['linux'] = _install_tap_linux
80 _make_taps['linux2'] = _install_tap_linux
84 return pwd.getpwuid(os.getuid())[0]
89 %(prog)s: Open vSwitch tcpdump helper.
90 usage: %(prog)s -i interface [TCPDUMP OPTIONS]
91 where TCPDUMP OPTIONS represents the options normally passed to tcpdump.
93 The following options are available:
94 -h, --help display this help message
95 -V, --version display version information
96 --db-sock A connection string to reach the Open vSwitch
98 Default 'unix:@RUNDIR@/db.sock'
99 --dump-cmd Command to use for tcpdump (default 'tcpdump')
100 -i, --interface Open vSwitch interface to mirror and tcpdump
101 --mirror-to The name for the mirror port to use (optional)
102 Default 'miINTERFACE'
103 """ % {'prog': sys.argv[0]})
107 class OVSDBException(Exception):
113 def wait_for_db_change(idl):
114 seq = idl.change_seqno
115 stop = time.time() + 10
116 while idl.change_seqno == seq and not idl.run():
120 if time.time() >= stop:
121 raise Exception('Retry Timeout')
123 def __init__(self, db_sock):
124 self._db_sock = db_sock
126 schema = self._get_schema()
127 schema.register_all()
128 self._idl_conn = idl.Idl(db_sock, schema)
129 OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB
131 def _get_schema(self):
132 error, strm = Stream.open_block(Stream.open(self._db_sock))
134 raise Exception("Unable to connect to %s" % self._db_sock)
135 rpc = jsonrpc.Connection(strm)
136 req = jsonrpc.Message.create_request('get_schema', ['Open_vSwitch'])
137 error, resp = rpc.transact_block(req)
140 if error or resp.error:
141 raise Exception('Unable to retrieve schema.')
142 return idl.SchemaHelper(None, resp.result)
144 def get_table(self, table_name):
145 return self._idl_conn.tables[table_name]
147 def _start_txn(self):
148 if self._txn is not None:
149 raise OVSDBException("ERROR: A transaction was started already")
150 self._idl_conn.change_seqno += 1
151 self._txn = idl.Transaction(self._idl_conn)
154 def _complete_txn(self, try_again_fn):
155 if self._txn is None:
156 raise OVSDBException("ERROR: Not in a transaction")
157 status = self._txn.commit_block()
158 if status is idl.Transaction.TRY_AGAIN:
159 if self._idl_conn._session.rpc.status != 0:
160 self._idl_conn.force_reconnect()
161 OVSDB.wait_for_db_change(self._idl_conn)
162 return try_again_fn(self)
163 elif status is idl.Transaction.ERROR:
166 def _find_row(self, table_name, find):
168 (row for row in self.get_table(table_name).rows.values()
171 def _find_row_by_name(self, table_name, value):
172 return self._find_row(table_name, lambda row: row.name == value)
174 def port_exists(self, port_name):
175 return bool(self._find_row_by_name('Port', port_name))
177 def port_bridge(self, port_name):
179 port = self._find_row_by_name('Port', port_name)
180 br = self._find_row('Bridge', lambda x: port in x.ports)
183 raise OVSDBException('Unable to find port %s bridge' % port_name)
185 def interface_mtu(self, intf_name):
187 intf = self._find_row_by_name('Interface', intf_name)
192 def interface_exists(self, intf_name):
193 return bool(self._find_row_by_name('Interface', intf_name))
195 def mirror_exists(self, mirror_name):
196 return bool(self._find_row_by_name('Mirror', mirror_name))
198 def interface_uuid(self, intf_name):
199 row = self._find_row_by_name('Interface', intf_name)
202 raise OVSDBException('No such interface: %s' % intf_name)
204 def make_interface(self, intf_name, execute_transaction=True):
205 if self.interface_exists(intf_name):
206 print("INFO: Interface exists.")
207 return self.interface_uuid(intf_name)
209 txn = self._start_txn()
210 tmp_row = txn.insert(self.get_table('Interface'))
211 tmp_row.name = intf_name
213 def try_again(db_entity):
214 db_entity.make_interface(intf_name)
216 if not execute_transaction:
219 txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s"
220 % (username(), intf_name))
221 status = self._complete_txn(try_again)
223 raise OVSDBException('Unable to create Interface %s: %s' %
224 (intf_name, txn.get_error()))
225 result = txn.get_insert_uuid(tmp_row.uuid)
229 def destroy_port(self, port_name, bridge_name):
230 if not self.interface_exists(port_name):
232 txn = self._start_txn()
233 br = self._find_row_by_name('Bridge', bridge_name)
234 ports = [port for port in br.ports if port.name != port_name]
237 def try_again(db_entity):
238 db_entity.destroy_port(port_name)
240 txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s"
241 % (username(), port_name))
242 status = self._complete_txn(try_again)
244 raise OVSDBException('unable to delete Port %s: %s' %
245 (port_name, txn.get_error()))
248 def destroy_mirror(self, intf_name, bridge_name):
249 mirror_name = 'm_%s' % intf_name
250 if not self.mirror_exists(mirror_name):
252 txn = self._start_txn()
253 mirror_row = self._find_row_by_name('Mirror', mirror_name)
254 br = self._find_row_by_name('Bridge', bridge_name)
255 mirrors = [mirror for mirror in br.mirrors
256 if mirror.uuid != mirror_row.uuid]
259 def try_again(db_entity):
260 db_entity.destroy_mirror(mirror_name, bridge_name)
262 txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s"
263 % (username(), mirror_name))
264 status = self._complete_txn(try_again)
266 raise OVSDBException('Unable to delete Mirror %s: %s' %
267 (mirror_name, txn.get_error()))
270 def make_port(self, port_name, bridge_name):
271 iface_row = self.make_interface(port_name, False)
274 br = self._find_row_by_name('Bridge', bridge_name)
276 raise OVSDBException('Bad bridge name %s' % bridge_name)
278 port = txn.insert(self.get_table('Port'))
279 port.name = port_name
282 ports = getattr(br, 'ports', [])
286 port.verify('interfaces')
287 ifaces = getattr(port, 'interfaces', [])
288 ifaces.append(iface_row)
289 port.interfaces = ifaces
291 def try_again(db_entity):
292 db_entity.make_port(port_name, bridge_name)
294 txn.add_comment("ovs-tcpdump: user=%s,create_port=%s"
295 % (username(), port_name))
296 status = self._complete_txn(try_again)
298 raise OVSDBException('Unable to create Port %s: %s' %
299 (port_name, txn.get_error()))
300 result = txn.get_insert_uuid(port.uuid)
304 def bridge_mirror(self, intf_name, mirror_intf_name, br_name):
306 txn = self._start_txn()
307 mirror = txn.insert(self.get_table('Mirror'))
308 mirror.name = 'm_%s' % intf_name
310 mirror.select_all = False
312 mirrored_port = self._find_row_by_name('Port', intf_name)
314 mirror.verify('select_dst_port')
315 dst_port = getattr(mirror, 'select_dst_port', [])
316 dst_port.append(mirrored_port)
317 mirror.select_dst_port = dst_port
319 mirror.verify('select_src_port')
320 src_port = getattr(mirror, 'select_src_port', [])
321 src_port.append(mirrored_port)
322 mirror.select_src_port = src_port
324 output_port = self._find_row_by_name('Port', mirror_intf_name)
326 mirror.verify('output_port')
327 out_port = getattr(mirror, 'output_port', [])
328 out_port.append(output_port.uuid)
329 mirror.output_port = out_port
331 br = self._find_row_by_name('Bridge', br_name)
333 mirrors = getattr(br, 'mirrors', [])
334 mirrors.append(mirror.uuid)
337 def try_again(db_entity):
338 db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name)
340 txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s"
341 % (username(), mirror.name))
342 status = self._complete_txn(try_again)
344 raise OVSDBException('Unable to create Mirror %s: %s' %
345 (mirror_intf_name, txn.get_error()))
346 result = txn.get_insert_uuid(mirror.uuid)
351 def argv_tuples(lst):
352 cur, nxt = iter(lst), iter(lst)
357 yield next(cur), next(nxt, None)
358 except StopIteration:
363 db_sock = 'unix:@RUNDIR@/db.sock'
368 mirror_interface = None
371 for cur, nxt in argv_tuples(sys.argv[1:]):
375 if cur in ['-h', '--help']:
377 elif cur in ['-V', '--version']:
378 print("ovs-tcpdump (Open vSwitch) @VERSION@")
380 elif cur in ['--db-sock']:
384 elif cur in ['--dump-cmd']:
388 elif cur in ['-i', '--interface']:
392 elif cur in ['--mirror-to']:
393 mirror_interface = nxt
398 if interface is None:
399 print("Error: must at least specify an interface with '-i' option")
402 if '-l' not in tcpdargs:
403 tcpdargs.insert(0, '-l')
405 if '-vv' in tcpdargs:
406 print("TCPDUMP Args: %s" % ' '.join(tcpdargs))
408 ovsdb = OVSDB(db_sock)
409 mirror_interface = mirror_interface or "mi%s" % interface
411 if sys.platform in _make_taps and \
412 mirror_interface not in netifaces.interfaces():
413 _make_taps[sys.platform](mirror_interface,
414 ovsdb.interface_mtu(interface))
416 if mirror_interface not in netifaces.interfaces():
417 print("ERROR: Please create an interface called `%s`" %
419 print("See your OS guide for how to do this.")
420 print("Ex: ip link add %s type veth peer name %s" %
421 (mirror_interface, mirror_interface + "2"))
424 if not ovsdb.port_exists(interface):
425 print("ERROR: Port %s does not exist." % interface)
427 if ovsdb.port_exists(mirror_interface):
428 print("ERROR: Mirror port (%s) exists for port %s." %
429 (mirror_interface, interface))
432 ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface))
433 ovsdb.bridge_mirror(interface, mirror_interface,
434 ovsdb.port_bridge(interface))
435 except OVSDBException as oe:
436 print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
438 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
443 pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
445 while pipes.poll() is None:
446 data = pipes.stdout.readline().strip('\n')
448 raise KeyboardInterrupt
450 raise KeyboardInterrupt
451 except KeyboardInterrupt:
452 if pipes.poll() is None:
455 ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
456 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
458 print("Unable to tear down the ports and mirrors.")
459 print("Please use ovs-vsctl to remove the ports and mirrors created.")
460 print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
467 if __name__ == '__main__':