]> git.proxmox.com Git - mirror_ovs.git/blob - utilities/ovs-tcpdump.in
ofp-table: Always format the table number in table features.
[mirror_ovs.git] / utilities / ovs-tcpdump.in
1 #! @PYTHON@
2 #
3 # Copyright (c) 2016 Red Hat, Inc.
4 #
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:
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 import fcntl
18
19 import os
20 import pwd
21 from random import randint
22 import struct
23 import subprocess
24 import sys
25 import time
26
27 import netifaces
28
29 try:
30 from ovs.db import idl
31 from ovs import jsonrpc
32 from ovs.poller import Poller
33 from ovs.stream import Stream
34 except Exception:
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.")
39 sys.exit(1)
40
41 tapdev_fd = None
42 _make_taps = {}
43 _make_mirror_name = {}
44 IFNAMSIZ_LINUX = 15 # this is the max name size, excluding the null byte.
45
46
47 def _doexec(*args, **kwargs):
48 """Executes an application and returns a set of pipes"""
49
50 shell = len(args) == 1
51 proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell,
52 bufsize=0)
53 return proc
54
55
56 def _install_tap_linux(tap_name, mtu_value=None):
57 """Uses /dev/net/tun to create a tap device"""
58 global tapdev_fd
59
60 IFF_TAP = 0x0002
61 IFF_NO_PI = 0x1000
62 TUNSETIFF = 0x400454CA # This is derived by printf() of TUNSETIFF
63 TUNSETOWNER = TUNSETIFF + 2
64
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())
69
70 time.sleep(1) # required to give the new device settling time
71 if mtu_value is not None:
72 pipe = _doexec(
73 *(['ip', 'link', 'set', 'dev', str(tap_name), 'mtu',
74 str(mtu_value)]))
75 pipe.wait()
76
77 pipe = _doexec(
78 *(['ip', 'link', 'set', 'dev', str(tap_name), 'up']))
79 pipe.wait()
80
81
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
86
87
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
92
93
94 def username():
95 return pwd.getpwuid(os.getuid())[0]
96
97
98 def usage():
99 print("""\
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.
103
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
108 ovsdb-server.
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 """ % {'prog': sys.argv[0]})
115 sys.exit(0)
116
117
118 class OVSDBException(Exception):
119 pass
120
121
122 class OVSDB(object):
123 @staticmethod
124 def wait_for_db_change(idl):
125 seq = idl.change_seqno
126 stop = time.time() + 10
127 while idl.change_seqno == seq and not idl.run():
128 poller = Poller()
129 idl.wait(poller)
130 poller.block()
131 if time.time() >= stop:
132 raise Exception('Retry Timeout')
133
134 def __init__(self, db_sock):
135 self._db_sock = db_sock
136 self._txn = None
137 schema = self._get_schema()
138 schema.register_all()
139 self._idl_conn = idl.Idl(db_sock, schema)
140 OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB
141
142 def _get_schema(self):
143 error, strm = Stream.open_block(Stream.open(self._db_sock))
144 if error:
145 raise Exception("Unable to connect to %s" % self._db_sock)
146 rpc = jsonrpc.Connection(strm)
147 req = jsonrpc.Message.create_request('get_schema', ['Open_vSwitch'])
148 error, resp = rpc.transact_block(req)
149 rpc.close()
150
151 if error or resp.error:
152 raise Exception('Unable to retrieve schema.')
153 return idl.SchemaHelper(None, resp.result)
154
155 def get_table(self, table_name):
156 return self._idl_conn.tables[table_name]
157
158 def _start_txn(self):
159 if self._txn is not None:
160 raise OVSDBException("ERROR: A transaction was started already")
161 self._idl_conn.change_seqno += 1
162 self._txn = idl.Transaction(self._idl_conn)
163 return self._txn
164
165 def _complete_txn(self, try_again_fn):
166 if self._txn is None:
167 raise OVSDBException("ERROR: Not in a transaction")
168 status = self._txn.commit_block()
169 if status is idl.Transaction.TRY_AGAIN:
170 if self._idl_conn._session.rpc.status != 0:
171 self._idl_conn.force_reconnect()
172 OVSDB.wait_for_db_change(self._idl_conn)
173 return try_again_fn(self)
174 elif status is idl.Transaction.ERROR:
175 return False
176
177 def _find_row(self, table_name, find):
178 return next(
179 (row for row in self.get_table(table_name).rows.values()
180 if find(row)), None)
181
182 def _find_row_by_name(self, table_name, value):
183 return self._find_row(table_name, lambda row: row.name == value)
184
185 def port_exists(self, port_name):
186 return bool(self._find_row_by_name('Port', port_name))
187
188 def port_bridge(self, port_name):
189 try:
190 port = self._find_row_by_name('Port', port_name)
191 br = self._find_row('Bridge', lambda x: port in x.ports)
192 return br.name
193 except Exception:
194 raise OVSDBException('Unable to find port %s bridge' % port_name)
195
196 def interface_mtu(self, intf_name):
197 try:
198 intf = self._find_row_by_name('Interface', intf_name)
199 return intf.mtu[0]
200 except Exception:
201 return None
202
203 def interface_exists(self, intf_name):
204 return bool(self._find_row_by_name('Interface', intf_name))
205
206 def mirror_exists(self, mirror_name):
207 return bool(self._find_row_by_name('Mirror', mirror_name))
208
209 def interface_uuid(self, intf_name):
210 row = self._find_row_by_name('Interface', intf_name)
211 if bool(row):
212 return row.uuid
213 raise OVSDBException('No such interface: %s' % intf_name)
214
215 def make_interface(self, intf_name, execute_transaction=True):
216 if self.interface_exists(intf_name):
217 print("INFO: Interface exists.")
218 return self.interface_uuid(intf_name)
219
220 txn = self._start_txn()
221 tmp_row = txn.insert(self.get_table('Interface'))
222 tmp_row.name = intf_name
223
224 def try_again(db_entity):
225 db_entity.make_interface(intf_name)
226
227 if not execute_transaction:
228 return tmp_row
229
230 txn.add_comment("ovs-tcpdump: user=%s,create_intf=%s"
231 % (username(), intf_name))
232 status = self._complete_txn(try_again)
233 if status is False:
234 raise OVSDBException('Unable to create Interface %s: %s' %
235 (intf_name, txn.get_error()))
236 result = txn.get_insert_uuid(tmp_row.uuid)
237 self._txn = None
238 return result
239
240 def destroy_port(self, port_name, bridge_name):
241 if not self.interface_exists(port_name):
242 return
243 txn = self._start_txn()
244 br = self._find_row_by_name('Bridge', bridge_name)
245 ports = [port for port in br.ports if port.name != port_name]
246 br.ports = ports
247
248 def try_again(db_entity):
249 db_entity.destroy_port(port_name)
250
251 txn.add_comment("ovs-tcpdump: user=%s,destroy_port=%s"
252 % (username(), port_name))
253 status = self._complete_txn(try_again)
254 if status is False:
255 raise OVSDBException('unable to delete Port %s: %s' %
256 (port_name, txn.get_error()))
257 self._txn = None
258
259 def destroy_mirror(self, intf_name, bridge_name):
260 mirror_name = 'm_%s' % intf_name
261 if not self.mirror_exists(mirror_name):
262 return
263 txn = self._start_txn()
264 mirror_row = self._find_row_by_name('Mirror', mirror_name)
265 br = self._find_row_by_name('Bridge', bridge_name)
266 mirrors = [mirror for mirror in br.mirrors
267 if mirror.uuid != mirror_row.uuid]
268 br.mirrors = mirrors
269
270 def try_again(db_entity):
271 db_entity.destroy_mirror(mirror_name, bridge_name)
272
273 txn.add_comment("ovs-tcpdump: user=%s,destroy_mirror=%s"
274 % (username(), mirror_name))
275 status = self._complete_txn(try_again)
276 if status is False:
277 raise OVSDBException('Unable to delete Mirror %s: %s' %
278 (mirror_name, txn.get_error()))
279 self._txn = None
280
281 def make_port(self, port_name, bridge_name):
282 iface_row = self.make_interface(port_name, False)
283 txn = self._txn
284
285 br = self._find_row_by_name('Bridge', bridge_name)
286 if not br:
287 raise OVSDBException('Bad bridge name %s' % bridge_name)
288
289 port = txn.insert(self.get_table('Port'))
290 port.name = port_name
291
292 br.verify('ports')
293 ports = getattr(br, 'ports', [])
294 ports.append(port)
295 br.ports = ports
296
297 port.verify('interfaces')
298 ifaces = getattr(port, 'interfaces', [])
299 ifaces.append(iface_row)
300 port.interfaces = ifaces
301
302 def try_again(db_entity):
303 db_entity.make_port(port_name, bridge_name)
304
305 txn.add_comment("ovs-tcpdump: user=%s,create_port=%s"
306 % (username(), port_name))
307 status = self._complete_txn(try_again)
308 if status is False:
309 raise OVSDBException('Unable to create Port %s: %s' %
310 (port_name, txn.get_error()))
311 result = txn.get_insert_uuid(port.uuid)
312 self._txn = None
313 return result
314
315 def bridge_mirror(self, intf_name, mirror_intf_name, br_name):
316
317 txn = self._start_txn()
318 mirror = txn.insert(self.get_table('Mirror'))
319 mirror.name = 'm_%s' % intf_name
320
321 mirror.select_all = False
322
323 mirrored_port = self._find_row_by_name('Port', intf_name)
324
325 mirror.verify('select_dst_port')
326 dst_port = getattr(mirror, 'select_dst_port', [])
327 dst_port.append(mirrored_port)
328 mirror.select_dst_port = dst_port
329
330 mirror.verify('select_src_port')
331 src_port = getattr(mirror, 'select_src_port', [])
332 src_port.append(mirrored_port)
333 mirror.select_src_port = src_port
334
335 output_port = self._find_row_by_name('Port', mirror_intf_name)
336
337 mirror.verify('output_port')
338 out_port = getattr(mirror, 'output_port', [])
339 out_port.append(output_port.uuid)
340 mirror.output_port = out_port
341
342 br = self._find_row_by_name('Bridge', br_name)
343 br.verify('mirrors')
344 mirrors = getattr(br, 'mirrors', [])
345 mirrors.append(mirror.uuid)
346 br.mirrors = mirrors
347
348 def try_again(db_entity):
349 db_entity.bridge_mirror(intf_name, mirror_intf_name, br_name)
350
351 txn.add_comment("ovs-tcpdump: user=%s,create_mirror=%s"
352 % (username(), mirror.name))
353 status = self._complete_txn(try_again)
354 if status is False:
355 raise OVSDBException('Unable to create Mirror %s: %s' %
356 (mirror_intf_name, txn.get_error()))
357 result = txn.get_insert_uuid(mirror.uuid)
358 self._txn = None
359 return result
360
361
362 def argv_tuples(lst):
363 cur, nxt = iter(lst), iter(lst)
364 next(nxt, None)
365
366 try:
367 while True:
368 yield next(cur), next(nxt, None)
369 except StopIteration:
370 pass
371
372
373 def main():
374 db_sock = 'unix:@RUNDIR@/db.sock'
375 interface = None
376 tcpdargs = []
377
378 skip_next = False
379 mirror_interface = None
380 dump_cmd = 'tcpdump'
381
382 for cur, nxt in argv_tuples(sys.argv[1:]):
383 if skip_next:
384 skip_next = False
385 continue
386 if cur in ['-h', '--help']:
387 usage()
388 elif cur in ['-V', '--version']:
389 print("ovs-tcpdump (Open vSwitch) @VERSION@")
390 sys.exit(0)
391 elif cur in ['--db-sock']:
392 db_sock = nxt
393 skip_next = True
394 continue
395 elif cur in ['--dump-cmd']:
396 dump_cmd = nxt
397 skip_next = True
398 continue
399 elif cur in ['-i', '--interface']:
400 interface = nxt
401 skip_next = True
402 continue
403 elif cur in ['--mirror-to']:
404 mirror_interface = nxt
405 skip_next = True
406 continue
407 tcpdargs.append(cur)
408
409 if interface is None:
410 print("Error: must at least specify an interface with '-i' option")
411 sys.exit(1)
412
413 if '-l' not in tcpdargs:
414 tcpdargs.insert(0, '-l')
415
416 if '-vv' in tcpdargs:
417 print("TCPDUMP Args: %s" % ' '.join(tcpdargs))
418
419 ovsdb = OVSDB(db_sock)
420 if mirror_interface is None:
421 mirror_interface = "mi%s" % interface
422 if sys.platform in _make_mirror_name:
423 mirror_interface = _make_mirror_name[sys.platform](interface)
424
425 if sys.platform in _make_taps and \
426 mirror_interface not in netifaces.interfaces():
427 _make_taps[sys.platform](mirror_interface,
428 ovsdb.interface_mtu(interface))
429
430 if mirror_interface not in netifaces.interfaces():
431 print("ERROR: Please create an interface called `%s`" %
432 mirror_interface)
433 print("See your OS guide for how to do this.")
434 print("Ex: ip link add %s type veth peer name %s" %
435 (mirror_interface, mirror_interface + "2"))
436 sys.exit(1)
437
438 if not ovsdb.port_exists(interface):
439 print("ERROR: Port %s does not exist." % interface)
440 sys.exit(1)
441 if ovsdb.port_exists(mirror_interface):
442 print("ERROR: Mirror port (%s) exists for port %s." %
443 (mirror_interface, interface))
444 sys.exit(1)
445 try:
446 ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface))
447 ovsdb.bridge_mirror(interface, mirror_interface,
448 ovsdb.port_bridge(interface))
449 except OVSDBException as oe:
450 print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
451 try:
452 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
453 except Exception:
454 pass
455 sys.exit(1)
456
457 pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
458 try:
459 while pipes.poll() is None:
460 data = pipes.stdout.readline().strip(b'\n')
461 if len(data) == 0:
462 raise KeyboardInterrupt
463 print(data.decode('utf-8'))
464 raise KeyboardInterrupt
465 except KeyboardInterrupt:
466 if pipes.poll() is None:
467 pipes.terminate()
468
469 ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
470 ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
471 except Exception:
472 print("Unable to tear down the ports and mirrors.")
473 print("Please use ovs-vsctl to remove the ports and mirrors created.")
474 print(" ex: ovs-vsctl --db=%s del-port %s" % (db_sock,
475 mirror_interface))
476 sys.exit(1)
477
478 sys.exit(0)
479
480
481 if __name__ == '__main__':
482 main()
483
484 # Local variables:
485 # mode: python
486 # End: