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
28 from netifaces import interfaces
30 if sys.platform in ['linux', 'linux2']:
33 with open("/proc/net/dev", "r") as f_netdev:
37 devices.append(line.split(":")[0].strip())
40 print("ERROR: Please install netifaces Python library.")
44 from ovs.db import idl
45 from ovs import jsonrpc
46 from ovs.poller import Poller
47 from ovs.stream import Stream
49 print("ERROR: Please install the correct Open vSwitch python support")
50 print(" libraries (version @VERSION@).")
51 print(" Alternatively, check that your PYTHONPATH is pointing to")
52 print(" the correct location.")
57 _make_mirror_name = {}
58 IFNAMSIZ_LINUX = 15 # this is the max name size, excluding the null byte.
61 def _doexec(*args, **kwargs):
62 """Executes an application and returns a set of pipes"""
64 shell = len(args) == 1
65 proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell,
70 def _install_tap_linux(tap_name, mtu_value=None):
71 """Uses /dev/net/tun to create a tap device"""
76 TUNSETIFF = 0x400454CA # This is derived by printf() of TUNSETIFF
77 TUNSETOWNER = TUNSETIFF + 2
79 tapdev_fd = os.open('/dev/net/tun', os.O_RDWR)
80 ifr = struct.pack('16sH', tap_name.encode('utf8'), IFF_TAP | IFF_NO_PI)
81 fcntl.ioctl(tapdev_fd, TUNSETIFF, ifr)
82 fcntl.ioctl(tapdev_fd, TUNSETOWNER, os.getegid())
84 time.sleep(1) # required to give the new device settling time
85 if mtu_value is not None:
87 *(['ip', 'link', 'set', 'dev', str(tap_name), 'mtu',
92 *(['ip', 'link', 'set', 'dev', str(tap_name), 'up']))
96 def _make_linux_mirror_name(interface_name):
97 if len(interface_name) > IFNAMSIZ_LINUX - 2:
98 return "ovsmi%06d" % randint(1, 999999)
99 return "mi%s" % interface_name
102 _make_taps['linux'] = _install_tap_linux
103 _make_taps['linux2'] = _install_tap_linux
104 _make_mirror_name['linux'] = _make_linux_mirror_name
105 _make_mirror_name['linux2'] = _make_linux_mirror_name
109 return pwd.getpwuid(os.getuid())[0]
114 %(prog)s: Open vSwitch tcpdump helper.
115 usage: %(prog)s -i interface [TCPDUMP OPTIONS]
116 where TCPDUMP OPTIONS represents the options normally passed to tcpdump.
118 The following options are available:
119 -h, --help display this help message
120 -V, --version display version information
121 --db-sock A connection string to reach the Open vSwitch
123 Default 'unix:@RUNDIR@/db.sock'
124 --dump-cmd Command to use for tcpdump (default 'tcpdump')
125 -i, --interface Open vSwitch interface to mirror and tcpdump
126 --mirror-to The name for the mirror port to use (optional)
127 Default 'miINTERFACE'
128 --span If specified, mirror all ports (optional)
129 """ % {'prog': sys.argv[0]})
133 class OVSDBException(Exception):
139 def wait_for_db_change(idl):
140 seq = idl.change_seqno
141 stop = time.time() + 10
142 while idl.change_seqno == seq and not idl.run():
146 if time.time() >= stop:
147 raise Exception('Retry Timeout')
149 def __init__(self, db_sock):
150 self._db_sock = db_sock
152 schema = self._get_schema()
153 schema.register_all()
154 self._idl_conn = idl.Idl(db_sock, schema)
155 OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB
157 def _get_schema(self):
158 error, strm = Stream.open_block(Stream.open(self._db_sock))
160 raise Exception("Unable to connect to %s" % self._db_sock)
161 rpc = jsonrpc.Connection(strm)
162 req = jsonrpc.Message.create_request('get_schema', ['Open_vSwitch'])
163 error, resp = rpc.transact_block(req)
166 if error or resp.error:
167 raise Exception('Unable to retrieve schema.')
168 return idl.SchemaHelper(None, resp.result)
170 def get_table(self, table_name):
171 return self._idl_conn.tables[table_name]
173 def _start_txn(self):
174 if self._txn is not None:
175 raise OVSDBException("ERROR: A transaction was started already")
176 self._idl_conn.change_seqno += 1
177 self._txn = idl.Transaction(self._idl_conn)
180 def _complete_txn(self, try_again_fn):
181 if self._txn is None:
182 raise OVSDBException("ERROR: Not in a transaction")
183 status = self._txn.commit_block()
184 if status is idl.Transaction.TRY_AGAIN:
185 if self._idl_conn._session.rpc.status != 0:
186 self._idl_conn.force_reconnect()
187 OVSDB.wait_for_db_change(self._idl_conn)
188 return try_again_fn(self)
189 elif status is idl.Transaction.ERROR:
192 def _find_row(self, table_name, find):
194 (row for row in self.get_table(table_name).rows.values()
197 def _find_row_by_name(self, table_name, value):
198 return self._find_row(table_name, lambda row: row.name == value)
200 def port_exists(self, port_name):
201 return bool(self._find_row_by_name('Port', port_name))
203 def port_bridge(self, port_name):
205 port = self._find_row_by_name('Port', port_name)
206 br = self._find_row('Bridge', lambda x: port in x.ports)
209 raise OVSDBException('Unable to find port %s bridge' % port_name)
211 def interface_mtu(self, intf_name):
213 intf = self._find_row_by_name('Interface', intf_name)
218 def interface_exists(self, intf_name):
219 return bool(self._find_row_by_name('Interface', intf_name))
221 def mirror_exists(self, mirror_name):
222 return bool(self._find_row_by_name('Mirror', mirror_name))
224 def interface_uuid(self, intf_name):
225 row = self._find_row_by_name('Interface', intf_name)
228 raise OVSDBException('No such interface: %s' % intf_name)
230 def make_interface(self, intf_name, execute_transaction=True):
231 if self.interface_exists(intf_name):
232 print("INFO: Interface exists.")
233 return self.interface_uuid(intf_name)
235 txn = self._start_txn()
236 tmp_row = txn.insert(self.get_table('Interface'))
237 tmp_row.name = intf_name
239 def try_again(db_entity):
240 db_entity.make_interface(intf_name)
242 if not execute_transaction:
245 txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s"
246 % (username(), intf_name))
247 status = self._complete_txn(try_again)
249 raise OVSDBException('Unable to create Interface %s: %s' %
250 (intf_name, txn.get_error()))
251 result = txn.get_insert_uuid(tmp_row.uuid)
255 def destroy_port(self, port_name, bridge_name):
256 if not self.interface_exists(port_name):
258 txn = self._start_txn()
259 br = self._find_row_by_name('Bridge', bridge_name)
260 ports = [port for port in br.ports if port.name != port_name]
263 def try_again(db_entity):
264 db_entity.destroy_port(port_name)
266 txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s"
267 % (username(), port_name))
268 status = self._complete_txn(try_again)
270 raise OVSDBException('unable to delete Port %s: %s' %
271 (port_name, txn.get_error()))
274 def destroy_mirror(self, intf_name, bridge_name):
275 mirror_name = 'm_%s' % intf_name
276 if not self.mirror_exists(mirror_name):
278 txn = self._start_txn()
279 mirror_row = self._find_row_by_name('Mirror', mirror_name)
280 br = self._find_row_by_name('Bridge', bridge_name)
281 mirrors = [mirror for mirror in br.mirrors
282 if mirror.uuid != mirror_row.uuid]
285 def try_again(db_entity):
286 db_entity.destroy_mirror(mirror_name, bridge_name)
288 txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s"
289 % (username(), mirror_name))
290 status = self._complete_txn(try_again)
292 raise OVSDBException('Unable to delete Mirror %s: %s' %
293 (mirror_name, txn.get_error()))
296 def make_port(self, port_name, bridge_name):
297 iface_row = self.make_interface(port_name, False)
300 br = self._find_row_by_name('Bridge', bridge_name)
302 raise OVSDBException('Bad bridge name %s' % bridge_name)
304 port = txn.insert(self.get_table('Port'))
305 port.name = port_name
308 ports = getattr(br, 'ports', [])
312 port.verify('interfaces')
313 ifaces = getattr(port, 'interfaces', [])
314 ifaces.append(iface_row)
315 port.interfaces = ifaces
317 def try_again(db_entity):
318 db_entity.make_port(port_name, bridge_name)
320 txn.add_comment("ovs-tcpdump: user=%s,create_port=%s"
321 % (username(), port_name))
322 status = self._complete_txn(try_again)
324 raise OVSDBException('Unable to create Port %s: %s' %
325 (port_name, txn.get_error()))
326 result = txn.get_insert_uuid(port.uuid)
330 def bridge_mirror(self, intf_name, mirror_intf_name, br_name,
331 mirror_select_all=False):
333 txn = self._start_txn()
334 mirror = txn.insert(self.get_table('Mirror'))
335 mirror.name = 'm_%s' % intf_name
337 mirror.select_all = mirror_select_all
339 mirrored_port = self._find_row_by_name('Port', intf_name)
341 mirror.verify('select_dst_port')
342 dst_port = getattr(mirror, 'select_dst_port', [])
343 dst_port.append(mirrored_port)
344 mirror.select_dst_port = dst_port
346 mirror.verify('select_src_port')
347 src_port = getattr(mirror, 'select_src_port', [])
348 src_port.append(mirrored_port)
349 mirror.select_src_port = src_port
351 output_port = self._find_row_by_name('Port', mirror_intf_name)
353 mirror.verify('output_port')
354 out_port = getattr(mirror, 'output_port', [])
355 out_port.append(output_port.uuid)
356 mirror.output_port = out_port
358 br = self._find_row_by_name('Bridge', br_name)
360 mirrors = getattr(br, 'mirrors', [])
361 mirrors.append(mirror.uuid)
364 def try_again(db_entity):
365 db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name)
367 txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s"
368 % (username(), mirror.name))
369 status = self._complete_txn(try_again)
371 raise OVSDBException('Unable to create Mirror %s: %s' %
372 (mirror_intf_name, txn.get_error()))
373 result = txn.get_insert_uuid(mirror.uuid)
378 def argv_tuples(lst):
379 cur, nxt = iter(lst), iter(lst)
384 yield next(cur), next(nxt, None)
385 except StopIteration:
389 def py_which(executable):
390 return any(os.access(os.path.join(path, executable), os.X_OK)
391 for path in os.environ["PATH"].split(os.pathsep))
395 db_sock = 'unix:@RUNDIR@/db.sock'
400 mirror_interface = None
401 mirror_select_all = False
404 for cur, nxt in argv_tuples(sys.argv[1:]):
408 if cur in ['-h', '--help']:
410 elif cur in ['-V', '--version']:
411 print("ovs-tcpdump (Open vSwitch) @VERSION@")
413 elif cur in ['--db-sock']:
417 elif cur in ['--dump-cmd']:
421 elif cur in ['-i', '--interface']:
425 elif cur in ['--mirror-to']:
426 mirror_interface = nxt
429 elif cur in ['--span']:
430 mirror_select_all = True
434 if interface is None:
435 print("Error: must at least specify an interface with '-i' option")
438 if not py_which(dump_cmd):
439 print("Error: unable to execute '%s' (check PATH)" % dump_cmd)
442 if '-l' not in tcpdargs:
443 tcpdargs.insert(0, '-l')
445 if '-vv' in tcpdargs:
446 print("TCPDUMP Args: %s" % ' '.join(tcpdargs))
448 ovsdb = OVSDB(db_sock)
449 if mirror_interface is None:
450 mirror_interface = "mi%s" % interface
451 if sys.platform in _make_mirror_name:
452 mirror_interface = _make_mirror_name[sys.platform](interface)
454 if sys.platform in _make_taps and \
455 mirror_interface not in interfaces():
456 _make_taps[sys.platform](mirror_interface,
457 ovsdb.interface_mtu(interface))
459 if mirror_interface not in interfaces():
460 print("ERROR: Please create an interface called `%s`" %
462 print("See your OS guide for how to do this.")
463 print("Ex: ip link add %s type veth peer name %s" %
464 (mirror_interface, mirror_interface + "2"))
467 if not ovsdb.port_exists(interface):
468 print("ERROR: Port %s does not exist." % interface)
470 if ovsdb.port_exists(mirror_interface):
471 print("ERROR: Mirror port (%s) exists for port %s." %
472 (mirror_interface, interface))
475 ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface))
476 ovsdb.bridge_mirror(interface, mirror_interface,
477 ovsdb.port_bridge(interface),
479 except OVSDBException as oe:
480 print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
482 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
487 pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
489 while pipes.poll() is None:
490 data = pipes.stdout.readline().strip(b'\n')
492 raise KeyboardInterrupt
493 print(data.decode('utf-8'))
494 raise KeyboardInterrupt
495 except KeyboardInterrupt:
496 if pipes.poll() is None:
499 ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
500 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
502 print("Unable to tear down the ports and mirrors.")
503 print("Please use ovs-vsctl to remove the ports and mirrors created.")
504 print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
511 if __name__ == '__main__':