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 --span If specified, mirror all ports (optional)
115 """ % {'prog': sys.argv[0]})
119 class OVSDBException(Exception):
125 def wait_for_db_change(idl):
126 seq = idl.change_seqno
127 stop = time.time() + 10
128 while idl.change_seqno == seq and not idl.run():
132 if time.time() >= stop:
133 raise Exception('Retry Timeout')
135 def __init__(self, db_sock):
136 self._db_sock = db_sock
138 schema = self._get_schema()
139 schema.register_all()
140 self._idl_conn = idl.Idl(db_sock, schema)
141 OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB
143 def _get_schema(self):
144 error, strm = Stream.open_block(Stream.open(self._db_sock))
146 raise Exception("Unable to connect to %s" % self._db_sock)
147 rpc = jsonrpc.Connection(strm)
148 req = jsonrpc.Message.create_request('get_schema', ['Open_vSwitch'])
149 error, resp = rpc.transact_block(req)
152 if error or resp.error:
153 raise Exception('Unable to retrieve schema.')
154 return idl.SchemaHelper(None, resp.result)
156 def get_table(self, table_name):
157 return self._idl_conn.tables[table_name]
159 def _start_txn(self):
160 if self._txn is not None:
161 raise OVSDBException("ERROR: A transaction was started already")
162 self._idl_conn.change_seqno += 1
163 self._txn = idl.Transaction(self._idl_conn)
166 def _complete_txn(self, try_again_fn):
167 if self._txn is None:
168 raise OVSDBException("ERROR: Not in a transaction")
169 status = self._txn.commit_block()
170 if status is idl.Transaction.TRY_AGAIN:
171 if self._idl_conn._session.rpc.status != 0:
172 self._idl_conn.force_reconnect()
173 OVSDB.wait_for_db_change(self._idl_conn)
174 return try_again_fn(self)
175 elif status is idl.Transaction.ERROR:
178 def _find_row(self, table_name, find):
180 (row for row in self.get_table(table_name).rows.values()
183 def _find_row_by_name(self, table_name, value):
184 return self._find_row(table_name, lambda row: row.name == value)
186 def port_exists(self, port_name):
187 return bool(self._find_row_by_name('Port', port_name))
189 def port_bridge(self, port_name):
191 port = self._find_row_by_name('Port', port_name)
192 br = self._find_row('Bridge', lambda x: port in x.ports)
195 raise OVSDBException('Unable to find port %s bridge' % port_name)
197 def interface_mtu(self, intf_name):
199 intf = self._find_row_by_name('Interface', intf_name)
204 def interface_exists(self, intf_name):
205 return bool(self._find_row_by_name('Interface', intf_name))
207 def mirror_exists(self, mirror_name):
208 return bool(self._find_row_by_name('Mirror', mirror_name))
210 def interface_uuid(self, intf_name):
211 row = self._find_row_by_name('Interface', intf_name)
214 raise OVSDBException('No such interface: %s' % intf_name)
216 def make_interface(self, intf_name, execute_transaction=True):
217 if self.interface_exists(intf_name):
218 print("INFO: Interface exists.")
219 return self.interface_uuid(intf_name)
221 txn = self._start_txn()
222 tmp_row = txn.insert(self.get_table('Interface'))
223 tmp_row.name = intf_name
225 def try_again(db_entity):
226 db_entity.make_interface(intf_name)
228 if not execute_transaction:
231 txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s"
232 % (username(), intf_name))
233 status = self._complete_txn(try_again)
235 raise OVSDBException('Unable to create Interface %s: %s' %
236 (intf_name, txn.get_error()))
237 result = txn.get_insert_uuid(tmp_row.uuid)
241 def destroy_port(self, port_name, bridge_name):
242 if not self.interface_exists(port_name):
244 txn = self._start_txn()
245 br = self._find_row_by_name('Bridge', bridge_name)
246 ports = [port for port in br.ports if port.name != port_name]
249 def try_again(db_entity):
250 db_entity.destroy_port(port_name)
252 txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s"
253 % (username(), port_name))
254 status = self._complete_txn(try_again)
256 raise OVSDBException('unable to delete Port %s: %s' %
257 (port_name, txn.get_error()))
260 def destroy_mirror(self, intf_name, bridge_name):
261 mirror_name = 'm_%s' % intf_name
262 if not self.mirror_exists(mirror_name):
264 txn = self._start_txn()
265 mirror_row = self._find_row_by_name('Mirror', mirror_name)
266 br = self._find_row_by_name('Bridge', bridge_name)
267 mirrors = [mirror for mirror in br.mirrors
268 if mirror.uuid != mirror_row.uuid]
271 def try_again(db_entity):
272 db_entity.destroy_mirror(mirror_name, bridge_name)
274 txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s"
275 % (username(), mirror_name))
276 status = self._complete_txn(try_again)
278 raise OVSDBException('Unable to delete Mirror %s: %s' %
279 (mirror_name, txn.get_error()))
282 def make_port(self, port_name, bridge_name):
283 iface_row = self.make_interface(port_name, False)
286 br = self._find_row_by_name('Bridge', bridge_name)
288 raise OVSDBException('Bad bridge name %s' % bridge_name)
290 port = txn.insert(self.get_table('Port'))
291 port.name = port_name
294 ports = getattr(br, 'ports', [])
298 port.verify('interfaces')
299 ifaces = getattr(port, 'interfaces', [])
300 ifaces.append(iface_row)
301 port.interfaces = ifaces
303 def try_again(db_entity):
304 db_entity.make_port(port_name, bridge_name)
306 txn.add_comment("ovs-tcpdump: user=%s,create_port=%s"
307 % (username(), port_name))
308 status = self._complete_txn(try_again)
310 raise OVSDBException('Unable to create Port %s: %s' %
311 (port_name, txn.get_error()))
312 result = txn.get_insert_uuid(port.uuid)
316 def bridge_mirror(self, intf_name, mirror_intf_name, br_name,
317 mirror_select_all=False):
319 txn = self._start_txn()
320 mirror = txn.insert(self.get_table('Mirror'))
321 mirror.name = 'm_%s' % intf_name
323 mirror.select_all = mirror_select_all
325 mirrored_port = self._find_row_by_name('Port', intf_name)
327 mirror.verify('select_dst_port')
328 dst_port = getattr(mirror, 'select_dst_port', [])
329 dst_port.append(mirrored_port)
330 mirror.select_dst_port = dst_port
332 mirror.verify('select_src_port')
333 src_port = getattr(mirror, 'select_src_port', [])
334 src_port.append(mirrored_port)
335 mirror.select_src_port = src_port
337 output_port = self._find_row_by_name('Port', mirror_intf_name)
339 mirror.verify('output_port')
340 out_port = getattr(mirror, 'output_port', [])
341 out_port.append(output_port.uuid)
342 mirror.output_port = out_port
344 br = self._find_row_by_name('Bridge', br_name)
346 mirrors = getattr(br, 'mirrors', [])
347 mirrors.append(mirror.uuid)
350 def try_again(db_entity):
351 db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name)
353 txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s"
354 % (username(), mirror.name))
355 status = self._complete_txn(try_again)
357 raise OVSDBException('Unable to create Mirror %s: %s' %
358 (mirror_intf_name, txn.get_error()))
359 result = txn.get_insert_uuid(mirror.uuid)
364 def argv_tuples(lst):
365 cur, nxt = iter(lst), iter(lst)
370 yield next(cur), next(nxt, None)
371 except StopIteration:
375 def py_which(executable):
376 return any(os.access(os.path.join(path, executable), os.X_OK)
377 for path in os.environ["PATH"].split(os.pathsep))
381 db_sock = 'unix:@RUNDIR@/db.sock'
386 mirror_interface = None
387 mirror_select_all = False
390 for cur, nxt in argv_tuples(sys.argv[1:]):
394 if cur in ['-h', '--help']:
396 elif cur in ['-V', '--version']:
397 print("ovs-tcpdump (Open vSwitch) @VERSION@")
399 elif cur in ['--db-sock']:
403 elif cur in ['--dump-cmd']:
407 elif cur in ['-i', '--interface']:
411 elif cur in ['--mirror-to']:
412 mirror_interface = nxt
415 elif cur in ['--span']:
416 mirror_select_all = True
420 if interface is None:
421 print("Error: must at least specify an interface with '-i' option")
424 if not py_which(dump_cmd):
425 print("Error: unable to execute '%s' (check PATH)" % dump_cmd)
428 if '-l' not in tcpdargs:
429 tcpdargs.insert(0, '-l')
431 if '-vv' in tcpdargs:
432 print("TCPDUMP Args: %s" % ' '.join(tcpdargs))
434 ovsdb = OVSDB(db_sock)
435 if mirror_interface is None:
436 mirror_interface = "mi%s" % interface
437 if sys.platform in _make_mirror_name:
438 mirror_interface = _make_mirror_name[sys.platform](interface)
440 if sys.platform in _make_taps and \
441 mirror_interface not in netifaces.interfaces():
442 _make_taps[sys.platform](mirror_interface,
443 ovsdb.interface_mtu(interface))
445 if mirror_interface not in netifaces.interfaces():
446 print("ERROR: Please create an interface called `%s`" %
448 print("See your OS guide for how to do this.")
449 print("Ex: ip link add %s type veth peer name %s" %
450 (mirror_interface, mirror_interface + "2"))
453 if not ovsdb.port_exists(interface):
454 print("ERROR: Port %s does not exist." % interface)
456 if ovsdb.port_exists(mirror_interface):
457 print("ERROR: Mirror port (%s) exists for port %s." %
458 (mirror_interface, interface))
461 ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface))
462 ovsdb.bridge_mirror(interface, mirror_interface,
463 ovsdb.port_bridge(interface),
465 except OVSDBException as oe:
466 print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
468 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
473 pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
475 while pipes.poll() is None:
476 data = pipes.stdout.readline().strip(b'\n')
478 raise KeyboardInterrupt
479 print(data.decode('utf-8'))
480 raise KeyboardInterrupt
481 except KeyboardInterrupt:
482 if pipes.poll() is None:
485 ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
486 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
488 print("Unable to tear down the ports and mirrors.")
489 print("Please use ovs-vsctl to remove the ports and mirrors created.")
490 print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
497 if __name__ == '__main__':